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; 384e85e535SNicolas Saenz Julienne 394e85e535SNicolas Saenz Julienne unsigned long min_rate; 404e85e535SNicolas Saenz Julienne unsigned long max_rate; 414e85e535SNicolas Saenz Julienne 424e85e535SNicolas Saenz Julienne struct clk_hw pllb; 434e85e535SNicolas Saenz Julienne struct clk_lookup *pllb_arm_lookup; 444e85e535SNicolas Saenz Julienne }; 454e85e535SNicolas Saenz Julienne 464e85e535SNicolas Saenz Julienne /* 474e85e535SNicolas Saenz Julienne * Structure of the message passed to Raspberry Pi's firmware in order to 484e85e535SNicolas Saenz Julienne * change clock rates. The 'disable_turbo' option is only available to the ARM 494e85e535SNicolas Saenz Julienne * clock (pllb) which we enable by default as turbo mode will alter multiple 504e85e535SNicolas Saenz Julienne * clocks at once. 514e85e535SNicolas Saenz Julienne * 524e85e535SNicolas Saenz Julienne * Even though we're able to access the clock registers directly we're bound to 534e85e535SNicolas Saenz Julienne * use the firmware interface as the firmware ultimately takes care of 544e85e535SNicolas Saenz Julienne * mitigating overheating/undervoltage situations and we would be changing 554e85e535SNicolas Saenz Julienne * frequencies behind his back. 564e85e535SNicolas Saenz Julienne * 574e85e535SNicolas Saenz Julienne * For more information on the firmware interface check: 584e85e535SNicolas Saenz Julienne * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface 594e85e535SNicolas Saenz Julienne */ 604e85e535SNicolas Saenz Julienne struct raspberrypi_firmware_prop { 614e85e535SNicolas Saenz Julienne __le32 id; 624e85e535SNicolas Saenz Julienne __le32 val; 634e85e535SNicolas Saenz Julienne __le32 disable_turbo; 644e85e535SNicolas Saenz Julienne } __packed; 654e85e535SNicolas Saenz Julienne 664e85e535SNicolas Saenz Julienne static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag, 674e85e535SNicolas Saenz Julienne u32 clk, u32 *val) 684e85e535SNicolas Saenz Julienne { 694e85e535SNicolas Saenz Julienne struct raspberrypi_firmware_prop msg = { 704e85e535SNicolas Saenz Julienne .id = cpu_to_le32(clk), 714e85e535SNicolas Saenz Julienne .val = cpu_to_le32(*val), 724e85e535SNicolas Saenz Julienne .disable_turbo = cpu_to_le32(1), 734e85e535SNicolas Saenz Julienne }; 744e85e535SNicolas Saenz Julienne int ret; 754e85e535SNicolas Saenz Julienne 764e85e535SNicolas Saenz Julienne ret = rpi_firmware_property(firmware, tag, &msg, sizeof(msg)); 774e85e535SNicolas Saenz Julienne if (ret) 784e85e535SNicolas Saenz Julienne return ret; 794e85e535SNicolas Saenz Julienne 804e85e535SNicolas Saenz Julienne *val = le32_to_cpu(msg.val); 814e85e535SNicolas Saenz Julienne 824e85e535SNicolas Saenz Julienne return 0; 834e85e535SNicolas Saenz Julienne } 844e85e535SNicolas Saenz Julienne 854e85e535SNicolas Saenz Julienne static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) 864e85e535SNicolas Saenz Julienne { 874e85e535SNicolas Saenz Julienne struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, 884e85e535SNicolas Saenz Julienne pllb); 894e85e535SNicolas Saenz Julienne u32 val = 0; 904e85e535SNicolas Saenz Julienne int ret; 914e85e535SNicolas Saenz Julienne 924e85e535SNicolas Saenz Julienne ret = raspberrypi_clock_property(rpi->firmware, 934e85e535SNicolas Saenz Julienne RPI_FIRMWARE_GET_CLOCK_STATE, 944e85e535SNicolas Saenz Julienne RPI_FIRMWARE_ARM_CLK_ID, &val); 954e85e535SNicolas Saenz Julienne if (ret) 964e85e535SNicolas Saenz Julienne return 0; 974e85e535SNicolas Saenz Julienne 984e85e535SNicolas Saenz Julienne return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT); 994e85e535SNicolas Saenz Julienne } 1004e85e535SNicolas Saenz Julienne 1014e85e535SNicolas Saenz Julienne 1024e85e535SNicolas Saenz Julienne static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, 1034e85e535SNicolas Saenz Julienne unsigned long parent_rate) 1044e85e535SNicolas Saenz Julienne { 1054e85e535SNicolas Saenz Julienne struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, 1064e85e535SNicolas Saenz Julienne pllb); 1074e85e535SNicolas Saenz Julienne u32 val = 0; 1084e85e535SNicolas Saenz Julienne int ret; 1094e85e535SNicolas Saenz Julienne 1104e85e535SNicolas Saenz Julienne ret = raspberrypi_clock_property(rpi->firmware, 1114e85e535SNicolas Saenz Julienne RPI_FIRMWARE_GET_CLOCK_RATE, 1124e85e535SNicolas Saenz Julienne RPI_FIRMWARE_ARM_CLK_ID, 1134e85e535SNicolas Saenz Julienne &val); 1144e85e535SNicolas Saenz Julienne if (ret) 1154e85e535SNicolas Saenz Julienne return ret; 1164e85e535SNicolas Saenz Julienne 1174e85e535SNicolas Saenz Julienne return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; 1184e85e535SNicolas Saenz Julienne } 1194e85e535SNicolas Saenz Julienne 1204e85e535SNicolas Saenz Julienne static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, 1214e85e535SNicolas Saenz Julienne unsigned long parent_rate) 1224e85e535SNicolas Saenz Julienne { 1234e85e535SNicolas Saenz Julienne struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, 1244e85e535SNicolas Saenz Julienne pllb); 1254e85e535SNicolas Saenz Julienne u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; 1264e85e535SNicolas Saenz Julienne int ret; 1274e85e535SNicolas Saenz Julienne 1284e85e535SNicolas Saenz Julienne ret = raspberrypi_clock_property(rpi->firmware, 1294e85e535SNicolas Saenz Julienne RPI_FIRMWARE_SET_CLOCK_RATE, 1304e85e535SNicolas Saenz Julienne RPI_FIRMWARE_ARM_CLK_ID, 1314e85e535SNicolas Saenz Julienne &new_rate); 1324e85e535SNicolas Saenz Julienne if (ret) 1334e85e535SNicolas Saenz Julienne dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", 1344e85e535SNicolas Saenz Julienne clk_hw_get_name(hw), ret); 1354e85e535SNicolas Saenz Julienne 1364e85e535SNicolas Saenz Julienne return ret; 1374e85e535SNicolas Saenz Julienne } 1384e85e535SNicolas Saenz Julienne 1394e85e535SNicolas Saenz Julienne /* 1404e85e535SNicolas Saenz Julienne * Sadly there is no firmware rate rounding interface. We borrowed it from 1414e85e535SNicolas Saenz Julienne * clk-bcm2835. 1424e85e535SNicolas Saenz Julienne */ 1434e85e535SNicolas Saenz Julienne static int raspberrypi_pll_determine_rate(struct clk_hw *hw, 1444e85e535SNicolas Saenz Julienne struct clk_rate_request *req) 1454e85e535SNicolas Saenz Julienne { 1464e85e535SNicolas Saenz Julienne struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, 1474e85e535SNicolas Saenz Julienne pllb); 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 */ 1524e85e535SNicolas Saenz Julienne final_rate = clamp(req->rate, rpi->min_rate, rpi->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 { 177869bd275SMaxime Ripard struct clk_init_data init = {}; 1784e85e535SNicolas Saenz Julienne u32 min_rate = 0, max_rate = 0; 1794e85e535SNicolas Saenz Julienne int ret; 1804e85e535SNicolas Saenz Julienne 1814e85e535SNicolas Saenz Julienne 1824e85e535SNicolas Saenz Julienne /* All of the PLLs derive from the external oscillator. */ 1834e85e535SNicolas Saenz Julienne init.parent_names = (const char *[]){ "osc" }; 1844e85e535SNicolas Saenz Julienne init.num_parents = 1; 1854e85e535SNicolas Saenz Julienne init.name = "pllb"; 1864e85e535SNicolas Saenz Julienne init.ops = &raspberrypi_firmware_pll_clk_ops; 1874e85e535SNicolas Saenz Julienne init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED; 1884e85e535SNicolas Saenz Julienne 1894e85e535SNicolas Saenz Julienne /* Get min & max rates set by the firmware */ 1904e85e535SNicolas Saenz Julienne ret = raspberrypi_clock_property(rpi->firmware, 1914e85e535SNicolas Saenz Julienne RPI_FIRMWARE_GET_MIN_CLOCK_RATE, 1924e85e535SNicolas Saenz Julienne RPI_FIRMWARE_ARM_CLK_ID, 1934e85e535SNicolas Saenz Julienne &min_rate); 1944e85e535SNicolas Saenz Julienne if (ret) { 1954e85e535SNicolas Saenz Julienne dev_err(rpi->dev, "Failed to get %s min freq: %d\n", 1964e85e535SNicolas Saenz Julienne init.name, ret); 1974e85e535SNicolas Saenz Julienne return ret; 1984e85e535SNicolas Saenz Julienne } 1994e85e535SNicolas Saenz Julienne 2004e85e535SNicolas Saenz Julienne ret = raspberrypi_clock_property(rpi->firmware, 2014e85e535SNicolas Saenz Julienne RPI_FIRMWARE_GET_MAX_CLOCK_RATE, 2024e85e535SNicolas Saenz Julienne RPI_FIRMWARE_ARM_CLK_ID, 2034e85e535SNicolas Saenz Julienne &max_rate); 2044e85e535SNicolas Saenz Julienne if (ret) { 2054e85e535SNicolas Saenz Julienne dev_err(rpi->dev, "Failed to get %s max freq: %d\n", 2064e85e535SNicolas Saenz Julienne init.name, ret); 2074e85e535SNicolas Saenz Julienne return ret; 2084e85e535SNicolas Saenz Julienne } 2094e85e535SNicolas Saenz Julienne 2104e85e535SNicolas Saenz Julienne if (!min_rate || !max_rate) { 2114e85e535SNicolas Saenz Julienne dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n", 2124e85e535SNicolas Saenz Julienne min_rate, max_rate); 2134e85e535SNicolas Saenz Julienne return -EINVAL; 2144e85e535SNicolas Saenz Julienne } 2154e85e535SNicolas Saenz Julienne 2164e85e535SNicolas Saenz Julienne dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", 2174e85e535SNicolas Saenz Julienne min_rate, max_rate); 2184e85e535SNicolas Saenz Julienne 2194e85e535SNicolas Saenz Julienne rpi->min_rate = min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; 2204e85e535SNicolas Saenz Julienne rpi->max_rate = max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; 2214e85e535SNicolas Saenz Julienne 2224e85e535SNicolas Saenz Julienne rpi->pllb.init = &init; 2234e85e535SNicolas Saenz Julienne 2244e85e535SNicolas Saenz Julienne return devm_clk_hw_register(rpi->dev, &rpi->pllb); 2254e85e535SNicolas Saenz Julienne } 2264e85e535SNicolas Saenz Julienne 22772856a4eSMaxime Ripard static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { 22872856a4eSMaxime Ripard .mult = 1, 22972856a4eSMaxime Ripard .div = 2, 23072856a4eSMaxime Ripard .hw.init = &(struct clk_init_data) { 23172856a4eSMaxime Ripard .name = "pllb_arm", 23272856a4eSMaxime Ripard .parent_names = (const char *[]){ "pllb" }, 23372856a4eSMaxime Ripard .num_parents = 1, 23472856a4eSMaxime Ripard .ops = &clk_fixed_factor_ops, 23572856a4eSMaxime Ripard .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, 23672856a4eSMaxime Ripard }, 23772856a4eSMaxime Ripard }; 23872856a4eSMaxime Ripard 2394e85e535SNicolas Saenz Julienne static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) 2404e85e535SNicolas Saenz Julienne { 24172856a4eSMaxime Ripard int ret; 24272856a4eSMaxime Ripard 24372856a4eSMaxime Ripard ret = clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); 24472856a4eSMaxime Ripard if (ret) { 2454e85e535SNicolas Saenz Julienne dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); 24672856a4eSMaxime Ripard return ret; 2474e85e535SNicolas Saenz Julienne } 2484e85e535SNicolas Saenz Julienne 249*c70011a9SMaxime Ripard rpi->pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, 250*c70011a9SMaxime Ripard NULL, "cpu0"); 2514e85e535SNicolas Saenz Julienne if (!rpi->pllb_arm_lookup) { 2524e85e535SNicolas Saenz Julienne dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); 253*c70011a9SMaxime Ripard clk_hw_unregister_fixed_factor(&raspberrypi_clk_pllb_arm.hw); 2544e85e535SNicolas Saenz Julienne return -ENOMEM; 2554e85e535SNicolas Saenz Julienne } 2564e85e535SNicolas Saenz Julienne 2574e85e535SNicolas Saenz Julienne return 0; 2584e85e535SNicolas Saenz Julienne } 2594e85e535SNicolas Saenz Julienne 2604e85e535SNicolas Saenz Julienne static int raspberrypi_clk_probe(struct platform_device *pdev) 2614e85e535SNicolas Saenz Julienne { 2624e85e535SNicolas Saenz Julienne struct device_node *firmware_node; 2634e85e535SNicolas Saenz Julienne struct device *dev = &pdev->dev; 2644e85e535SNicolas Saenz Julienne struct rpi_firmware *firmware; 2654e85e535SNicolas Saenz Julienne struct raspberrypi_clk *rpi; 2664e85e535SNicolas Saenz Julienne int ret; 2674e85e535SNicolas Saenz Julienne 268fbac2e77SMaxime Ripard /* 269fbac2e77SMaxime Ripard * We can be probed either through the an old-fashioned 270fbac2e77SMaxime Ripard * platform device registration or through a DT node that is a 271fbac2e77SMaxime Ripard * child of the firmware node. Handle both cases. 272fbac2e77SMaxime Ripard */ 273fbac2e77SMaxime Ripard if (dev->of_node) 274fbac2e77SMaxime Ripard firmware_node = of_get_parent(dev->of_node); 275fbac2e77SMaxime Ripard else 2764e85e535SNicolas Saenz Julienne firmware_node = of_find_compatible_node(NULL, NULL, 2774e85e535SNicolas Saenz Julienne "raspberrypi,bcm2835-firmware"); 2784e85e535SNicolas Saenz Julienne if (!firmware_node) { 2794e85e535SNicolas Saenz Julienne dev_err(dev, "Missing firmware node\n"); 2804e85e535SNicolas Saenz Julienne return -ENOENT; 2814e85e535SNicolas Saenz Julienne } 2824e85e535SNicolas Saenz Julienne 2834e85e535SNicolas Saenz Julienne firmware = rpi_firmware_get(firmware_node); 2844e85e535SNicolas Saenz Julienne of_node_put(firmware_node); 2854e85e535SNicolas Saenz Julienne if (!firmware) 2864e85e535SNicolas Saenz Julienne return -EPROBE_DEFER; 2874e85e535SNicolas Saenz Julienne 2884e85e535SNicolas Saenz Julienne rpi = devm_kzalloc(dev, sizeof(*rpi), GFP_KERNEL); 2894e85e535SNicolas Saenz Julienne if (!rpi) 2904e85e535SNicolas Saenz Julienne return -ENOMEM; 2914e85e535SNicolas Saenz Julienne 2924e85e535SNicolas Saenz Julienne rpi->dev = dev; 2934e85e535SNicolas Saenz Julienne rpi->firmware = firmware; 294e2bb1834SNicolas Saenz Julienne platform_set_drvdata(pdev, rpi); 2954e85e535SNicolas Saenz Julienne 2964e85e535SNicolas Saenz Julienne ret = raspberrypi_register_pllb(rpi); 2974e85e535SNicolas Saenz Julienne if (ret) { 2984e85e535SNicolas Saenz Julienne dev_err(dev, "Failed to initialize pllb, %d\n", ret); 2994e85e535SNicolas Saenz Julienne return ret; 3004e85e535SNicolas Saenz Julienne } 3014e85e535SNicolas Saenz Julienne 3024e85e535SNicolas Saenz Julienne ret = raspberrypi_register_pllb_arm(rpi); 3034e85e535SNicolas Saenz Julienne if (ret) 3044e85e535SNicolas Saenz Julienne return ret; 3054e85e535SNicolas Saenz Julienne 306e2bb1834SNicolas Saenz Julienne rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", 307e2bb1834SNicolas Saenz Julienne -1, NULL, 0); 308e2bb1834SNicolas Saenz Julienne 309e2bb1834SNicolas Saenz Julienne return 0; 310e2bb1834SNicolas Saenz Julienne } 311e2bb1834SNicolas Saenz Julienne 312e2bb1834SNicolas Saenz Julienne static int raspberrypi_clk_remove(struct platform_device *pdev) 313e2bb1834SNicolas Saenz Julienne { 314e2bb1834SNicolas Saenz Julienne struct raspberrypi_clk *rpi = platform_get_drvdata(pdev); 315e2bb1834SNicolas Saenz Julienne 316e2bb1834SNicolas Saenz Julienne platform_device_unregister(rpi->cpufreq); 317e2bb1834SNicolas Saenz Julienne 3184e85e535SNicolas Saenz Julienne return 0; 3194e85e535SNicolas Saenz Julienne } 3204e85e535SNicolas Saenz Julienne 321fbac2e77SMaxime Ripard static const struct of_device_id raspberrypi_clk_match[] = { 322fbac2e77SMaxime Ripard { .compatible = "raspberrypi,firmware-clocks" }, 323fbac2e77SMaxime Ripard { }, 324fbac2e77SMaxime Ripard }; 325fbac2e77SMaxime Ripard MODULE_DEVICE_TABLE(of, raspberrypi_clk_match); 326fbac2e77SMaxime Ripard 3274e85e535SNicolas Saenz Julienne static struct platform_driver raspberrypi_clk_driver = { 3284e85e535SNicolas Saenz Julienne .driver = { 3294e85e535SNicolas Saenz Julienne .name = "raspberrypi-clk", 330fbac2e77SMaxime Ripard .of_match_table = raspberrypi_clk_match, 3314e85e535SNicolas Saenz Julienne }, 3324e85e535SNicolas Saenz Julienne .probe = raspberrypi_clk_probe, 333e2bb1834SNicolas Saenz Julienne .remove = raspberrypi_clk_remove, 3344e85e535SNicolas Saenz Julienne }; 3354e85e535SNicolas Saenz Julienne module_platform_driver(raspberrypi_clk_driver); 3364e85e535SNicolas Saenz Julienne 3374e85e535SNicolas Saenz Julienne MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>"); 3384e85e535SNicolas Saenz Julienne MODULE_DESCRIPTION("Raspberry Pi firmware clock driver"); 3394e85e535SNicolas Saenz Julienne MODULE_LICENSE("GPL"); 3404e85e535SNicolas Saenz Julienne MODULE_ALIAS("platform:raspberrypi-clk"); 341