pwm: sunxi: add support for PWM found on Allwinner A64 and H3
Subsystems (5)
Arm (32-bit) Arm sub-architectures Framebuffer layer Arm/Allwinner sunXi SoC support +1 more subsystems
PATCH 1/3 pwm: sunxi: add support for PWM found on Allwinner A64 and H3
This commit adds basic support for PWM found on Allwinner A64 and H3
Signed-off-by Vasily Khoruzhick <[email protected]>
--- arch/arm/include/asm/arch-sunxi/gpio.h | 1 + arch/arm/include/asm/arch-sunxi/pwm.h | 12 +++ arch/arm/mach-sunxi/board.c | 11 +++ drivers/pwm/Kconfig | 7 ++ drivers/pwm/Makefile | 1 + drivers/pwm/sunxi_pwm.c | 174 +++++++++++++++++++++++++++++++++ 6 files changed, 206 insertions(+) create mode 100644 drivers/pwm/sunxi_pwm.c
arch/arm/include/asm/arch-sunxi/gpio.h +1 -0
@@ -173,6 +173,7 @@ enum sunxi_gpio_number {#define SUN8I_GPD_SDC1 3#define SUNXI_GPD_LCD0 2#define SUNXI_GPD_LVDS0 3 +#define SUNXI_GPD_PWM 2#define SUN5I_GPE_SDC2 3#define SUN8I_GPE_TWI2 3
arch/arm/include/asm/arch-sunxi/pwm.h +12 -0
@@ -11,8 +11,15 @@#define SUNXI_PWM_CH0_PERIOD (SUNXI_PWM_BASE + 4)#define SUNXI_PWM_CTRL_PRESCALE0 (x) ((x) & 0xf) +#define SUNXI_PWM_CTRL_PRESCALE0_MASK (0xf)#define SUNXI_PWM_CTRL_ENABLE0 (0x5 << 4)#define SUNXI_PWM_CTRL_POLARITY0 (x) ((x) << 5) +#define SUNXI_PWM_CTRL_POLARITY0_MASK (1 << 5) +#define SUNXI_PWM_CTRL_CLK_GATE (1 << 6) + +#define SUNXI_PWM_CH0_PERIOD_MAX (0xffff) +#define SUNXI_PWM_CH0_PERIOD_PRD (x) ((x & 0xffff) << 16) +#define SUNXI_PWM_CH0_PERIOD_DUTY (x) ((x) & 0xffff)#define SUNXI_PWM_PERIOD_80PCT 0x04af03c0
@@ -31,4 +38,9 @@#define SUNXI_PWM_MUX SUN8I_GPH_PWM#endif +struct sunxi_pwm { +u32 ctrl; +u32 ch0_period; +}; +#endif
arch/arm/mach-sunxi/board.c +11 -0
@@ -141,6 +141,16 @@ static int gpio_init(void)return 0 ; } +static int pwm_init (void ) +{ +#ifdef CONFIG_PWM_SUNXI +#ifdef CONFIG_MACH_SUN50I +sunxi_gpio_set_cfgpin (SUNXI_GPD (22 ),SUNXI_GPD_PWM ); +#endif +#endif +return 0 ; +} +#if defined(CONFIG_SPL_BOARD_LOAD_IMAGE ) && defined(CONFIG_SPL_BUILD )static int spl_board_load_image (struct spl_image_info *spl_image,struct spl_boot_device *bootdev)
@@ -204,6 +214,7 @@ void s_init(void)clock_init ();timer_init ();gpio_init (); +pwm_init ();#ifndef CONFIG_DM_I2C i2c_init_board ();#endif
drivers/pwm/Kconfig +7 -0
@@ -43,3 +43,10 @@ config PWM_TEGRA four channels with a programmable period and duty cycle. Only a 32KHz clock is supported by the driver but the duty cycle is configurable. + +config PWM_SUNXI + bool "Enable support for the Allwinner Sunxi PWM" + depends on DM_PWM + help + This PWM is found on A64 and other Allwinner SoCs. It supports a + programmable period and duty cycle. A 32-bit counter is used.
drivers/pwm/Makefile +1 -0
@@ -17,3 +17,4 @@ obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o +obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o
drivers/pwm/sunxi_pwm.c new file +174 -0
174 lines not on this page · raw message
Signature · 1 line
2.14.1
PATCH 1/3 pwm: sunxi: add support for PWM found on Allwinner A64 and H3
Please discard this, I forgot to include maintainers in CC. Will resend.
Quoted 291 lines · Vasily Khoruzhick
This commit adds basic support for PWM found on Allwinner A64 and H3
Signed-off-by Vasily Khoruzhick <[email protected]>--- arch/arm/include/asm/arch-sunxi/gpio.h | 1 + arch/arm/include/asm/arch-sunxi/pwm.h | 12 +++ arch/arm/mach-sunxi/board.c | 11 +++ drivers/pwm/Kconfig | 7 ++ drivers/pwm/Makefile | 1 + drivers/pwm/sunxi_pwm.c | 174 +++++++++++++++++++++++++++++++++ 6 files changed, 206 insertions(+) create mode 100644 drivers/pwm/sunxi_pwm.carch/arm/include/asm/arch-sunxi/gpio.h +1
-0@@ -173,6 +173,7 @@ enum sunxi_gpio_number {#define SUN8I_GPD_SDC1 3#define SUNXI_GPD_LCD0 2#define SUNXI_GPD_LVDS0 3 +#define SUNXI_GPD_PWM 2#define SUN5I_GPE_SDC2 3#define SUN8I_GPE_TWI2 3arch/arm/include/asm/arch-sunxi/pwm.h +12
-0@@ -11,8 +11,15 @@#define SUNXI_PWM_CH0_PERIOD (SUNXI_PWM_BASE + 4)#define SUNXI_PWM_CTRL_PRESCALE0 (x) ((x) & 0xf) +#define SUNXI_PWM_CTRL_PRESCALE0_MASK (0xf)#define SUNXI_PWM_CTRL_ENABLE0 (0x5 << 4)#define SUNXI_PWM_CTRL_POLARITY0 (x) ((x) << 5) +#define SUNXI_PWM_CTRL_POLARITY0_MASK (1 << 5) +#define SUNXI_PWM_CTRL_CLK_GATE (1 << 6) + +#define SUNXI_PWM_CH0_PERIOD_MAX (0xffff) +#define SUNXI_PWM_CH0_PERIOD_PRD (x) ((x & 0xffff) << 16) +#define SUNXI_PWM_CH0_PERIOD_DUTY (x) ((x) & 0xffff)#define SUNXI_PWM_PERIOD_80PCT 0x04af03c0@@ -31,4 +38,9 @@#define SUNXI_PWM_MUX SUN8I_GPH_PWM#endif +struct sunxi_pwm { +u32 ctrl; +u32 ch0_period; +}; +#endif arch/arm/mach-sunxi/board.c +11
-0@@ -141,6 +141,16 @@ static int gpio_init(void)return 0 ; } +static int pwm_init (void ) +{ +#ifdef CONFIG_PWM_SUNXI +#ifdef CONFIG_MACH_SUN50I +sunxi_gpio_set_cfgpin (SUNXI_GPD (22 ),SUNXI_GPD_PWM ); +#endif +#endif +return 0 ; +} +#if defined(CONFIG_SPL_BOARD_LOAD_IMAGE ) && defined(CONFIG_SPL_BUILD )static int spl_board_load_image (struct spl_image_info *spl_image,struct spl_boot_device *bootdev)@@ -204,6 +214,7 @@ void s_init(void)clock_init ();timer_init ();gpio_init (); +pwm_init ();#ifndef CONFIG_DM_I2C i2c_init_board ();#endif drivers/pwm/Kconfig +7
-0@@ -43,3 +43,10 @@ config PWM_TEGRA four channels with a programmable period and duty cycle. Only a 32KHz clock is supported by the driver but the duty cycle is configurable. + +config PWM_SUNXI + bool "Enable support for the Allwinner Sunxi PWM" + depends on DM_PWM + help + This PWM is found on A64 and other Allwinner SoCs. It supports a + programmable period and duty cycle. A 32-bit counter is used.drivers/pwm/Makefile +1
-0@@ -17,3 +17,4 @@ obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o +obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.odrivers/pwm/sunxi_pwm.c new file +174
-1@@ -0,0 +1,174 @@ +/* +* Copyright (c) 2017 Vasily Khoruzhick <[email protected]> +* +* SPDX-License-Identifier: GPL-2.0+ +*/ + +#include <common.h> +#include <div64.h> +#include <dm.h> +#include <pwm.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/io.h> +#include <asm/arch/pwm.h> +#include <power/regulator.h> + +DECLARE_GLOBAL_DATA_PTR ; + +struct sunxi_pwm_priv { +struct sunxi_pwm *regs; +ulong freq; +bool invert; +uint32_t prescaler; +}; + +static const uint32_t prescaler_table[] = { +120 ,/* 0000 */ +180 ,/* 0001 */ +240 ,/* 0010 */ +360 ,/* 0011 */ +480 ,/* 0100 */ +0 ,/* 0101 */ +0 ,/* 0110 */ +0 ,/* 0111 */ +12000 ,/* 1000 */ +24000 ,/* 1001 */ +36000 ,/* 1010 */ +48000 ,/* 1011 */ +72000 ,/* 1100 */ +0 ,/* 1101 */ +0 ,/* 1110 */ +1 ,/* 1111 */ +}; + +static const uint64_t nsecs_per_sec =1000000000L ; + +static int sunxi_pwm_set_invert (struct udevice *dev,uint channel,bool polarity) +{ +struct sunxi_pwm_priv *priv =dev_get_priv (dev); + +debug ("%s: polarity=%u\n" , __func__, polarity); + priv->invert = polarity; + +return 0 ; +} + +static int sunxi_pwm_set_config (struct udevice *dev,uint channel,uint period_ns, +uint duty_ns) +{ +struct sunxi_pwm_priv *priv =dev_get_priv (dev); +struct sunxi_pwm *regs = priv->regs; +int prescaler; +u32 v, period, duty; +uint64_t div =0 , pval =0 , scaled_freq =0 ; + +debug ("%s: period_ns=%u, duty_ns=%u\n" , __func__, period_ns, duty_ns); + +for (prescaler =0 ; prescaler <SUNXI_PWM_CTRL_PRESCALE0_MASK ; prescaler++) { +if (!prescaler_table[prescaler]) +continue ; + div = priv->freq; + pval = prescaler_table[prescaler]; + scaled_freq =lldiv (div, pval); + div = scaled_freq * period_ns; + div =lldiv (div, nsecs_per_sec); +if (div -1 <=SUNXI_PWM_CH0_PERIOD_MAX ) +break ; + } + +if (div -1 >SUNXI_PWM_CH0_PERIOD_MAX ) { +debug ("%s: failed to find prescaler value\n" , __func__); +return -EINVAL ; + } + + period = div; + div = scaled_freq * duty_ns; + div =lldiv (div, nsecs_per_sec); + duty = div; + +if (priv->prescaler != prescaler) { +/* Mask clock to update prescaler */ + v =readl (®s->ctrl); + v &= ~SUNXI_PWM_CTRL_CLK_GATE ; +writel (v, ®s->ctrl); + v &= ~SUNXI_PWM_CTRL_PRESCALE0_MASK ; + v |= (priv->prescaler &SUNXI_PWM_CTRL_PRESCALE0_MASK ); +writel (v, ®s->ctrl); + v |=SUNXI_PWM_CTRL_CLK_GATE ; +writel (v, ®s->ctrl); + priv->prescaler = prescaler; + } + +writel (SUNXI_PWM_CH0_PERIOD_PRD (period) | +SUNXI_PWM_CH0_PERIOD_DUTY (duty), ®s->ch0_period); + +debug ("%s: prescaler: %d, period: %d, duty: %d\n" , __func__, priv->prescaler, + period, duty); + +return 0 ; +} + +static int sunxi_pwm_set_enable (struct udevice *dev,uint channel,bool enable) +{ +struct sunxi_pwm_priv *priv =dev_get_priv (dev); +struct sunxi_pwm *regs = priv->regs; +uint32_t v; + +debug ("%s: Enable '%s'\n" , __func__, dev->name); + + v =readl (®s->ctrl); +if (!enable) { + v &= ~SUNXI_PWM_CTRL_ENABLE0 ; +writel (v, ®s->ctrl); +return 0 ; + } + + v &= ~SUNXI_PWM_CTRL_POLARITY0_MASK ; + v |= priv->invert ?SUNXI_PWM_CTRL_POLARITY0 (0 ) : +SUNXI_PWM_CTRL_POLARITY0 (1 ); + v |=SUNXI_PWM_CTRL_ENABLE0 ; +writel (v, ®s->ctrl); + +return 0 ; +} + +static int sunxi_pwm_ofdata_to_platdata (struct udevice *dev) +{ +struct sunxi_pwm_priv *priv =dev_get_priv (dev); + + priv->regs = (struct sunxi_pwm *)devfdt_get_addr (dev); + +return 0 ; +} + +static int sunxi_pwm_probe (struct udevice *dev) +{ +struct sunxi_pwm_priv *priv =dev_get_priv (dev); + + priv->freq =24000000 ; + +return 0 ; +} + +static const struct pwm_ops sunxi_pwm_ops = { + .set_invert = sunxi_pwm_set_invert, + .set_config = sunxi_pwm_set_config, + .set_enable = sunxi_pwm_set_enable, +}; + +static const struct udevice_id sunxi_pwm_ids[] = { + { .compatible ="allwinner,sun8i-h3-pwm" }, + { .compatible ="allwinner,sun50i-a64-pwm" }, + { } +}; + +U_BOOT_DRIVER (sunxi_pwm) = { + .name ="sunxi_pwm" , + .id =UCLASS_PWM , + .of_match = sunxi_pwm_ids, + .ops = &sunxi_pwm_ops, + .ofdata_to_platdata = sunxi_pwm_ofdata_to_platdata, + .probe = sunxi_pwm_probe, + .priv_auto_alloc_size =sizeof (struct sunxi_pwm_priv ), +}; --2.14.1
PATCH 2/3 video: pwm_backlight: make regulator optional
In reply to Vasily Khoruzhicku-boot doesn't have dummy regulators, so pwm_backlight probe will fail if regulator is missing. Make it optional to get this driver working on platforms where there's no backlight regultor.
Signed-off-by Vasily Khoruzhick <[email protected]>
--- drivers/video/pwm_backlight.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-)
drivers/video/pwm_backlight.c +16 -12
@@ -32,16 +32,21 @@ static int pwm_backlight_enable(struct udevice *dev)uint duty_cycle;int ret; - plat =dev_get_uclass_platdata (priv->reg); -debug ("%s: Enable '%s', regulator '%s'/'%s'\n" , __func__, dev->name, - priv->reg->name, plat->name); - ret =regulator_set_enable (priv->reg, true); -if (ret) { -debug ("%s: Cannot enable regulator for PWM '%s'\n" , __func__, - dev->name); -return ret; +if (priv->reg) { + plat =dev_get_uclass_platdata (priv->reg); +debug ("%s: Enable '%s', regulator '%s'/'%s'\n" , __func__, dev->name, + priv->reg->name, plat->name); + ret =regulator_set_enable (priv->reg, true); +if (ret) { +debug ("%s: Cannot enable regulator for PWM '%s'\n" , __func__, + dev->name); +return ret; + } +mdelay (120 ); } -mdelay (120 ); + +debug ("%s: default: %d, min: %d, max: %d\n" , __func__, + priv->default_level, priv->min_level, priv->max_level); duty_cycle = priv->period_ns * (priv->default_level - priv->min_level) / (priv->max_level - priv->min_level +1 );
@@ -68,10 +73,9 @@ static int pwm_backlight_ofdata_to_platdata(struct udevice *dev)debug ("%s: start\n" , __func__); ret =uclass_get_device_by_phandle (UCLASS_REGULATOR , dev,"power-supply" , &priv->reg); -if (ret) { +if (ret)debug ("%s: Cannot get power supply: ret=%d\n" , __func__, ret); -return ret; - } + ret =gpio_request_by_name (dev,"enable-gpios" ,0 , &priv->enable,GPIOD_IS_OUT );if (ret) {
Signature · 1 line
2.14.1
PATCH 3/3 dts: sunxi: add PWM node for sun50i
In reply to Vasily KhoruzhickSigned-off-by Vasily Khoruzhick <[email protected]>
--- arch/arm/dts/sun50i-a64.dtsi | 8 ++++++++ 1 file changed, 8 insertions(+)
arch/arm/dts/sun50i-a64.dtsi +8 -0
@@ -319,6 +319,14 @@ }; }; + pwm: pwm at 01c21400 { + compatible = "allwinner,sun50i-a64-pwm"; + reg = <0x01c21400 0x8>; + clocks = <&osc24M>; + #pwm-cells = <3>; + status = "disabled"; + }; + uart0: serial at 1c28000 { compatible = "snps,dw-apb-uart"; reg = <0x01c28000 0x400>;
Signature · 1 line
2.14.1