18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Raspberry Pi driver for firmware controlled clocks 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Even though clk-bcm2835 provides an interface to the hardware registers for 68c2ecf20Sopenharmony_ci * the system clocks we've had to factor out 'pllb' as the firmware 'owns' it. 78c2ecf20Sopenharmony_ci * We're not allowed to change it directly as we might race with the 88c2ecf20Sopenharmony_ci * over-temperature and under-voltage protections provided by the firmware. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2019 Nicolas Saenz Julienne <nsaenzjulienne@suse.de> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 148c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <soc/bcm2835/raspberrypi-firmware.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cienum rpi_firmware_clk_id { 228c2ecf20Sopenharmony_ci RPI_FIRMWARE_EMMC_CLK_ID = 1, 238c2ecf20Sopenharmony_ci RPI_FIRMWARE_UART_CLK_ID, 248c2ecf20Sopenharmony_ci RPI_FIRMWARE_ARM_CLK_ID, 258c2ecf20Sopenharmony_ci RPI_FIRMWARE_CORE_CLK_ID, 268c2ecf20Sopenharmony_ci RPI_FIRMWARE_V3D_CLK_ID, 278c2ecf20Sopenharmony_ci RPI_FIRMWARE_H264_CLK_ID, 288c2ecf20Sopenharmony_ci RPI_FIRMWARE_ISP_CLK_ID, 298c2ecf20Sopenharmony_ci RPI_FIRMWARE_SDRAM_CLK_ID, 308c2ecf20Sopenharmony_ci RPI_FIRMWARE_PIXEL_CLK_ID, 318c2ecf20Sopenharmony_ci RPI_FIRMWARE_PWM_CLK_ID, 328c2ecf20Sopenharmony_ci RPI_FIRMWARE_HEVC_CLK_ID, 338c2ecf20Sopenharmony_ci RPI_FIRMWARE_EMMC2_CLK_ID, 348c2ecf20Sopenharmony_ci RPI_FIRMWARE_M2MC_CLK_ID, 358c2ecf20Sopenharmony_ci RPI_FIRMWARE_PIXEL_BVB_CLK_ID, 368c2ecf20Sopenharmony_ci RPI_FIRMWARE_NUM_CLK_ID, 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic char *rpi_firmware_clk_names[] = { 408c2ecf20Sopenharmony_ci [RPI_FIRMWARE_EMMC_CLK_ID] = "emmc", 418c2ecf20Sopenharmony_ci [RPI_FIRMWARE_UART_CLK_ID] = "uart", 428c2ecf20Sopenharmony_ci [RPI_FIRMWARE_ARM_CLK_ID] = "arm", 438c2ecf20Sopenharmony_ci [RPI_FIRMWARE_CORE_CLK_ID] = "core", 448c2ecf20Sopenharmony_ci [RPI_FIRMWARE_V3D_CLK_ID] = "v3d", 458c2ecf20Sopenharmony_ci [RPI_FIRMWARE_H264_CLK_ID] = "h264", 468c2ecf20Sopenharmony_ci [RPI_FIRMWARE_ISP_CLK_ID] = "isp", 478c2ecf20Sopenharmony_ci [RPI_FIRMWARE_SDRAM_CLK_ID] = "sdram", 488c2ecf20Sopenharmony_ci [RPI_FIRMWARE_PIXEL_CLK_ID] = "pixel", 498c2ecf20Sopenharmony_ci [RPI_FIRMWARE_PWM_CLK_ID] = "pwm", 508c2ecf20Sopenharmony_ci [RPI_FIRMWARE_HEVC_CLK_ID] = "hevc", 518c2ecf20Sopenharmony_ci [RPI_FIRMWARE_EMMC2_CLK_ID] = "emmc2", 528c2ecf20Sopenharmony_ci [RPI_FIRMWARE_M2MC_CLK_ID] = "m2mc", 538c2ecf20Sopenharmony_ci [RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb", 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0) 578c2ecf20Sopenharmony_ci#define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct raspberrypi_clk { 608c2ecf20Sopenharmony_ci struct device *dev; 618c2ecf20Sopenharmony_ci struct rpi_firmware *firmware; 628c2ecf20Sopenharmony_ci struct platform_device *cpufreq; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct raspberrypi_clk_data { 668c2ecf20Sopenharmony_ci struct clk_hw hw; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci unsigned int id; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci struct raspberrypi_clk *rpi; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* 748c2ecf20Sopenharmony_ci * Structure of the message passed to Raspberry Pi's firmware in order to 758c2ecf20Sopenharmony_ci * change clock rates. The 'disable_turbo' option is only available to the ARM 768c2ecf20Sopenharmony_ci * clock (pllb) which we enable by default as turbo mode will alter multiple 778c2ecf20Sopenharmony_ci * clocks at once. 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * Even though we're able to access the clock registers directly we're bound to 808c2ecf20Sopenharmony_ci * use the firmware interface as the firmware ultimately takes care of 818c2ecf20Sopenharmony_ci * mitigating overheating/undervoltage situations and we would be changing 828c2ecf20Sopenharmony_ci * frequencies behind his back. 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * For more information on the firmware interface check: 858c2ecf20Sopenharmony_ci * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_cistruct raspberrypi_firmware_prop { 888c2ecf20Sopenharmony_ci __le32 id; 898c2ecf20Sopenharmony_ci __le32 val; 908c2ecf20Sopenharmony_ci __le32 disable_turbo; 918c2ecf20Sopenharmony_ci} __packed; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int raspberrypi_clock_property(struct rpi_firmware *firmware, 948c2ecf20Sopenharmony_ci const struct raspberrypi_clk_data *data, 958c2ecf20Sopenharmony_ci u32 tag, u32 *val) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct raspberrypi_firmware_prop msg = { 988c2ecf20Sopenharmony_ci .id = cpu_to_le32(data->id), 998c2ecf20Sopenharmony_ci .val = cpu_to_le32(*val), 1008c2ecf20Sopenharmony_ci .disable_turbo = cpu_to_le32(1), 1018c2ecf20Sopenharmony_ci }; 1028c2ecf20Sopenharmony_ci int ret; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ret = rpi_firmware_property(firmware, tag, &msg, sizeof(msg)); 1058c2ecf20Sopenharmony_ci if (ret) 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci *val = le32_to_cpu(msg.val); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int raspberrypi_fw_is_prepared(struct clk_hw *hw) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct raspberrypi_clk_data *data = 1168c2ecf20Sopenharmony_ci container_of(hw, struct raspberrypi_clk_data, hw); 1178c2ecf20Sopenharmony_ci struct raspberrypi_clk *rpi = data->rpi; 1188c2ecf20Sopenharmony_ci u32 val = 0; 1198c2ecf20Sopenharmony_ci int ret; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci ret = raspberrypi_clock_property(rpi->firmware, data, 1228c2ecf20Sopenharmony_ci RPI_FIRMWARE_GET_CLOCK_STATE, &val); 1238c2ecf20Sopenharmony_ci if (ret) 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw, 1318c2ecf20Sopenharmony_ci unsigned long parent_rate) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct raspberrypi_clk_data *data = 1348c2ecf20Sopenharmony_ci container_of(hw, struct raspberrypi_clk_data, hw); 1358c2ecf20Sopenharmony_ci struct raspberrypi_clk *rpi = data->rpi; 1368c2ecf20Sopenharmony_ci u32 val = 0; 1378c2ecf20Sopenharmony_ci int ret; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci ret = raspberrypi_clock_property(rpi->firmware, data, 1408c2ecf20Sopenharmony_ci RPI_FIRMWARE_GET_CLOCK_RATE, &val); 1418c2ecf20Sopenharmony_ci if (ret) 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return val; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate, 1488c2ecf20Sopenharmony_ci unsigned long parent_rate) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct raspberrypi_clk_data *data = 1518c2ecf20Sopenharmony_ci container_of(hw, struct raspberrypi_clk_data, hw); 1528c2ecf20Sopenharmony_ci struct raspberrypi_clk *rpi = data->rpi; 1538c2ecf20Sopenharmony_ci u32 _rate = rate; 1548c2ecf20Sopenharmony_ci int ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ret = raspberrypi_clock_property(rpi->firmware, data, 1578c2ecf20Sopenharmony_ci RPI_FIRMWARE_SET_CLOCK_RATE, &_rate); 1588c2ecf20Sopenharmony_ci if (ret) 1598c2ecf20Sopenharmony_ci dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d\n", 1608c2ecf20Sopenharmony_ci clk_hw_get_name(hw), ret); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return ret; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, 1668c2ecf20Sopenharmony_ci struct clk_rate_request *req) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci /* 1698c2ecf20Sopenharmony_ci * The firmware will do the rounding but that isn't part of 1708c2ecf20Sopenharmony_ci * the interface with the firmware, so we just do our best 1718c2ecf20Sopenharmony_ci * here. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci req->rate = clamp(req->rate, req->min_rate, req->max_rate); 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic const struct clk_ops raspberrypi_firmware_clk_ops = { 1788c2ecf20Sopenharmony_ci .is_prepared = raspberrypi_fw_is_prepared, 1798c2ecf20Sopenharmony_ci .recalc_rate = raspberrypi_fw_get_rate, 1808c2ecf20Sopenharmony_ci .determine_rate = raspberrypi_fw_dumb_determine_rate, 1818c2ecf20Sopenharmony_ci .set_rate = raspberrypi_fw_set_rate, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi, 1858c2ecf20Sopenharmony_ci unsigned int parent, 1868c2ecf20Sopenharmony_ci unsigned int id) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct raspberrypi_clk_data *data; 1898c2ecf20Sopenharmony_ci struct clk_init_data init = {}; 1908c2ecf20Sopenharmony_ci u32 min_rate, max_rate; 1918c2ecf20Sopenharmony_ci int ret; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); 1948c2ecf20Sopenharmony_ci if (!data) 1958c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1968c2ecf20Sopenharmony_ci data->rpi = rpi; 1978c2ecf20Sopenharmony_ci data->id = id; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci init.name = devm_kasprintf(rpi->dev, GFP_KERNEL, 2008c2ecf20Sopenharmony_ci "fw-clk-%s", 2018c2ecf20Sopenharmony_ci rpi_firmware_clk_names[id]); 2028c2ecf20Sopenharmony_ci init.ops = &raspberrypi_firmware_clk_ops; 2038c2ecf20Sopenharmony_ci init.flags = CLK_GET_RATE_NOCACHE; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci data->hw.init = &init; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ret = raspberrypi_clock_property(rpi->firmware, data, 2088c2ecf20Sopenharmony_ci RPI_FIRMWARE_GET_MIN_CLOCK_RATE, 2098c2ecf20Sopenharmony_ci &min_rate); 2108c2ecf20Sopenharmony_ci if (ret) { 2118c2ecf20Sopenharmony_ci dev_err(rpi->dev, "Failed to get clock %d min freq: %d\n", 2128c2ecf20Sopenharmony_ci id, ret); 2138c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci ret = raspberrypi_clock_property(rpi->firmware, data, 2178c2ecf20Sopenharmony_ci RPI_FIRMWARE_GET_MAX_CLOCK_RATE, 2188c2ecf20Sopenharmony_ci &max_rate); 2198c2ecf20Sopenharmony_ci if (ret) { 2208c2ecf20Sopenharmony_ci dev_err(rpi->dev, "Failed to get clock %d max freq: %d\n", 2218c2ecf20Sopenharmony_ci id, ret); 2228c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(rpi->dev, &data->hw); 2268c2ecf20Sopenharmony_ci if (ret) 2278c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci clk_hw_set_rate_range(&data->hw, min_rate, max_rate); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (id == RPI_FIRMWARE_ARM_CLK_ID) { 2328c2ecf20Sopenharmony_ci ret = devm_clk_hw_register_clkdev(rpi->dev, &data->hw, 2338c2ecf20Sopenharmony_ci NULL, "cpu0"); 2348c2ecf20Sopenharmony_ci if (ret) { 2358c2ecf20Sopenharmony_ci dev_err(rpi->dev, "Failed to initialize clkdev\n"); 2368c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return &data->hw; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistruct rpi_firmware_get_clocks_response { 2448c2ecf20Sopenharmony_ci u32 parent; 2458c2ecf20Sopenharmony_ci u32 id; 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi, 2498c2ecf20Sopenharmony_ci struct clk_hw_onecell_data *data) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct rpi_firmware_get_clocks_response *clks; 2528c2ecf20Sopenharmony_ci int ret; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * The firmware doesn't guarantee that the last element of 2568c2ecf20Sopenharmony_ci * RPI_FIRMWARE_GET_CLOCKS is zeroed. So allocate an additional 2578c2ecf20Sopenharmony_ci * zero element as sentinel. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci clks = devm_kcalloc(rpi->dev, 2608c2ecf20Sopenharmony_ci RPI_FIRMWARE_NUM_CLK_ID + 1, sizeof(*clks), 2618c2ecf20Sopenharmony_ci GFP_KERNEL); 2628c2ecf20Sopenharmony_ci if (!clks) 2638c2ecf20Sopenharmony_ci return -ENOMEM; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS, 2668c2ecf20Sopenharmony_ci clks, 2678c2ecf20Sopenharmony_ci sizeof(*clks) * RPI_FIRMWARE_NUM_CLK_ID); 2688c2ecf20Sopenharmony_ci if (ret) 2698c2ecf20Sopenharmony_ci return ret; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci while (clks->id) { 2728c2ecf20Sopenharmony_ci struct clk_hw *hw; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci switch (clks->id) { 2758c2ecf20Sopenharmony_ci case RPI_FIRMWARE_ARM_CLK_ID: 2768c2ecf20Sopenharmony_ci case RPI_FIRMWARE_CORE_CLK_ID: 2778c2ecf20Sopenharmony_ci case RPI_FIRMWARE_M2MC_CLK_ID: 2788c2ecf20Sopenharmony_ci case RPI_FIRMWARE_V3D_CLK_ID: 2798c2ecf20Sopenharmony_ci case RPI_FIRMWARE_PIXEL_BVB_CLK_ID: 2808c2ecf20Sopenharmony_ci hw = raspberrypi_clk_register(rpi, clks->parent, 2818c2ecf20Sopenharmony_ci clks->id); 2828c2ecf20Sopenharmony_ci if (IS_ERR(hw)) 2838c2ecf20Sopenharmony_ci return PTR_ERR(hw); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci data->hws[clks->id] = hw; 2868c2ecf20Sopenharmony_ci data->num = clks->id + 1; 2878c2ecf20Sopenharmony_ci fallthrough; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci default: 2908c2ecf20Sopenharmony_ci clks++; 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int raspberrypi_clk_probe(struct platform_device *pdev) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct clk_hw_onecell_data *clk_data; 3018c2ecf20Sopenharmony_ci struct device_node *firmware_node; 3028c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3038c2ecf20Sopenharmony_ci struct rpi_firmware *firmware; 3048c2ecf20Sopenharmony_ci struct raspberrypi_clk *rpi; 3058c2ecf20Sopenharmony_ci int ret; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * We can be probed either through the an old-fashioned 3098c2ecf20Sopenharmony_ci * platform device registration or through a DT node that is a 3108c2ecf20Sopenharmony_ci * child of the firmware node. Handle both cases. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci if (dev->of_node) 3138c2ecf20Sopenharmony_ci firmware_node = of_get_parent(dev->of_node); 3148c2ecf20Sopenharmony_ci else 3158c2ecf20Sopenharmony_ci firmware_node = of_find_compatible_node(NULL, NULL, 3168c2ecf20Sopenharmony_ci "raspberrypi,bcm2835-firmware"); 3178c2ecf20Sopenharmony_ci if (!firmware_node) { 3188c2ecf20Sopenharmony_ci dev_err(dev, "Missing firmware node\n"); 3198c2ecf20Sopenharmony_ci return -ENOENT; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci firmware = rpi_firmware_get(firmware_node); 3238c2ecf20Sopenharmony_ci of_node_put(firmware_node); 3248c2ecf20Sopenharmony_ci if (!firmware) 3258c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci rpi = devm_kzalloc(dev, sizeof(*rpi), GFP_KERNEL); 3288c2ecf20Sopenharmony_ci if (!rpi) 3298c2ecf20Sopenharmony_ci return -ENOMEM; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci rpi->dev = dev; 3328c2ecf20Sopenharmony_ci rpi->firmware = firmware; 3338c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, rpi); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, 3368c2ecf20Sopenharmony_ci RPI_FIRMWARE_NUM_CLK_ID), 3378c2ecf20Sopenharmony_ci GFP_KERNEL); 3388c2ecf20Sopenharmony_ci if (!clk_data) 3398c2ecf20Sopenharmony_ci return -ENOMEM; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = raspberrypi_discover_clocks(rpi, clk_data); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci return ret; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, 3468c2ecf20Sopenharmony_ci clk_data); 3478c2ecf20Sopenharmony_ci if (ret) 3488c2ecf20Sopenharmony_ci return ret; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", 3518c2ecf20Sopenharmony_ci -1, NULL, 0); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int raspberrypi_clk_remove(struct platform_device *pdev) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct raspberrypi_clk *rpi = platform_get_drvdata(pdev); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci platform_device_unregister(rpi->cpufreq); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic const struct of_device_id raspberrypi_clk_match[] = { 3668c2ecf20Sopenharmony_ci { .compatible = "raspberrypi,firmware-clocks" }, 3678c2ecf20Sopenharmony_ci { }, 3688c2ecf20Sopenharmony_ci}; 3698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, raspberrypi_clk_match); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic struct platform_driver raspberrypi_clk_driver = { 3728c2ecf20Sopenharmony_ci .driver = { 3738c2ecf20Sopenharmony_ci .name = "raspberrypi-clk", 3748c2ecf20Sopenharmony_ci .of_match_table = raspberrypi_clk_match, 3758c2ecf20Sopenharmony_ci }, 3768c2ecf20Sopenharmony_ci .probe = raspberrypi_clk_probe, 3778c2ecf20Sopenharmony_ci .remove = raspberrypi_clk_remove, 3788c2ecf20Sopenharmony_ci}; 3798c2ecf20Sopenharmony_cimodule_platform_driver(raspberrypi_clk_driver); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>"); 3828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Raspberry Pi firmware clock driver"); 3838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3848c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:raspberrypi-clk"); 385