author | Bichao Zheng <bichao.zheng@amlogic.com> | 2019-03-10 13:26:05 (GMT) |
---|---|---|
committer | Luan Yuan <luan.yuan@amlogic.com> | 2019-05-13 07:23:11 (GMT) |
commit | 43e1b61dba94f9699757ed7103f72f095793640a (patch) | |
tree | 9e5d70c552fa1834f4aeb22e0509095499cf92eb | |
parent | c2d8490ce4cfacf3fe5a54487ef24b0e851470f1 (diff) | |
download | uboot-43e1b61dba94f9699757ed7103f72f095793640a.zip uboot-43e1b61dba94f9699757ed7103f72f095793640a.tar.gz uboot-43e1b61dba94f9699757ed7103f72f095793640a.tar.bz2 |
pwm: add pwm drvier base on DM [1/1]
PD#SWPL-5800
Problem:
There is no PWM driver here.
Solution:
Add pwm drvier base on DM for g12a/b, tl1.
Verify:
test pass on g12a_u211 tl1_x309
Change-Id: Ie66ee460a6130ad67a4d1869fbd152dee3775c9a
Signed-off-by: Bichao Zheng <bichao.zheng@amlogic.com>
Signed-off-by: Luan Yuan <luan.yuan@amlogic.com>
24 files changed, 1051 insertions, 1 deletions
diff --git a/board/amlogic/configs/g12a_u200_v1.h b/board/amlogic/configs/g12a_u200_v1.h index d10951f..9417306 100644 --- a/board/amlogic/configs/g12a_u200_v1.h +++ b/board/amlogic/configs/g12a_u200_v1.h @@ -575,6 +575,10 @@ #define CONFIG_SYS_I2C_SPEED 400000 #endif +/* PWM DM driver*/ +#define CONFIG_DM_PWM +#define CONFIG_PWM_MESON + #define CONFIG_EFUSE 1 /* commands */ diff --git a/board/amlogic/configs/g12a_u211_v1.h b/board/amlogic/configs/g12a_u211_v1.h index 00dbba0..cdf29b9 100644 --- a/board/amlogic/configs/g12a_u211_v1.h +++ b/board/amlogic/configs/g12a_u211_v1.h @@ -567,6 +567,10 @@ #define CONFIG_SYS_I2C_SPEED 400000 #endif +/* PWM DM driver*/ +#define CONFIG_DM_PWM +#define CONFIG_PWM_MESON + #define CONFIG_EFUSE 1 /* commands */ diff --git a/board/amlogic/configs/g12a_u212_v1.h b/board/amlogic/configs/g12a_u212_v1.h index 886e636..efd5cdb 100644 --- a/board/amlogic/configs/g12a_u212_v1.h +++ b/board/amlogic/configs/g12a_u212_v1.h @@ -569,6 +569,10 @@ #define CONFIG_SYS_I2C_SPEED 400000 #endif +/* PWM DM driver*/ +#define CONFIG_DM_PWM +#define CONFIG_PWM_MESON + #define CONFIG_EFUSE 1 /* commands */ diff --git a/board/amlogic/configs/g12a_u220_v1.h b/board/amlogic/configs/g12a_u220_v1.h index f20d638..2b1f8b9 100644 --- a/board/amlogic/configs/g12a_u220_v1.h +++ b/board/amlogic/configs/g12a_u220_v1.h @@ -565,6 +565,10 @@ #define CONFIG_SYS_I2C_SPEED 400000 #endif +/* PWM DM driver*/ +#define CONFIG_DM_PWM +#define CONFIG_PWM_MESON + #define CONFIG_EFUSE 1 /* commands */ diff --git a/board/amlogic/configs/g12a_u221_v1.h b/board/amlogic/configs/g12a_u221_v1.h index 6a0676e..34e75a8 100644 --- a/board/amlogic/configs/g12a_u221_v1.h +++ b/board/amlogic/configs/g12a_u221_v1.h @@ -568,6 +568,10 @@ #define CONFIG_SYS_I2C_SPEED 400000 #endif +/* PWM DM driver*/ +#define CONFIG_DM_PWM +#define CONFIG_PWM_MESON + #define CONFIG_EFUSE 1 /* commands */ diff --git a/board/amlogic/configs/g12b_skt_v1.h b/board/amlogic/configs/g12b_skt_v1.h index d4e6d02..d4cb57b 100644 --- a/board/amlogic/configs/g12b_skt_v1.h +++ b/board/amlogic/configs/g12b_skt_v1.h @@ -511,6 +511,11 @@ #define CONFIG_SYS_I2C_AML 1 #define CONFIG_SYS_I2C_SPEED 400000 #endif + +/* PWM DM driver*/ +#define CONFIG_DM_PWM +#define CONFIG_PWM_MESON + #define CONFIG_EFUSE 1 /* commands */ diff --git a/board/amlogic/configs/g12b_w400_v1.h b/board/amlogic/configs/g12b_w400_v1.h index d3df895..428602a 100644 --- a/board/amlogic/configs/g12b_w400_v1.h +++ b/board/amlogic/configs/g12b_w400_v1.h @@ -567,6 +567,11 @@ #define CONFIG_SYS_I2C_AML 1 #define CONFIG_SYS_I2C_SPEED 400000 #endif + +/* PWM DM driver*/ +#define CONFIG_DM_PWM +#define CONFIG_PWM_MESON + #define CONFIG_EFUSE 1 /* commands */ diff --git a/board/amlogic/configs/g12b_w411_v1.h b/board/amlogic/configs/g12b_w411_v1.h index a1b2fe8..e28ca98 100644 --- a/board/amlogic/configs/g12b_w411_v1.h +++ b/board/amlogic/configs/g12b_w411_v1.h @@ -571,6 +571,11 @@ #define CONFIG_SYS_I2C_AML 1 #define CONFIG_SYS_I2C_SPEED 400000 #endif + +/* PWM DM driver*/ +#define CONFIG_DM_PWM +#define CONFIG_PWM_MESON + #define CONFIG_EFUSE 1 /* commands */ diff --git a/board/amlogic/g12a_skt_v1/g12a_skt_v1.c b/board/amlogic/g12a_skt_v1/g12a_skt_v1.c index 5100553..15a7500 100644 --- a/board/amlogic/g12a_skt_v1/g12a_skt_v1.c +++ b/board/amlogic/g12a_skt_v1/g12a_skt_v1.c @@ -33,6 +33,10 @@ #ifdef CONFIG_SYS_I2C_MESON #include <amlogic/i2c.h> #endif +#ifdef CONFIG_PWM_MESON +#include <pwm.h> +#include <amlogic/pwm.h> +#endif #include <dm.h> #ifdef CONFIG_AML_VPU #include <vpu.h> @@ -598,6 +602,24 @@ void set_i2c_ao_pinmux(void) } #endif /*end CONFIG_SYS_I2C_MESON*/ +#ifdef CONFIG_PWM_MESON +static const struct meson_pwm_platdata pwm_data[] = { + { PWM_AB, 0xffd1b000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_CD, 0xffd1a000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_EF, 0xffd19000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_AB, 0xff807000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_CD, 0xff802000, IS_DOUBLE_CHANNEL, IS_BLINK }, +}; + +U_BOOT_DEVICES(meson_pwm) = { + { "amlogic,general-pwm", &pwm_data[0] }, + { "amlogic,general-pwm", &pwm_data[1] }, + { "amlogic,general-pwm", &pwm_data[2] }, + { "amlogic,general-pwm", &pwm_data[3] }, + { "amlogic,general-pwm", &pwm_data[4] }, +}; +#endif /*end CONFIG_PWM_MESON*/ + int board_init(void) { //Please keep CONFIG_AML_V2_FACTORY_BURN at first place of board_init diff --git a/board/amlogic/g12a_u200_v1/g12a_u200_v1.c b/board/amlogic/g12a_u200_v1/g12a_u200_v1.c index 78dd59c..7675539 100644 --- a/board/amlogic/g12a_u200_v1/g12a_u200_v1.c +++ b/board/amlogic/g12a_u200_v1/g12a_u200_v1.c @@ -33,6 +33,10 @@ #ifdef CONFIG_SYS_I2C_MESON #include <amlogic/i2c.h> #endif +#ifdef CONFIG_PWM_MESON +#include <pwm.h> +#include <amlogic/pwm.h> +#endif #ifdef CONFIG_AML_VPU #include <vpu.h> #endif @@ -576,6 +580,24 @@ void set_i2c_ao_pinmux(void) } #endif /*end CONFIG_SYS_I2C_MESON*/ +#ifdef CONFIG_PWM_MESON +static const struct meson_pwm_platdata pwm_data[] = { + { PWM_AB, 0xffd1b000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_CD, 0xffd1a000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_EF, 0xffd19000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_AB, 0xff807000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_CD, 0xff802000, IS_DOUBLE_CHANNEL, IS_BLINK }, +}; + +U_BOOT_DEVICES(meson_pwm) = { + { "amlogic,general-pwm", &pwm_data[0] }, + { "amlogic,general-pwm", &pwm_data[1] }, + { "amlogic,general-pwm", &pwm_data[2] }, + { "amlogic,general-pwm", &pwm_data[3] }, + { "amlogic,general-pwm", &pwm_data[4] }, +}; +#endif /*end CONFIG_PWM_MESON*/ + int board_init(void) { sys_led_init(); diff --git a/board/amlogic/g12a_u211_v1/g12a_u211_v1.c b/board/amlogic/g12a_u211_v1/g12a_u211_v1.c index 3ad6d4f..07b56cd 100644 --- a/board/amlogic/g12a_u211_v1/g12a_u211_v1.c +++ b/board/amlogic/g12a_u211_v1/g12a_u211_v1.c @@ -33,6 +33,10 @@ #ifdef CONFIG_SYS_I2C_MESON #include <amlogic/i2c.h> #endif +#ifdef CONFIG_PWM_MESON +#include <pwm.h> +#include <amlogic/pwm.h> +#endif #ifdef CONFIG_AML_VPU #include <vpu.h> #endif @@ -571,6 +575,24 @@ void set_i2c_ao_pinmux(void) } #endif /*end CONFIG_SYS_I2C_MESON*/ +#ifdef CONFIG_PWM_MESON +static const struct meson_pwm_platdata pwm_data[] = { + { PWM_AB, 0xffd1b000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_CD, 0xffd1a000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_EF, 0xffd19000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_AB, 0xff807000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_CD, 0xff802000, IS_DOUBLE_CHANNEL, IS_BLINK }, +}; + +U_BOOT_DEVICES(meson_pwm) = { + { "amlogic,general-pwm", &pwm_data[0] }, + { "amlogic,general-pwm", &pwm_data[1] }, + { "amlogic,general-pwm", &pwm_data[2] }, + { "amlogic,general-pwm", &pwm_data[3] }, + { "amlogic,general-pwm", &pwm_data[4] }, +}; +#endif /*end CONFIG_PWM_MESON*/ + int board_init(void) { //Please keep CONFIG_AML_V2_FACTORY_BURN at first place of board_init diff --git a/board/amlogic/g12a_u212_v1/g12a_u212_v1.c b/board/amlogic/g12a_u212_v1/g12a_u212_v1.c index fec5fdb..1836757 100644 --- a/board/amlogic/g12a_u212_v1/g12a_u212_v1.c +++ b/board/amlogic/g12a_u212_v1/g12a_u212_v1.c @@ -33,6 +33,10 @@ #ifdef CONFIG_SYS_I2C_MESON #include <amlogic/i2c.h> #endif +#ifdef CONFIG_PWM_MESON +#include <pwm.h> +#include <amlogic/pwm.h> +#endif #ifdef CONFIG_AML_VPU #include <vpu.h> #endif @@ -571,6 +575,24 @@ void set_i2c_ao_pinmux(void) } #endif /*end CONFIG_SYS_I2C_MESON*/ +#ifdef CONFIG_PWM_MESON +static const struct meson_pwm_platdata pwm_data[] = { + { PWM_AB, 0xffd1b000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_CD, 0xffd1a000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_EF, 0xffd19000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_AB, 0xff807000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_CD, 0xff802000, IS_DOUBLE_CHANNEL, IS_BLINK }, +}; + +U_BOOT_DEVICES(meson_pwm) = { + { "amlogic,general-pwm", &pwm_data[0] }, + { "amlogic,general-pwm", &pwm_data[1] }, + { "amlogic,general-pwm", &pwm_data[2] }, + { "amlogic,general-pwm", &pwm_data[3] }, + { "amlogic,general-pwm", &pwm_data[4] }, +}; +#endif /*end CONFIG_PWM_MESON*/ + int board_init(void) { //Please keep CONFIG_AML_V2_FACTORY_BURN at first place of board_init diff --git a/board/amlogic/g12a_u220_v1/g12a_u220_v1.c b/board/amlogic/g12a_u220_v1/g12a_u220_v1.c index d1f2610..e7f166c 100644 --- a/board/amlogic/g12a_u220_v1/g12a_u220_v1.c +++ b/board/amlogic/g12a_u220_v1/g12a_u220_v1.c @@ -33,6 +33,10 @@ #ifdef CONFIG_SYS_I2C_MESON #include <amlogic/i2c.h> #endif +#ifdef CONFIG_PWM_MESON +#include <pwm.h> +#include <amlogic/pwm.h> +#endif #ifdef CONFIG_AML_VPU #include <vpu.h> #endif @@ -578,6 +582,24 @@ void set_i2c_ao_pinmux(void) } #endif /*end CONFIG_SYS_I2C_MESON*/ +#ifdef CONFIG_PWM_MESON +static const struct meson_pwm_platdata pwm_data[] = { + { PWM_AB, 0xffd1b000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_CD, 0xffd1a000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_EF, 0xffd19000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_AB, 0xff807000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_CD, 0xff802000, IS_DOUBLE_CHANNEL, IS_BLINK }, +}; + +U_BOOT_DEVICES(meson_pwm) = { + { "amlogic,general-pwm", &pwm_data[0] }, + { "amlogic,general-pwm", &pwm_data[1] }, + { "amlogic,general-pwm", &pwm_data[2] }, + { "amlogic,general-pwm", &pwm_data[3] }, + { "amlogic,general-pwm", &pwm_data[4] }, +}; +#endif /*end CONFIG_PWM_MESON*/ + extern void aml_pwm_cal_init(int mode); int board_init(void) diff --git a/board/amlogic/g12a_u221_v1/g12a_u221_v1.c b/board/amlogic/g12a_u221_v1/g12a_u221_v1.c index c0f6e10..20a3381 100644 --- a/board/amlogic/g12a_u221_v1/g12a_u221_v1.c +++ b/board/amlogic/g12a_u221_v1/g12a_u221_v1.c @@ -33,6 +33,10 @@ #ifdef CONFIG_SYS_I2C_MESON #include <amlogic/i2c.h> #endif +#ifdef CONFIG_PWM_MESON +#include <pwm.h> +#include <amlogic/pwm.h> +#endif #ifdef CONFIG_AML_VPU #include <vpu.h> #endif @@ -577,6 +581,24 @@ void set_i2c_ao_pinmux(void) } #endif /*end CONFIG_SYS_I2C_MESON*/ +#ifdef CONFIG_PWM_MESON +static const struct meson_pwm_platdata pwm_data[] = { + { PWM_AB, 0xffd1b000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_CD, 0xffd1a000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_EF, 0xffd19000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_AB, 0xff807000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_CD, 0xff802000, IS_DOUBLE_CHANNEL, IS_BLINK }, +}; + +U_BOOT_DEVICES(meson_pwm) = { + { "amlogic,general-pwm", &pwm_data[0] }, + { "amlogic,general-pwm", &pwm_data[1] }, + { "amlogic,general-pwm", &pwm_data[2] }, + { "amlogic,general-pwm", &pwm_data[3] }, + { "amlogic,general-pwm", &pwm_data[4] }, +}; +#endif /*end CONFIG_PWM_MESON*/ + extern void aml_pwm_cal_init(int mode); int board_init(void) diff --git a/board/amlogic/g12b_skt_v1/g12b_skt_v1.c b/board/amlogic/g12b_skt_v1/g12b_skt_v1.c index e049ce7..1f3fa78 100644 --- a/board/amlogic/g12b_skt_v1/g12b_skt_v1.c +++ b/board/amlogic/g12b_skt_v1/g12b_skt_v1.c @@ -33,6 +33,10 @@ #ifdef CONFIG_SYS_I2C_MESON #include <amlogic/i2c.h> #endif +#ifdef CONFIG_PWM_MESON +#include <pwm.h> +#include <amlogic/pwm.h> +#endif #ifdef CONFIG_AML_VPU #include <vpu.h> #endif @@ -608,6 +612,24 @@ void set_i2c_m1_pinmux(void) } #endif /*end CONFIG_SYS_I2C_MESON*/ +#ifdef CONFIG_PWM_MESON +static const struct meson_pwm_platdata pwm_data[] = { + { PWM_AB, 0xffd1b000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_CD, 0xffd1a000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_EF, 0xffd19000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_AB, 0xff807000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_CD, 0xff802000, IS_DOUBLE_CHANNEL, IS_BLINK }, +}; + +U_BOOT_DEVICES(meson_pwm) = { + { "amlogic,general-pwm", &pwm_data[0] }, + { "amlogic,general-pwm", &pwm_data[1] }, + { "amlogic,general-pwm", &pwm_data[2] }, + { "amlogic,general-pwm", &pwm_data[3] }, + { "amlogic,general-pwm", &pwm_data[4] }, +}; +#endif /*end CONFIG_PWM_MESON*/ + extern void aml_pwm_cal_init(int mode); int board_init(void) diff --git a/board/amlogic/g12b_w400_v1/g12b_w400_v1.c b/board/amlogic/g12b_w400_v1/g12b_w400_v1.c index 2e727e5..ef9559f 100644 --- a/board/amlogic/g12b_w400_v1/g12b_w400_v1.c +++ b/board/amlogic/g12b_w400_v1/g12b_w400_v1.c @@ -33,6 +33,10 @@ #ifdef CONFIG_SYS_I2C_MESON #include <amlogic/i2c.h> #endif +#ifdef CONFIG_PWM_MESON +#include <pwm.h> +#include <amlogic/pwm.h> +#endif #ifdef CONFIG_AML_VPU #include <vpu.h> #endif @@ -609,6 +613,24 @@ void set_i2c_m1_pinmux(void) #endif /*end CONFIG_SYS_I2C_MESON*/ +#ifdef CONFIG_PWM_MESON +static const struct meson_pwm_platdata pwm_data[] = { + { PWM_AB, 0xffd1b000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_CD, 0xffd1a000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_EF, 0xffd19000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_AB, 0xff807000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_CD, 0xff802000, IS_DOUBLE_CHANNEL, IS_BLINK }, +}; + +U_BOOT_DEVICES(meson_pwm) = { + { "amlogic,general-pwm", &pwm_data[0] }, + { "amlogic,general-pwm", &pwm_data[1] }, + { "amlogic,general-pwm", &pwm_data[2] }, + { "amlogic,general-pwm", &pwm_data[3] }, + { "amlogic,general-pwm", &pwm_data[4] }, +}; +#endif /*end CONFIG_PWM_MESON*/ + extern void aml_pwm_cal_init(int mode); int board_init(void) diff --git a/board/amlogic/g12b_w411_v1/g12b_w411_v1.c b/board/amlogic/g12b_w411_v1/g12b_w411_v1.c index 98bf5c5..ffc2ea6 100644 --- a/board/amlogic/g12b_w411_v1/g12b_w411_v1.c +++ b/board/amlogic/g12b_w411_v1/g12b_w411_v1.c @@ -33,6 +33,10 @@ #ifdef CONFIG_SYS_I2C_MESON #include <amlogic/i2c.h> #endif +#ifdef CONFIG_PWM_MESON +#include <pwm.h> +#include <amlogic/pwm.h> +#endif #ifdef CONFIG_AML_VPU #include <vpu.h> #endif @@ -609,6 +613,24 @@ void set_i2c_m1_pinmux(void) #endif /*end CONFIG_SYS_I2C_MESON*/ +#ifdef CONFIG_PWM_MESON +static const struct meson_pwm_platdata pwm_data[] = { + { PWM_AB, 0xffd1b000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_CD, 0xffd1a000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWM_EF, 0xffd19000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_AB, 0xff807000, IS_DOUBLE_CHANNEL, IS_BLINK }, + { PWMAO_CD, 0xff802000, IS_DOUBLE_CHANNEL, IS_BLINK }, +}; + +U_BOOT_DEVICES(meson_pwm) = { + { "amlogic,general-pwm", &pwm_data[0] }, + { "amlogic,general-pwm", &pwm_data[1] }, + { "amlogic,general-pwm", &pwm_data[2] }, + { "amlogic,general-pwm", &pwm_data[3] }, + { "amlogic,general-pwm", &pwm_data[4] }, +}; +#endif /*end CONFIG_PWM_MESON*/ + extern void aml_pwm_cal_init(int mode); int board_init(void) diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index c0c4883..8e06cd8 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -9,5 +9,9 @@ # #ccflags-y += -DDEBUG +ifdef CONFIG_DM_PWM +obj-y += pwm-uclass.o +obj-$(CONFIG_PWM_MESON) += pwm-meson.o +endif -obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o +obj-$(CONFIG_PWM_IMX) += pwm-imx.o pwm-imx-util.o
\ No newline at end of file diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c new file mode 100644 index 0000000..8f1cb71 --- a/dev/null +++ b/drivers/pwm/pwm-meson.c @@ -0,0 +1,518 @@ +/* + * Pwm controller driver for Amlogic Meson SOC + * + * Copyright (c) 2018 Amlogic, Inc. All rights reserved. + * Auther: Bichao Zheng <bichao.zheng@amlogic.com> + * + * SPDX-License-Identifier: (GPL-2.0+ or MIT) + */ + +#include <common.h> +#include <dm.h> +#include <pwm.h> +#include <amlogic/pwm.h> +#include <linux/err.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <linux/sizes.h> +#include <div64.h> +#include <amlogic/pwm.h> +#include "pwm-meson.h" + +#define pwm_info(fmt, args...) \ + printf("[info]%s: " fmt, __func__, ## args) + +#define pwm_err(fmt, args...) \ + printf("[error]%s: " fmt, __func__, ## args) + +struct meson_pwm_priv{ + struct meson_pwm_reg *regs; + struct meson_pwm_state *pwm_state; + bool is_double_channel; + bool is_blink; +}; + +static u64 meson_pwm_clock_get_rate(void) +{ + return 24000000; +} + +static int pwm_meson_get_polarity(struct udevice *dev, uint channel) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); +#if 0 + struct meson_pwm_reg *regs = priv->regs; + + unsigned int tmp, val; + + switch (channel) { + case MESON_PWM0: + case MESON_PWM2: + val = 0x1 << 26; + break; + + case MESON_PWM1: + case MESON_PWM3: + val = 0x1 << 27; + break; + + default: + pwm_err("Id is invalid\n"); + return -EINVAL; + } + + tmp = readl(®s->miscr); + tmp = tmp & val; + if (tmp == 0) + return 0; + else + return 1; +#else + struct meson_pwm_state *pwm_state = priv->pwm_state; + + return pwm_state[channel].polarity; +#endif +} + +static void pwm_constant_enable(struct udevice *dev, uint channel) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_reg *regs = priv->regs; + + switch (channel) { + case MESON_PWM0: + case MESON_PWM2: + setbits_le32(®s->miscr, 1 << 28); + break; + + case MESON_PWM1: + case MESON_PWM3: + setbits_le32(®s->miscr, 1 << 29); + break; + + default: + pwm_err("Id is invalid\n"); + break; + } +} + +static void pwm_constant_disable(struct udevice *dev, uint channel) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_reg *regs = priv->regs; + + switch (channel) { + case MESON_PWM0: + case MESON_PWM2: + clrbits_le32(®s->miscr, 1 << 28); + break; + + case MESON_PWM1: + case MESON_PWM3: + clrbits_le32(®s->miscr, 1 << 29); + break; + + default: + pwm_err("Id is invalid\n"); + break; + } +} + +static void pwm_meson_config(struct udevice *dev, unsigned channel) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_reg *regs = priv->regs; + struct meson_pwm_state *pwm_state = priv->pwm_state; + + switch (channel) { + case MESON_PWM0: + /*set div and clock enable*/ + setbits_le32(®s->miscr, (pwm_state[channel].pre_div << 8 | 1 << 15)); + /*set duty*/ + writel((pwm_state[channel].hi << 16 | pwm_state[channel].lo), ®s->dar); + break; + + case MESON_PWM1: + /*set div and clock enable*/ + setbits_le32(®s->miscr, (pwm_state[channel].pre_div << 16 | 1 << 23)); + /*set duty*/ + writel((pwm_state[channel].hi << 16 | pwm_state[channel].lo), ®s->dbr); + break; + + default: + break; + } +} + +static void pwm_meson_config_ext(struct udevice *dev, unsigned channel) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_reg *regs = priv->regs; + struct meson_pwm_state *pwm_state = priv->pwm_state; + + switch (channel) { + case MESON_PWM2: + /*set div and clock enable*/ + /*setbits_le32(®s->miscr, (pwm_state[channel].pre_div << 8 | 1 << 15));*/ + /*set duty*/ + writel((pwm_state[channel].hi << 16 | pwm_state[channel].lo), ®s->da2r); + break; + + case MESON_PWM3: + /*set div and clock enable*/ + /*setbits_le32(®s->miscr, (pwm_state[channel].pre_div << 16 | 1 << 23));*/ + /*set duty*/ + writel((pwm_state[channel].hi << 16 | pwm_state[channel].lo), ®s->db2r); + break; + + default: + break; + } +} + +static int meson_pwm_cacl(struct udevice *dev, uint channel, uint period, + uint duty) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_state *pwm_state = priv->pwm_state; + unsigned int pre_div, cnt, duty_cnt, inv; + unsigned long fin_freq = -1; + u64 fin_ps; + + inv = pwm_meson_get_polarity(dev, channel); + if (inv) + duty = period - duty; + + fin_freq = meson_pwm_clock_get_rate(); + fin_ps = (u64)NSEC_PER_SEC * 1000; + do_div(fin_ps, fin_freq); + + for (pre_div = 0; pre_div < 0x7f; pre_div++) { + cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000, + fin_ps * (pre_div + 1)); + if (cnt <= 0xffff) + break; + } + + if (pre_div >= 0x7f) { + pwm_err("unable to get period pre_div\n"); + return -EINVAL; + } + + if (duty == period) { + pwm_state[channel].pre_div = pre_div; + pwm_state[channel].hi = cnt; + pwm_state[channel].lo = 0; + } else if (duty == 0) { + pwm_state[channel].pre_div = pre_div; + pwm_state[channel].hi = 0; + pwm_state[channel].lo = cnt; + } else { + /* Then check is we can have the duty with the same pre_div */ + duty_cnt = DIV_ROUND_CLOSEST_ULL((u64)duty * 1000, + fin_ps * (pre_div + 1)); + if (duty_cnt > 0xffff) { + pwm_err("unable to get duty cycle\n"); + return -EINVAL; + } + + if (duty_cnt == 0) + duty_cnt = 1; + + if (cnt == duty_cnt) + duty_cnt -= 1; + + pwm_state[channel].pre_div = pre_div; + pwm_state[channel].hi = duty_cnt - 1; + pwm_state[channel].lo = cnt - duty_cnt - 1; + } + + if (duty == period || duty == 0) + pwm_constant_enable(dev, channel); + else + pwm_constant_disable(dev, channel); + + return 0; +} + +static int meson_pwm_set_config(struct udevice *dev, uint channel, uint period_ns, + uint duty_ns) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_state *pwm_state = priv->pwm_state; + + if ((!priv->is_double_channel) && (channel >= MESON_PWM2)) { + pwm_err("sub channel is not support\n"); + return -EINVAL; + } + + if ((duty_ns < 0) || (period_ns <= 0)) { + pwm_err("Not available duty_ns period_ns error\n"); + return -EINVAL; + } + + if (duty_ns > period_ns) { + pwm_err("Not available duty_ns period_ns error\n"); + return -EINVAL; + } + + if (pwm_state[channel].period != period_ns || + pwm_state[channel].duty_cycle != duty_ns) { + meson_pwm_cacl(dev, channel, period_ns, duty_ns); + switch (channel) { + case MESON_PWM0: + case MESON_PWM1: + pwm_meson_config(dev, channel); + break; + + case MESON_PWM2: + case MESON_PWM3: + pwm_meson_config_ext(dev, channel); + break; + + default: + pwm_err("Id is invalid\n"); + return -EINVAL; + } + } + + pwm_state[channel].period = period_ns; + pwm_state[channel].duty_cycle = duty_ns; + + return 0; +}; + +static int meson_pwm_set_enable(struct udevice *dev, uint channel, bool enable) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_state *pwm_state = priv->pwm_state; + struct meson_pwm_reg *regs = priv->regs; + unsigned int val, orig; + + if (pwm_state[channel].enabled != enable) { + + if ((!priv->is_double_channel) && (channel >= MESON_PWM2)) { + pwm_err("sub channel is not support\n"); + return -EINVAL; + } + + switch (channel) { + case MESON_PWM0: + val = 1 << 0; + break; + case MESON_PWM1: + val = 1 << 1; + break; + case MESON_PWM2: + val = 1 << 25; + break; + case MESON_PWM3: + val = 1 << 24; + break; + default: + pwm_err("channel is not legal\n"); + return -EINVAL; + } + + orig = readl(®s->miscr); + if (enable) + orig |= val; + else + orig &= ~val; + + writel(orig, ®s->miscr); + pwm_state[channel].enabled = 0; + } + + return 0; +} + +static int meson_pwm_set_invert(struct udevice *dev, uint channel, bool polarity) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_state *pwm_state = priv->pwm_state; +#if 0 + struct meson_pwm_reg *regs = priv->regs; + + switch (channel) { + case MESON_PWM0: + if (polarity) + setbits_le32(®s->miscr, 0x01 << 26); + else + clrbits_le32(®s->miscr, 0x01 << 26); + break; + + case MESON_PWM1: + if (polarity) + setbits_le32(®s->miscr, 0x01 << 27); + else + clrbits_le32(®s->miscr, 0x01 << 27); + break; + + default: + pwm_err("Id is invalid\n"); + break; + } +#endif + if (polarity) + pwm_state[channel].polarity = 1; + else + pwm_state[channel].polarity = 0; + return 0; +}; + +static int meson_pwm_set_times(struct udevice *dev, uint channel, uint times) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_reg *regs = priv->regs; + + if (!priv->is_double_channel) { + pwm_err("times is not support\n"); + return -EINVAL; + } + + if ((times > 256) || (times <= 0)) { + pwm_err("Not available times error\n"); + return -EINVAL; + } + + switch (channel) { + case MESON_PWM0: + clrbits_le32(®s->tr, 0xff << 24); + setbits_le32(®s->tr, (times - 1) << 24); + break; + + case MESON_PWM1: + clrbits_le32(®s->tr, 0xff << 8); + setbits_le32(®s->tr, (times - 1) << 8); + break; + + case MESON_PWM2: + clrbits_le32(®s->tr, 0xff << 16); + setbits_le32(®s->tr, (times - 1) << 16); + break; + + case MESON_PWM3: + clrbits_le32(®s->tr, 0xff << 0); + setbits_le32(®s->tr, (times - 1) << 0); + break; + + default: + pwm_err("Id is invalid\n"); + return -EINVAL; + } + + return 0; +}; + +static int meson_pwm_set_blink_times(struct udevice *dev, uint channel, uint times) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_reg *regs = priv->regs; + + if (!priv->is_double_channel || !priv->is_blink) { + pwm_err("Blink is not support\n"); + return -EINVAL; + } + + if ((times > 16) || (times <= 0)) { + pwm_err("Not available times error\n"); + return -EINVAL; + } + + switch (channel) { + case MESON_PWM0: + case MESON_PWM2: + clrbits_le32(®s->br, 0xf); + setbits_le32(®s->br, times - 1); + break; + + case MESON_PWM1: + case MESON_PWM3: + clrbits_le32(®s->br, 0xf << 4); + setbits_le32(®s->br, (times - 1) << 4); + break; + + default: + pwm_err("Id is invalid\n"); + return -EINVAL; + } + + return 0; +}; + +static int meson_pwm_blink_enable(struct udevice *dev, uint channel, bool enable) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_reg *regs = priv->regs; + + if (!priv->is_double_channel || !priv->is_blink) { + pwm_err("Blink is not support\n"); + return -EINVAL; + } + + switch (channel) { + case MESON_PWM0: + case MESON_PWM2: + if (enable) + setbits_le32(®s->br, 1 << 8); + else + clrbits_le32(®s->br, 1 << 8); + break; + + case MESON_PWM1: + case MESON_PWM3: + if (enable) + setbits_le32(®s->br, 1 << 9); + else + clrbits_le32(®s->br, 1 << 9); + break; + + default: + pwm_err("Id is invalid\n"); + return -EINVAL; + } + + return 0; +}; + +static int meson_pwm_probe(struct udevice *dev) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + struct meson_pwm_platdata *plat = dev_get_platdata(dev); + + priv->regs = (struct meson_pwm_reg *)plat->reg; + priv->pwm_state = (struct meson_pwm_state *)calloc(4, sizeof(struct meson_pwm_state)); + priv->is_double_channel = plat->is_double_channel; + priv->is_blink = plat->is_blink; + + return 0; +} + +int meson_pwm_remove(struct udevice *dev) +{ + struct meson_pwm_priv *priv = dev_get_priv(dev); + + free(priv->pwm_state); + + return 0; +} + +static const struct pwm_ops meson_pwm_ops = { + .set_config = meson_pwm_set_config, + .set_enable = meson_pwm_set_enable, + .set_invert = meson_pwm_set_invert, + .set_times = meson_pwm_set_times, + .set_blink_times = meson_pwm_set_blink_times, + .set_blink_enable = meson_pwm_blink_enable, +}; + +U_BOOT_DRIVER(meson_pwm) = { + .name = "amlogic,general-pwm", + .id = UCLASS_PWM, + .ops = &meson_pwm_ops, + .probe = meson_pwm_probe, + .remove = meson_pwm_remove, + .priv_auto_alloc_size = sizeof(struct meson_pwm_priv), +}; diff --git a/drivers/pwm/pwm-meson.h b/drivers/pwm/pwm-meson.h new file mode 100644 index 0000000..8146b13 --- a/dev/null +++ b/drivers/pwm/pwm-meson.h @@ -0,0 +1,45 @@ +#ifndef PWM_MESON_H +#define PWM_MESON_H + +#define NSEC_PER_SEC 1000000000ULL + +struct meson_pwm_reg { + u32 dar;/* A/C/E Duty Register */ + u32 dbr;/* B/D/F Duty Register */ + u32 miscr;/* misc Register */ + u32 dsr;/*DS Register*/ + u32 tr;/*times Register*/ + u32 da2r;/* A2/C2/E2 Duty Register */ + u32 db2r;/* B2/D2/F2 Duty Register */ + u32 br;/*Blink Register*/ +}; + +enum pwm_polarity { + PWM_POLARITY_NORMAL, + PWM_POLARITY_INVERSED, +}; + +struct meson_pwm_state { + unsigned int period; + unsigned int duty_cycle; + unsigned int hi; + unsigned int lo; + unsigned int pre_div; + enum pwm_polarity polarity; + bool enabled; +}; + +/* + * Same as above but for u64 dividends. divisor must be a 32-bit + * number. + */ +#define DIV_ROUND_CLOSEST_ULL(x, divisor)( \ +{ \ + typeof(divisor) __d = divisor; \ + unsigned long long _tmp = (x) + (__d) / 2; \ + do_div(_tmp, __d); \ + _tmp; \ +} \ +) + +#endif diff --git a/drivers/pwm/pwm-uclass.c b/drivers/pwm/pwm-uclass.c new file mode 100644 index 0000000..f9c4c96 --- a/dev/null +++ b/drivers/pwm/pwm-uclass.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2016 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <pwm.h> +#include <asm/errno.h> + +int pwm_set_invert(struct udevice *dev, uint channel, bool polarity) +{ + struct pwm_ops *ops = pwm_get_ops(dev); + + if (!ops->set_invert) + return -ENOSYS; + + return ops->set_invert(dev, channel, polarity); +} + +int pwm_set_config(struct udevice *dev, uint channel, uint period_ns, + uint duty_ns) +{ + struct pwm_ops *ops = pwm_get_ops(dev); + + if (!ops->set_config) + return -ENOSYS; + + return ops->set_config(dev, channel, period_ns, duty_ns); +} + +int pwm_set_enable(struct udevice *dev, uint channel, bool enable) +{ + struct pwm_ops *ops = pwm_get_ops(dev); + + if (!ops->set_enable) + return -ENOSYS; + + return ops->set_enable(dev, channel, enable); +} +#ifdef CONFIG_PWM_MESON +int pwm_set_times(struct udevice *dev, uint channel, uint times) +{ + struct pwm_ops *ops = pwm_get_ops(dev); + + if (!ops->set_enable) + return -ENOSYS; + + return ops->set_times(dev, channel, times); +} + +int pwm_set_blink_times(struct udevice *dev, uint channel, uint times) +{ + struct pwm_ops *ops = pwm_get_ops(dev); + + if (!ops->set_enable) + return -ENOSYS; + + return ops->set_blink_times(dev, channel, times); +} + +int pwm_set_blink_enable(struct udevice *dev, uint channel, bool enable) +{ + struct pwm_ops *ops = pwm_get_ops(dev); + + if (!ops->set_enable) + return -ENOSYS; + + return ops->set_blink_enable(dev, channel, enable); +} +#endif +UCLASS_DRIVER(pwm) = { + .id = UCLASS_PWM, + .name = "pwm", +}; diff --git a/include/amlogic/pwm.h b/include/amlogic/pwm.h new file mode 100644 index 0000000..9d741ff --- a/dev/null +++ b/include/amlogic/pwm.h @@ -0,0 +1,43 @@ +/* + * Amlogic I2C controller Diver + * + * Copyright (C) 2018 Amlogic Corporation + * + * Licensed under the GPL-2 or later. + * + */ + +#ifndef __PWM_H__ +#define __PWM_H__ + +/* + * @pwm_index: Controller Index. + * @reg: Controller registers address. + */ + +#define MESON_PWM0 0 +#define MESON_PWM1 1 +#define MESON_PWM2 2 +#define MESON_PWM3 3 + +#define NO_DOUBLE_CHANNEL 0 +#define IS_DOUBLE_CHANNEL 1 +#define NO_BLINK 0 +#define IS_BLINK 1 + +enum { + PWM_AB = 0x0, + PWM_CD = 0x1, + PWM_EF = 0x2, + PWMAO_AB = 0x3, + PWMAO_CD = 0x4, +}; + +struct meson_pwm_platdata { + unsigned int pwm_index; + ulong reg; + bool is_double_channel; + bool is_blink; +}; + +#endif /* __PWM_H__ */ diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index f17c3c2..5373938 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -33,6 +33,7 @@ enum uclass_id { UCLASS_I2C, /* I2C bus */ UCLASS_I2C_GENERIC, /* Generic I2C device */ UCLASS_I2C_EEPROM, /* I2C EEPROM device */ + UCLASS_PWM, /* Pulse-width modulator */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/pwm.h b/include/pwm.h index f24f220..1a5b4d2 100644 --- a/include/pwm.h +++ b/include/pwm.h @@ -10,9 +10,139 @@ #ifndef _pwm_h_ #define _pwm_h_ +/* struct pwm_ops: Operations for the PWM uclass */ +struct pwm_ops { + /** + * set_config() - Set the PWM configuration + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @period_ns: PWM period in nanoseconds + * @duty_ns: PWM duty period in nanoseconds + * @return 0 if OK, -ve on error + */ + int (*set_config)(struct udevice *dev, uint channel, uint period_ns, + uint duty_ns); + + /** + * set_enable() - Enable or disable the PWM + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @enable: true to enable, false to disable + * @return 0 if OK, -ve on error + */ + int (*set_enable)(struct udevice *dev, uint channel, bool enable); + /** + * set_invert() - Set the PWM invert + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @polarity: true to invert, false to keep normal polarity + * @return 0 if OK, -ve on error + */ + int (*set_invert)(struct udevice *dev, uint channel, bool polarity); +#ifdef CONFIG_PWM_MESON + /** + * set_times() - Set the PWM times + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @times: dual channel to set times + * @return 0 if OK, -ve on error + */ + int (*set_times)(struct udevice *dev, uint channel, uint times); + /** + * set_blink_times() - Set the PWM blink times + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @times: set blink times + * @return 0 if OK, -ve on error + */ + int (*set_blink_times)(struct udevice *dev, uint channel, uint times); + /** + * set_blink_enable() - Enable or disable the PWM blink + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @enable: true to enable, false to disable + * @return 0 if OK, -ve on error + */ + int (*set_blink_enable)(struct udevice *dev, uint channel, bool enable); +#endif +}; + +#define pwm_get_ops(dev) ((struct pwm_ops *)(dev)->driver->ops) + +/** + * pwm_set_config() - Set the PWM configuration + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @period_ns: PWM period in nanoseconds + * @duty_ns: PWM duty period in nanoseconds + * @return 0 if OK, -ve on error + */ +int pwm_set_config(struct udevice *dev, uint channel, uint period_ns, + uint duty_ns); + +/** + * pwm_set_enable() - Enable or disable the PWM + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @enable: true to enable, false to disable + * @return 0 if OK, -ve on error + */ +int pwm_set_enable(struct udevice *dev, uint channel, bool enable); + +/** + * pwm_set_invert() - Set pwm default polarity + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @polarity: true to invert, false to keep normal polarity + * @return 0 if OK, -ve on error + */ +int pwm_set_invert(struct udevice *dev, uint channel, bool polarity); +#ifdef CONFIG_PWM_MESON +/** + * pwm_set_times() - Set the PWM times + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @times: dual channel to set times + * @return 0 if OK, -ve on error + */ +int pwm_set_times(struct udevice *dev, uint channel, uint times); + +/** + * pwm_set_blink_times() - Set the PWM blink times + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @times: set blink times + * @return 0 if OK, -ve on error + */ +int pwm_set_blink_times(struct udevice *dev, uint channel, uint times); + +/** + * pwm_set_blink_enable() - Enable or disable the PWM blink + * + * @dev: PWM device to update + * @channel: PWM channel to update + * @enable: true to enable, false to disable + * @return 0 if OK, -ve on error + */ +int pwm_set_blink_enable(struct udevice *dev, uint channel, bool enable); +#endif +/* Legacy interface */ +#ifndef CONFIG_DM_PWM int pwm_init (int pwm_id, int div, int invert); int pwm_config (int pwm_id, int duty_ns, int period_ns); int pwm_enable (int pwm_id); void pwm_disable (int pwm_id); +#endif #endif /* _pwm_h_ */ |