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 5912c90f3fSMaxime Ripard struct raspberrypi_clk_variant; 6012c90f3fSMaxime Ripard 614e85e535SNicolas Saenz Julienne struct raspberrypi_clk { 624e85e535SNicolas Saenz Julienne struct device *dev; 634e85e535SNicolas Saenz Julienne struct rpi_firmware *firmware; 64e2bb1834SNicolas Saenz Julienne struct platform_device *cpufreq; 65f922c560SMaxime Ripard }; 664e85e535SNicolas Saenz Julienne 67f922c560SMaxime Ripard struct raspberrypi_clk_data { 68f922c560SMaxime Ripard struct clk_hw hw; 698a1f3ebcSMaxime Ripard 708a1f3ebcSMaxime Ripard unsigned int id; 7112c90f3fSMaxime Ripard struct raspberrypi_clk_variant *variant; 728a1f3ebcSMaxime Ripard 73f922c560SMaxime Ripard struct raspberrypi_clk *rpi; 744e85e535SNicolas Saenz Julienne }; 754e85e535SNicolas Saenz Julienne 7612c90f3fSMaxime Ripard struct raspberrypi_clk_variant { 7712c90f3fSMaxime Ripard bool export; 7812c90f3fSMaxime Ripard char *clkdev; 79542acfecSMaxime Ripard unsigned long min_rate; 80e9d6cea2SMaxime Ripard bool minimize; 8112c90f3fSMaxime Ripard }; 8212c90f3fSMaxime Ripard 8312c90f3fSMaxime Ripard static struct raspberrypi_clk_variant 8412c90f3fSMaxime Ripard raspberrypi_clk_variants[RPI_FIRMWARE_NUM_CLK_ID] = { 8512c90f3fSMaxime Ripard [RPI_FIRMWARE_ARM_CLK_ID] = { 8612c90f3fSMaxime Ripard .export = true, 8712c90f3fSMaxime Ripard .clkdev = "cpu0", 8812c90f3fSMaxime Ripard }, 8912c90f3fSMaxime Ripard [RPI_FIRMWARE_CORE_CLK_ID] = { 9012c90f3fSMaxime Ripard .export = true, 91e9d6cea2SMaxime Ripard 92e9d6cea2SMaxime Ripard /* 93e9d6cea2SMaxime Ripard * The clock is shared between the HVS and the CSI 94e9d6cea2SMaxime Ripard * controllers, on the BCM2711 and will change depending 95e9d6cea2SMaxime Ripard * on the pixels composited on the HVS and the capture 96e9d6cea2SMaxime Ripard * resolution on Unicam. 97e9d6cea2SMaxime Ripard * 98e9d6cea2SMaxime Ripard * Since the rate can get quite large, and we need to 99e9d6cea2SMaxime Ripard * coordinate between both driver instances, let's 100e9d6cea2SMaxime Ripard * always use the minimum the drivers will let us. 101e9d6cea2SMaxime Ripard */ 102e9d6cea2SMaxime Ripard .minimize = true, 10312c90f3fSMaxime Ripard }, 10412c90f3fSMaxime Ripard [RPI_FIRMWARE_M2MC_CLK_ID] = { 10512c90f3fSMaxime Ripard .export = true, 106542acfecSMaxime Ripard 107542acfecSMaxime Ripard /* 108542acfecSMaxime Ripard * If we boot without any cable connected to any of the 109542acfecSMaxime Ripard * HDMI connector, the firmware will skip the HSM 110542acfecSMaxime Ripard * initialization and leave it with a rate of 0, 111542acfecSMaxime Ripard * resulting in a bus lockup when we're accessing the 112542acfecSMaxime Ripard * registers even if it's enabled. 113542acfecSMaxime Ripard * 114542acfecSMaxime Ripard * Let's put a sensible default so that we don't end up 115542acfecSMaxime Ripard * in this situation. 116542acfecSMaxime Ripard */ 117542acfecSMaxime Ripard .min_rate = 120000000, 118e9d6cea2SMaxime Ripard 119e9d6cea2SMaxime Ripard /* 120e9d6cea2SMaxime Ripard * The clock is shared between the two HDMI controllers 121e9d6cea2SMaxime Ripard * on the BCM2711 and will change depending on the 122e9d6cea2SMaxime Ripard * resolution output on each. Since the rate can get 123e9d6cea2SMaxime Ripard * quite large, and we need to coordinate between both 124e9d6cea2SMaxime Ripard * driver instances, let's always use the minimum the 125e9d6cea2SMaxime Ripard * drivers will let us. 126e9d6cea2SMaxime Ripard */ 127e9d6cea2SMaxime Ripard .minimize = true, 12812c90f3fSMaxime Ripard }, 12912c90f3fSMaxime Ripard [RPI_FIRMWARE_V3D_CLK_ID] = { 13012c90f3fSMaxime Ripard .export = true, 13112c90f3fSMaxime Ripard }, 132*16baa8c1SIvan T. Ivanov [RPI_FIRMWARE_PIXEL_CLK_ID] = { 133*16baa8c1SIvan T. Ivanov .export = true, 134*16baa8c1SIvan T. Ivanov }, 1354c68a345SIvan T. Ivanov [RPI_FIRMWARE_HEVC_CLK_ID] = { 1364c68a345SIvan T. Ivanov .export = true, 1374c68a345SIvan T. Ivanov }, 13812c90f3fSMaxime Ripard [RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = { 13912c90f3fSMaxime Ripard .export = true, 14012c90f3fSMaxime Ripard }, 14112c90f3fSMaxime Ripard }; 14212c90f3fSMaxime Ripard 1434e85e535SNicolas Saenz Julienne /* 1444e85e535SNicolas Saenz Julienne * Structure of the message passed to Raspberry Pi's firmware in order to 1454e85e535SNicolas Saenz Julienne * change clock rates. The 'disable_turbo' option is only available to the ARM 1464e85e535SNicolas Saenz Julienne * clock (pllb) which we enable by default as turbo mode will alter multiple 1474e85e535SNicolas Saenz Julienne * clocks at once. 1484e85e535SNicolas Saenz Julienne * 1494e85e535SNicolas Saenz Julienne * Even though we're able to access the clock registers directly we're bound to 1504e85e535SNicolas Saenz Julienne * use the firmware interface as the firmware ultimately takes care of 1514e85e535SNicolas Saenz Julienne * mitigating overheating/undervoltage situations and we would be changing 1524e85e535SNicolas Saenz Julienne * frequencies behind his back. 1534e85e535SNicolas Saenz Julienne * 1544e85e535SNicolas Saenz Julienne * For more information on the firmware interface check: 1554e85e535SNicolas Saenz Julienne * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface 1564e85e535SNicolas Saenz Julienne */ 1574e85e535SNicolas Saenz Julienne struct raspberrypi_firmware_prop { 1584e85e535SNicolas Saenz Julienne __le32 id; 1594e85e535SNicolas Saenz Julienne __le32 val; 1604e85e535SNicolas Saenz Julienne __le32 disable_turbo; 1614e85e535SNicolas Saenz Julienne } __packed; 1624e85e535SNicolas Saenz Julienne 16381df0151SMaxime Ripard static int raspberrypi_clock_property(struct rpi_firmware *firmware, 16481df0151SMaxime Ripard const struct raspberrypi_clk_data *data, 16581df0151SMaxime Ripard u32 tag, u32 *val) 1664e85e535SNicolas Saenz Julienne { 1674e85e535SNicolas Saenz Julienne struct raspberrypi_firmware_prop msg = { 16881df0151SMaxime Ripard .id = cpu_to_le32(data->id), 1694e85e535SNicolas Saenz Julienne .val = cpu_to_le32(*val), 1704e85e535SNicolas Saenz Julienne .disable_turbo = cpu_to_le32(1), 1714e85e535SNicolas Saenz Julienne }; 1724e85e535SNicolas Saenz Julienne int ret; 1734e85e535SNicolas Saenz Julienne 1744e85e535SNicolas Saenz Julienne ret = rpi_firmware_property(firmware, tag, &msg, sizeof(msg)); 1754e85e535SNicolas Saenz Julienne if (ret) 1764e85e535SNicolas Saenz Julienne return ret; 1774e85e535SNicolas Saenz Julienne 1784e85e535SNicolas Saenz Julienne *val = le32_to_cpu(msg.val); 1794e85e535SNicolas Saenz Julienne 1804e85e535SNicolas Saenz Julienne return 0; 1814e85e535SNicolas Saenz Julienne } 1824e85e535SNicolas Saenz Julienne 183c1ce3509SMaxime Ripard static int raspberrypi_fw_is_prepared(struct clk_hw *hw) 1844e85e535SNicolas Saenz Julienne { 185f922c560SMaxime Ripard struct raspberrypi_clk_data *data = 186f922c560SMaxime Ripard container_of(hw, struct raspberrypi_clk_data, hw); 187f922c560SMaxime Ripard struct raspberrypi_clk *rpi = data->rpi; 1884e85e535SNicolas Saenz Julienne u32 val = 0; 1894e85e535SNicolas Saenz Julienne int ret; 1904e85e535SNicolas Saenz Julienne 19181df0151SMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 19281df0151SMaxime Ripard RPI_FIRMWARE_GET_CLOCK_STATE, &val); 1934e85e535SNicolas Saenz Julienne if (ret) 1944e85e535SNicolas Saenz Julienne return 0; 1954e85e535SNicolas Saenz Julienne 1964e85e535SNicolas Saenz Julienne return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT); 1974e85e535SNicolas Saenz Julienne } 1984e85e535SNicolas Saenz Julienne 1994e85e535SNicolas Saenz Julienne 2003ea59aceSMaxime Ripard static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw, 2014e85e535SNicolas Saenz Julienne unsigned long parent_rate) 2024e85e535SNicolas Saenz Julienne { 203f922c560SMaxime Ripard struct raspberrypi_clk_data *data = 204f922c560SMaxime Ripard container_of(hw, struct raspberrypi_clk_data, hw); 205f922c560SMaxime Ripard struct raspberrypi_clk *rpi = data->rpi; 2064e85e535SNicolas Saenz Julienne u32 val = 0; 2074e85e535SNicolas Saenz Julienne int ret; 2084e85e535SNicolas Saenz Julienne 20981df0151SMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 21081df0151SMaxime Ripard RPI_FIRMWARE_GET_CLOCK_RATE, &val); 2114e85e535SNicolas Saenz Julienne if (ret) 2124e85e535SNicolas Saenz Julienne return ret; 2134e85e535SNicolas Saenz Julienne 2143ea59aceSMaxime Ripard return val; 2154e85e535SNicolas Saenz Julienne } 2164e85e535SNicolas Saenz Julienne 2173ea59aceSMaxime Ripard static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate, 2184e85e535SNicolas Saenz Julienne unsigned long parent_rate) 2194e85e535SNicolas Saenz Julienne { 220f922c560SMaxime Ripard struct raspberrypi_clk_data *data = 221f922c560SMaxime Ripard container_of(hw, struct raspberrypi_clk_data, hw); 222f922c560SMaxime Ripard struct raspberrypi_clk *rpi = data->rpi; 2233ea59aceSMaxime Ripard u32 _rate = rate; 2244e85e535SNicolas Saenz Julienne int ret; 2254e85e535SNicolas Saenz Julienne 22681df0151SMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 2273ea59aceSMaxime Ripard RPI_FIRMWARE_SET_CLOCK_RATE, &_rate); 2284e85e535SNicolas Saenz Julienne if (ret) 2294e85e535SNicolas Saenz Julienne dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", 2304e85e535SNicolas Saenz Julienne clk_hw_get_name(hw), ret); 2314e85e535SNicolas Saenz Julienne 2324e85e535SNicolas Saenz Julienne return ret; 2334e85e535SNicolas Saenz Julienne } 2344e85e535SNicolas Saenz Julienne 23593d2725aSMaxime Ripard static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, 23693d2725aSMaxime Ripard struct clk_rate_request *req) 23793d2725aSMaxime Ripard { 238e9d6cea2SMaxime Ripard struct raspberrypi_clk_data *data = 239e9d6cea2SMaxime Ripard container_of(hw, struct raspberrypi_clk_data, hw); 240e9d6cea2SMaxime Ripard struct raspberrypi_clk_variant *variant = data->variant; 241e9d6cea2SMaxime Ripard 24293d2725aSMaxime Ripard /* 24393d2725aSMaxime Ripard * The firmware will do the rounding but that isn't part of 24493d2725aSMaxime Ripard * the interface with the firmware, so we just do our best 24593d2725aSMaxime Ripard * here. 24693d2725aSMaxime Ripard */ 247e9d6cea2SMaxime Ripard 24893d2725aSMaxime Ripard req->rate = clamp(req->rate, req->min_rate, req->max_rate); 249e9d6cea2SMaxime Ripard 250e9d6cea2SMaxime Ripard /* 251e9d6cea2SMaxime Ripard * We want to aggressively reduce the clock rate here, so let's 252e9d6cea2SMaxime Ripard * just ignore the requested rate and return the bare minimum 253e9d6cea2SMaxime Ripard * rate we can get away with. 254e9d6cea2SMaxime Ripard */ 255e9d6cea2SMaxime Ripard if (variant->minimize && req->min_rate > 0) 256e9d6cea2SMaxime Ripard req->rate = req->min_rate; 257e9d6cea2SMaxime Ripard 25893d2725aSMaxime Ripard return 0; 25993d2725aSMaxime Ripard } 26093d2725aSMaxime Ripard 26193d2725aSMaxime Ripard static const struct clk_ops raspberrypi_firmware_clk_ops = { 26293d2725aSMaxime Ripard .is_prepared = raspberrypi_fw_is_prepared, 26393d2725aSMaxime Ripard .recalc_rate = raspberrypi_fw_get_rate, 26493d2725aSMaxime Ripard .determine_rate = raspberrypi_fw_dumb_determine_rate, 26593d2725aSMaxime Ripard .set_rate = raspberrypi_fw_set_rate, 26693d2725aSMaxime Ripard }; 26793d2725aSMaxime Ripard 26893d2725aSMaxime Ripard static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi, 26993d2725aSMaxime Ripard unsigned int parent, 27012c90f3fSMaxime Ripard unsigned int id, 27112c90f3fSMaxime Ripard struct raspberrypi_clk_variant *variant) 27293d2725aSMaxime Ripard { 27393d2725aSMaxime Ripard struct raspberrypi_clk_data *data; 27493d2725aSMaxime Ripard struct clk_init_data init = {}; 27593d2725aSMaxime Ripard u32 min_rate, max_rate; 27693d2725aSMaxime Ripard int ret; 27793d2725aSMaxime Ripard 27893d2725aSMaxime Ripard data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); 27993d2725aSMaxime Ripard if (!data) 28093d2725aSMaxime Ripard return ERR_PTR(-ENOMEM); 28193d2725aSMaxime Ripard data->rpi = rpi; 28293d2725aSMaxime Ripard data->id = id; 28312c90f3fSMaxime Ripard data->variant = variant; 28493d2725aSMaxime Ripard 2857dad8a61SMaxime Ripard init.name = devm_kasprintf(rpi->dev, GFP_KERNEL, 2867dad8a61SMaxime Ripard "fw-clk-%s", 2877dad8a61SMaxime Ripard rpi_firmware_clk_names[id]); 28893d2725aSMaxime Ripard init.ops = &raspberrypi_firmware_clk_ops; 28993d2725aSMaxime Ripard init.flags = CLK_GET_RATE_NOCACHE; 29093d2725aSMaxime Ripard 29193d2725aSMaxime Ripard data->hw.init = &init; 29293d2725aSMaxime Ripard 29393d2725aSMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 29493d2725aSMaxime Ripard RPI_FIRMWARE_GET_MIN_CLOCK_RATE, 29593d2725aSMaxime Ripard &min_rate); 29693d2725aSMaxime Ripard if (ret) { 29793d2725aSMaxime Ripard dev_err(rpi->dev, "Failed to get clock %d min freq: %d", 29893d2725aSMaxime Ripard id, ret); 29993d2725aSMaxime Ripard return ERR_PTR(ret); 30093d2725aSMaxime Ripard } 30193d2725aSMaxime Ripard 30293d2725aSMaxime Ripard ret = raspberrypi_clock_property(rpi->firmware, data, 30393d2725aSMaxime Ripard RPI_FIRMWARE_GET_MAX_CLOCK_RATE, 30493d2725aSMaxime Ripard &max_rate); 30593d2725aSMaxime Ripard if (ret) { 30693d2725aSMaxime Ripard dev_err(rpi->dev, "Failed to get clock %d max freq: %d\n", 30793d2725aSMaxime Ripard id, ret); 30893d2725aSMaxime Ripard return ERR_PTR(ret); 30993d2725aSMaxime Ripard } 31093d2725aSMaxime Ripard 31193d2725aSMaxime Ripard ret = devm_clk_hw_register(rpi->dev, &data->hw); 31293d2725aSMaxime Ripard if (ret) 31393d2725aSMaxime Ripard return ERR_PTR(ret); 31493d2725aSMaxime Ripard 31593d2725aSMaxime Ripard clk_hw_set_rate_range(&data->hw, min_rate, max_rate); 31693d2725aSMaxime Ripard 31712c90f3fSMaxime Ripard if (variant->clkdev) { 31893d2725aSMaxime Ripard ret = devm_clk_hw_register_clkdev(rpi->dev, &data->hw, 31912c90f3fSMaxime Ripard NULL, variant->clkdev); 32093d2725aSMaxime Ripard if (ret) { 32193d2725aSMaxime Ripard dev_err(rpi->dev, "Failed to initialize clkdev\n"); 32293d2725aSMaxime Ripard return ERR_PTR(ret); 32393d2725aSMaxime Ripard } 32493d2725aSMaxime Ripard } 32593d2725aSMaxime Ripard 326542acfecSMaxime Ripard if (variant->min_rate) { 327542acfecSMaxime Ripard unsigned long rate; 328542acfecSMaxime Ripard 329542acfecSMaxime Ripard clk_hw_set_rate_range(&data->hw, variant->min_rate, max_rate); 330542acfecSMaxime Ripard 331542acfecSMaxime Ripard rate = raspberrypi_fw_get_rate(&data->hw, 0); 332542acfecSMaxime Ripard if (rate < variant->min_rate) { 333542acfecSMaxime Ripard ret = raspberrypi_fw_set_rate(&data->hw, variant->min_rate, 0); 334542acfecSMaxime Ripard if (ret) 335542acfecSMaxime Ripard return ERR_PTR(ret); 336542acfecSMaxime Ripard } 337542acfecSMaxime Ripard } 338542acfecSMaxime Ripard 33993d2725aSMaxime Ripard return &data->hw; 34093d2725aSMaxime Ripard } 34193d2725aSMaxime Ripard 34293d2725aSMaxime Ripard struct rpi_firmware_get_clocks_response { 34393d2725aSMaxime Ripard u32 parent; 34493d2725aSMaxime Ripard u32 id; 34593d2725aSMaxime Ripard }; 34693d2725aSMaxime Ripard 34793d2725aSMaxime Ripard static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi, 34893d2725aSMaxime Ripard struct clk_hw_onecell_data *data) 34993d2725aSMaxime Ripard { 35093d2725aSMaxime Ripard struct rpi_firmware_get_clocks_response *clks; 35193d2725aSMaxime Ripard int ret; 35293d2725aSMaxime Ripard 35393d2725aSMaxime Ripard clks = devm_kcalloc(rpi->dev, 354b7fa6242SChristophe JAILLET RPI_FIRMWARE_NUM_CLK_ID, sizeof(*clks), 35593d2725aSMaxime Ripard GFP_KERNEL); 35693d2725aSMaxime Ripard if (!clks) 35793d2725aSMaxime Ripard return -ENOMEM; 35893d2725aSMaxime Ripard 35993d2725aSMaxime Ripard ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS, 36093d2725aSMaxime Ripard clks, 36193d2725aSMaxime Ripard sizeof(*clks) * RPI_FIRMWARE_NUM_CLK_ID); 36293d2725aSMaxime Ripard if (ret) 36393d2725aSMaxime Ripard return ret; 36493d2725aSMaxime Ripard 36593d2725aSMaxime Ripard while (clks->id) { 36612c90f3fSMaxime Ripard struct raspberrypi_clk_variant *variant; 36712c90f3fSMaxime Ripard 36812c90f3fSMaxime Ripard if (clks->id > RPI_FIRMWARE_NUM_CLK_ID) { 36912c90f3fSMaxime Ripard dev_err(rpi->dev, "Unknown clock id: %u", clks->id); 37012c90f3fSMaxime Ripard return -EINVAL; 37112c90f3fSMaxime Ripard } 37212c90f3fSMaxime Ripard 37312c90f3fSMaxime Ripard variant = &raspberrypi_clk_variants[clks->id]; 37412c90f3fSMaxime Ripard if (variant->export) { 37593d2725aSMaxime Ripard struct clk_hw *hw; 37693d2725aSMaxime Ripard 37793d2725aSMaxime Ripard hw = raspberrypi_clk_register(rpi, clks->parent, 37812c90f3fSMaxime Ripard clks->id, variant); 37993d2725aSMaxime Ripard if (IS_ERR(hw)) 38093d2725aSMaxime Ripard return PTR_ERR(hw); 38193d2725aSMaxime Ripard 38293d2725aSMaxime Ripard data->hws[clks->id] = hw; 38393d2725aSMaxime Ripard data->num = clks->id + 1; 38493d2725aSMaxime Ripard } 38512c90f3fSMaxime Ripard 38612c90f3fSMaxime Ripard clks++; 38793d2725aSMaxime Ripard } 38893d2725aSMaxime Ripard 38993d2725aSMaxime Ripard return 0; 39093d2725aSMaxime Ripard } 39193d2725aSMaxime Ripard 3924e85e535SNicolas Saenz Julienne static int raspberrypi_clk_probe(struct platform_device *pdev) 3934e85e535SNicolas Saenz Julienne { 394d4b4f1b6SMaxime Ripard struct clk_hw_onecell_data *clk_data; 3954e85e535SNicolas Saenz Julienne struct device_node *firmware_node; 3964e85e535SNicolas Saenz Julienne struct device *dev = &pdev->dev; 3974e85e535SNicolas Saenz Julienne struct rpi_firmware *firmware; 3984e85e535SNicolas Saenz Julienne struct raspberrypi_clk *rpi; 399d4b4f1b6SMaxime Ripard int ret; 4004e85e535SNicolas Saenz Julienne 401fbac2e77SMaxime Ripard /* 402fbac2e77SMaxime Ripard * We can be probed either through the an old-fashioned 403fbac2e77SMaxime Ripard * platform device registration or through a DT node that is a 404fbac2e77SMaxime Ripard * child of the firmware node. Handle both cases. 405fbac2e77SMaxime Ripard */ 406fbac2e77SMaxime Ripard if (dev->of_node) 407fbac2e77SMaxime Ripard firmware_node = of_get_parent(dev->of_node); 408fbac2e77SMaxime Ripard else 4094e85e535SNicolas Saenz Julienne firmware_node = of_find_compatible_node(NULL, NULL, 4104e85e535SNicolas Saenz Julienne "raspberrypi,bcm2835-firmware"); 4114e85e535SNicolas Saenz Julienne if (!firmware_node) { 4124e85e535SNicolas Saenz Julienne dev_err(dev, "Missing firmware node\n"); 4134e85e535SNicolas Saenz Julienne return -ENOENT; 4144e85e535SNicolas Saenz Julienne } 4154e85e535SNicolas Saenz Julienne 4163c4084f9SNicolas Saenz Julienne firmware = devm_rpi_firmware_get(&pdev->dev, firmware_node); 4174e85e535SNicolas Saenz Julienne of_node_put(firmware_node); 4184e85e535SNicolas Saenz Julienne if (!firmware) 4194e85e535SNicolas Saenz Julienne return -EPROBE_DEFER; 4204e85e535SNicolas Saenz Julienne 4214e85e535SNicolas Saenz Julienne rpi = devm_kzalloc(dev, sizeof(*rpi), GFP_KERNEL); 4224e85e535SNicolas Saenz Julienne if (!rpi) 4234e85e535SNicolas Saenz Julienne return -ENOMEM; 4244e85e535SNicolas Saenz Julienne 4254e85e535SNicolas Saenz Julienne rpi->dev = dev; 4264e85e535SNicolas Saenz Julienne rpi->firmware = firmware; 427e2bb1834SNicolas Saenz Julienne platform_set_drvdata(pdev, rpi); 4284e85e535SNicolas Saenz Julienne 429be1559f6SMaxime Ripard clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, 430be1559f6SMaxime Ripard RPI_FIRMWARE_NUM_CLK_ID), 431d4b4f1b6SMaxime Ripard GFP_KERNEL); 432d4b4f1b6SMaxime Ripard if (!clk_data) 433d4b4f1b6SMaxime Ripard return -ENOMEM; 434d4b4f1b6SMaxime Ripard 43593d2725aSMaxime Ripard ret = raspberrypi_discover_clocks(rpi, clk_data); 43693d2725aSMaxime Ripard if (ret) 43793d2725aSMaxime Ripard return ret; 438d4b4f1b6SMaxime Ripard 439d4b4f1b6SMaxime Ripard ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, 440d4b4f1b6SMaxime Ripard clk_data); 441d4b4f1b6SMaxime Ripard if (ret) 442d4b4f1b6SMaxime Ripard return ret; 4434e85e535SNicolas Saenz Julienne 444e2bb1834SNicolas Saenz Julienne rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", 445e2bb1834SNicolas Saenz Julienne -1, NULL, 0); 446e2bb1834SNicolas Saenz Julienne 447e2bb1834SNicolas Saenz Julienne return 0; 448e2bb1834SNicolas Saenz Julienne } 449e2bb1834SNicolas Saenz Julienne 450e2bb1834SNicolas Saenz Julienne static int raspberrypi_clk_remove(struct platform_device *pdev) 451e2bb1834SNicolas Saenz Julienne { 452e2bb1834SNicolas Saenz Julienne struct raspberrypi_clk *rpi = platform_get_drvdata(pdev); 453e2bb1834SNicolas Saenz Julienne 454e2bb1834SNicolas Saenz Julienne platform_device_unregister(rpi->cpufreq); 455e2bb1834SNicolas Saenz Julienne 4564e85e535SNicolas Saenz Julienne return 0; 4574e85e535SNicolas Saenz Julienne } 4584e85e535SNicolas Saenz Julienne 459fbac2e77SMaxime Ripard static const struct of_device_id raspberrypi_clk_match[] = { 460fbac2e77SMaxime Ripard { .compatible = "raspberrypi,firmware-clocks" }, 461fbac2e77SMaxime Ripard { }, 462fbac2e77SMaxime Ripard }; 463fbac2e77SMaxime Ripard MODULE_DEVICE_TABLE(of, raspberrypi_clk_match); 464fbac2e77SMaxime Ripard 4654e85e535SNicolas Saenz Julienne static struct platform_driver raspberrypi_clk_driver = { 4664e85e535SNicolas Saenz Julienne .driver = { 4674e85e535SNicolas Saenz Julienne .name = "raspberrypi-clk", 468fbac2e77SMaxime Ripard .of_match_table = raspberrypi_clk_match, 4694e85e535SNicolas Saenz Julienne }, 4704e85e535SNicolas Saenz Julienne .probe = raspberrypi_clk_probe, 471e2bb1834SNicolas Saenz Julienne .remove = raspberrypi_clk_remove, 4724e85e535SNicolas Saenz Julienne }; 4734e85e535SNicolas Saenz Julienne module_platform_driver(raspberrypi_clk_driver); 4744e85e535SNicolas Saenz Julienne 4754e85e535SNicolas Saenz Julienne MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>"); 4764e85e535SNicolas Saenz Julienne MODULE_DESCRIPTION("Raspberry Pi firmware clock driver"); 4774e85e535SNicolas Saenz Julienne MODULE_LICENSE("GPL"); 4784e85e535SNicolas Saenz Julienne MODULE_ALIAS("platform:raspberrypi-clk"); 479