From: Siarhei Volkau <[email protected]> sun6i PWM has different register map in comparison to sun4i compatible SoCs. But bit map of the registers and behavior are very similar.
This patch introduces a uniform way to access PWM registers. Signed-off-by: Siarhei Volkau <[email protected]> --- drivers/pwm/Kconfig | 2 +- drivers/pwm/pwm-sun4i.c | 263 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 191 insertions(+), 74 deletions(-) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 2d0cfaa..6b4dc1a 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -416,7 +416,7 @@ config PWM_STMPE config PWM_SUN4I tristate "Allwinner PWM support" depends on ARCH_SUNXI || COMPILE_TEST - depends on HAS_IOMEM && COMMON_CLK + depends on REGMAP_MMIO && COMMON_CLK help Generic PWM framework driver for Allwinner SoCs. diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index b0803f6..7291000 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -9,7 +9,7 @@ #include <linux/bitops.h> #include <linux/clk.h> #include <linux/err.h> -#include <linux/io.h> +#include <linux/regmap.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> @@ -26,25 +26,56 @@ #define PWM_CH_PRD(ch) (PWM_CH_PRD_BASE + PWM_CH_PRD_OFFSET * (ch)) #define PWMCH_OFFSET 15 -#define PWM_PRESCAL_MASK GENMASK(3, 0) -#define PWM_PRESCAL_OFF 0 -#define PWM_EN BIT(4) -#define PWM_ACT_STATE BIT(5) -#define PWM_CLK_GATING BIT(6) -#define PWM_MODE BIT(7) -#define PWM_PULSE BIT(8) -#define PWM_BYPASS BIT(9) + +#define PWM_PRESCAL_LSB 0 +#define PWM_PRESCAL_MSB 3 +#define PWM_PRESCAL_MASK GENMASK(PWM_PRESCAL_MSB - PWM_PRESCAL_LSB, 0) + +#define PWM_EN_BIT 4 +#define PWM_ACT_STATE_BIT 5 +#define PWM_CLK_GATING_BIT 6 +#define PWM_MODE_BIT 7 +#define PWM_PULSE_BIT 8 +#define PWM_BYPASS_BIT 9 #define PWM_RDY_BASE 28 #define PWM_RDY_OFFSET 1 -#define PWM_RDY(ch) BIT(PWM_RDY_BASE + PWM_RDY_OFFSET * (ch)) +#define PWM_RDY_BIT(ch) (PWM_RDY_BASE + PWM_RDY_OFFSET * (ch)) #define PWM_PRD(prd) (((prd) - 1) << 16) #define PWM_PRD_MASK GENMASK(15, 0) #define PWM_DTY_MASK GENMASK(15, 0) -#define BIT_CH(bit, chan) ((bit) << ((chan) * PWMCH_OFFSET)) +#define BIT_CH(bit, chan) ((bit) + ((chan) * PWMCH_OFFSET)) + +#define FIELD_PRESCALER 0 +#define FIELD_POLARITY 1 +#define FIELD_CLK_GATING 2 +#define FIELD_READY 3 +#define NUM_FIELDS 4 + +#define MAX_CHANNELS 2 + +#define SUN4I_REGMAP_FIELDS(chan) {\ + [FIELD_PRESCALER] = \ + REG_FIELD(PWM_CTRL_REG, \ + BIT_CH(PWM_PRESCAL_LSB, chan), \ + BIT_CH(PWM_PRESCAL_MSB, chan)), \ + [FIELD_POLARITY] = \ + REG_FIELD(PWM_CTRL_REG, \ + BIT_CH(PWM_ACT_STATE_BIT, chan), \ + BIT_CH(PWM_ACT_STATE_BIT, chan)), \ + [FIELD_CLK_GATING] = \ + REG_FIELD(PWM_CTRL_REG, \ + BIT_CH(PWM_CLK_GATING_BIT, chan), \ + BIT_CH(PWM_CLK_GATING_BIT, chan)), \ + [FIELD_READY] = \ + REG_FIELD(PWM_CTRL_REG, \ + PWM_RDY_BIT(chan), \ + PWM_RDY_BIT(chan)), \ +} + static const u32 prescaler_table[] = { 120, @@ -65,17 +96,26 @@ static const u32 prescaler_table[] = { 0, /* Actually 1 but tested separately */ }; +struct sunxi_pwmch_reg_info { + struct reg_field fields[NUM_FIELDS]; + unsigned int control_reg; + unsigned int period_reg; + u32 enable_bitmask; +}; + struct sun4i_pwm_data { bool has_prescaler_bypass; bool has_rdy; unsigned int npwm; + const struct sunxi_pwmch_reg_info *chan_info; }; struct sun4i_pwm_chip { struct pwm_chip chip; struct clk *clk; - void __iomem *base; spinlock_t ctrl_lock; + struct regmap *regmap; + struct regmap_field *fields[MAX_CHANNELS][NUM_FIELDS]; const struct sun4i_pwm_data *data; }; @@ -84,23 +124,12 @@ static inline struct sun4i_pwm_chip *to_sun4i_pwm_chip(struct pwm_chip *chip) return container_of(chip, struct sun4i_pwm_chip, chip); } -static inline u32 sun4i_pwm_readl(struct sun4i_pwm_chip *chip, - unsigned long offset) -{ - return readl(chip->base + offset); -} - -static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *chip, - u32 val, unsigned long offset) -{ - writel(val, chip->base + offset); -} - static int sun4i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); - u32 prd, dty, val, clk_gate; + struct regmap_field **chan_fields = sun4i_pwm->fields[pwm->hwpwm]; + u32 prd, dty, busy, clk_gate; u64 clk_rate, div = 0; unsigned int prescaler = 0; int err; @@ -152,45 +181,63 @@ static int sun4i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } spin_lock(&sun4i_pwm->ctrl_lock); - val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); - if (sun4i_pwm->data->has_rdy && (val & PWM_RDY(pwm->hwpwm))) { - spin_unlock(&sun4i_pwm->ctrl_lock); - clk_disable_unprepare(sun4i_pwm->clk); - return -EBUSY; + if (sun4i_pwm->data->has_rdy) { + err = regmap_field_read(chan_fields[FIELD_READY], &busy); + if (err) { + dev_err(chip->dev, "failed to get ready bit\n"); + goto err_cleanup; + } + if (busy) { + err = -EBUSY; + goto err_cleanup; + } } - clk_gate = val & BIT_CH(PWM_CLK_GATING, pwm->hwpwm); + err = regmap_field_read(chan_fields[FIELD_CLK_GATING], &clk_gate); + if (err) { + dev_err(chip->dev, "failed to get clock_gate bit\n"); + goto err_cleanup; + } if (clk_gate) { - val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); - sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); + err = regmap_field_write(chan_fields[FIELD_CLK_GATING], 0); + if (err) { + dev_err(chip->dev, "failed to set clock_gate bit\n"); + goto err_cleanup; + } } - val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); - val &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm); - val |= BIT_CH(prescaler, pwm->hwpwm); - sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); - - val = (dty & PWM_DTY_MASK) | PWM_PRD(prd); - sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm)); - + err = regmap_field_write(chan_fields[FIELD_PRESCALER], prescaler); + if (err) { + dev_err(chip->dev, "failed to set prescaler\n"); + goto err_cleanup; + } + err = regmap_write(sun4i_pwm->regmap, + sun4i_pwm->data->chan_info[pwm->hwpwm].period_reg, + (dty & PWM_DTY_MASK) | PWM_PRD(prd)); + if (err) { + dev_err(chip->dev, "failed to set period and duty cycle\n"); + goto err_cleanup; + } if (clk_gate) { - val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); - val |= clk_gate; - sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); + err = regmap_field_write(chan_fields[FIELD_CLK_GATING], 1); + if (err) { + dev_err(chip->dev, "failed to set clock_gate bit\n"); + goto err_cleanup; + } } +err_cleanup: spin_unlock(&sun4i_pwm->ctrl_lock); clk_disable_unprepare(sun4i_pwm->clk); - return 0; + return err; } static int sun4i_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity) { struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); - u32 val; int ret; ret = clk_prepare_enable(sun4i_pwm->clk); @@ -200,25 +247,21 @@ static int sun4i_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, } spin_lock(&sun4i_pwm->ctrl_lock); - val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); - - if (polarity != PWM_POLARITY_NORMAL) - val &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm); - else - val |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm); - - sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); - + ret = regmap_field_write(sun4i_pwm->fields[pwm->hwpwm][FIELD_POLARITY], + polarity == PWM_POLARITY_NORMAL); + if (ret) + dev_err(chip->dev, "failed to set polarity bit\n"); spin_unlock(&sun4i_pwm->ctrl_lock); clk_disable_unprepare(sun4i_pwm->clk); - return 0; + return ret; } static int sun4i_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); - u32 val; + unsigned int ctl_reg; + u32 bit_mask; int ret; ret = clk_prepare_enable(sun4i_pwm->clk); @@ -228,30 +271,51 @@ static int sun4i_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) } spin_lock(&sun4i_pwm->ctrl_lock); - val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); - val |= BIT_CH(PWM_EN, pwm->hwpwm); - val |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm); - sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); + ctl_reg = sun4i_pwm->data->chan_info[pwm->hwpwm].control_reg; + bit_mask = sun4i_pwm->data->chan_info[pwm->hwpwm].enable_bitmask; + ret = regmap_update_bits(sun4i_pwm->regmap, ctl_reg, + bit_mask, bit_mask); + if (ret) + dev_err(chip->dev, "failed to set clock_gate and enable bit\n"); spin_unlock(&sun4i_pwm->ctrl_lock); - return 0; + return ret; } static void sun4i_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); - u32 val; + unsigned int ctl_reg; + u32 bit_mask; + int ret; spin_lock(&sun4i_pwm->ctrl_lock); - val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); - val &= ~BIT_CH(PWM_EN, pwm->hwpwm); - val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); - sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); + ctl_reg = sun4i_pwm->data->chan_info[pwm->hwpwm].control_reg; + bit_mask = sun4i_pwm->data->chan_info[pwm->hwpwm].enable_bitmask; + ret = regmap_update_bits(sun4i_pwm->regmap, ctl_reg, bit_mask, 0); + if (ret) + dev_err(chip->dev, "failed to set clock_gate and enable bit\n"); spin_unlock(&sun4i_pwm->ctrl_lock); clk_disable_unprepare(sun4i_pwm->clk); } +static const struct sunxi_pwmch_reg_info sun4i_field_info[2] = { + { + .fields = SUN4I_REGMAP_FIELDS(0), + .control_reg = PWM_CTRL_REG, + .period_reg = PWM_CH_PRD(0), + .enable_bitmask = BIT(PWM_EN_BIT) | BIT(PWM_CLK_GATING_BIT), + }, + { + .fields = SUN4I_REGMAP_FIELDS(1), + .control_reg = PWM_CTRL_REG, + .period_reg = PWM_CH_PRD(1), + .enable_bitmask = BIT(PWM_EN_BIT + PWMCH_OFFSET) | + BIT(PWM_CLK_GATING_BIT + PWMCH_OFFSET), + }, +}; + static const struct pwm_ops sun4i_pwm_ops = { .config = sun4i_pwm_config, .set_polarity = sun4i_pwm_set_polarity, @@ -264,30 +328,35 @@ static const struct sun4i_pwm_data sun4i_pwm_data_a10 = { .has_prescaler_bypass = false, .has_rdy = false, .npwm = 2, + .chan_info = sun4i_field_info, }; static const struct sun4i_pwm_data sun4i_pwm_data_a10s = { .has_prescaler_bypass = true, .has_rdy = true, .npwm = 2, + .chan_info = sun4i_field_info, }; static const struct sun4i_pwm_data sun4i_pwm_data_a13 = { .has_prescaler_bypass = true, .has_rdy = true, .npwm = 1, + .chan_info = sun4i_field_info, }; static const struct sun4i_pwm_data sun4i_pwm_data_a20 = { .has_prescaler_bypass = true, .has_rdy = true, .npwm = 2, + .chan_info = sun4i_field_info, }; static const struct sun4i_pwm_data sun4i_pwm_data_h3 = { .has_prescaler_bypass = true, .has_rdy = true, .npwm = 1, + .chan_info = sun4i_field_info, }; static const struct of_device_id sun4i_pwm_dt_ids[] = { @@ -312,10 +381,37 @@ static const struct of_device_id sun4i_pwm_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, sun4i_pwm_dt_ids); +static const struct regmap_config sunxi_mmio_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static int sunxi_alloc_chan_fields(struct device *dev, + struct sun4i_pwm_chip *pwm, int chan) +{ + struct regmap_field **fields = pwm->fields[chan]; + const struct reg_field *info = pwm->data->chan_info[chan].fields; + int i; + + for (i = 0; i < NUM_FIELDS; i++) { + fields[i] = devm_regmap_field_alloc(dev, pwm->regmap, info[i]); + if (IS_ERR(fields[i])) { + int err = PTR_ERR(fields[i]); + + fields[i] = NULL; + return err; + } + } + + return 0; +} + static int sun4i_pwm_probe(struct platform_device *pdev) { struct sun4i_pwm_chip *pwm; struct resource *res; + void __iomem *base; u32 val; int i, ret; const struct of_device_id *match; @@ -327,9 +423,16 @@ static int sun4i_pwm_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pwm->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pwm->base)) - return PTR_ERR(pwm->base); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + pwm->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sunxi_mmio_regmap_config); + if (IS_ERR(pwm->regmap)) { + dev_err(&pdev->dev, "failed to initialise regmap\n"); + return PTR_ERR(pwm->regmap); + } pwm->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(pwm->clk)) @@ -346,6 +449,14 @@ static int sun4i_pwm_probe(struct platform_device *pdev) spin_lock_init(&pwm->ctrl_lock); + for (i = 0; i < pwm->chip.npwm; i++) { + ret = sunxi_alloc_chan_fields(&pdev->dev, pwm, i); + if (ret) { + dev_err(&pdev->dev, "failed to alloc regmap fields\n"); + return ret; + } + } + ret = pwmchip_add(&pwm->chip); if (ret < 0) { dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); @@ -360,11 +471,17 @@ static int sun4i_pwm_probe(struct platform_device *pdev) goto clk_error; } - val = sun4i_pwm_readl(pwm, PWM_CTRL_REG); - for (i = 0; i < pwm->chip.npwm; i++) - if (!(val & BIT_CH(PWM_ACT_STATE, i))) + for (i = 0; i < pwm->chip.npwm; i++) { + ret = regmap_field_read(pwm->fields[i][FIELD_POLARITY], &val); + if (ret) { + dev_err(&pdev->dev, "failed to get polarity bit\n"); + goto clk_error; + } + if (!val) pwm_set_polarity(&pwm->chip.pwms[i], PWM_POLARITY_INVERSED); + } + clk_disable_unprepare(pwm->clk); return 0; -- 2.4.11 -- You received this message because you are subscribed to the Google Groups "linux-sunxi" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
