]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
pwm: sophgo-sg2042: Add support for SG2044
authorLongbin Li <looong.bin@gmail.com>
Wed, 28 May 2025 10:11:38 +0000 (18:11 +0800)
committerUwe Kleine-König <ukleinek@kernel.org>
Mon, 7 Jul 2025 06:39:34 +0000 (08:39 +0200)
Add PWM controller for SG2044 on base of SG2042.

Reviewed-by: Chen Wang <unicorn_wang@outlook.com>
Tested-by: Chen Wang <unicorn_wang@outlook.com>
Signed-off-by: Longbin Li <looong.bin@gmail.com>
Link: https://lore.kernel.org/r/20250528101139.28702-4-looong.bin@gmail.com
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
drivers/pwm/pwm-sophgo-sg2042.c

index f3956563037faed41283238edaf8ccf66cb7c0c8..7d07b0ca7d2988683ff11bc48c1229ea88d60b8e 100644 (file)
@@ -13,6 +13,7 @@
  *   the running period.
  * - When PERIOD and HLPERIOD is set to 0, the PWM wave output will
  *   be stopped and the output is pulled to high.
+ * - SG2044 supports both polarities, SG2042 only normal polarity.
  * See the datasheet [1] for more details.
  * [1]:https://github.com/sophgo/sophgo-doc/tree/main/SG2042/TRM
  */
 #define SG2042_PWM_HLPERIOD(chan) ((chan) * 8 + 0)
 #define SG2042_PWM_PERIOD(chan) ((chan) * 8 + 4)
 
+#define SG2044_PWM_POLARITY            0x40
+#define SG2044_PWM_PWMSTART            0x44
+#define SG2044_PWM_OE                  0xd0
+
 #define SG2042_PWM_CHANNELNUM  4
 
 /**
@@ -84,8 +89,8 @@ static void pwm_sg2042_set_dutycycle(struct pwm_chip *chip, struct pwm_device *p
        period_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->period, NSEC_PER_SEC), U32_MAX);
        hlperiod_ticks = min(mul_u64_u64_div_u64(ddata->clk_rate_hz, state->duty_cycle, NSEC_PER_SEC), U32_MAX);
 
-       dev_dbg(pwmchip_parent(chip), "chan[%u]: PERIOD=%u, HLPERIOD=%u\n",
-               pwm->hwpwm, period_ticks, hlperiod_ticks);
+       dev_dbg(pwmchip_parent(chip), "chan[%u]: ENABLE=%u, PERIOD=%u, HLPERIOD=%u, POLARITY=%u\n",
+               pwm->hwpwm, state->enabled, period_ticks, hlperiod_ticks, state->polarity);
 
        pwm_sg2042_config(ddata, pwm->hwpwm, period_ticks, hlperiod_ticks);
 }
@@ -135,6 +140,74 @@ static int pwm_sg2042_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
        return 0;
 }
 
+static void pwm_sg2044_set_outputen(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
+                                   bool enabled)
+{
+       u32 pwmstart;
+
+       pwmstart = readl(ddata->base + SG2044_PWM_PWMSTART);
+
+       if (enabled)
+               pwmstart |= BIT(pwm->hwpwm);
+       else
+               pwmstart &= ~BIT(pwm->hwpwm);
+
+       writel(pwmstart, ddata->base + SG2044_PWM_PWMSTART);
+}
+
+static void pwm_sg2044_set_outputdir(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
+                                    bool enabled)
+{
+       u32 pwm_oe;
+
+       pwm_oe = readl(ddata->base + SG2044_PWM_OE);
+
+       if (enabled)
+               pwm_oe |= BIT(pwm->hwpwm);
+       else
+               pwm_oe &= ~BIT(pwm->hwpwm);
+
+       writel(pwm_oe, ddata->base + SG2044_PWM_OE);
+}
+
+static void pwm_sg2044_set_polarity(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
+                                   const struct pwm_state *state)
+{
+       u32 pwm_polarity;
+
+       pwm_polarity = readl(ddata->base + SG2044_PWM_POLARITY);
+
+       if (state->polarity == PWM_POLARITY_NORMAL)
+               pwm_polarity &= ~BIT(pwm->hwpwm);
+       else
+               pwm_polarity |= BIT(pwm->hwpwm);
+
+       writel(pwm_polarity, ddata->base + SG2044_PWM_POLARITY);
+}
+
+static int pwm_sg2044_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+                           const struct pwm_state *state)
+{
+       struct sg2042_pwm_ddata *ddata = pwmchip_get_drvdata(chip);
+
+       pwm_sg2044_set_polarity(ddata, pwm, state);
+
+       pwm_sg2042_set_dutycycle(chip, pwm, state);
+
+       /*
+        * re-enable PWMSTART to refresh the register period
+        */
+       pwm_sg2044_set_outputen(ddata, pwm, false);
+
+       if (!state->enabled)
+               return 0;
+
+       pwm_sg2044_set_outputdir(ddata, pwm, true);
+       pwm_sg2044_set_outputen(ddata, pwm, true);
+
+       return 0;
+}
+
 static const struct sg2042_chip_data sg2042_chip_data = {
        .ops = {
                .apply = pwm_sg2042_apply,
@@ -142,11 +215,22 @@ static const struct sg2042_chip_data sg2042_chip_data = {
        },
 };
 
+static const struct sg2042_chip_data sg2044_chip_data = {
+       .ops = {
+               .apply = pwm_sg2044_apply,
+               .get_state = pwm_sg2042_get_state,
+       },
+};
+
 static const struct of_device_id sg2042_pwm_ids[] = {
        {
                .compatible = "sophgo,sg2042-pwm",
                .data = &sg2042_chip_data
        },
+       {
+               .compatible = "sophgo,sg2044-pwm",
+               .data = &sg2044_chip_data
+       },
        { }
 };
 MODULE_DEVICE_TABLE(of, sg2042_pwm_ids);
@@ -212,5 +296,6 @@ static struct platform_driver pwm_sg2042_driver = {
 module_platform_driver(pwm_sg2042_driver);
 
 MODULE_AUTHOR("Chen Wang");
+MODULE_AUTHOR("Longbin Li <looong.bin@gmail.com>");
 MODULE_DESCRIPTION("Sophgo SG2042 PWM driver");
 MODULE_LICENSE("GPL");