From: Longbin Li <looong.bin@gmail.com>
To: "Uwe Kleine-König" <ukleinek@kernel.org>,
"Rob Herring" <robh@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Chen Wang" <unicorn_wang@outlook.com>,
"Inochi Amaoto" <inochiama@gmail.com>,
"Paul Walmsley" <paul.walmsley@sifive.com>,
"Palmer Dabbelt" <palmer@dabbelt.com>,
"Albert Ou" <aou@eecs.berkeley.edu>,
"Alexandre Ghiti" <alex@ghiti.fr>
Cc: ghost <2990955050@qq.com>,
linux-pwm@vger.kernel.org, devicetree@vger.kernel.org,
sophgo@lists.linux.dev, linux-kernel@vger.kernel.org,
linux-riscv@lists.infradead.org,
Longbin Li <looong.bin@gmail.com>
Subject: [PATCH 2/2] pwm: sophgo: add driver for SG2044
Date: Mon, 7 Apr 2025 15:20:39 +0800 [thread overview]
Message-ID: <20250407072056.8629-3-looong.bin@gmail.com> (raw)
In-Reply-To: <20250407072056.8629-1-looong.bin@gmail.com>
From: ghost <2990955050@qq.com>
Add PWM controller for SG2044.
Signed-off-by: Longbin Li <looong.bin@gmail.com>
---
drivers/pwm/pwm-sophgo-sg2042.c | 162 +++++++++++++++++++++++++++-----
1 file changed, 138 insertions(+), 24 deletions(-)
diff --git a/drivers/pwm/pwm-sophgo-sg2042.c b/drivers/pwm/pwm-sophgo-sg2042.c
index ff4639d849ce..c62e8c758d87 100644
--- a/drivers/pwm/pwm-sophgo-sg2042.c
+++ b/drivers/pwm/pwm-sophgo-sg2042.c
@@ -26,20 +26,22 @@
#include <linux/pwm.h>
#include <linux/reset.h>
-/*
- * Offset RegisterName
- * 0x0000 HLPERIOD0
- * 0x0004 PERIOD0
- * 0x0008 HLPERIOD1
- * 0x000C PERIOD1
- * 0x0010 HLPERIOD2
- * 0x0014 PERIOD2
- * 0x0018 HLPERIOD3
- * 0x001C PERIOD3
- * Four groups and every group is composed of HLPERIOD & PERIOD
- */
-#define SG2042_PWM_HLPERIOD(chan) ((chan) * 8 + 0)
-#define SG2042_PWM_PERIOD(chan) ((chan) * 8 + 4)
+#define REG_HLPERIOD 0x0
+#define REG_PERIOD 0x4
+#define REG_GROUP 0x8
+#define REG_POLARITY 0x40
+
+#define REG_PWMSTART 0x44
+#define REG_PWMUPDATE 0x4C
+#define REG_SHIFTCOUNT 0x80
+#define REG_SHIFTSTART 0x90
+#define REG_PWM_OE 0xD0
+
+#define PWM_REG_NUM 0x80
+
+#define PWM_POLARITY_MASK(n) BIT(n)
+#define PWM_HLPERIOD(chan) ((chan) * REG_GROUP + REG_HLPERIOD)
+#define PWM_PERIOD(chan) ((chan) * REG_GROUP + REG_PERIOD)
#define SG2042_PWM_CHANNELNUM 4
@@ -51,6 +53,12 @@
struct sg2042_pwm_ddata {
void __iomem *base;
unsigned long clk_rate_hz;
+ struct mutex lock;
+};
+
+struct sg2042_chip_data {
+ const struct pwm_ops ops;
+ bool atomic;
};
/*
@@ -62,8 +70,8 @@ static void pwm_sg2042_config(struct sg2042_pwm_ddata *ddata, unsigned int chan,
{
void __iomem *base = ddata->base;
- writel(period_ticks, base + SG2042_PWM_PERIOD(chan));
- writel(hlperiod_ticks, base + SG2042_PWM_HLPERIOD(chan));
+ writel(period_ticks, base + PWM_PERIOD(chan));
+ writel(hlperiod_ticks, base + PWM_HLPERIOD(chan));
}
static int pwm_sg2042_apply(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -104,8 +112,8 @@ static int pwm_sg2042_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
u32 hlperiod_ticks;
u32 period_ticks;
- period_ticks = readl(ddata->base + SG2042_PWM_PERIOD(chan));
- hlperiod_ticks = readl(ddata->base + SG2042_PWM_HLPERIOD(chan));
+ period_ticks = readl(ddata->base + PWM_PERIOD(chan));
+ hlperiod_ticks = readl(ddata->base + PWM_HLPERIOD(chan));
if (!period_ticks) {
state->enabled = false;
@@ -123,13 +131,112 @@ static int pwm_sg2042_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
return 0;
}
-static const struct pwm_ops pwm_sg2042_ops = {
- .apply = pwm_sg2042_apply,
- .get_state = pwm_sg2042_get_state,
+static void pwm_sg2044_config(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm, bool enabled)
+{
+ u32 pwm_value;
+
+ pwm_value = readl(ddata->base + REG_PWMSTART);
+
+ if (enabled)
+ writel(pwm_value | BIT(pwm->hwpwm), ddata->base + REG_PWMSTART);
+ else
+ writel(pwm_value & ~BIT(pwm->hwpwm), ddata->base + REG_PWMSTART);
+}
+
+static void pwm_sg2044_set_outputenable(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
+ bool enabled)
+{
+ u32 pwm_value;
+
+ pwm_value = readl(ddata->base + REG_PWM_OE);
+
+ if (enabled)
+ writel(pwm_value | BIT(pwm->hwpwm), ddata->base + REG_PWM_OE);
+ else
+ writel(pwm_value & ~BIT(pwm->hwpwm), ddata->base + REG_PWM_OE);
+}
+
+static int pwm_sg2044_set_polarity(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ enum pwm_polarity polarity;
+ u32 pwm_value;
+
+ pwm_value = readl(ddata->base + REG_POLARITY);
+
+ polarity = state->polarity;
+
+ if (polarity == PWM_POLARITY_NORMAL)
+ pwm_value &= ~BIT(pwm->hwpwm);
+ else
+ pwm_value |= BIT(pwm->hwpwm);
+
+ writel(pwm_value, ddata->base + REG_POLARITY);
+
+ return 0;
+}
+
+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);
+ u32 hlperiod_ticks;
+ u32 period_ticks;
+
+ if (!state->enabled) {
+ pwm_sg2044_config(ddata, pwm, false);
+ return 0;
+ }
+
+ pwm_sg2044_set_polarity(ddata, pwm, state);
+
+ /*
+ * Duration of High level (duty_cycle) = HLPERIOD x Period_of_input_clk
+ * Duration of One Cycle (period) = PERIOD x Period_of_input_clk
+ */
+ 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);
+
+ pwm_sg2042_config(ddata, pwm->hwpwm, period_ticks, hlperiod_ticks);
+
+ guard(mutex)(&ddata->lock);
+
+ /*
+ * re-enable PWMSTART to refresh the register period
+ */
+ pwm_sg2044_config(ddata, pwm, false);
+ pwm_sg2044_set_outputenable(ddata, pwm, true);
+ pwm_sg2044_config(ddata, pwm, true);
+
+ return 0;
+}
+
+static const struct sg2042_chip_data sg2042_chip_data = {
+ .ops = {
+ .apply = pwm_sg2042_apply,
+ .get_state = pwm_sg2042_get_state,
+ },
+ .atomic = true,
+};
+
+static const struct sg2042_chip_data sg2044_chip_data = {
+ .ops = {
+ .apply = pwm_sg2044_apply,
+ .get_state = pwm_sg2042_get_state,
+ },
+ .atomic = false,
};
static const struct of_device_id sg2042_pwm_ids[] = {
- { .compatible = "sophgo,sg2042-pwm" },
+ { .compatible = "sophgo,sg2042-pwm",
+ .data = &sg2042_chip_data },
+ { .compatible = "sophgo,sg2044-pwm",
+ .data = &sg2044_chip_data },
{ }
};
MODULE_DEVICE_TABLE(of, sg2042_pwm_ids);
@@ -137,17 +244,24 @@ MODULE_DEVICE_TABLE(of, sg2042_pwm_ids);
static int pwm_sg2042_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ const struct sg2042_chip_data *chip_data;
struct sg2042_pwm_ddata *ddata;
struct reset_control *rst;
struct pwm_chip *chip;
struct clk *clk;
int ret;
+ chip_data = device_get_match_data(dev);
+ if (!chip_data)
+ return -ENODEV;
+
chip = devm_pwmchip_alloc(dev, SG2042_PWM_CHANNELNUM, sizeof(*ddata));
if (IS_ERR(chip))
return PTR_ERR(chip);
ddata = pwmchip_get_drvdata(chip);
+ mutex_init(&ddata->lock);
+
ddata->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ddata->base))
return PTR_ERR(ddata->base);
@@ -170,8 +284,8 @@ static int pwm_sg2042_probe(struct platform_device *pdev)
if (IS_ERR(rst))
return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset\n");
- chip->ops = &pwm_sg2042_ops;
- chip->atomic = true;
+ chip->ops = &chip_data->ops;
+ chip->atomic = chip_data->atomic;
ret = devm_pwmchip_add(dev, chip);
if (ret < 0)
--
2.48.1
WARNING: multiple messages have this Message-ID (diff)
From: Longbin Li <looong.bin@gmail.com>
To: "Uwe Kleine-König" <ukleinek@kernel.org>,
"Rob Herring" <robh@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Chen Wang" <unicorn_wang@outlook.com>,
"Inochi Amaoto" <inochiama@gmail.com>,
"Paul Walmsley" <paul.walmsley@sifive.com>,
"Palmer Dabbelt" <palmer@dabbelt.com>,
"Albert Ou" <aou@eecs.berkeley.edu>,
"Alexandre Ghiti" <alex@ghiti.fr>
Cc: ghost <2990955050@qq.com>,
linux-pwm@vger.kernel.org, devicetree@vger.kernel.org,
sophgo@lists.linux.dev, linux-kernel@vger.kernel.org,
linux-riscv@lists.infradead.org,
Longbin Li <looong.bin@gmail.com>
Subject: [PATCH 2/2] pwm: sophgo: add driver for SG2044
Date: Mon, 7 Apr 2025 15:20:39 +0800 [thread overview]
Message-ID: <20250407072056.8629-3-looong.bin@gmail.com> (raw)
In-Reply-To: <20250407072056.8629-1-looong.bin@gmail.com>
From: ghost <2990955050@qq.com>
Add PWM controller for SG2044.
Signed-off-by: Longbin Li <looong.bin@gmail.com>
---
drivers/pwm/pwm-sophgo-sg2042.c | 162 +++++++++++++++++++++++++++-----
1 file changed, 138 insertions(+), 24 deletions(-)
diff --git a/drivers/pwm/pwm-sophgo-sg2042.c b/drivers/pwm/pwm-sophgo-sg2042.c
index ff4639d849ce..c62e8c758d87 100644
--- a/drivers/pwm/pwm-sophgo-sg2042.c
+++ b/drivers/pwm/pwm-sophgo-sg2042.c
@@ -26,20 +26,22 @@
#include <linux/pwm.h>
#include <linux/reset.h>
-/*
- * Offset RegisterName
- * 0x0000 HLPERIOD0
- * 0x0004 PERIOD0
- * 0x0008 HLPERIOD1
- * 0x000C PERIOD1
- * 0x0010 HLPERIOD2
- * 0x0014 PERIOD2
- * 0x0018 HLPERIOD3
- * 0x001C PERIOD3
- * Four groups and every group is composed of HLPERIOD & PERIOD
- */
-#define SG2042_PWM_HLPERIOD(chan) ((chan) * 8 + 0)
-#define SG2042_PWM_PERIOD(chan) ((chan) * 8 + 4)
+#define REG_HLPERIOD 0x0
+#define REG_PERIOD 0x4
+#define REG_GROUP 0x8
+#define REG_POLARITY 0x40
+
+#define REG_PWMSTART 0x44
+#define REG_PWMUPDATE 0x4C
+#define REG_SHIFTCOUNT 0x80
+#define REG_SHIFTSTART 0x90
+#define REG_PWM_OE 0xD0
+
+#define PWM_REG_NUM 0x80
+
+#define PWM_POLARITY_MASK(n) BIT(n)
+#define PWM_HLPERIOD(chan) ((chan) * REG_GROUP + REG_HLPERIOD)
+#define PWM_PERIOD(chan) ((chan) * REG_GROUP + REG_PERIOD)
#define SG2042_PWM_CHANNELNUM 4
@@ -51,6 +53,12 @@
struct sg2042_pwm_ddata {
void __iomem *base;
unsigned long clk_rate_hz;
+ struct mutex lock;
+};
+
+struct sg2042_chip_data {
+ const struct pwm_ops ops;
+ bool atomic;
};
/*
@@ -62,8 +70,8 @@ static void pwm_sg2042_config(struct sg2042_pwm_ddata *ddata, unsigned int chan,
{
void __iomem *base = ddata->base;
- writel(period_ticks, base + SG2042_PWM_PERIOD(chan));
- writel(hlperiod_ticks, base + SG2042_PWM_HLPERIOD(chan));
+ writel(period_ticks, base + PWM_PERIOD(chan));
+ writel(hlperiod_ticks, base + PWM_HLPERIOD(chan));
}
static int pwm_sg2042_apply(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -104,8 +112,8 @@ static int pwm_sg2042_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
u32 hlperiod_ticks;
u32 period_ticks;
- period_ticks = readl(ddata->base + SG2042_PWM_PERIOD(chan));
- hlperiod_ticks = readl(ddata->base + SG2042_PWM_HLPERIOD(chan));
+ period_ticks = readl(ddata->base + PWM_PERIOD(chan));
+ hlperiod_ticks = readl(ddata->base + PWM_HLPERIOD(chan));
if (!period_ticks) {
state->enabled = false;
@@ -123,13 +131,112 @@ static int pwm_sg2042_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
return 0;
}
-static const struct pwm_ops pwm_sg2042_ops = {
- .apply = pwm_sg2042_apply,
- .get_state = pwm_sg2042_get_state,
+static void pwm_sg2044_config(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm, bool enabled)
+{
+ u32 pwm_value;
+
+ pwm_value = readl(ddata->base + REG_PWMSTART);
+
+ if (enabled)
+ writel(pwm_value | BIT(pwm->hwpwm), ddata->base + REG_PWMSTART);
+ else
+ writel(pwm_value & ~BIT(pwm->hwpwm), ddata->base + REG_PWMSTART);
+}
+
+static void pwm_sg2044_set_outputenable(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
+ bool enabled)
+{
+ u32 pwm_value;
+
+ pwm_value = readl(ddata->base + REG_PWM_OE);
+
+ if (enabled)
+ writel(pwm_value | BIT(pwm->hwpwm), ddata->base + REG_PWM_OE);
+ else
+ writel(pwm_value & ~BIT(pwm->hwpwm), ddata->base + REG_PWM_OE);
+}
+
+static int pwm_sg2044_set_polarity(struct sg2042_pwm_ddata *ddata, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ enum pwm_polarity polarity;
+ u32 pwm_value;
+
+ pwm_value = readl(ddata->base + REG_POLARITY);
+
+ polarity = state->polarity;
+
+ if (polarity == PWM_POLARITY_NORMAL)
+ pwm_value &= ~BIT(pwm->hwpwm);
+ else
+ pwm_value |= BIT(pwm->hwpwm);
+
+ writel(pwm_value, ddata->base + REG_POLARITY);
+
+ return 0;
+}
+
+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);
+ u32 hlperiod_ticks;
+ u32 period_ticks;
+
+ if (!state->enabled) {
+ pwm_sg2044_config(ddata, pwm, false);
+ return 0;
+ }
+
+ pwm_sg2044_set_polarity(ddata, pwm, state);
+
+ /*
+ * Duration of High level (duty_cycle) = HLPERIOD x Period_of_input_clk
+ * Duration of One Cycle (period) = PERIOD x Period_of_input_clk
+ */
+ 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);
+
+ pwm_sg2042_config(ddata, pwm->hwpwm, period_ticks, hlperiod_ticks);
+
+ guard(mutex)(&ddata->lock);
+
+ /*
+ * re-enable PWMSTART to refresh the register period
+ */
+ pwm_sg2044_config(ddata, pwm, false);
+ pwm_sg2044_set_outputenable(ddata, pwm, true);
+ pwm_sg2044_config(ddata, pwm, true);
+
+ return 0;
+}
+
+static const struct sg2042_chip_data sg2042_chip_data = {
+ .ops = {
+ .apply = pwm_sg2042_apply,
+ .get_state = pwm_sg2042_get_state,
+ },
+ .atomic = true,
+};
+
+static const struct sg2042_chip_data sg2044_chip_data = {
+ .ops = {
+ .apply = pwm_sg2044_apply,
+ .get_state = pwm_sg2042_get_state,
+ },
+ .atomic = false,
};
static const struct of_device_id sg2042_pwm_ids[] = {
- { .compatible = "sophgo,sg2042-pwm" },
+ { .compatible = "sophgo,sg2042-pwm",
+ .data = &sg2042_chip_data },
+ { .compatible = "sophgo,sg2044-pwm",
+ .data = &sg2044_chip_data },
{ }
};
MODULE_DEVICE_TABLE(of, sg2042_pwm_ids);
@@ -137,17 +244,24 @@ MODULE_DEVICE_TABLE(of, sg2042_pwm_ids);
static int pwm_sg2042_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ const struct sg2042_chip_data *chip_data;
struct sg2042_pwm_ddata *ddata;
struct reset_control *rst;
struct pwm_chip *chip;
struct clk *clk;
int ret;
+ chip_data = device_get_match_data(dev);
+ if (!chip_data)
+ return -ENODEV;
+
chip = devm_pwmchip_alloc(dev, SG2042_PWM_CHANNELNUM, sizeof(*ddata));
if (IS_ERR(chip))
return PTR_ERR(chip);
ddata = pwmchip_get_drvdata(chip);
+ mutex_init(&ddata->lock);
+
ddata->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ddata->base))
return PTR_ERR(ddata->base);
@@ -170,8 +284,8 @@ static int pwm_sg2042_probe(struct platform_device *pdev)
if (IS_ERR(rst))
return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset\n");
- chip->ops = &pwm_sg2042_ops;
- chip->atomic = true;
+ chip->ops = &chip_data->ops;
+ chip->atomic = chip_data->atomic;
ret = devm_pwmchip_add(dev, chip);
if (ret < 0)
--
2.48.1
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
next prev parent reply other threads:[~2025-04-07 7:22 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-07 7:20 [PATCH 0/2] riscv: pwm: sophgo: add pwm support for SG2044 Longbin Li
2025-04-07 7:20 ` Longbin Li
2025-04-07 7:20 ` [PATCH 1/2] dt-bindings: pwm: sophgo: add pwm controller " Longbin Li
2025-04-07 7:20 ` Longbin Li
2025-04-07 12:31 ` Uwe Kleine-König
2025-04-07 12:31 ` Uwe Kleine-König
2025-04-07 14:11 ` Rob Herring
2025-04-07 14:11 ` Rob Herring
2025-04-08 2:33 ` Inochi Amaoto
2025-04-08 2:33 ` Inochi Amaoto
2025-04-09 7:57 ` Krzysztof Kozlowski
2025-04-09 7:57 ` Krzysztof Kozlowski
2025-04-07 7:20 ` Longbin Li [this message]
2025-04-07 7:20 ` [PATCH 2/2] pwm: sophgo: add driver " Longbin Li
2025-04-07 9:38 ` Uwe Kleine-König
2025-04-07 9:38 ` Uwe Kleine-König
2025-04-07 10:02 ` Longbin Li
2025-04-07 10:02 ` Longbin Li
2025-04-07 15:30 ` kernel test robot
2025-04-07 15:30 ` kernel test robot
2025-04-08 1:02 ` Chen Wang
2025-04-08 1:02 ` Chen Wang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250407072056.8629-3-looong.bin@gmail.com \
--to=looong.bin@gmail.com \
--cc=2990955050@qq.com \
--cc=alex@ghiti.fr \
--cc=aou@eecs.berkeley.edu \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=inochiama@gmail.com \
--cc=krzk+dt@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pwm@vger.kernel.org \
--cc=linux-riscv@lists.infradead.org \
--cc=palmer@dabbelt.com \
--cc=paul.walmsley@sifive.com \
--cc=robh@kernel.org \
--cc=sophgo@lists.linux.dev \
--cc=ukleinek@kernel.org \
--cc=unicorn_wang@outlook.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.