diff options
author | Paul Cercueil <paul@crapouillou.net> | 2020-03-23 15:24:20 +0100 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2020-03-30 18:03:09 +0200 |
commit | c2693514a0a1ec3abcb4673775af3edc797e8a07 (patch) | |
tree | ef380b00d25a48e8b41a8b52eb4f945a36f786bd /drivers | |
parent | 485b56f08f3342f353369ee6f9c26053efb2b925 (diff) | |
download | linux-stable-c2693514a0a1ec3abcb4673775af3edc797e8a07.tar.gz linux-stable-c2693514a0a1ec3abcb4673775af3edc797e8a07.tar.bz2 linux-stable-c2693514a0a1ec3abcb4673775af3edc797e8a07.zip |
pwm: jz4740: Obtain regmap from parent node
The TCU registers are shared between a handful of drivers, accessing
them through the same regmap.
While this driver is devicetree-compatible, it is never (as of now)
probed from devicetree, so this change does not introduce a ABI problem
with current devicetree files.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Tested-by: Artur Rojek <contact@artur-rojek.eu>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pwm/Kconfig | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-jz4740.c | 67 |
2 files changed, 44 insertions, 24 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6729b0ba1252..eebbc917ac97 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -236,6 +236,7 @@ config PWM_JZ4740 tristate "Ingenic JZ47xx PWM support" depends on MACH_INGENIC depends on COMMON_CLK + select MFD_SYSCON help Generic PWM framework driver for Ingenic JZ47xx based machines. diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c index f4c6c69d0bf6..549e6c750bac 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -13,17 +13,19 @@ #include <linux/err.h> #include <linux/gpio.h> #include <linux/kernel.h> +#include <linux/mfd/ingenic-tcu.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pwm.h> - -#include <asm/mach-jz4740/timer.h> +#include <linux/regmap.h> #define NUM_PWM 8 struct jz4740_pwm_chip { struct pwm_chip chip; + struct regmap *map; }; static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip) @@ -76,36 +78,39 @@ static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { - uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm); + struct jz4740_pwm_chip *jz = to_jz4740(chip); + + /* Enable PWM output */ + regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), + TCU_TCSR_PWM_EN, TCU_TCSR_PWM_EN); - ctrl |= JZ_TIMER_CTRL_PWM_ENABLE; - jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); - jz4740_timer_enable(pwm->hwpwm); + /* Start counter */ + regmap_write(jz->map, TCU_REG_TESR, BIT(pwm->hwpwm)); return 0; } static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { - uint32_t ctrl = jz4740_timer_get_ctrl(pwm->hwpwm); + struct jz4740_pwm_chip *jz = to_jz4740(chip); /* * Set duty > period. This trick allows the TCU channels in TCU2 mode to * properly return to their init level. */ - jz4740_timer_set_duty(pwm->hwpwm, 0xffff); - jz4740_timer_set_period(pwm->hwpwm, 0x0); + regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), 0xffff); + regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), 0x0); /* * Disable PWM output. * In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the * counter is stopped, while in TCU1 mode the order does not matter. */ - ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE; - jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); + regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm), + TCU_TCSR_PWM_EN, 0); /* Stop counter */ - jz4740_timer_disable(pwm->hwpwm); + regmap_write(jz->map, TCU_REG_TECR, BIT(pwm->hwpwm)); } static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, @@ -115,7 +120,6 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long long tmp = 0xffffull * NSEC_PER_SEC; struct clk *clk = pwm_get_chip_data(pwm); unsigned long period, duty; - uint16_t ctrl; long rate; int err; @@ -163,24 +167,32 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return err; } - jz4740_timer_set_count(pwm->hwpwm, 0); - jz4740_timer_set_duty(pwm->hwpwm, duty); - jz4740_timer_set_period(pwm->hwpwm, period); + /* Reset counter to 0 */ + regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0); + + /* Set duty */ + regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), duty); - ctrl = jz4740_timer_get_ctrl(pwm->hwpwm); - ctrl |= JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN; + /* Set period */ + regmap_write(jz4740->map, TCU_REG_TDFRc(pwm->hwpwm), period); + /* Set abrupt shutdown */ + regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), + TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD); + + /* Set polarity */ switch (state->polarity) { case PWM_POLARITY_NORMAL: - ctrl &= ~JZ_TIMER_CTRL_PWM_ACTIVE_LOW; + regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), + TCU_TCSR_PWM_INITL_HIGH, 0); break; case PWM_POLARITY_INVERSED: - ctrl |= JZ_TIMER_CTRL_PWM_ACTIVE_LOW; + regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm), + TCU_TCSR_PWM_INITL_HIGH, + TCU_TCSR_PWM_INITL_HIGH); break; } - jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); - if (state->enabled) jz4740_pwm_enable(chip, pwm); @@ -196,13 +208,20 @@ static const struct pwm_ops jz4740_pwm_ops = { static int jz4740_pwm_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct jz4740_pwm_chip *jz4740; - jz4740 = devm_kzalloc(&pdev->dev, sizeof(*jz4740), GFP_KERNEL); + jz4740 = devm_kzalloc(dev, sizeof(*jz4740), GFP_KERNEL); if (!jz4740) return -ENOMEM; - jz4740->chip.dev = &pdev->dev; + jz4740->map = device_node_to_regmap(dev->parent->of_node); + if (IS_ERR(jz4740->map)) { + dev_err(dev, "regmap not found: %ld\n", PTR_ERR(jz4740->map)); + return PTR_ERR(jz4740->map); + } + + jz4740->chip.dev = dev; jz4740->chip.ops = &jz4740_pwm_ops; jz4740->chip.npwm = NUM_PWM; jz4740->chip.base = -1; |