From 1031c2b4befd725cb88e373d207bd147572d8fbc Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Wed, 21 Feb 2024 16:11:51 +0100 Subject: pwm: meson: Add generic compatible for meson8 to sm1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new compatible support in the Amlogic PWM driver. The PWM HW is actually the same for all SoCs supported so far. A specific compatible is needed only because the clock sources of the PWMs are hard-coded in the driver. It is better to have the clock source described in DT but this changes the bindings so a new compatible must be introduced. When all supported platform have migrated to the new compatible, support for the legacy ones may be removed from the driver. The addition of this new compatible makes the old ones obsolete, as described in the DT documentation. Adding a callback to setup the clock will also make it easier to add support for the new PWM HW found in a1, s4, c3 and t7 SoC families. Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20240221151154.26452-6-jbrunet@baylibre.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-meson.c | 195 ++++++++++++++++++++++++++++++------------------ 1 file changed, 121 insertions(+), 74 deletions(-) (limited to 'drivers/pwm/pwm-meson.c') diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index a02fdbc61256..ea96c5973488 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -98,6 +98,7 @@ struct meson_pwm_channel { struct meson_pwm_data { const char *const parent_names[MESON_NUM_MUX_PARENTS]; + int (*channels_init)(struct pwm_chip *chip); }; struct meson_pwm { @@ -338,86 +339,16 @@ static const struct pwm_ops meson_pwm_ops = { .get_state = meson_pwm_get_state, }; -static const struct meson_pwm_data pwm_meson8b_data = { - .parent_names = { "xtal", NULL, "fclk_div4", "fclk_div3" }, -}; - -/* - * Only the 2 first inputs of the GXBB AO PWMs are valid - * The last 2 are grounded - */ -static const struct meson_pwm_data pwm_gxbb_ao_data = { - .parent_names = { "xtal", "clk81", NULL, NULL }, -}; - -static const struct meson_pwm_data pwm_axg_ee_data = { - .parent_names = { "xtal", "fclk_div5", "fclk_div4", "fclk_div3" }, -}; - -static const struct meson_pwm_data pwm_axg_ao_data = { - .parent_names = { "xtal", "axg_ao_clk81", "fclk_div4", "fclk_div5" }, -}; - -static const struct meson_pwm_data pwm_g12a_ao_ab_data = { - .parent_names = { "xtal", "g12a_ao_clk81", "fclk_div4", "fclk_div5" }, -}; - -static const struct meson_pwm_data pwm_g12a_ao_cd_data = { - .parent_names = { "xtal", "g12a_ao_clk81", NULL, NULL }, -}; - -static const struct of_device_id meson_pwm_matches[] = { - { - .compatible = "amlogic,meson8b-pwm", - .data = &pwm_meson8b_data - }, - { - .compatible = "amlogic,meson-gxbb-pwm", - .data = &pwm_meson8b_data - }, - { - .compatible = "amlogic,meson-gxbb-ao-pwm", - .data = &pwm_gxbb_ao_data - }, - { - .compatible = "amlogic,meson-axg-ee-pwm", - .data = &pwm_axg_ee_data - }, - { - .compatible = "amlogic,meson-axg-ao-pwm", - .data = &pwm_axg_ao_data - }, - { - .compatible = "amlogic,meson-g12a-ee-pwm", - .data = &pwm_meson8b_data - }, - { - .compatible = "amlogic,meson-g12a-ao-pwm-ab", - .data = &pwm_g12a_ao_ab_data - }, - { - .compatible = "amlogic,meson-g12a-ao-pwm-cd", - .data = &pwm_g12a_ao_cd_data - }, - {}, -}; -MODULE_DEVICE_TABLE(of, meson_pwm_matches); - -static int meson_pwm_init_channels(struct pwm_chip *chip) +static int meson_pwm_init_clocks_meson8b(struct pwm_chip *chip, + struct clk_parent_data *mux_parent_data) { struct meson_pwm *meson = to_meson_pwm(chip); - struct clk_parent_data mux_parent_data[MESON_NUM_MUX_PARENTS] = {}; struct device *dev = pwmchip_parent(chip); unsigned int i; char name[255]; int err; - for (i = 0; i < MESON_NUM_MUX_PARENTS; i++) { - mux_parent_data[i].index = -1; - mux_parent_data[i].name = meson->data->parent_names[i]; - } - - for (i = 0; i < chip->npwm; i++) { + for (i = 0; i < MESON_NUM_PWMS; i++) { struct meson_pwm_channel *channel = &meson->channels[i]; struct clk_parent_data div_parent = {}, gate_parent = {}; struct clk_init_data init = {}; @@ -495,6 +426,122 @@ static int meson_pwm_init_channels(struct pwm_chip *chip) return 0; } +static int meson_pwm_init_channels_meson8b_legacy(struct pwm_chip *chip) +{ + struct clk_parent_data mux_parent_data[MESON_NUM_MUX_PARENTS] = {}; + struct meson_pwm *meson = to_meson_pwm(chip); + int i; + + dev_warn_once(pwmchip_parent(chip), + "using obsolete compatible, please consider updating dt\n"); + + for (i = 0; i < MESON_NUM_MUX_PARENTS; i++) { + mux_parent_data[i].index = -1; + mux_parent_data[i].name = meson->data->parent_names[i]; + } + + return meson_pwm_init_clocks_meson8b(chip, mux_parent_data); +} + +static int meson_pwm_init_channels_meson8b_v2(struct pwm_chip *chip) +{ + struct clk_parent_data mux_parent_data[MESON_NUM_MUX_PARENTS] = {}; + int i; + + /* + * NOTE: Instead of relying on the hard coded names in the driver + * as the legacy version, this relies on DT to provide the list of + * clocks. + * For once, using input numbers actually makes more sense than names. + * Also DT requires clock-names to be explicitly ordered, so there is + * no point bothering with clock names in this case. + */ + for (i = 0; i < MESON_NUM_MUX_PARENTS; i++) + mux_parent_data[i].index = i; + + return meson_pwm_init_clocks_meson8b(chip, mux_parent_data); +} + +static const struct meson_pwm_data pwm_meson8b_data = { + .parent_names = { "xtal", NULL, "fclk_div4", "fclk_div3" }, + .channels_init = meson_pwm_init_channels_meson8b_legacy, +}; + +/* + * Only the 2 first inputs of the GXBB AO PWMs are valid + * The last 2 are grounded + */ +static const struct meson_pwm_data pwm_gxbb_ao_data = { + .parent_names = { "xtal", "clk81", NULL, NULL }, + .channels_init = meson_pwm_init_channels_meson8b_legacy, +}; + +static const struct meson_pwm_data pwm_axg_ee_data = { + .parent_names = { "xtal", "fclk_div5", "fclk_div4", "fclk_div3" }, + .channels_init = meson_pwm_init_channels_meson8b_legacy, +}; + +static const struct meson_pwm_data pwm_axg_ao_data = { + .parent_names = { "xtal", "axg_ao_clk81", "fclk_div4", "fclk_div5" }, + .channels_init = meson_pwm_init_channels_meson8b_legacy, +}; + +static const struct meson_pwm_data pwm_g12a_ao_ab_data = { + .parent_names = { "xtal", "g12a_ao_clk81", "fclk_div4", "fclk_div5" }, + .channels_init = meson_pwm_init_channels_meson8b_legacy, +}; + +static const struct meson_pwm_data pwm_g12a_ao_cd_data = { + .parent_names = { "xtal", "g12a_ao_clk81", NULL, NULL }, + .channels_init = meson_pwm_init_channels_meson8b_legacy, +}; + +static const struct meson_pwm_data pwm_meson8_v2_data = { + .channels_init = meson_pwm_init_channels_meson8b_v2, +}; + +static const struct of_device_id meson_pwm_matches[] = { + { + .compatible = "amlogic,meson8-pwm-v2", + .data = &pwm_meson8_v2_data + }, + /* The following compatibles are obsolete */ + { + .compatible = "amlogic,meson8b-pwm", + .data = &pwm_meson8b_data + }, + { + .compatible = "amlogic,meson-gxbb-pwm", + .data = &pwm_meson8b_data + }, + { + .compatible = "amlogic,meson-gxbb-ao-pwm", + .data = &pwm_gxbb_ao_data + }, + { + .compatible = "amlogic,meson-axg-ee-pwm", + .data = &pwm_axg_ee_data + }, + { + .compatible = "amlogic,meson-axg-ao-pwm", + .data = &pwm_axg_ao_data + }, + { + .compatible = "amlogic,meson-g12a-ee-pwm", + .data = &pwm_meson8b_data + }, + { + .compatible = "amlogic,meson-g12a-ao-pwm-ab", + .data = &pwm_g12a_ao_ab_data + }, + { + .compatible = "amlogic,meson-g12a-ao-pwm-cd", + .data = &pwm_g12a_ao_cd_data + }, + {}, +}; +MODULE_DEVICE_TABLE(of, meson_pwm_matches); + static int meson_pwm_probe(struct platform_device *pdev) { struct pwm_chip *chip; @@ -515,7 +562,7 @@ static int meson_pwm_probe(struct platform_device *pdev) meson->data = of_device_get_match_data(&pdev->dev); - err = meson_pwm_init_channels(chip); + err = meson->data->channels_init(chip); if (err < 0) return err; -- cgit v1.2.3-59-g8ed1b From 6b2d60a543c4ffbb6bd9703a2714b2d5fbfc5781 Mon Sep 17 00:00:00 2001 From: George Stark Date: Thu, 25 Apr 2024 20:12:51 +0300 Subject: pwm: meson: Drop unneeded check in .get_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop checking state argument for NULL pointer in meson_pwm_get_state() due to it is called only from pwm core with always valid arguments. Signed-off-by: Dmitry Rokosov Signed-off-by: George Stark Link: https://lore.kernel.org/r/20240425171253.2752877-2-gnstark@salutedevices.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-meson.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/pwm/pwm-meson.c') diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index ea96c5973488..f4d70da621ec 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -311,9 +311,6 @@ static int meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct meson_pwm_channel *channel; u32 value; - if (!state) - return 0; - channel = &meson->channels[pwm->hwpwm]; channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; -- cgit v1.2.3-59-g8ed1b From 3e551115aee079931b82e1ec78c05f3d5033473f Mon Sep 17 00:00:00 2001 From: George Stark Date: Thu, 25 Apr 2024 20:12:52 +0300 Subject: pwm: meson: Add check for error from clk_round_rate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clk_round_rate() can return not only zero if requested frequency can not be provided but also negative error code so add check for it too. Also change type of variable holding clk_round_rate() result from unsigned long to long. It's safe due to clk_round_rate() returns long. Fixes: 329db102a26d ("pwm: meson: make full use of common clock framework") Signed-off-by: Dmitry Rokosov Signed-off-by: George Stark Link: https://lore.kernel.org/r/20240425171253.2752877-3-gnstark@salutedevices.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-meson.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/pwm/pwm-meson.c') diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index f4d70da621ec..4a652d500dfc 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -148,7 +148,7 @@ static int meson_pwm_calc(struct pwm_chip *chip, struct pwm_device *pwm, struct meson_pwm *meson = to_meson_pwm(chip); struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; unsigned int cnt, duty_cnt; - unsigned long fin_freq; + long fin_freq; u64 duty, period, freq; duty = state->duty_cycle; @@ -168,12 +168,13 @@ static int meson_pwm_calc(struct pwm_chip *chip, struct pwm_device *pwm, freq = ULONG_MAX; fin_freq = clk_round_rate(channel->clk, freq); - if (fin_freq == 0) { - dev_err(pwmchip_parent(chip), "invalid source clock frequency\n"); - return -EINVAL; + if (fin_freq <= 0) { + dev_err(pwmchip_parent(chip), + "invalid source clock frequency %llu\n", freq); + return fin_freq ? fin_freq : -EINVAL; } - dev_dbg(pwmchip_parent(chip), "fin_freq: %lu Hz\n", fin_freq); + dev_dbg(pwmchip_parent(chip), "fin_freq: %ld Hz\n", fin_freq); cnt = div_u64(fin_freq * period, NSEC_PER_SEC); if (cnt > 0xffff) { -- cgit v1.2.3-59-g8ed1b From 32c44e1fa921aebf8a5ef9f778534a30aab39313 Mon Sep 17 00:00:00 2001 From: George Stark Date: Thu, 25 Apr 2024 20:12:53 +0300 Subject: pwm: meson: Use mul_u64_u64_div_u64() for frequency calculating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While calculating frequency for the given period u64 numbers are multiplied before division what can lead to overflow in theory so use secure mul_u64_u64_div_u64() which handles overflow correctly. Fixes: 329db102a26d ("pwm: meson: make full use of common clock framework") Suggested-by: Uwe Kleine-König Signed-off-by: George Stark Link: https://lore.kernel.org/r/20240425171253.2752877-4-gnstark@salutedevices.com Signed-off-by: Uwe Kleine-König --- drivers/pwm/pwm-meson.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pwm/pwm-meson.c') diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index 4a652d500dfc..b2f97dfb01bb 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -176,7 +176,7 @@ static int meson_pwm_calc(struct pwm_chip *chip, struct pwm_device *pwm, dev_dbg(pwmchip_parent(chip), "fin_freq: %ld Hz\n", fin_freq); - cnt = div_u64(fin_freq * period, NSEC_PER_SEC); + cnt = mul_u64_u64_div_u64(fin_freq, period, NSEC_PER_SEC); if (cnt > 0xffff) { dev_err(pwmchip_parent(chip), "unable to get period cnt\n"); return -EINVAL; @@ -191,7 +191,7 @@ static int meson_pwm_calc(struct pwm_chip *chip, struct pwm_device *pwm, channel->hi = 0; channel->lo = cnt; } else { - duty_cnt = div_u64(fin_freq * duty, NSEC_PER_SEC); + duty_cnt = mul_u64_u64_div_u64(fin_freq, duty, NSEC_PER_SEC); dev_dbg(pwmchip_parent(chip), "duty=%llu duty_cnt=%u\n", duty, duty_cnt); -- cgit v1.2.3-59-g8ed1b