Hi Nicolas, the following comments applies only in case Eric is fine with the whole approach.
On 20.05.19 12:47, Nicolas Saenz Julienne wrote: > Raspberry Pi's firmware, which runs in a dedicated processor, keeps maybe we should clarify that the firmware is running in the VPU > track of the board's temperature and voltage. It's resposible for > scaling the CPU frequency whenever it deems the device reached an unsafe > state. On top of that the firmware provides an interface which allows > Linux to to query the clock's state or change it's frequency. I think this requires a separate update of the devicetree binding. > > Being the sole user of the bcm2835 clock driver, this integrates the > firmware interface into the clock driver and adds a first user: the CPU > pll, also known as 'pllb'. Please verify that the kernel still works (and this clock driver probe) under the following conditions: - CONFIG_RASPBERRYPI_FIRMWARE=n - CONFIG_RASPBERRYPI_FIRMWARE=m - older DTBs without patch #1 > > Signed-off-by: Nicolas Saenz Julienne <[email protected]> > --- > drivers/clk/bcm/clk-bcm2835.c | 274 ++++++++++++++++++++++++++++++++-- > 1 file changed, 259 insertions(+), 15 deletions(-) > > diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c > index 5aea110672f3..ce5b16f3704e 100644 > --- a/drivers/clk/bcm/clk-bcm2835.c > +++ b/drivers/clk/bcm/clk-bcm2835.c > @@ -35,6 +35,7 @@ > #include <linux/platform_device.h> > #include <linux/slab.h> > #include <dt-bindings/clock/bcm2835.h> > +#include <soc/bcm2835/raspberrypi-firmware.h> > > #define CM_PASSWORD 0x5a000000 > > @@ -289,6 +290,30 @@ > #define LOCK_TIMEOUT_NS 100000000 > #define BCM2835_MAX_FB_RATE 1750000000u > > +#define RPI_FIRMWARE_ARM_CLK_ID 0x000000003 > +#define RPI_FIRMWARE_STATE_ENABLE_BIT 0x1 > +#define RPI_FIRMWARE_STATE_WAIT_BIT 0x2 > + > +/* > + * Structure of the message passed to Raspberry Pi's firmware in order to > + * change clock rates. The 'disable_turbo' option is only available to the > ARM > + * clock (pllb) which we enable by default as turbo mode will alter multiple > + * clocks at once. > + * > + * Even though we're able to access the clock registers directly we're bound > to > + * use the firmware interface as the firmware ultimately takes care of > + * mitigating overheating/undervoltage situations and we would be changing > + * frequencies behind his back. > + * > + * For more information on the firmware interface check: > + * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface > + */ > +struct bcm2835_firmware_prop { > + u32 id; > + u32 val; > + u32 disable_turbo; > +} __packed; > + > /* > * Names of clocks used within the driver that need to be replaced > * with an external parent's name. This array is in the order that > @@ -316,6 +341,8 @@ struct bcm2835_cprman { > */ > const char *real_parent_names[ARRAY_SIZE(cprman_parent_names)]; > > + struct rpi_firmware *firmware; > + > /* Must be last */ > struct clk_hw_onecell_data onecell; > }; > @@ -424,6 +451,23 @@ struct bcm2835_pll_data { > unsigned long max_fb_rate; > }; > > +struct bcm2835_fw_pll_data { > + const char *name; > + int firmware_clk_id; > + u32 flags; > + > + unsigned long min_rate; > + unsigned long max_rate; > + > + /* > + * The rate provided to the firmware interface might not always match > + * the clock subsystem's. For instance, in the case of the ARM cpu > + * clock, even though the firmware ultimately edits 'pllb' it takes the > + * expected 'pllb_arm' rate as it's input. > + */ > + unsigned int rate_rescale; > +}; > + > struct bcm2835_pll_ana_bits { > u32 mask0; > u32 set0; > @@ -505,6 +549,7 @@ struct bcm2835_pll { > struct clk_hw hw; > struct bcm2835_cprman *cprman; > const struct bcm2835_pll_data *data; > + const struct bcm2835_fw_pll_data *fw_data; > }; > > static int bcm2835_pll_is_on(struct clk_hw *hw) > @@ -517,6 +562,41 @@ static int bcm2835_pll_is_on(struct clk_hw *hw) > A2W_PLL_CTRL_PRST_DISABLE; > } > > +static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag, > + u32 clk, u32 *val) > +{ > + int ret; > + struct bcm2835_firmware_prop msg = { > + .id = clk, > + .val = *val, > + .disable_turbo = 1, > + }; > + > + ret = rpi_firmware_property(firmware, tag, &msg, sizeof(msg)); > + if (ret) > + return ret; > + > + *val = msg.val; > + > + return 0; > +} > + > +static int bcm2835_fw_pll_is_on(struct clk_hw *hw) > +{ > + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); > + struct bcm2835_cprman *cprman = pll->cprman; > + u32 val = 0; > + int ret; > + > + ret = raspberrypi_clock_property(cprman->firmware, > + RPI_FIRMWARE_GET_CLOCK_STATE, > + pll->fw_data->firmware_clk_id, &val); > + if (ret) > + return 0; > + > + return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT); > +} > + > static void bcm2835_pll_choose_ndiv_and_fdiv(unsigned long rate, > unsigned long parent_rate, > u32 *ndiv, u32 *fdiv) > @@ -547,16 +627,37 @@ static long bcm2835_pll_round_rate(struct clk_hw *hw, > unsigned long rate, > unsigned long *parent_rate) > { > struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); > - const struct bcm2835_pll_data *data = pll->data; > u32 ndiv, fdiv; > > - rate = clamp(rate, data->min_rate, data->max_rate); > + if (pll->data) > + rate = clamp(rate, pll->data->min_rate, pll->data->max_rate); > + else > + rate = clamp(rate, pll->fw_data->min_rate, > + pll->fw_data->max_rate); > > bcm2835_pll_choose_ndiv_and_fdiv(rate, *parent_rate, &ndiv, &fdiv); > > return bcm2835_pll_rate_from_divisors(*parent_rate, ndiv, fdiv, 1); > } > > +static unsigned long bcm2835_fw_pll_get_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); > + struct bcm2835_cprman *cprman = pll->cprman; > + u32 val = 0; > + int ret; > + > + ret = raspberrypi_clock_property(cprman->firmware, > + RPI_FIRMWARE_GET_CLOCK_RATE, > + pll->fw_data->firmware_clk_id, > + &val); > + if (ret) > + return ret; > + > + return val * pll->fw_data->rate_rescale; > +} > + > static unsigned long bcm2835_pll_get_rate(struct clk_hw *hw, > unsigned long parent_rate) > { > @@ -584,6 +685,35 @@ static unsigned long bcm2835_pll_get_rate(struct clk_hw > *hw, > return bcm2835_pll_rate_from_divisors(parent_rate, ndiv, fdiv, pdiv); > } > > +static int bcm2835_fw_pll_on(struct clk_hw *hw) > +{ > + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); > + struct bcm2835_cprman *cprman = pll->cprman; > + u32 val; > + int ret; > + > + val = RPI_FIRMWARE_STATE_ENABLE_BIT | RPI_FIRMWARE_STATE_WAIT_BIT; > + > + ret = raspberrypi_clock_property(cprman->firmware, > + RPI_FIRMWARE_SET_CLOCK_STATE, > + pll->fw_data->firmware_clk_id, &val); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static void bcm2835_fw_pll_off(struct clk_hw *hw) > +{ > + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); > + struct bcm2835_cprman *cprman = pll->cprman; > + u32 val = RPI_FIRMWARE_STATE_WAIT_BIT; > + > + raspberrypi_clock_property(cprman->firmware, > + RPI_FIRMWARE_SET_CLOCK_STATE, > + pll->fw_data->firmware_clk_id, &val); > +} > + > static void bcm2835_pll_off(struct clk_hw *hw) > { > struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); > @@ -651,6 +781,25 @@ bcm2835_pll_write_ana(struct bcm2835_cprman *cprman, u32 > ana_reg_base, u32 *ana) > cprman_write(cprman, ana_reg_base + i * 4, ana[i]); > } > > +static int bcm2835_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long parent_rate) > +{ > + struct bcm2835_pll *pll = container_of(hw, struct bcm2835_pll, hw); > + u32 new_rate = rate / pll->fw_data->rate_rescale; > + struct bcm2835_cprman *cprman = pll->cprman; > + int ret; > + > + ret = raspberrypi_clock_property(cprman->firmware, > + RPI_FIRMWARE_SET_CLOCK_RATE, > + pll->fw_data->firmware_clk_id, > + &new_rate); > + if (ret) > + dev_err(cprman->dev, "Failed to change %s frequency: %d", > + clk_hw_get_name(hw), ret); I think we should print this error only once or at least rate limited. > + > + return ret; > +} > + > static int bcm2835_pll_set_rate(struct clk_hw *hw, > unsigned long rate, unsigned long parent_rate) > { > @@ -759,6 +908,15 @@ static const struct clk_ops bcm2835_pll_clk_ops = { > .debug_init = bcm2835_pll_debug_init, > }; > > +static const struct clk_ops bcm2835_firmware_pll_clk_ops = { > + .is_prepared = bcm2835_fw_pll_is_on, > + .prepare = bcm2835_fw_pll_on, > + .unprepare = bcm2835_fw_pll_off, > + .recalc_rate = bcm2835_fw_pll_get_rate, > + .set_rate = bcm2835_fw_pll_set_rate, > + .round_rate = bcm2835_pll_round_rate, > +}; > + > struct bcm2835_pll_divider { > struct clk_divider div; > struct bcm2835_cprman *cprman; > @@ -1287,6 +1445,83 @@ static const struct clk_ops bcm2835_vpu_clock_clk_ops > = { > .debug_init = bcm2835_clock_debug_init, > }; > > +static int bcm2835_firmware_get_min_max_rates(struct bcm2835_cprman *cprman, > + struct bcm2835_fw_pll_data *data) > +{ > + u32 min_rate = 0, max_rate = 0; > + int ret; > + > + ret = raspberrypi_clock_property(cprman->firmware, > + RPI_FIRMWARE_GET_MIN_CLOCK_RATE, > + data->firmware_clk_id, > + &min_rate); > + if (ret) { > + dev_err(cprman->dev, "Failed to get %s min freq: %d\n", > + data->name, ret); > + return ret; > + } > + > + ret = raspberrypi_clock_property(cprman->firmware, > + RPI_FIRMWARE_GET_MAX_CLOCK_RATE, > + data->firmware_clk_id, > + &max_rate); > + if (ret) { > + dev_err(cprman->dev, "Failed to get %s max freq: %d\n", > + data->name, ret); > + return ret; > + } > + > + data->min_rate = min_rate * data->rate_rescale; > + data->max_rate = max_rate * data->rate_rescale; > + > + dev_info(cprman->dev, "min %lu, max %lu\n", data->min_rate, > data->max_rate); > + return 0; > +} > + > +static struct clk_hw *bcm2835_register_fw_pll(struct bcm2835_cprman *cprman, > + const struct bcm2835_fw_pll_data *data) > +{ > + struct bcm2835_fw_pll_data *fw_data; > + struct clk_init_data init; > + struct bcm2835_pll *pll; > + int ret; > + > + memset(&init, 0, sizeof(init)); > + > + /* All of the PLLs derive from the external oscillator. */ > + init.parent_names = &cprman->real_parent_names[0]; > + init.num_parents = 1; > + init.name = data->name; > + init.ops = &bcm2835_firmware_pll_clk_ops; > + init.flags = data->flags | CLK_IGNORE_UNUSED; > + > + pll = kzalloc(sizeof(*pll), GFP_KERNEL); > + if (!pll) > + return NULL; > + > + /* > + * As the max and min rate values are firmware dependent we need a non > + * constant version of struct bcm2835_fw_pll_data. > + */ > + fw_data = devm_kmemdup(cprman->dev, data, sizeof(*data), > + GFP_KERNEL); > + if (!fw_data) > + return NULL; > + > + ret = bcm2835_firmware_get_min_max_rates(cprman, fw_data); > + if (ret) > + return NULL; > + > + pll->cprman = cprman; > + pll->hw.init = &init; > + pll->fw_data = fw_data; > + > + ret = devm_clk_hw_register(cprman->dev, &pll->hw); > + if (ret) > + return NULL; > + return &pll->hw; > +} > + > static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman, > const struct bcm2835_pll_data *data) > { > @@ -1462,6 +1697,9 @@ struct bcm2835_clk_desc { > #define REGISTER_PLL(...) _REGISTER(&bcm2835_register_pll, \ > &(struct bcm2835_pll_data) \ > {__VA_ARGS__}) > +#define REGISTER_FW_PLL(...) _REGISTER(&bcm2835_register_fw_pll, \ > + &(struct bcm2835_fw_pll_data) \ > + {__VA_ARGS__}) > #define REGISTER_PLL_DIV(...) > _REGISTER(&bcm2835_register_pll_divider, \ > &(struct bcm2835_pll_divider_data) \ > {__VA_ARGS__}) > @@ -1654,21 +1892,11 @@ static const struct bcm2835_clk_desc clk_desc_array[] > = { > .flags = CLK_SET_RATE_PARENT), > > /* PLLB is used for the ARM's clock. */ > - [BCM2835_PLLB] = REGISTER_PLL( > + [BCM2835_PLLB] = REGISTER_FW_PLL( > .name = "pllb", > - .cm_ctrl_reg = CM_PLLB, > - .a2w_ctrl_reg = A2W_PLLB_CTRL, > - .frac_reg = A2W_PLLB_FRAC, > - .ana_reg_base = A2W_PLLB_ANA0, > - .reference_enable_mask = A2W_XOSC_CTRL_PLLB_ENABLE, > - .lock_mask = CM_LOCK_FLOCKB, > .flags = CLK_GET_RATE_NOCACHE, > - > - .ana = &bcm2835_ana_default, > - > - .min_rate = 600000000u, > - .max_rate = 3000000000u, > - .max_fb_rate = BCM2835_MAX_FB_RATE), > + .rate_rescale = 2, > + .firmware_clk_id = RPI_FIRMWARE_ARM_CLK_ID), > [BCM2835_PLLB_ARM] = REGISTER_PLL_DIV( > .name = "pllb_arm", > .source_pll = "pllb", > @@ -2133,9 +2361,24 @@ static int bcm2835_clk_probe(struct platform_device > *pdev) > struct resource *res; > const struct bcm2835_clk_desc *desc; > const size_t asize = ARRAY_SIZE(clk_desc_array); > + struct device_node *firmware_node; > + struct rpi_firmware *firmware; > size_t i; > int ret; > > + firmware_node = of_find_node_by_name(NULL, "firmware"); I prefer of_find_compatible_node() which is more specific. > + if (!firmware_node) { > + dev_err(dev, "Missing firmware node\n"); > + return -ENOENT; > + } The firmware node should be optional for the driver. > + > + firmware = rpi_firmware_get(firmware_node); > + of_node_put(firmware_node); > + if (!firmware) { > + dev_err(dev, "Can't get raspberrypi's firmware\n"); For in case the driver for the Raspberry Pifirmware is enabled, we shouldn't spam with errors here. Stefan

