[linux-sunxi] [PATCH] Add the Allwinner A31/A31s PWM driver.
From: Siarhei VolkauA31 SoC have a different map of PWM registers than others Allwinner SoCs, so the operation of access to the registers reworked for all existing in driver SoCs. Tested on Onda V972 (a31) and Marsboard A20, but only PWM channel 0, because other channels pins are not routed or have another function on these boards. Signed-off-by: Siarhei Volkau --- arch/arm/boot/dts/sun6i-a31.dtsi | 8 ++ drivers/pwm/pwm-sun4i.c | 195 +-- 2 files changed, 175 insertions(+), 28 deletions(-) diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index ee1eb6d..fcba129 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -627,6 +627,14 @@ status = "disabled"; }; + pwm: pwm@01c21400 { + compatible = "allwinner,sun6i-a31-pwm"; + reg = <0x01c21400 0x400>; + clocks = <>; + #pwm-cells = <3>; + status = "disabled"; + }; + lradc: lradc@01c22800 { compatible = "allwinner,sun4i-a10-lradc-keys"; reg = <0x01c22800 0x100>; diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index b0803f6..bd049c3 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -34,6 +34,7 @@ #define PWM_MODE BIT(7) #define PWM_PULSE BIT(8) #define PWM_BYPASS BIT(9) +#define PWM_CHCTL_MASK GENMASK(9, 0) #define PWM_RDY_BASE 28 #define PWM_RDY_OFFSET 1 @@ -46,7 +47,13 @@ #define BIT_CH(bit, chan) ((bit) << ((chan) * PWMCH_OFFSET)) -static const u32 prescaler_table[] = { +#define SUN6I_PWM_RDY_BIT PWM_RDY_BASE +#define SUN6I_PWM_CTL_OFFS 0x0 +#define SUN6I_PWM_PRD_OFFS 0x4 +#define SUN6I_PWM_CH_CTL(ch) (0x10 * (ch) + SUN6I_PWM_CTL_OFFS) +#define SUN6I_PWM_CH_PRD(ch) (0x10 * (ch) + SUN6I_PWM_PRD_OFFS) + +static const u32 sun4i_prescaler_table[] = { 120, 180, 240, @@ -65,10 +72,41 @@ static const u32 prescaler_table[] = { 0, /* Actually 1 but tested separately */ }; +static const u32 sun6i_prescaler_table[] = { + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; + +struct sun4i_pwm_chip; + +struct sunxi_reg_ops { + int (*ctl_rdy)(struct sun4i_pwm_chip *chip, int npwm); + u32 (*ctl_read)(struct sun4i_pwm_chip *chip, int npwm); + void (*ctl_write)(struct sun4i_pwm_chip *chip, int npwm, u32 val); + u32 (*prd_read)(struct sun4i_pwm_chip *chip, int npwm); + void (*prd_write)(struct sun4i_pwm_chip *chip, int npwm, u32 val); +}; + struct sun4i_pwm_data { bool has_prescaler_bypass; bool has_rdy; unsigned int npwm; + const u32 *prescaler_table; + struct sunxi_reg_ops *ops; }; struct sun4i_pwm_chip { @@ -96,10 +134,71 @@ static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *chip, writel(val, chip->base + offset); } +static int sun4i_reg_ctl_rdy(struct sun4i_pwm_chip *chip, int npwm) +{ + return PWM_RDY(npwm) & sun4i_pwm_readl(chip, PWM_CTRL_REG); +} + +static int sun6i_reg_ctl_rdy(struct sun4i_pwm_chip *chip, int npwm) +{ + u32 val = sun4i_pwm_readl(chip, SUN6I_PWM_CH_CTL(npwm)); + + return val & BIT(SUN6I_PWM_RDY_BIT); +} + +static u32 sun4i_reg_ctl_read(struct sun4i_pwm_chip *chip, int npwm) +{ + u32 val = sun4i_pwm_readl(chip, PWM_CTRL_REG); + + return val >> (PWMCH_OFFSET * (npwm)); +} + +static u32 sun6i_reg_ctl_read(struct sun4i_pwm_chip *chip, int npwm) +{ + return sun4i_pwm_readl(chip, SUN6I_PWM_CH_CTL(npwm)); +} + +static void sun4i_reg_ctl_write(struct sun4i_pwm_chip *chip, int npwm, u32 val) +{ + u32 rd = sun4i_pwm_readl(chip, PWM_CTRL_REG); + + rd &= ~(PWM_CHCTL_MASK << (PWMCH_OFFSET * npwm)); + val &= (PWM_CHCTL_MASK << (PWMCH_OFFSET * npwm)); + sun4i_pwm_writel(chip, rd | val, PWM_CTRL_REG); +} + +static void sun6i_reg_ctl_write(struct sun4i_pwm_chip *chip, int npwm, u32 val) +{ + return sun4i_pwm_writel(chip, val, SUN6I_PWM_CH_CTL(npwm)); +} + +static u32 sun4i_reg_prd_read(struct sun4i_pwm_chip *chip, int npwm) +{ + return sun4i_pwm_readl(chip, PWM_CH_PRD(npwm)); +} + +static u32 sun6i_reg_prd_read(struct sun4i_pwm_chip *chip, int npwm) +{ + return sun4i_pwm_readl(chip, SUN6I_PWM_CH_PRD(npwm)); +} + +static void sun4i_reg_prd_write(struct sun4i_pwm_chip *chip, int npwm, u32 val) +{ + sun4i_pwm_writel(chip, val, PWM_CH_PRD(npwm)); +} + +static void sun6i_reg_prd_write(struct sun4i_pwm_chip *chip, int npwm, u32 val) +{ + return sun4i_pwm_writel(chip, val, SUN6I_PWM_CH_PRD(npwm)); +} + static int
Re: [linux-sunxi] [PATCH] Add the Allwinner A31/A31s PWM driver.
On Sat, Feb 4, 2017 at 10:55 PM,wrote: > From: Siarhei Volkau > > A31 SoC have a different map of PWM registers than others Allwinner > SoCs, so the operation of access to the registers reworked for all > existing in driver SoCs. > > Tested on Onda V972 (a31) and Marsboard A20, but only PWM > channel 0, because other channels pins are not routed or > have another function on these boards. > > Signed-off-by: Siarhei Volkau Please use scripts/get_maintainers.pl in the kernel tree to get the relevant list of maintainers and mailing lists you should send this patch to. Not having them means a) no proper public record of reviews and acceptance, and b) maintainers that are supposed to ack and/or merge the patch will not know about it. ChenYu -- 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 linux-sunxi+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
Re: [linux-sunxi] [PATCH] Add the Allwinner A31/A31s PWM driver.
Hi Siarhei, On Sun, Feb 5, 2017 at 1:55 AM,wrote: > From: Siarhei Volkau > > A31 SoC have a different map of PWM registers than others Allwinner > SoCs, so the operation of access to the registers reworked for all > existing in driver SoCs. > > Tested on Onda V972 (a31) and Marsboard A20, but only PWM > channel 0, because other channels pins are not routed or > have another function on these boards. > > Signed-off-by: Siarhei Volkau > --- > arch/arm/boot/dts/sun6i-a31.dtsi | 8 ++ > drivers/pwm/pwm-sun4i.c | 195 > +-- > 2 files changed, 175 insertions(+), 28 deletions(-) You should put the dts changes in the separate patches as they go through different trees. Also, you haven't updated the binding documentation. > > diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c > index b0803f6..bd049c3 100644 > --- a/drivers/pwm/pwm-sun4i.c > +++ b/drivers/pwm/pwm-sun4i.c > @@ -65,10 +72,41 @@ static const u32 prescaler_table[] = { > 0, /* Actually 1 but tested separately */ > }; > > +static const u32 sun6i_prescaler_table[] = { > + 1, > + 2, > + 4, > + 8, > + 16, > + 32, > + 64, > + 0, > + 0, > + 0, > + 0, > + 0, > + 0, > + 0, > + 0, > + 0, > +}; > + > +struct sun4i_pwm_chip; Move the definition up here instead of doing this. (Probably should also be in a separate patch) > + > +struct sunxi_reg_ops { > + int (*ctl_rdy)(struct sun4i_pwm_chip *chip, int npwm); > + u32 (*ctl_read)(struct sun4i_pwm_chip *chip, int npwm); > + void (*ctl_write)(struct sun4i_pwm_chip *chip, int npwm, u32 val); > + u32 (*prd_read)(struct sun4i_pwm_chip *chip, int npwm); > + void (*prd_write)(struct sun4i_pwm_chip *chip, int npwm, u32 val); > +}; > + > struct sun4i_pwm_data { > bool has_prescaler_bypass; > bool has_rdy; > unsigned int npwm; > + const u32 *prescaler_table; > + struct sunxi_reg_ops *ops; > }; > > struct sun4i_pwm_chip { > @@ -96,10 +134,71 @@ static inline void sun4i_pwm_writel(struct > sun4i_pwm_chip *chip, > writel(val, chip->base + offset); > } > > +static int sun4i_reg_ctl_rdy(struct sun4i_pwm_chip *chip, int npwm) > +{ > + return PWM_RDY(npwm) & sun4i_pwm_readl(chip, PWM_CTRL_REG); > +} > + > +static int sun6i_reg_ctl_rdy(struct sun4i_pwm_chip *chip, int npwm) > +{ > + u32 val = sun4i_pwm_readl(chip, SUN6I_PWM_CH_CTL(npwm)); > + > + return val & BIT(SUN6I_PWM_RDY_BIT); > +} > + > +static u32 sun4i_reg_ctl_read(struct sun4i_pwm_chip *chip, int npwm) > +{ > + u32 val = sun4i_pwm_readl(chip, PWM_CTRL_REG); > + > + return val >> (PWMCH_OFFSET * (npwm)); > +} > + > +static u32 sun6i_reg_ctl_read(struct sun4i_pwm_chip *chip, int npwm) > +{ > + return sun4i_pwm_readl(chip, SUN6I_PWM_CH_CTL(npwm)); > +} > + > +static void sun4i_reg_ctl_write(struct sun4i_pwm_chip *chip, int npwm, u32 > val) > +{ > + u32 rd = sun4i_pwm_readl(chip, PWM_CTRL_REG); > + > + rd &= ~(PWM_CHCTL_MASK << (PWMCH_OFFSET * npwm)); > + val &= (PWM_CHCTL_MASK << (PWMCH_OFFSET * npwm)); > + sun4i_pwm_writel(chip, rd | val, PWM_CTRL_REG); > +} > + > +static void sun6i_reg_ctl_write(struct sun4i_pwm_chip *chip, int npwm, u32 > val) > +{ > + return sun4i_pwm_writel(chip, val, SUN6I_PWM_CH_CTL(npwm)); > +} > + > +static u32 sun4i_reg_prd_read(struct sun4i_pwm_chip *chip, int npwm) > +{ > + return sun4i_pwm_readl(chip, PWM_CH_PRD(npwm)); > +} > + > +static u32 sun6i_reg_prd_read(struct sun4i_pwm_chip *chip, int npwm) > +{ > + return sun4i_pwm_readl(chip, SUN6I_PWM_CH_PRD(npwm)); > +} > + > +static void sun4i_reg_prd_write(struct sun4i_pwm_chip *chip, int npwm, u32 > val) > +{ > + sun4i_pwm_writel(chip, val, PWM_CH_PRD(npwm)); > +} > + > +static void sun6i_reg_prd_write(struct sun4i_pwm_chip *chip, int npwm, u32 > val) > +{ > + return sun4i_pwm_writel(chip, val, SUN6I_PWM_CH_PRD(npwm)); > +} > + > 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); > + const struct sun4i_pwm_data *data = sun4i_pwm->data; I'm not sure of the value of defining a "data" variable as all it adds a lot of churn to the patch and I believe that the compiler will do something like this internally so there should be no performance benefit. > + struct sunxi_reg_ops *reg_ops = data->ops; > + const u32 *prescaler_table = data->prescaler_table; > u32 prd, dty, val, clk_gate; > u64 clk_rate, div = 0; > unsigned int prescaler = 0; > @@ -319,6 +454,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev) > u32 val; > int i, ret; > const struct of_device_id *match; > +
[linux-sunxi] [PATCH] Add the Allwinner A31/A31s PWM driver.
From: Siarhei VolkauA31 SoC have a different map of PWM registers than others Allwinner SoCs, so the operation of access to the registers reworked for all existing in driver SoCs. Tested on Onda V972 (a31) and Marsboard A20, but only PWM channel 0, because other channels pins are not routed or have another function on these boards. Signed-off-by: Siarhei Volkau --- arch/arm/boot/dts/sun6i-a31.dtsi | 8 ++ drivers/pwm/pwm-sun4i.c | 195 +-- 2 files changed, 175 insertions(+), 28 deletions(-) diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi index ee1eb6d..fcba129 100644 --- a/arch/arm/boot/dts/sun6i-a31.dtsi +++ b/arch/arm/boot/dts/sun6i-a31.dtsi @@ -627,6 +627,14 @@ status = "disabled"; }; + pwm: pwm@01c21400 { + compatible = "allwinner,sun6i-a31-pwm"; + reg = <0x01c21400 0x400>; + clocks = <>; + #pwm-cells = <3>; + status = "disabled"; + }; + lradc: lradc@01c22800 { compatible = "allwinner,sun4i-a10-lradc-keys"; reg = <0x01c22800 0x100>; diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index b0803f6..bd049c3 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -34,6 +34,7 @@ #define PWM_MODE BIT(7) #define PWM_PULSE BIT(8) #define PWM_BYPASS BIT(9) +#define PWM_CHCTL_MASK GENMASK(9, 0) #define PWM_RDY_BASE 28 #define PWM_RDY_OFFSET 1 @@ -46,7 +47,13 @@ #define BIT_CH(bit, chan) ((bit) << ((chan) * PWMCH_OFFSET)) -static const u32 prescaler_table[] = { +#define SUN6I_PWM_RDY_BIT PWM_RDY_BASE +#define SUN6I_PWM_CTL_OFFS 0x0 +#define SUN6I_PWM_PRD_OFFS 0x4 +#define SUN6I_PWM_CH_CTL(ch) (0x10 * (ch) + SUN6I_PWM_CTL_OFFS) +#define SUN6I_PWM_CH_PRD(ch) (0x10 * (ch) + SUN6I_PWM_PRD_OFFS) + +static const u32 sun4i_prescaler_table[] = { 120, 180, 240, @@ -65,10 +72,41 @@ static const u32 prescaler_table[] = { 0, /* Actually 1 but tested separately */ }; +static const u32 sun6i_prescaler_table[] = { + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; + +struct sun4i_pwm_chip; + +struct sunxi_reg_ops { + int (*ctl_rdy)(struct sun4i_pwm_chip *chip, int npwm); + u32 (*ctl_read)(struct sun4i_pwm_chip *chip, int npwm); + void (*ctl_write)(struct sun4i_pwm_chip *chip, int npwm, u32 val); + u32 (*prd_read)(struct sun4i_pwm_chip *chip, int npwm); + void (*prd_write)(struct sun4i_pwm_chip *chip, int npwm, u32 val); +}; + struct sun4i_pwm_data { bool has_prescaler_bypass; bool has_rdy; unsigned int npwm; + const u32 *prescaler_table; + struct sunxi_reg_ops *ops; }; struct sun4i_pwm_chip { @@ -96,10 +134,71 @@ static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *chip, writel(val, chip->base + offset); } +static int sun4i_reg_ctl_rdy(struct sun4i_pwm_chip *chip, int npwm) +{ + return PWM_RDY(npwm) & sun4i_pwm_readl(chip, PWM_CTRL_REG); +} + +static int sun6i_reg_ctl_rdy(struct sun4i_pwm_chip *chip, int npwm) +{ + u32 val = sun4i_pwm_readl(chip, SUN6I_PWM_CH_CTL(npwm)); + + return val & BIT(SUN6I_PWM_RDY_BIT); +} + +static u32 sun4i_reg_ctl_read(struct sun4i_pwm_chip *chip, int npwm) +{ + u32 val = sun4i_pwm_readl(chip, PWM_CTRL_REG); + + return val >> (PWMCH_OFFSET * (npwm)); +} + +static u32 sun6i_reg_ctl_read(struct sun4i_pwm_chip *chip, int npwm) +{ + return sun4i_pwm_readl(chip, SUN6I_PWM_CH_CTL(npwm)); +} + +static void sun4i_reg_ctl_write(struct sun4i_pwm_chip *chip, int npwm, u32 val) +{ + u32 rd = sun4i_pwm_readl(chip, PWM_CTRL_REG); + + rd &= ~(PWM_CHCTL_MASK << (PWMCH_OFFSET * npwm)); + val &= (PWM_CHCTL_MASK << (PWMCH_OFFSET * npwm)); + sun4i_pwm_writel(chip, rd | val, PWM_CTRL_REG); +} + +static void sun6i_reg_ctl_write(struct sun4i_pwm_chip *chip, int npwm, u32 val) +{ + return sun4i_pwm_writel(chip, val, SUN6I_PWM_CH_CTL(npwm)); +} + +static u32 sun4i_reg_prd_read(struct sun4i_pwm_chip *chip, int npwm) +{ + return sun4i_pwm_readl(chip, PWM_CH_PRD(npwm)); +} + +static u32 sun6i_reg_prd_read(struct sun4i_pwm_chip *chip, int npwm) +{ + return sun4i_pwm_readl(chip, SUN6I_PWM_CH_PRD(npwm)); +} + +static void sun4i_reg_prd_write(struct sun4i_pwm_chip *chip, int npwm, u32 val) +{ + sun4i_pwm_writel(chip, val, PWM_CH_PRD(npwm)); +} + +static void sun6i_reg_prd_write(struct sun4i_pwm_chip *chip, int npwm, u32 val) +{ + return sun4i_pwm_writel(chip, val, SUN6I_PWM_CH_PRD(npwm)); +} + static int