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 21be1559f6SMaxime Ripard enum rpi_firmware_clk_id { 22be1559f6SMaxime Ripard RPI_FIRMWARE_EMMC_CLK_ID = 1, 23be1559f6SMaxime Ripard RPI_FIRMWARE_UART_CLK_ID, 24be1559f6SMaxime Ripard RPI_FIRMWARE_ARM_CLK_ID, 25be1559f6SMaxime Ripard RPI_FIRMWARE_CORE_CLK_ID, 26be1559f6SMaxime Ripard RPI_FIRMWARE_V3D_CLK_ID, 27be1559f6SMaxime Ripard RPI_FIRMWARE_H264_CLK_ID, 28be1559f6SMaxime Ripard RPI_FIRMWARE_ISP_CLK_ID, 29be1559f6SMaxime Ripard RPI_FIRMWARE_SDRAM_CLK_ID, 30be1559f6SMaxime Ripard RPI_FIRMWARE_PIXEL_CLK_ID, 31be1559f6SMaxime Ripard RPI_FIRMWARE_PWM_CLK_ID, 32be1559f6SMaxime Ripard RPI_FIRMWARE_HEVC_CLK_ID, 33be1559f6SMaxime Ripard RPI_FIRMWARE_EMMC2_CLK_ID, 34be1559f6SMaxime Ripard RPI_FIRMWARE_M2MC_CLK_ID, 35be1559f6SMaxime Ripard RPI_FIRMWARE_PIXEL_BVB_CLK_ID, 36be1559f6SMaxime Ripard RPI_FIRMWARE_NUM_CLK_ID, 37be1559f6SMaxime Ripard }; 384e85e535SNicolas Saenz Julienne 397dad8a61SMaxime Ripard static char *rpi_firmware_clk_names[] = { 407dad8a61SMaxime Ripard [RPI_FIRMWARE_EMMC_CLK_ID] = "emmc", 417dad8a61SMaxime Ripard [RPI_FIRMWARE_UART_CLK_ID] = "uart", 427dad8a61SMaxime Ripard [RPI_FIRMWARE_ARM_CLK_ID] = "arm", 437dad8a61SMaxime Ripard [RPI_FIRMWARE_CORE_CLK_ID] = "core", 447dad8a61SMaxime Ripard [RPI_FIRMWARE_V3D_CLK_ID] = "v3d", 457dad8a61SMaxime Ripard [RPI_FIRMWARE_H264_CLK_ID] = "h264", 467dad8a61SMaxime Ripard [RPI_FIRMWARE_ISP_CLK_ID] = "isp", 477dad8a61SMaxime Ripard [RPI_FIRMWARE_SDRAM_CLK_ID] = "sdram", 487dad8a61SMaxime Ripard [RPI_FIRMWARE_PIXEL_CLK_ID] = "pixel", 497dad8a61SMaxime Ripard [RPI_FIRMWARE_PWM_CLK_ID] = "pwm", 507dad8a61SMaxime Ripard [RPI_FIRMWARE_HEVC_CLK_ID] = "hevc", 517dad8a61SMaxime Ripard [RPI_FIRMWARE_EMMC2_CLK_ID] = "emmc2", 527dad8a61SMaxime Ripard [RPI_FIRMWARE_M2MC_CLK_ID] = "m2mc", 537dad8a61SMaxime Ripard [RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb", 547dad8a61SMaxime Ripard }; 557dad8a61SMaxime Ripard 564e85e535SNicolas Saenz Julienne #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0) 574e85e535SNicolas Saenz Julienne #define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1) 584e85e535SNicolas Saenz Julienne 594e85e535SNicolas Saenz Julienne struct raspberrypi_clk { 604e85e535SNicolas Saenz Julienne struct device *dev; 614e85e535SNicolas Saenz Julienne struct rpi_firmware *firmware; 62e2bb1834SNicolas Saenz Julienne struct platform_device *cpufreq; 63f922c560SMaxime Ripard }; 644e85e535SNicolas Saenz Julienne 65f922c560SMaxime Ripard struct raspberrypi_clk_data { 66f922c560SMaxime Ripard struct clk_hw hw; 678a1f3ebcSMaxime Ripard 688a1f3ebcSMaxime Ripard unsigned int id; 698a1f3ebcSMaxime Ripard 70f922c560SMaxime Ripard struct raspberrypi_clk *rpi; 714e85e535SNicolas Saenz Julienne }; 724e85e535SNicolas Saenz Julienne 734e85e535SNicolas Saenz Julienne /* 744e85e535SNicolas Saenz Julienne * Structure of the message passed to Raspberry Pi's firmware in order to 754e85e535SNicolas Saenz Julienne * change clock rates. The 'disable_turbo' option is only available to the ARM 764e85e535SNicolas Saenz Julienne * clock (pllb) which we enable by default as turbo mode will alter multiple 774e85e535SNicolas Saenz Julienne * clocks at once. 784e85e535SNicolas Saenz Julienne * 794e85e535SNicolas Saenz Julienne * Even though we're able to access the clock registers directly we're bound to 804e85e535SNicolas Saenz Julienne * use the firmware interface as the firmware ultimately takes care of 814e85e535SNicolas Saenz Julienne * mitigating overheating/undervoltage situations and we would be changing 824e85e535SNicolas Saenz Julienne * frequencies behind his back. 834e85e535SNicolas Saenz Julienne * 844e85e535SNicolas Saenz Julienne * For more information on the firmware interface check: 854e85e535SNicolas Saenz Julienne * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface 864e85e535SNicolas Saenz Julienne */ 874e85e535SNicolas Saenz Julienne struct raspberrypi_firmware_prop { 884e85e535SNicolas Saenz Julienne __le32 id; 894e85e535SNicolas Saenz Julienne __le32 val; 904e85e535SNicolas Saenz Julienne __le32 disable_turbo; 914e85e535SNicolas Saenz Julienne } __packed; 924e85e535SNicolas Saenz Julienne 9381df0151SMaxime Ripard static int raspberrypi_clock_property(struct rpi_firmware *firmware, 9481df0151SMaxime Ripard const struct raspberrypi_clk_data *data, 9581df0151SMaxime Ripard u32 tag, u32 *val) 964e85e535SNicolas Saenz Julienne { 974e85e535SNicolas Saenz Julienne struct raspberrypi_firmware_prop msg = { 9881df0151SMaxime Ripard .id = cpu_to_le32(data->id), 994e85e535SNicolas Saenz Julienne .val = cpu_to_le32(*val), 1004e85e535SNicolas Saenz Julienne .disable_turbo = cpu_to_le32(1), 1014e85e535SNicolas Saenz Julienne }; 1024e85e535SNicolas Saenz Julienne int ret; 1034e85e535SNicolas Saenz Julienne 1044e85e535SNicolas Saenz Julienne ret = rpi_firmware_property(firmware, tag, &msg, sizeof(msg)); 1054e85e535SNicolas Saenz Julienne if (ret) 1064e85e535SNicolas Saenz Julienne return ret; 1074e85e535SNicolas Saenz Julienne 1084e85e535SNicolas Saenz Julienne *val = le32_to_cpu(msg.val); 1094e85e535SNicolas Saenz Julienne 1104e85e535SNicolas Saenz Julienne return 0; 1114e85e535SNicolas Saenz Julienne } 1124e85e535SNicolas Saenz Julienne 113c1ce3509SMaxime Ripard static int raspberrypi_fw_is_prepared(struct clk_hw *hw) 1144e85e535SNicolas Saenz Julienne { 115f922c560SMaxime Ripard struct raspberrypi_clk_data *data = 116f922c560SMaxime Ripard container_of(hw, struct raspberrypi_clk_data, hw); 117f922c560SMaxime Ripard struct raspberrypi_clk *rpi = data->rpi; 1184e85e535SNicolas Saenz Julienne u32 val = 0; 1194e85e535SNicolas Saenz Julienne int ret; 1204e85e535SNicolas Saenz Julienne 12181df0151SMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 12281df0151SMaxime Ripard RPI_FIRMWARE_GET_CLOCK_STATE, &val); 1234e85e535SNicolas Saenz Julienne if (ret) 1244e85e535SNicolas Saenz Julienne return 0; 1254e85e535SNicolas Saenz Julienne 1264e85e535SNicolas Saenz Julienne return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT); 1274e85e535SNicolas Saenz Julienne } 1284e85e535SNicolas Saenz Julienne 1294e85e535SNicolas Saenz Julienne 1303ea59aceSMaxime Ripard static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw, 1314e85e535SNicolas Saenz Julienne unsigned long parent_rate) 1324e85e535SNicolas Saenz Julienne { 133f922c560SMaxime Ripard struct raspberrypi_clk_data *data = 134f922c560SMaxime Ripard container_of(hw, struct raspberrypi_clk_data, hw); 135f922c560SMaxime Ripard struct raspberrypi_clk *rpi = data->rpi; 1364e85e535SNicolas Saenz Julienne u32 val = 0; 1374e85e535SNicolas Saenz Julienne int ret; 1384e85e535SNicolas Saenz Julienne 13981df0151SMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 14081df0151SMaxime Ripard RPI_FIRMWARE_GET_CLOCK_RATE, &val); 1414e85e535SNicolas Saenz Julienne if (ret) 1424e85e535SNicolas Saenz Julienne return ret; 1434e85e535SNicolas Saenz Julienne 1443ea59aceSMaxime Ripard return val; 1454e85e535SNicolas Saenz Julienne } 1464e85e535SNicolas Saenz Julienne 1473ea59aceSMaxime Ripard static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate, 1484e85e535SNicolas Saenz Julienne unsigned long parent_rate) 1494e85e535SNicolas Saenz Julienne { 150f922c560SMaxime Ripard struct raspberrypi_clk_data *data = 151f922c560SMaxime Ripard container_of(hw, struct raspberrypi_clk_data, hw); 152f922c560SMaxime Ripard struct raspberrypi_clk *rpi = data->rpi; 1533ea59aceSMaxime Ripard u32 _rate = rate; 1544e85e535SNicolas Saenz Julienne int ret; 1554e85e535SNicolas Saenz Julienne 15681df0151SMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 1573ea59aceSMaxime Ripard RPI_FIRMWARE_SET_CLOCK_RATE, &_rate); 1584e85e535SNicolas Saenz Julienne if (ret) 1594e85e535SNicolas Saenz Julienne dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", 1604e85e535SNicolas Saenz Julienne clk_hw_get_name(hw), ret); 1614e85e535SNicolas Saenz Julienne 1624e85e535SNicolas Saenz Julienne return ret; 1634e85e535SNicolas Saenz Julienne } 1644e85e535SNicolas Saenz Julienne 16593d2725aSMaxime Ripard static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, 16693d2725aSMaxime Ripard struct clk_rate_request *req) 16793d2725aSMaxime Ripard { 16893d2725aSMaxime Ripard /* 16993d2725aSMaxime Ripard * The firmware will do the rounding but that isn't part of 17093d2725aSMaxime Ripard * the interface with the firmware, so we just do our best 17193d2725aSMaxime Ripard * here. 17293d2725aSMaxime Ripard */ 17393d2725aSMaxime Ripard req->rate = clamp(req->rate, req->min_rate, req->max_rate); 17493d2725aSMaxime Ripard return 0; 17593d2725aSMaxime Ripard } 17693d2725aSMaxime Ripard 17793d2725aSMaxime Ripard static const struct clk_ops raspberrypi_firmware_clk_ops = { 17893d2725aSMaxime Ripard .is_prepared = raspberrypi_fw_is_prepared, 17993d2725aSMaxime Ripard .recalc_rate = raspberrypi_fw_get_rate, 18093d2725aSMaxime Ripard .determine_rate = raspberrypi_fw_dumb_determine_rate, 18193d2725aSMaxime Ripard .set_rate = raspberrypi_fw_set_rate, 18293d2725aSMaxime Ripard }; 18393d2725aSMaxime Ripard 18493d2725aSMaxime Ripard static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi, 18593d2725aSMaxime Ripard unsigned int parent, 18693d2725aSMaxime Ripard unsigned int id) 18793d2725aSMaxime Ripard { 18893d2725aSMaxime Ripard struct raspberrypi_clk_data *data; 18993d2725aSMaxime Ripard struct clk_init_data init = {}; 19093d2725aSMaxime Ripard u32 min_rate, max_rate; 19193d2725aSMaxime Ripard int ret; 19293d2725aSMaxime Ripard 19393d2725aSMaxime Ripard data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); 19493d2725aSMaxime Ripard if (!data) 19593d2725aSMaxime Ripard return ERR_PTR(-ENOMEM); 19693d2725aSMaxime Ripard data->rpi = rpi; 19793d2725aSMaxime Ripard data->id = id; 19893d2725aSMaxime Ripard 1997dad8a61SMaxime Ripard init.name = devm_kasprintf(rpi->dev, GFP_KERNEL, 2007dad8a61SMaxime Ripard "fw-clk-%s", 2017dad8a61SMaxime Ripard rpi_firmware_clk_names[id]); 20293d2725aSMaxime Ripard init.ops = &raspberrypi_firmware_clk_ops; 20393d2725aSMaxime Ripard init.flags = CLK_GET_RATE_NOCACHE; 20493d2725aSMaxime Ripard 20593d2725aSMaxime Ripard data->hw.init = &init; 20693d2725aSMaxime Ripard 20793d2725aSMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 20893d2725aSMaxime Ripard RPI_FIRMWARE_GET_MIN_CLOCK_RATE, 20993d2725aSMaxime Ripard &min_rate); 21093d2725aSMaxime Ripard if (ret) { 21193d2725aSMaxime Ripard dev_err(rpi->dev, "Failed to get clock %d min freq: %d", 21293d2725aSMaxime Ripard id, ret); 21393d2725aSMaxime Ripard return ERR_PTR(ret); 21493d2725aSMaxime Ripard } 21593d2725aSMaxime Ripard 21693d2725aSMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 21793d2725aSMaxime Ripard RPI_FIRMWARE_GET_MAX_CLOCK_RATE, 21893d2725aSMaxime Ripard &max_rate); 21993d2725aSMaxime Ripard if (ret) { 22093d2725aSMaxime Ripard dev_err(rpi->dev, "Failed to get clock %d max freq: %d\n", 22193d2725aSMaxime Ripard id, ret); 22293d2725aSMaxime Ripard return ERR_PTR(ret); 22393d2725aSMaxime Ripard } 22493d2725aSMaxime Ripard 22593d2725aSMaxime Ripard ret = devm_clk_hw_register(rpi->dev, &data->hw); 22693d2725aSMaxime Ripard if (ret) 22793d2725aSMaxime Ripard return ERR_PTR(ret); 22893d2725aSMaxime Ripard 22993d2725aSMaxime Ripard clk_hw_set_rate_range(&data->hw, min_rate, max_rate); 23093d2725aSMaxime Ripard 23193d2725aSMaxime Ripard if (id == RPI_FIRMWARE_ARM_CLK_ID) { 23293d2725aSMaxime Ripard ret = devm_clk_hw_register_clkdev(rpi->dev, &data->hw, 23393d2725aSMaxime Ripard NULL, "cpu0"); 23493d2725aSMaxime Ripard if (ret) { 23593d2725aSMaxime Ripard dev_err(rpi->dev, "Failed to initialize clkdev\n"); 23693d2725aSMaxime Ripard return ERR_PTR(ret); 23793d2725aSMaxime Ripard } 23893d2725aSMaxime Ripard } 23993d2725aSMaxime Ripard 24093d2725aSMaxime Ripard return &data->hw; 24193d2725aSMaxime Ripard } 24293d2725aSMaxime Ripard 24393d2725aSMaxime Ripard struct rpi_firmware_get_clocks_response { 24493d2725aSMaxime Ripard u32 parent; 24593d2725aSMaxime Ripard u32 id; 24693d2725aSMaxime Ripard }; 24793d2725aSMaxime Ripard 24893d2725aSMaxime Ripard static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi, 24993d2725aSMaxime Ripard struct clk_hw_onecell_data *data) 25093d2725aSMaxime Ripard { 25193d2725aSMaxime Ripard struct rpi_firmware_get_clocks_response *clks; 25293d2725aSMaxime Ripard int ret; 25393d2725aSMaxime Ripard 25493d2725aSMaxime Ripard clks = devm_kcalloc(rpi->dev, 25593d2725aSMaxime Ripard sizeof(*clks), RPI_FIRMWARE_NUM_CLK_ID, 25693d2725aSMaxime Ripard GFP_KERNEL); 25793d2725aSMaxime Ripard if (!clks) 25893d2725aSMaxime Ripard return -ENOMEM; 25993d2725aSMaxime Ripard 26093d2725aSMaxime Ripard ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS, 26193d2725aSMaxime Ripard clks, 26293d2725aSMaxime Ripard sizeof(*clks) * RPI_FIRMWARE_NUM_CLK_ID); 26393d2725aSMaxime Ripard if (ret) 26493d2725aSMaxime Ripard return ret; 26593d2725aSMaxime Ripard 26693d2725aSMaxime Ripard while (clks->id) { 26793d2725aSMaxime Ripard struct clk_hw *hw; 26893d2725aSMaxime Ripard 26993d2725aSMaxime Ripard switch (clks->id) { 27093d2725aSMaxime Ripard case RPI_FIRMWARE_ARM_CLK_ID: 27193d2725aSMaxime Ripard case RPI_FIRMWARE_CORE_CLK_ID: 27293d2725aSMaxime Ripard case RPI_FIRMWARE_M2MC_CLK_ID: 27393d2725aSMaxime Ripard case RPI_FIRMWARE_V3D_CLK_ID: 2744ceb4b6bSHoegeun Kwon case RPI_FIRMWARE_PIXEL_BVB_CLK_ID: 27593d2725aSMaxime Ripard hw = raspberrypi_clk_register(rpi, clks->parent, 27693d2725aSMaxime Ripard clks->id); 27793d2725aSMaxime Ripard if (IS_ERR(hw)) 27893d2725aSMaxime Ripard return PTR_ERR(hw); 27993d2725aSMaxime Ripard 28093d2725aSMaxime Ripard data->hws[clks->id] = hw; 28193d2725aSMaxime Ripard data->num = clks->id + 1; 28293d2725aSMaxime Ripard fallthrough; 28393d2725aSMaxime Ripard 28493d2725aSMaxime Ripard default: 28593d2725aSMaxime Ripard clks++; 28693d2725aSMaxime Ripard break; 28793d2725aSMaxime Ripard } 28893d2725aSMaxime Ripard } 28993d2725aSMaxime Ripard 29093d2725aSMaxime Ripard return 0; 29193d2725aSMaxime Ripard } 29293d2725aSMaxime Ripard 2934e85e535SNicolas Saenz Julienne static int raspberrypi_clk_probe(struct platform_device *pdev) 2944e85e535SNicolas Saenz Julienne { 295d4b4f1b6SMaxime Ripard struct clk_hw_onecell_data *clk_data; 2964e85e535SNicolas Saenz Julienne struct device_node *firmware_node; 2974e85e535SNicolas Saenz Julienne struct device *dev = &pdev->dev; 2984e85e535SNicolas Saenz Julienne struct rpi_firmware *firmware; 2994e85e535SNicolas Saenz Julienne struct raspberrypi_clk *rpi; 300d4b4f1b6SMaxime Ripard int ret; 3014e85e535SNicolas Saenz Julienne 302fbac2e77SMaxime Ripard /* 303fbac2e77SMaxime Ripard * We can be probed either through the an old-fashioned 304fbac2e77SMaxime Ripard * platform device registration or through a DT node that is a 305fbac2e77SMaxime Ripard * child of the firmware node. Handle both cases. 306fbac2e77SMaxime Ripard */ 307fbac2e77SMaxime Ripard if (dev->of_node) 308fbac2e77SMaxime Ripard firmware_node = of_get_parent(dev->of_node); 309fbac2e77SMaxime Ripard else 3104e85e535SNicolas Saenz Julienne firmware_node = of_find_compatible_node(NULL, NULL, 3114e85e535SNicolas Saenz Julienne "raspberrypi,bcm2835-firmware"); 3124e85e535SNicolas Saenz Julienne if (!firmware_node) { 3134e85e535SNicolas Saenz Julienne dev_err(dev, "Missing firmware node\n"); 3144e85e535SNicolas Saenz Julienne return -ENOENT; 3154e85e535SNicolas Saenz Julienne } 3164e85e535SNicolas Saenz Julienne 317*3c4084f9SNicolas Saenz Julienne firmware = devm_rpi_firmware_get(&pdev->dev, firmware_node); 3184e85e535SNicolas Saenz Julienne of_node_put(firmware_node); 3194e85e535SNicolas Saenz Julienne if (!firmware) 3204e85e535SNicolas Saenz Julienne return -EPROBE_DEFER; 3214e85e535SNicolas Saenz Julienne 3224e85e535SNicolas Saenz Julienne rpi = devm_kzalloc(dev, sizeof(*rpi), GFP_KERNEL); 3234e85e535SNicolas Saenz Julienne if (!rpi) 3244e85e535SNicolas Saenz Julienne return -ENOMEM; 3254e85e535SNicolas Saenz Julienne 3264e85e535SNicolas Saenz Julienne rpi->dev = dev; 3274e85e535SNicolas Saenz Julienne rpi->firmware = firmware; 328e2bb1834SNicolas Saenz Julienne platform_set_drvdata(pdev, rpi); 3294e85e535SNicolas Saenz Julienne 330be1559f6SMaxime Ripard clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, 331be1559f6SMaxime Ripard RPI_FIRMWARE_NUM_CLK_ID), 332d4b4f1b6SMaxime Ripard GFP_KERNEL); 333d4b4f1b6SMaxime Ripard if (!clk_data) 334d4b4f1b6SMaxime Ripard return -ENOMEM; 335d4b4f1b6SMaxime Ripard 33693d2725aSMaxime Ripard ret = raspberrypi_discover_clocks(rpi, clk_data); 33793d2725aSMaxime Ripard if (ret) 33893d2725aSMaxime Ripard return ret; 339d4b4f1b6SMaxime Ripard 340d4b4f1b6SMaxime Ripard ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, 341d4b4f1b6SMaxime Ripard clk_data); 342d4b4f1b6SMaxime Ripard if (ret) 343d4b4f1b6SMaxime Ripard return ret; 3444e85e535SNicolas Saenz Julienne 345e2bb1834SNicolas Saenz Julienne rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", 346e2bb1834SNicolas Saenz Julienne -1, NULL, 0); 347e2bb1834SNicolas Saenz Julienne 348e2bb1834SNicolas Saenz Julienne return 0; 349e2bb1834SNicolas Saenz Julienne } 350e2bb1834SNicolas Saenz Julienne 351e2bb1834SNicolas Saenz Julienne static int raspberrypi_clk_remove(struct platform_device *pdev) 352e2bb1834SNicolas Saenz Julienne { 353e2bb1834SNicolas Saenz Julienne struct raspberrypi_clk *rpi = platform_get_drvdata(pdev); 354e2bb1834SNicolas Saenz Julienne 355e2bb1834SNicolas Saenz Julienne platform_device_unregister(rpi->cpufreq); 356e2bb1834SNicolas Saenz Julienne 3574e85e535SNicolas Saenz Julienne return 0; 3584e85e535SNicolas Saenz Julienne } 3594e85e535SNicolas Saenz Julienne 360fbac2e77SMaxime Ripard static const struct of_device_id raspberrypi_clk_match[] = { 361fbac2e77SMaxime Ripard { .compatible = "raspberrypi,firmware-clocks" }, 362fbac2e77SMaxime Ripard { }, 363fbac2e77SMaxime Ripard }; 364fbac2e77SMaxime Ripard MODULE_DEVICE_TABLE(of, raspberrypi_clk_match); 365fbac2e77SMaxime Ripard 3664e85e535SNicolas Saenz Julienne static struct platform_driver raspberrypi_clk_driver = { 3674e85e535SNicolas Saenz Julienne .driver = { 3684e85e535SNicolas Saenz Julienne .name = "raspberrypi-clk", 369fbac2e77SMaxime Ripard .of_match_table = raspberrypi_clk_match, 3704e85e535SNicolas Saenz Julienne }, 3714e85e535SNicolas Saenz Julienne .probe = raspberrypi_clk_probe, 372e2bb1834SNicolas Saenz Julienne .remove = raspberrypi_clk_remove, 3734e85e535SNicolas Saenz Julienne }; 3744e85e535SNicolas Saenz Julienne module_platform_driver(raspberrypi_clk_driver); 3754e85e535SNicolas Saenz Julienne 3764e85e535SNicolas Saenz Julienne MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>"); 3774e85e535SNicolas Saenz Julienne MODULE_DESCRIPTION("Raspberry Pi firmware clock driver"); 3784e85e535SNicolas Saenz Julienne MODULE_LICENSE("GPL"); 3794e85e535SNicolas Saenz Julienne MODULE_ALIAS("platform:raspberrypi-clk"); 380