14e85e535SNicolas Saenz Julienne // SPDX-License-Identifier: GPL-2.0+ 24e85e535SNicolas Saenz Julienne /* 34e85e535SNicolas Saenz Julienne * Raspberry Pi driver for firmware controlled clocks 44e85e535SNicolas Saenz Julienne * 54e85e535SNicolas Saenz Julienne * Even though clk-bcm2835 provides an interface to the hardware registers for 64e85e535SNicolas Saenz Julienne * the system clocks we've had to factor out 'pllb' as the firmware 'owns' it. 74e85e535SNicolas Saenz Julienne * We're not allowed to change it directly as we might race with the 84e85e535SNicolas Saenz Julienne * over-temperature and under-voltage protections provided by the firmware. 94e85e535SNicolas Saenz Julienne * 104e85e535SNicolas Saenz Julienne * Copyright (C) 2019 Nicolas Saenz Julienne <nsaenzjulienne@suse.de> 114e85e535SNicolas Saenz Julienne */ 124e85e535SNicolas Saenz Julienne 134e85e535SNicolas Saenz Julienne #include <linux/clkdev.h> 144e85e535SNicolas Saenz Julienne #include <linux/clk-provider.h> 154e85e535SNicolas Saenz Julienne #include <linux/io.h> 164e85e535SNicolas Saenz Julienne #include <linux/module.h> 174e85e535SNicolas Saenz Julienne #include <linux/platform_device.h> 184e85e535SNicolas Saenz Julienne 194e85e535SNicolas Saenz Julienne #include <soc/bcm2835/raspberrypi-firmware.h> 204e85e535SNicolas Saenz Julienne 214e85e535SNicolas Saenz Julienne #define RPI_FIRMWARE_ARM_CLK_ID 0x00000003 224e85e535SNicolas Saenz Julienne 234e85e535SNicolas Saenz Julienne #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0) 244e85e535SNicolas Saenz Julienne #define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1) 254e85e535SNicolas Saenz Julienne 264e85e535SNicolas Saenz Julienne /* 274e85e535SNicolas Saenz Julienne * Even though the firmware interface alters 'pllb' the frequencies are 284e85e535SNicolas Saenz Julienne * provided as per 'pllb_arm'. We need to scale before passing them trough. 294e85e535SNicolas Saenz Julienne */ 304e85e535SNicolas Saenz Julienne #define RPI_FIRMWARE_PLLB_ARM_DIV_RATE 2 314e85e535SNicolas Saenz Julienne 324e85e535SNicolas Saenz Julienne #define A2W_PLL_FRAC_BITS 20 334e85e535SNicolas Saenz Julienne 344e85e535SNicolas Saenz Julienne struct raspberrypi_clk { 354e85e535SNicolas Saenz Julienne struct device *dev; 364e85e535SNicolas Saenz Julienne struct rpi_firmware *firmware; 37e2bb1834SNicolas Saenz Julienne struct platform_device *cpufreq; 38f922c560SMaxime Ripard }; 394e85e535SNicolas Saenz Julienne 40f922c560SMaxime Ripard struct raspberrypi_clk_data { 41f922c560SMaxime Ripard struct clk_hw hw; 428a1f3ebcSMaxime Ripard 438a1f3ebcSMaxime Ripard unsigned int id; 448a1f3ebcSMaxime Ripard 45f922c560SMaxime Ripard struct raspberrypi_clk *rpi; 464e85e535SNicolas Saenz Julienne }; 474e85e535SNicolas Saenz Julienne 484e85e535SNicolas Saenz Julienne /* 494e85e535SNicolas Saenz Julienne * Structure of the message passed to Raspberry Pi's firmware in order to 504e85e535SNicolas Saenz Julienne * change clock rates. The 'disable_turbo' option is only available to the ARM 514e85e535SNicolas Saenz Julienne * clock (pllb) which we enable by default as turbo mode will alter multiple 524e85e535SNicolas Saenz Julienne * clocks at once. 534e85e535SNicolas Saenz Julienne * 544e85e535SNicolas Saenz Julienne * Even though we're able to access the clock registers directly we're bound to 554e85e535SNicolas Saenz Julienne * use the firmware interface as the firmware ultimately takes care of 564e85e535SNicolas Saenz Julienne * mitigating overheating/undervoltage situations and we would be changing 574e85e535SNicolas Saenz Julienne * frequencies behind his back. 584e85e535SNicolas Saenz Julienne * 594e85e535SNicolas Saenz Julienne * For more information on the firmware interface check: 604e85e535SNicolas Saenz Julienne * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface 614e85e535SNicolas Saenz Julienne */ 624e85e535SNicolas Saenz Julienne struct raspberrypi_firmware_prop { 634e85e535SNicolas Saenz Julienne __le32 id; 644e85e535SNicolas Saenz Julienne __le32 val; 654e85e535SNicolas Saenz Julienne __le32 disable_turbo; 664e85e535SNicolas Saenz Julienne } __packed; 674e85e535SNicolas Saenz Julienne 68*81df0151SMaxime Ripard static int raspberrypi_clock_property(struct rpi_firmware *firmware, 69*81df0151SMaxime Ripard const struct raspberrypi_clk_data *data, 70*81df0151SMaxime Ripard u32 tag, u32 *val) 714e85e535SNicolas Saenz Julienne { 724e85e535SNicolas Saenz Julienne struct raspberrypi_firmware_prop msg = { 73*81df0151SMaxime Ripard .id = cpu_to_le32(data->id), 744e85e535SNicolas Saenz Julienne .val = cpu_to_le32(*val), 754e85e535SNicolas Saenz Julienne .disable_turbo = cpu_to_le32(1), 764e85e535SNicolas Saenz Julienne }; 774e85e535SNicolas Saenz Julienne int ret; 784e85e535SNicolas Saenz Julienne 794e85e535SNicolas Saenz Julienne ret = rpi_firmware_property(firmware, tag, &msg, sizeof(msg)); 804e85e535SNicolas Saenz Julienne if (ret) 814e85e535SNicolas Saenz Julienne return ret; 824e85e535SNicolas Saenz Julienne 834e85e535SNicolas Saenz Julienne *val = le32_to_cpu(msg.val); 844e85e535SNicolas Saenz Julienne 854e85e535SNicolas Saenz Julienne return 0; 864e85e535SNicolas Saenz Julienne } 874e85e535SNicolas Saenz Julienne 884e85e535SNicolas Saenz Julienne static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) 894e85e535SNicolas Saenz Julienne { 90f922c560SMaxime Ripard struct raspberrypi_clk_data *data = 91f922c560SMaxime Ripard container_of(hw, struct raspberrypi_clk_data, hw); 92f922c560SMaxime Ripard struct raspberrypi_clk *rpi = data->rpi; 934e85e535SNicolas Saenz Julienne u32 val = 0; 944e85e535SNicolas Saenz Julienne int ret; 954e85e535SNicolas Saenz Julienne 96*81df0151SMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 97*81df0151SMaxime Ripard RPI_FIRMWARE_GET_CLOCK_STATE, &val); 984e85e535SNicolas Saenz Julienne if (ret) 994e85e535SNicolas Saenz Julienne return 0; 1004e85e535SNicolas Saenz Julienne 1014e85e535SNicolas Saenz Julienne return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT); 1024e85e535SNicolas Saenz Julienne } 1034e85e535SNicolas Saenz Julienne 1044e85e535SNicolas Saenz Julienne 1054e85e535SNicolas Saenz Julienne static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, 1064e85e535SNicolas Saenz Julienne unsigned long parent_rate) 1074e85e535SNicolas Saenz Julienne { 108f922c560SMaxime Ripard struct raspberrypi_clk_data *data = 109f922c560SMaxime Ripard container_of(hw, struct raspberrypi_clk_data, hw); 110f922c560SMaxime Ripard struct raspberrypi_clk *rpi = data->rpi; 1114e85e535SNicolas Saenz Julienne u32 val = 0; 1124e85e535SNicolas Saenz Julienne int ret; 1134e85e535SNicolas Saenz Julienne 114*81df0151SMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 115*81df0151SMaxime Ripard RPI_FIRMWARE_GET_CLOCK_RATE, &val); 1164e85e535SNicolas Saenz Julienne if (ret) 1174e85e535SNicolas Saenz Julienne return ret; 1184e85e535SNicolas Saenz Julienne 1194e85e535SNicolas Saenz Julienne return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; 1204e85e535SNicolas Saenz Julienne } 1214e85e535SNicolas Saenz Julienne 1224e85e535SNicolas Saenz Julienne static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, 1234e85e535SNicolas Saenz Julienne unsigned long parent_rate) 1244e85e535SNicolas Saenz Julienne { 125f922c560SMaxime Ripard struct raspberrypi_clk_data *data = 126f922c560SMaxime Ripard container_of(hw, struct raspberrypi_clk_data, hw); 127f922c560SMaxime Ripard struct raspberrypi_clk *rpi = data->rpi; 1284e85e535SNicolas Saenz Julienne u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; 1294e85e535SNicolas Saenz Julienne int ret; 1304e85e535SNicolas Saenz Julienne 131*81df0151SMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 1324e85e535SNicolas Saenz Julienne RPI_FIRMWARE_SET_CLOCK_RATE, 133*81df0151SMaxime Ripard &new_rate); 1344e85e535SNicolas Saenz Julienne if (ret) 1354e85e535SNicolas Saenz Julienne dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", 1364e85e535SNicolas Saenz Julienne clk_hw_get_name(hw), ret); 1374e85e535SNicolas Saenz Julienne 1384e85e535SNicolas Saenz Julienne return ret; 1394e85e535SNicolas Saenz Julienne } 1404e85e535SNicolas Saenz Julienne 1414e85e535SNicolas Saenz Julienne /* 1424e85e535SNicolas Saenz Julienne * Sadly there is no firmware rate rounding interface. We borrowed it from 1434e85e535SNicolas Saenz Julienne * clk-bcm2835. 1444e85e535SNicolas Saenz Julienne */ 1454e85e535SNicolas Saenz Julienne static int raspberrypi_pll_determine_rate(struct clk_hw *hw, 1464e85e535SNicolas Saenz Julienne struct clk_rate_request *req) 1474e85e535SNicolas Saenz Julienne { 1484e85e535SNicolas Saenz Julienne u64 div, final_rate; 1494e85e535SNicolas Saenz Julienne u32 ndiv, fdiv; 1504e85e535SNicolas Saenz Julienne 1514e85e535SNicolas Saenz Julienne /* We can't use req->rate directly as it would overflow */ 152df4b6a4cSMaxime Ripard final_rate = clamp(req->rate, req->min_rate, req->max_rate); 1534e85e535SNicolas Saenz Julienne 1544e85e535SNicolas Saenz Julienne div = (u64)final_rate << A2W_PLL_FRAC_BITS; 1554e85e535SNicolas Saenz Julienne do_div(div, req->best_parent_rate); 1564e85e535SNicolas Saenz Julienne 1574e85e535SNicolas Saenz Julienne ndiv = div >> A2W_PLL_FRAC_BITS; 1584e85e535SNicolas Saenz Julienne fdiv = div & ((1 << A2W_PLL_FRAC_BITS) - 1); 1594e85e535SNicolas Saenz Julienne 1604e85e535SNicolas Saenz Julienne final_rate = ((u64)req->best_parent_rate * 1614e85e535SNicolas Saenz Julienne ((ndiv << A2W_PLL_FRAC_BITS) + fdiv)); 1624e85e535SNicolas Saenz Julienne 1634e85e535SNicolas Saenz Julienne req->rate = final_rate >> A2W_PLL_FRAC_BITS; 1644e85e535SNicolas Saenz Julienne 1654e85e535SNicolas Saenz Julienne return 0; 1664e85e535SNicolas Saenz Julienne } 1674e85e535SNicolas Saenz Julienne 1684e85e535SNicolas Saenz Julienne static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { 1694e85e535SNicolas Saenz Julienne .is_prepared = raspberrypi_fw_pll_is_on, 1704e85e535SNicolas Saenz Julienne .recalc_rate = raspberrypi_fw_pll_get_rate, 1714e85e535SNicolas Saenz Julienne .set_rate = raspberrypi_fw_pll_set_rate, 1724e85e535SNicolas Saenz Julienne .determine_rate = raspberrypi_pll_determine_rate, 1734e85e535SNicolas Saenz Julienne }; 1744e85e535SNicolas Saenz Julienne 1754e85e535SNicolas Saenz Julienne static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) 1764e85e535SNicolas Saenz Julienne { 177f922c560SMaxime Ripard struct raspberrypi_clk_data *data; 178869bd275SMaxime Ripard struct clk_init_data init = {}; 1794e85e535SNicolas Saenz Julienne u32 min_rate = 0, max_rate = 0; 1804e85e535SNicolas Saenz Julienne int ret; 1814e85e535SNicolas Saenz Julienne 182f922c560SMaxime Ripard data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); 183f922c560SMaxime Ripard if (!data) 184f922c560SMaxime Ripard return -ENOMEM; 185f922c560SMaxime Ripard data->rpi = rpi; 1868a1f3ebcSMaxime Ripard data->id = RPI_FIRMWARE_ARM_CLK_ID; 1874e85e535SNicolas Saenz Julienne 1884e85e535SNicolas Saenz Julienne /* All of the PLLs derive from the external oscillator. */ 1894e85e535SNicolas Saenz Julienne init.parent_names = (const char *[]){ "osc" }; 1904e85e535SNicolas Saenz Julienne init.num_parents = 1; 1914e85e535SNicolas Saenz Julienne init.name = "pllb"; 1924e85e535SNicolas Saenz Julienne init.ops = &raspberrypi_firmware_pll_clk_ops; 1934e85e535SNicolas Saenz Julienne init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED; 1944e85e535SNicolas Saenz Julienne 1954e85e535SNicolas Saenz Julienne /* Get min & max rates set by the firmware */ 196*81df0151SMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 1974e85e535SNicolas Saenz Julienne RPI_FIRMWARE_GET_MIN_CLOCK_RATE, 198*81df0151SMaxime Ripard &min_rate); 1994e85e535SNicolas Saenz Julienne if (ret) { 2004e85e535SNicolas Saenz Julienne dev_err(rpi->dev, "Failed to get %s min freq: %d\n", 2014e85e535SNicolas Saenz Julienne init.name, ret); 2024e85e535SNicolas Saenz Julienne return ret; 2034e85e535SNicolas Saenz Julienne } 2044e85e535SNicolas Saenz Julienne 205*81df0151SMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 2064e85e535SNicolas Saenz Julienne RPI_FIRMWARE_GET_MAX_CLOCK_RATE, 207*81df0151SMaxime Ripard &max_rate); 2084e85e535SNicolas Saenz Julienne if (ret) { 2094e85e535SNicolas Saenz Julienne dev_err(rpi->dev, "Failed to get %s max freq: %d\n", 2104e85e535SNicolas Saenz Julienne init.name, ret); 2114e85e535SNicolas Saenz Julienne return ret; 2124e85e535SNicolas Saenz Julienne } 2134e85e535SNicolas Saenz Julienne 2144e85e535SNicolas Saenz Julienne if (!min_rate || !max_rate) { 2154e85e535SNicolas Saenz Julienne dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n", 2164e85e535SNicolas Saenz Julienne min_rate, max_rate); 2174e85e535SNicolas Saenz Julienne return -EINVAL; 2184e85e535SNicolas Saenz Julienne } 2194e85e535SNicolas Saenz Julienne 2204e85e535SNicolas Saenz Julienne dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", 2214e85e535SNicolas Saenz Julienne min_rate, max_rate); 2224e85e535SNicolas Saenz Julienne 223f922c560SMaxime Ripard data->hw.init = &init; 2244e85e535SNicolas Saenz Julienne 225f922c560SMaxime Ripard ret = devm_clk_hw_register(rpi->dev, &data->hw); 226df4b6a4cSMaxime Ripard if (!ret) 227f922c560SMaxime Ripard clk_hw_set_rate_range(&data->hw, 228df4b6a4cSMaxime Ripard min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE, 229df4b6a4cSMaxime Ripard max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE); 230df4b6a4cSMaxime Ripard 231df4b6a4cSMaxime Ripard return ret; 2324e85e535SNicolas Saenz Julienne } 2334e85e535SNicolas Saenz Julienne 23472856a4eSMaxime Ripard static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { 23572856a4eSMaxime Ripard .mult = 1, 23672856a4eSMaxime Ripard .div = 2, 23772856a4eSMaxime Ripard .hw.init = &(struct clk_init_data) { 23872856a4eSMaxime Ripard .name = "pllb_arm", 23972856a4eSMaxime Ripard .parent_names = (const char *[]){ "pllb" }, 24072856a4eSMaxime Ripard .num_parents = 1, 24172856a4eSMaxime Ripard .ops = &clk_fixed_factor_ops, 24272856a4eSMaxime Ripard .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, 24372856a4eSMaxime Ripard }, 24472856a4eSMaxime Ripard }; 24572856a4eSMaxime Ripard 2464e85e535SNicolas Saenz Julienne static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) 2474e85e535SNicolas Saenz Julienne { 24872856a4eSMaxime Ripard int ret; 24972856a4eSMaxime Ripard 250683de186SMaxime Ripard ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); 25172856a4eSMaxime Ripard if (ret) { 2524e85e535SNicolas Saenz Julienne dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); 25372856a4eSMaxime Ripard return ret; 2544e85e535SNicolas Saenz Julienne } 2554e85e535SNicolas Saenz Julienne 2569bd43a61SMaxime Ripard ret = devm_clk_hw_register_clkdev(rpi->dev, 2579bd43a61SMaxime Ripard &raspberrypi_clk_pllb_arm.hw, 258c70011a9SMaxime Ripard NULL, "cpu0"); 25955ee6a99SMaxime Ripard if (ret) { 26055ee6a99SMaxime Ripard dev_err(rpi->dev, "Failed to initialize clkdev\n"); 26155ee6a99SMaxime Ripard return ret; 2624e85e535SNicolas Saenz Julienne } 2634e85e535SNicolas Saenz Julienne 2644e85e535SNicolas Saenz Julienne return 0; 2654e85e535SNicolas Saenz Julienne } 2664e85e535SNicolas Saenz Julienne 2674e85e535SNicolas Saenz Julienne static int raspberrypi_clk_probe(struct platform_device *pdev) 2684e85e535SNicolas Saenz Julienne { 2694e85e535SNicolas Saenz Julienne struct device_node *firmware_node; 2704e85e535SNicolas Saenz Julienne struct device *dev = &pdev->dev; 2714e85e535SNicolas Saenz Julienne struct rpi_firmware *firmware; 2724e85e535SNicolas Saenz Julienne struct raspberrypi_clk *rpi; 2734e85e535SNicolas Saenz Julienne int ret; 2744e85e535SNicolas Saenz Julienne 275fbac2e77SMaxime Ripard /* 276fbac2e77SMaxime Ripard * We can be probed either through the an old-fashioned 277fbac2e77SMaxime Ripard * platform device registration or through a DT node that is a 278fbac2e77SMaxime Ripard * child of the firmware node. Handle both cases. 279fbac2e77SMaxime Ripard */ 280fbac2e77SMaxime Ripard if (dev->of_node) 281fbac2e77SMaxime Ripard firmware_node = of_get_parent(dev->of_node); 282fbac2e77SMaxime Ripard else 2834e85e535SNicolas Saenz Julienne firmware_node = of_find_compatible_node(NULL, NULL, 2844e85e535SNicolas Saenz Julienne "raspberrypi,bcm2835-firmware"); 2854e85e535SNicolas Saenz Julienne if (!firmware_node) { 2864e85e535SNicolas Saenz Julienne dev_err(dev, "Missing firmware node\n"); 2874e85e535SNicolas Saenz Julienne return -ENOENT; 2884e85e535SNicolas Saenz Julienne } 2894e85e535SNicolas Saenz Julienne 2904e85e535SNicolas Saenz Julienne firmware = rpi_firmware_get(firmware_node); 2914e85e535SNicolas Saenz Julienne of_node_put(firmware_node); 2924e85e535SNicolas Saenz Julienne if (!firmware) 2934e85e535SNicolas Saenz Julienne return -EPROBE_DEFER; 2944e85e535SNicolas Saenz Julienne 2954e85e535SNicolas Saenz Julienne rpi = devm_kzalloc(dev, sizeof(*rpi), GFP_KERNEL); 2964e85e535SNicolas Saenz Julienne if (!rpi) 2974e85e535SNicolas Saenz Julienne return -ENOMEM; 2984e85e535SNicolas Saenz Julienne 2994e85e535SNicolas Saenz Julienne rpi->dev = dev; 3004e85e535SNicolas Saenz Julienne rpi->firmware = firmware; 301e2bb1834SNicolas Saenz Julienne platform_set_drvdata(pdev, rpi); 3024e85e535SNicolas Saenz Julienne 3034e85e535SNicolas Saenz Julienne ret = raspberrypi_register_pllb(rpi); 3044e85e535SNicolas Saenz Julienne if (ret) { 3054e85e535SNicolas Saenz Julienne dev_err(dev, "Failed to initialize pllb, %d\n", ret); 3064e85e535SNicolas Saenz Julienne return ret; 3074e85e535SNicolas Saenz Julienne } 3084e85e535SNicolas Saenz Julienne 3094e85e535SNicolas Saenz Julienne ret = raspberrypi_register_pllb_arm(rpi); 3104e85e535SNicolas Saenz Julienne if (ret) 3114e85e535SNicolas Saenz Julienne return ret; 3124e85e535SNicolas Saenz Julienne 313e2bb1834SNicolas Saenz Julienne rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", 314e2bb1834SNicolas Saenz Julienne -1, NULL, 0); 315e2bb1834SNicolas Saenz Julienne 316e2bb1834SNicolas Saenz Julienne return 0; 317e2bb1834SNicolas Saenz Julienne } 318e2bb1834SNicolas Saenz Julienne 319e2bb1834SNicolas Saenz Julienne static int raspberrypi_clk_remove(struct platform_device *pdev) 320e2bb1834SNicolas Saenz Julienne { 321e2bb1834SNicolas Saenz Julienne struct raspberrypi_clk *rpi = platform_get_drvdata(pdev); 322e2bb1834SNicolas Saenz Julienne 323e2bb1834SNicolas Saenz Julienne platform_device_unregister(rpi->cpufreq); 324e2bb1834SNicolas Saenz Julienne 3254e85e535SNicolas Saenz Julienne return 0; 3264e85e535SNicolas Saenz Julienne } 3274e85e535SNicolas Saenz Julienne 328fbac2e77SMaxime Ripard static const struct of_device_id raspberrypi_clk_match[] = { 329fbac2e77SMaxime Ripard { .compatible = "raspberrypi,firmware-clocks" }, 330fbac2e77SMaxime Ripard { }, 331fbac2e77SMaxime Ripard }; 332fbac2e77SMaxime Ripard MODULE_DEVICE_TABLE(of, raspberrypi_clk_match); 333fbac2e77SMaxime Ripard 3344e85e535SNicolas Saenz Julienne static struct platform_driver raspberrypi_clk_driver = { 3354e85e535SNicolas Saenz Julienne .driver = { 3364e85e535SNicolas Saenz Julienne .name = "raspberrypi-clk", 337fbac2e77SMaxime Ripard .of_match_table = raspberrypi_clk_match, 3384e85e535SNicolas Saenz Julienne }, 3394e85e535SNicolas Saenz Julienne .probe = raspberrypi_clk_probe, 340e2bb1834SNicolas Saenz Julienne .remove = raspberrypi_clk_remove, 3414e85e535SNicolas Saenz Julienne }; 3424e85e535SNicolas Saenz Julienne module_platform_driver(raspberrypi_clk_driver); 3434e85e535SNicolas Saenz Julienne 3444e85e535SNicolas Saenz Julienne MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>"); 3454e85e535SNicolas Saenz Julienne MODULE_DESCRIPTION("Raspberry Pi firmware clock driver"); 3464e85e535SNicolas Saenz Julienne MODULE_LICENSE("GPL"); 3474e85e535SNicolas Saenz Julienne MODULE_ALIAS("platform:raspberrypi-clk"); 348