On current firmware versions, RPI_FIRMWARE_SET_CLOCK_STATE doesn't actually power off the clock. To achieve meaningful power savings, the clock rate must be set to the minimum before disabling. This might be fixed in future firmware releases.
Rather than pushing rate management to clock consumers, handle it directly in the clock framework's prepare/unprepare callbacks. In unprepare, set the rate to the minimum before disabling the clock. In prepare, for clocks marked with `maximize` (currently v3d), restore the rate to the maximum after enabling. Signed-off-by: Maíra Canal <[email protected]> --- drivers/clk/bcm/clk-raspberrypi.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 1a9162f0ae31e330c46f6eafdd00350599b0eede..df2d246eb6efd0c9a9c508eb7a43a6a70e1c3810 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -289,16 +289,31 @@ static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, static int raspberrypi_fw_prepare(struct clk_hw *hw) { const struct raspberrypi_clk_data *data = clk_hw_to_data(hw); + struct raspberrypi_clk_variant *variant = data->variant; struct raspberrypi_clk *rpi = data->rpi; u32 state = RPI_FIRMWARE_STATE_ENABLE_BIT; int ret; ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_SET_CLOCK_STATE, &state); - if (ret) + if (ret) { dev_err_ratelimited(rpi->dev, "Failed to set clock %s state to on: %d\n", clk_hw_get_name(hw), ret); + return ret; + } + + /* + * For clocks marked with 'maximize', restore the rate to the + * maximum after enabling. This compensates for the rate being + * set to minimum during unprepare (see raspberrypi_fw_unprepare). + */ + if (variant->maximize) { + unsigned long min_rate, max_rate; + + clk_hw_get_rate_range(hw, &min_rate, &max_rate); + ret = raspberrypi_fw_set_rate(hw, max_rate, 0); + } return ret; } @@ -307,9 +322,27 @@ static void raspberrypi_fw_unprepare(struct clk_hw *hw) { const struct raspberrypi_clk_data *data = clk_hw_to_data(hw); struct raspberrypi_clk *rpi = data->rpi; + unsigned long min_rate, max_rate; u32 state = 0; int ret; + clk_hw_get_rate_range(hw, &min_rate, &max_rate); + + /* + * Setting the rate in unprepare is a deviation from the usual CCF + * behavior, where unprepare only gates the clock. However, this is + * needed, as RPI_FIRMWARE_SET_CLOCK_STATE doesn't actually power off + * the clock on current firmware versions. Setting the rate to minimum + * before disabling the clock is the only way to achieve meaningful + * power savings. + * + * This is safe because no consumer should rely on the rate of an + * unprepared clock. Any consumer must call clk_prepare() before use, + * at which point the rate is either restored to maximum (for clocks + * with the 'maximize' flag) or re-established by the consumer. + */ + raspberrypi_fw_set_rate(hw, min_rate, 0); + ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_SET_CLOCK_STATE, &state); if (ret) @@ -387,9 +420,6 @@ static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi, } } - if (variant->maximize) - variant->min_rate = max_rate; - if (variant->min_rate) { unsigned long rate; -- 2.53.0
