From ab931734fb19cdf383742dc122f261a29ab0d305 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E8=AE=B8=E7=9B=9B=E9=A3=9E?= Date: Fri, 25 Apr 2014 11:25:59 +0800 Subject: [PATCH] pwm: support vop-pwm function --- arch/arm/boot/dts/rk3288-pinctrl.dtsi | 15 + arch/arm/boot/dts/rk3288.dtsi | 24 +- drivers/pwm/pwm-rockchip.c | 722 ++++++++++++++++---------- 3 files changed, 483 insertions(+), 278 deletions(-) diff --git a/arch/arm/boot/dts/rk3288-pinctrl.dtsi b/arch/arm/boot/dts/rk3288-pinctrl.dtsi index 3816ea611b78..1eff2dcb475b 100755 --- a/arch/arm/boot/dts/rk3288-pinctrl.dtsi +++ b/arch/arm/boot/dts/rk3288-pinctrl.dtsi @@ -634,6 +634,21 @@ }; gpio7_pwm { + vop0_pwm_pin:vop0-pwm { + rockchip,pins = ; + rockchip,pull = ; + rockchip,drive = ; + //rockchip,tristate = ; + + }; + + vop1_pwm_pin:vop1-pwm { + rockchip,pins = ; + rockchip,pull = ; + rockchip,drive = ; + //rockchip,tristate = ; + }; + pwm0_pin:pwm0 { rockchip,pins = ; rockchip,pull = ; diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index adc54761e11a..a577ba0cadee 100755 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -826,6 +826,28 @@ pinctrl-0 = <&spdif_tx>; }; + vop1pwm: pwm@ff9401a0 { + compatible = "rockchip,vop-pwm"; + reg = <0xff9401a0 0x10>; + #pwm-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&vop1_pwm_pin>; + clocks = <&clk_gates13 11>; + clock-names = "pclk_pwm"; + status = "disabled"; + }; + + vop0pwm: pwm@ff9301a0 { + compatible = "rockchip,vop-pwm"; + reg = <0xff9301a0 0x10>; + #pwm-cells = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&vop0_pwm_pin>; + clocks = <&clk_gates13 10>; + clock-names = "pclk_pwm"; + status = "disabled"; + }; + pwm0: pwm@ff680000 { compatible = "rockchip,rk-pwm"; reg = <0xff680000 0x10>; @@ -834,7 +856,7 @@ pinctrl-0 = <&pwm0_pin>; clocks = <&clk_gates11 11>; clock-names = "pclk_pwm"; - status = "okay"; + status = "disabled"; }; pwm1: pwm@ff680010 { diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index bdad0138f310..6ac796bbab97 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -12,66 +12,12 @@ #include #include #include +#include +#include +#include +#include -#define NUM_PWM 1 -/* PWM registers */ -#if 0 -#define PWM_REG_CNTR 0x00 /* Counter Register */ -#define PWM_REG_PERIOD 0x04 /* Period Register */ -#define PWM_REG_DUTY 0x08 /* Duty Cycle Register */ -#define PWM_REG_CTRL 0x0c /* Control Register */ - -/*bits definitions*/ - -#define PWM_ENABLE (1 << 0) -#define PWM_DISABLE (0 << 0) - -#define PWM_SHOT (0x00 << 1) -#define PWM_CONTINUMOUS (0x01 << 1) -#define PWM_CAPTURE (0x01 << 1) - -#define PWM_DUTY_POSTIVE (0x01 << 3) -#define PWM_DUTY_NEGATIVE (0x00 << 3) - -#define PWM_INACTIVE_POSTIVE (0x01 << 4) -#define PWM_INACTIVE_NEGATIVE (0x00 << 4) - -#define PWM_OUTPUT_LEFT (0x00 << 5) -#define PWM_OUTPUT_ENTER (0x01 << 5) - - -#define PWM_LP_ENABLE (1<<8) -#define PWM_LP_DISABLE (0<<8) - -#define PWM_CLK_SCALE (1 << 9) -#define PWM_CLK_NON_SCALE (0 << 9) - - - -#define PWMCR_MIN_PRESCALE 0x00 - -#define PWMCR_MIN_PRESCALE 0x00 -#define PWMCR_MAX_PRESCALE 0x07 - -#define PWMDCR_MIN_DUTY 0x0001 -#define PWMDCR_MAX_DUTY 0xFFFF - -#define PWMPCR_MIN_PERIOD 0x0001 -#define PWMPCR_MAX_PERIOD 0xFFFF - - -enum pwm_div { - PWM_DIV1 = (0x0 << 12), - PWM_DIV2 = (0x1 << 12), - PWM_DIV4 = (0x2 << 12), - PWM_DIV8 = (0x3 << 12), - PWM_DIV16 = (0x4 << 12), - PWM_DIV32 = (0x5 << 12), - PWM_DIV64 = (0x6 << 12), - PWM_DIV128 = (0x7 << 12), -}; -#endif static int pwm_dbg_level = 0; module_param_named(dbg_level, pwm_dbg_level, int, 0644); #define DBG( args...) \ @@ -81,51 +27,55 @@ module_param_named(dbg_level, pwm_dbg_level, int, 0644); } \ } while (0) -#define PWM_REG_CNTR 0x00 -#define PWM_REG_HRC 0x04 -#define PWM_REG_LRC 0x08 -#define PWM_REG_CTRL 0x0c +#define PWM_CLK 1 +#define NUM_PWM 1 +/* PWM registers */ +#define PWM_REG_CNTR 0x00 +#define PWM_REG_HRC 0x04 +#define PWM_REG_LRC 0x08 +#define PWM_REG_CTRL 0x0c /* PWM Control Register */ -#define PWM_REG_PERIOD PWM_REG_HRC /* Period Register */ -#define PWM_REG_DUTY PWM_REG_LRC /* Duty Cycle Register */ -//#define PWM_REG_CTRL 0x0c /* Control Register */ +#define PWM_REG_PERIOD PWM_REG_HRC /* Period Register */ +#define PWM_REG_DUTY PWM_REG_LRC /* Duty Cycle Register */ +#define VOP_REG_CNTR 0x0C +#define VOP_REG_CTRL 0x00 /* VOP-PWM Control Register */ +//#define PWM_REG_CTRL 0x0c /* Control Register */ -#define PWM_DIV_MASK (0xf << 9) -#define PWM_CAPTURE (1 << 8) -#define PWM_RESET (1 << 7) -#define PWM_INTCLR (1 << 6) -#define PWM_INTEN (1 << 5) -#define PWM_SINGLE (1 << 4) +#define PWM_DIV_MASK (0xf << 9) +#define PWM_CAPTURE (1 << 8) +#define PWM_RESET (1 << 7) +#define PWM_INTCLR (1 << 6) +#define PWM_INTEN (1 << 5) +#define PWM_SINGLE (1 << 4) -#define PWM_ENABLE (1 << 3) -#define PWM_TIMER_EN (1 << 0) +#define PWM_ENABLE (1 << 3) +#define PWM_TIMER_EN (1 << 0) #define RK_PWM_DISABLE (0 << 0) -#define RK_PWM_ENABLE (1 << 0) +#define RK_PWM_ENABLE (1 << 0) -#define PWM_SHOT (0 << 1) +#define PWM_SHOT (0 << 1) #define PWM_CONTINUMOUS (1 << 1) -#define RK_PWM_CAPTURE (1 << 2) +#define RK_PWM_CAPTURE (1 << 2) #define PWM_DUTY_POSTIVE (1 << 3) -#define PWM_DUTY_NEGATIVE (0 << 3) +#define PWM_DUTY_NEGATIVE (0 << 3) -#define PWM_INACTIVE_POSTIVE (1 << 4) -#define PWM_INACTIVE_NEGATIVE (0 << 4) +#define PWM_INACTIVE_POSTIVE (1 << 4) +#define PWM_INACTIVE_NEGATIVE (0 << 4) #define PWM_OUTPUT_LEFT (0 << 5) -#define PWM_OUTPUT_ENTER (1 << 5) +#define PWM_OUTPUT_ENTER (1 << 5) -#define PWM_LP_ENABLE (1<<8) -#define PWM_LP_DISABLE (0<<8) +#define PWM_LP_ENABLE (1<<8) +#define PWM_LP_DISABLE (0<<8) #define DW_PWM_PRESCALE 9 #define RK_PWM_PRESCALE 16 -#define PWM_TimeEN PWM_TIMER_EN #define PWMCR_MIN_PRESCALE 0x00 #define PWMCR_MIN_PRESCALE 0x00 @@ -136,105 +86,40 @@ module_param_named(dbg_level, pwm_dbg_level, int, 0644); #define PWMPCR_MIN_PERIOD 0x0001 #define PWMPCR_MAX_PERIOD 0xFFFF - -#define DW_PWM 0x00 -#define RK_PWM 0x01 - -/** - * struct rk_pwm_chip - struct representing pwm chip - * - * @base: base address of pwm chip - * @clk: pointer to clk structure of pwm chip - * @chip: linux pwm chip representation - */ - +#if 0 static spinlock_t pwm_lock[4] = { __SPIN_LOCK_UNLOCKED(pwm_lock0), __SPIN_LOCK_UNLOCKED(pwm_lock1), __SPIN_LOCK_UNLOCKED(pwm_lock2), __SPIN_LOCK_UNLOCKED(pwm_lock3), }; +#endif +int spinlock_num = 0; +/******************************************** + * struct rk_pwm_chip - struct representing pwm chip -struct rk_pwm_chip { + * @base: base address of pwm chip + * @clk: pointer to clk structure of pwm chip + * @chip: linux pwm chip representation + *********************************************/ + struct rk_pwm_chip { void __iomem *base; struct clk *clk; struct pwm_chip chip; unsigned int pwm_id; -}; - -#define PWM_CLK 1 -#if 0 -static void __iomem *rk30_grf_base = NULL; -static void __iomem *rk30_cru_base = NULL; -static void __iomem *rk30_pwm_base = NULL; -//#define SZ_16K 0x4000 -//#define SZ_8K 0x2000 -#define RK30_GRF_PHYS 0x20008000 -#define RK30_GRF_SIZE SZ_8K -#define RK30_CRU_PHYS 0x20000000 -#define RK30_CRU_SIZE SZ_16K -#define RK30_PWM_PHYS 0x20050000 -#define RK30_PWM_SIZE SZ_16K - -static void dump_register_of_pwm(void) -{ - int off; -//rk30_grf_base = ioremap(RK30_GRF_PHYS, RK30_GRF_SIZE); -// rk30_cru_base = ioremap(RK30_CRU_PHYS, RK30_CRU_SIZE); - //rk30_pwm_base = ioremap(RK30_PWM_PHYS, RK30_PWM_SIZE); - -// DBG("GRF IOMUX GPIO3_D6 = 0x%08x\n",readl_relaxed(rk30_grf_base+ 0x9C) ); - //DBG("CRU = 0x%08x\n",readl_relaxed(rk30_cru_base+ 0xeC) ); -//writel_relaxed(0x10001000, rk30_grf_base+ 0x9C); - //DBG("GRF IOMUX GPIO3_D6 = 0x%08x\n",readl_relaxed(rk30_grf_base+ 0x9C) ); - //DBG("CRU = 0x%08x\n",readl_relaxed(rk30_cru_base+ 0xeC) ); + spinlock_t lock; + int pwm_ctrl; + int pwm_duty; + int pwm_period; + int pwm_count; + int (*config)(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns); + void (*set_enable)(struct pwm_chip *chip, struct pwm_device *pwm,bool enable); + void (*pwm_suspend)(struct pwm_chip *chip, struct pwm_device *pwm); + void (*pwm_resume)(struct pwm_chip *chip, struct pwm_device *pwm); -#if 0 - barrier(); - writel_relaxed(off, rk30_pwm_base+3*0x10+ PWM_REG_CTRL); - - dsb(); - writel_relaxed(0x1900, rk30_pwm_base+3*0x10+ PWM_REG_HRC);//rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_HRC,0x1900);// dc); - writel_relaxed(0x5dc0, rk30_pwm_base+3*0x10+ PWM_REG_LRC);//rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_LRC, 0x5dc0);//pv); - writel_relaxed(0, rk30_pwm_base+3*0x10+ PWM_REG_CNTR);//rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CNTR,0); - dsb(); - writel_relaxed(0x09, rk30_pwm_base+3*0x10+ PWM_REG_CTRL);// rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,on); - dsb(); -#endif -} -static void dump_pwm_register(struct rk_pwm_chip *chip) -{ -DBG("dump pwm regitster start\n"); -#if 1 -DBG("PWM0\n"); -DBG("PWM_REG_CTRL =0x%08x\n",rk_pwm_readl(chip, 0, PWM_REG_CTRL)); -DBG("PWM_REG_HRC = 0x%08x\n",rk_pwm_readl(chip,0, PWM_REG_HRC)); -DBG("PWM_REG_LRC = 0x%08x\n",rk_pwm_readl(chip,0, PWM_REG_LRC)); -DBG("PWM_REG_CNTR = 0x%08x\n",rk_pwm_readl(chip,0, PWM_REG_CNTR)); - -DBG("PWM1\n"); -DBG("PWM_REG_CTRL =0x%08x\n",rk_pwm_readl(chip, 1, PWM_REG_CTRL)); -DBG("PWM_REG_HRC = 0x%08x\n",rk_pwm_readl(chip,1, PWM_REG_HRC)); -DBG("PWM_REG_LRC = 0x%08x\n",rk_pwm_readl(chip,1, PWM_REG_LRC)); -DBG("PWM_REG_CNTR = 0x%08x\n",rk_pwm_readl(chip,1, PWM_REG_CNTR)); - -DBG("PWM2\n"); -DBG("PWM_REG_CTRL =0x%08x\n",rk_pwm_readl(chip, 2, PWM_REG_CTRL)); -DBG("PWM_REG_HRC = 0x%08x\n",rk_pwm_readl(chip,2, PWM_REG_HRC)); -DBG("PWM_REG_LRC = 0x%08x\n",rk_pwm_readl(chip,2, PWM_REG_LRC)); -DBG("PWM_REG_CNTR = 0x%08x\n",rk_pwm_readl(chip,2, PWM_REG_CNTR)); - -DBG("PWM3\n"); -DBG("PWM_REG_CTRL =0x%08x\n",rk_pwm_readl(chip,3, PWM_REG_CTRL)); -DBG("PWM_REG_HRC = 0x%08x\n",rk_pwm_readl(chip,3, PWM_REG_HRC)); -DBG("PWM_REG_LRC = 0x%08x\n",rk_pwm_readl(chip,3, PWM_REG_LRC)); -DBG("PWM_REG_CNTR = 0x%08x\n",rk_pwm_readl(chip,3, PWM_REG_CNTR)); -#endif -printk("dump pwm regitster end\n"); - -} +}; -#endif static inline struct rk_pwm_chip *to_rk_pwm_chip(struct pwm_chip *chip) { return container_of(chip, struct rk_pwm_chip, chip); @@ -253,9 +138,8 @@ static inline void rk_pwm_writel(struct rk_pwm_chip *chip, writel_relaxed(val, chip->base + (num << 4) + offset); } - -#if 1 -static int rk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, +/* config for rockchip,pwm*/ +static int rk_pwm_config_v1(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); @@ -267,18 +151,122 @@ static int rk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, unsigned long flags; spinlock_t *lock; - lock = &pwm_lock[pwm->hwpwm]; - if(pc->pwm_id == DW_PWM){ - off = PWM_RESET; - on = PWM_ENABLE | PWM_TIMER_EN; - } - if(pc->pwm_id == RK_PWM){ - on = RK_PWM_ENABLE ; - conf = PWM_OUTPUT_LEFT|PWM_LP_DISABLE| - PWM_CONTINUMOUS|PWM_DUTY_POSTIVE|PWM_INACTIVE_NEGATIVE; + lock =(&pc->lock);// &pwm_lock[pwm->hwpwm]; + off = PWM_RESET; + on = PWM_ENABLE | PWM_TIMER_EN; + /* + * Find pv, dc and prescale to suit duty_ns and period_ns. This is done + * according to formulas described below: + * + * period_ns = 10^9 * (PRESCALE ) * PV / PWM_CLK_RATE + * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE + * + * PV = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1)) + * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1)) + */ +#if PWM_CLK + clk_rate = clk_get_rate(pc->clk); +#else + clk_rate = 24000000; +#endif + while (1) { + div = 1000000000; + div *= 1 + prescale; + val = clk_rate * period_ns; + pv = div64_u64(val, div); + val = clk_rate * duty_ns; + dc = div64_u64(val, div); + + /* if duty_ns and period_ns are not achievable then return */ + if (pv < PWMPCR_MIN_PERIOD || dc < PWMDCR_MIN_DUTY) + return -EINVAL; + + /* + * if pv and dc have crossed their upper limit, then increase + * prescale and recalculate pv and dc. + */ + if (pv > PWMPCR_MAX_PERIOD || dc > PWMDCR_MAX_DUTY) { + if (++prescale > PWMCR_MAX_PRESCALE) + return -EINVAL; + continue; + } + break; } - //dump_pwm_register(pc); + /* NOTE: the clock to PWM has to be enabled first before writing to the registers. */ + +#if PWM_CLK + ret = clk_enable(pc->clk); + if (ret) + return ret; +#endif + spin_lock_irqsave(lock, flags); + + conf |= (prescale << DW_PWM_PRESCALE); + barrier(); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,off); + dsb(); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_HRC,dc);//0x1900);// dc); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_LRC, pv);//0x5dc0);//pv); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CNTR,0); + dsb(); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,on|conf); + + spin_unlock_irqrestore(lock, flags); + return 0; +} +static void rk_pwm_set_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm, bool enable) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + u32 val; + + val = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_CTRL); + if (enable) + val |= PWM_ENABLE; + else + val &= ~PWM_ENABLE; + + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL, val); + +} +static void rk_pwm_suspend_v1(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + pc->pwm_ctrl = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_CTRL); + pc->pwm_duty= rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_HRC);//0x1900);// dc); + pc->pwm_period = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_LRC );//0x5dc0);//pv); + pc->pwm_count= rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_CNTR); +} +static void rk_pwm_resume_v1(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + int off = PWM_RESET; + + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,off); + dsb(); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_HRC,pc->pwm_duty);//0x1900);// dc); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_LRC, pc->pwm_period);//0x5dc0);//pv); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CNTR,pc->pwm_count); + dsb(); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,pc->pwm_ctrl); +} +/* config for rockchip,pwm*/ +static int rk_pwm_config_v2(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + u64 val, div, clk_rate; + unsigned long prescale = PWMCR_MIN_PRESCALE, pv, dc; + int ret; + u32 on; + int conf=0; + unsigned long flags; + spinlock_t *lock; + + lock = (&pc->lock);//&pwm_lock[pwm->hwpwm]; + on = RK_PWM_ENABLE ; + conf = PWM_OUTPUT_LEFT|PWM_LP_DISABLE| + PWM_CONTINUMOUS|PWM_DUTY_POSTIVE|PWM_INACTIVE_NEGATIVE; /* * Find pv, dc and prescale to suit duty_ns and period_ns. This is done * according to formulas described below: @@ -323,91 +311,214 @@ static int rk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * registers. */ -#if PWM_CLK ret = clk_enable(pc->clk); if (ret) return ret; -#endif spin_lock_irqsave(lock, flags); - if(pc->pwm_id == DW_PWM){ - - conf |= (prescale << DW_PWM_PRESCALE); - barrier(); - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,off); - - dsb(); - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_HRC,dc);//0x1900);// dc); - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_LRC, pv);//0x5dc0);//pv); - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CNTR,0); - dsb(); - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,on|conf); - } - if(pc->pwm_id == RK_PWM){ - conf |= (prescale << RK_PWM_PRESCALE); - - barrier(); - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,off); - - dsb(); - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_DUTY,dc);//0x1900);// dc); - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_PERIOD,pv);//0x5dc0);//pv); - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CNTR,0); - dsb(); - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,on|conf); - } - spin_unlock_irqrestore(lock, flags); + conf |= (prescale << RK_PWM_PRESCALE); + barrier(); + //rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,off); + //dsb(); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_DUTY,dc);//0x1900);// dc); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_PERIOD,pv);//0x5dc0);//pv); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CNTR,0); + dsb(); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,on|conf); + spin_unlock_irqrestore(lock, flags); + + return 0; +} + +static void rk_pwm_set_enable_v2(struct pwm_chip *chip, struct pwm_device *pwm,bool enable) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + u32 val; + + val = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_CTRL); + if (enable) + val |= RK_PWM_ENABLE; + else + val &= ~RK_PWM_ENABLE; + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL, val); + DBG("%s %d \n", __FUNCTION__, rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_CTRL)); + + +} + +static void rk_pwm_suspend_v2(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + pc->pwm_ctrl = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_CTRL); + pc->pwm_duty = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_DUTY);//0x1900);// dc); + pc->pwm_period = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_PERIOD );//0x5dc0);//pv); + pc->pwm_count = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_CNTR); +} +static void rk_pwm_resume_v2(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_DUTY, pc->pwm_duty);//0x1900);// dc); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_PERIOD, pc->pwm_period);//0x5dc0);//pv); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CNTR,pc->pwm_count); + dsb(); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL,pc->pwm_ctrl); +} +//#define grf_readl(offset) readl_relaxed(RK_GRF_VIRT + offset) +//#define grf_writel(v, offset) do { writel_relaxed(v, RK_GRF_VIRT + offset); dsb(); } while (0) +/* config for rockchip,pwm*/ +static int rk_pwm_config_v3(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + u64 val, div, clk_rate; + unsigned long prescale = PWMCR_MIN_PRESCALE, pv, dc; + int ret; + u32 on; + int conf=0; + unsigned long flags; + spinlock_t *lock; + + lock = (&pc->lock);//&pwm_lock[pwm->hwpwm]; + on = RK_PWM_ENABLE ; + conf = PWM_OUTPUT_LEFT|PWM_LP_DISABLE| + PWM_CONTINUMOUS|PWM_DUTY_POSTIVE|PWM_INACTIVE_NEGATIVE; + /* + * Find pv, dc and prescale to suit duty_ns and period_ns. This is done + * according to formulas described below: + * + * period_ns = 10^9 * (PRESCALE ) * PV / PWM_CLK_RATE + * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE + * + * PV = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1)) + * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1)) + */ #if PWM_CLK - clk_disable(pc->clk); + clk_rate = clk_get_rate(pc->clk); +#else + clk_rate = 24000000; +#endif + while (1) { + div = 1000000000; + div *= 1 + prescale; + val = clk_rate * period_ns; + pv = div64_u64(val, div); + val = clk_rate * duty_ns; + dc = div64_u64(val, div); + + /* if duty_ns and period_ns are not achievable then return */ + if (pv < PWMPCR_MIN_PERIOD || dc < PWMDCR_MIN_DUTY) + return -EINVAL; + + /* + * if pv and dc have crossed their upper limit, then increase + * prescale and recalculate pv and dc. + */ + if (pv > PWMPCR_MAX_PERIOD || dc > PWMDCR_MAX_DUTY) { + if (++prescale > PWMCR_MAX_PRESCALE) + return -EINVAL; + continue; + } + break; + } + + /* + * NOTE: the clock to PWM has to be enabled first before writing to the + * registers. + */ + + ret = clk_enable(pc->clk); + if (ret) + return ret; + spin_lock_irqsave(lock, flags); + + conf |= (prescale << RK_PWM_PRESCALE); + + barrier(); +// rk_pwm_writel(pc, pwm->hwpwm, VOP_REG_CTRL,off); + +// dsb(); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_DUTY,dc); // 2 0x1900);// dc); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_PERIOD,pv); // 4 0x5dc0);//pv); + rk_pwm_writel(pc, pwm->hwpwm, VOP_REG_CNTR,0); + dsb(); + rk_pwm_writel(pc, pwm->hwpwm, VOP_REG_CTRL,on|conf); +#if 0 + //grf_writel(0x00030002,0x6c); + DBG("VOP iomux 0x%x\n", grf_readl(0x6c)); + DBG("VOP_REG_CTRL0x%x\n", rk_pwm_readl(pc, pwm->hwpwm, VOP_REG_CTRL)); + DBG("PWM_REG_DUTY0x%x\n", rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_DUTY)); + DBG("PWM_REG_PERIOD 0x%x\n", rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_PERIOD)); + DBG("VOP_REG_CNTR 0x%x\n", rk_pwm_readl(pc, pwm->hwpwm, VOP_REG_CNTR)); #endif + spin_unlock_irqrestore(lock, flags); return 0; } -#endif +static void rk_pwm_set_enable_v3(struct pwm_chip *chip, struct pwm_device *pwm,bool enable) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + u32 val; + + val = rk_pwm_readl(pc, pwm->hwpwm, VOP_REG_CTRL); + if (enable) + val |= RK_PWM_ENABLE; + else + val &= ~RK_PWM_ENABLE; + rk_pwm_writel(pc, pwm->hwpwm, VOP_REG_CTRL, val); + +} +static void rk_pwm_suspend_v3(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + pc->pwm_ctrl = rk_pwm_readl(pc, pwm->hwpwm, VOP_REG_CTRL); + pc->pwm_duty = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_DUTY);//0x1900);// dc); + pc->pwm_period = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_PERIOD );//0x5dc0);//pv); + pc->pwm_count = rk_pwm_readl(pc, pwm->hwpwm, VOP_REG_CNTR); +} +static void rk_pwm_resume_v3(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_DUTY, pc->pwm_duty);//0x1900);// dc); + rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_PERIOD, pc->pwm_period);//0x5dc0);//pv); + rk_pwm_writel(pc, pwm->hwpwm, VOP_REG_CNTR,pc->pwm_count); + dsb(); + rk_pwm_writel(pc, pwm->hwpwm, VOP_REG_CTRL,pc->pwm_ctrl); +} + +static int rk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); + int ret; + + ret = pc->config(chip, pwm, duty_ns, period_ns); + + return 0; +} static int rk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); int rc = 0; - u32 val; -#if PWM_CLK + rc = clk_enable(pc->clk); if (rc) return rc; -#endif - val = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_CTRL); - if(pc->pwm_id == DW_PWM){ - val |= PWM_ENABLE; - } - if(pc->pwm_id == RK_PWM){ - val |= RK_PWM_ENABLE; - } - - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL, val); + pc->set_enable(chip, pwm,true); return 0; } static void rk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct rk_pwm_chip *pc = to_rk_pwm_chip(chip); - u32 val; - val = rk_pwm_readl(pc, pwm->hwpwm, PWM_REG_CTRL); - if(pc->pwm_id == DW_PWM){ - val &= ~PWM_ENABLE; - } - if(pc->pwm_id == RK_PWM){ - val &= ~RK_PWM_ENABLE; - } - - rk_pwm_writel(pc, pwm->hwpwm, PWM_REG_CTRL, val); -#if PWM_CLK + pc->set_enable(chip, pwm,false); clk_disable(pc->clk); -#endif } - static const struct pwm_ops rk_pwm_ops = { .config = rk_pwm_config, .enable = rk_pwm_enable, @@ -415,18 +526,58 @@ static const struct pwm_ops rk_pwm_ops = { .owner = THIS_MODULE, }; +struct rk_pwm_data{ + int (*config)(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns); + void (*set_enable)(struct pwm_chip *chip, struct pwm_device *pwm,bool enable); + void (*pwm_suspend)(struct pwm_chip *chip, struct pwm_device *pwm); + void (*pwm_resume)(struct pwm_chip *chip, struct pwm_device *pwm); + +}; + +static struct rk_pwm_data rk_pwm_data_v1={ + .config = rk_pwm_config_v1, + .set_enable = rk_pwm_set_enable_v1, + .pwm_suspend = rk_pwm_suspend_v1, + .pwm_resume = rk_pwm_resume_v1, + +}; + +static struct rk_pwm_data rk_pwm_data_v2={ + .config = rk_pwm_config_v2, + .set_enable = rk_pwm_set_enable_v2, + .pwm_suspend = rk_pwm_suspend_v2, + .pwm_resume = rk_pwm_resume_v2, + +}; + +static struct rk_pwm_data rk_pwm_data_v3={ + .config = rk_pwm_config_v3, + .set_enable = rk_pwm_set_enable_v3, + .pwm_suspend = rk_pwm_suspend_v3, + .pwm_resume = rk_pwm_resume_v3, + +}; +static const struct of_device_id rk_pwm_of_match[] = { + { .compatible = "rockchip,pwm", .data = &rk_pwm_data_v1,}, + { .compatible = "rockchip,rk-pwm", .data = &rk_pwm_data_v2,}, + { .compatible = "rockchip,vop-pwm", .data = &rk_pwm_data_v3,}, + { } +}; + +MODULE_DEVICE_TABLE(of, rk_pwm_of_match); static int rk_pwm_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = + of_match_device(rk_pwm_of_match, &pdev->dev); struct device_node *np = pdev->dev.of_node; + const struct rk_pwm_data *data; struct rk_pwm_chip *pc; - struct resource *r; int ret; - DBG("%s start \n",__FUNCTION__); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) { - dev_err(&pdev->dev, "no memory resources defined\n"); + + if (!of_id){ + dev_err(&pdev->dev, "failed to match device\n"); return -ENODEV; } pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); @@ -434,84 +585,101 @@ static int rk_pwm_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to allocate memory\n"); return -ENOMEM; } +#if 0 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resources defined\n"); + return -ENODEV; + } pc->base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(pc->base)) return PTR_ERR(pc->base); - -#if PWM_CLK - if (of_device_is_compatible(np, "rockchip,pwm")){ - pc->pwm_id = DW_PWM; - }else if (of_device_is_compatible(np, "rockchip,rk-pwm")){ - pc->pwm_id = RK_PWM; +#endif + pc->base = of_iomap(np, 0); + if (IS_ERR(pc->base)){ + printk("PWM base ERR \n"); + return PTR_ERR(pc->base); } - //pc->clk = devm_clk_get(&pdev->dev, NULL); pc->clk = devm_clk_get(&pdev->dev,"pclk_pwm"); - - if (IS_ERR(pc->clk)) return PTR_ERR(pc->clk); -#endif platform_set_drvdata(pdev, pc); - + data = of_id->data; + pc->config = data->config; + pc->set_enable = data->set_enable; + pc->pwm_suspend = data->pwm_suspend; + pc->pwm_resume = data->pwm_resume; pc->chip.dev = &pdev->dev; - pc->chip.ops = &rk_pwm_ops; + pc->chip.ops = &rk_pwm_ops; pc->chip.base = -1; pc->chip.npwm = NUM_PWM; - + pc->lock = __SPIN_LOCK_UNLOCKED(PWM##spinlock_num); + spinlock_num ++; #if PWM_CLK ret = clk_prepare(pc->clk); if (ret) return ret; #endif -#if PWM_CLK -// if (of_device_is_compatible(np, "rockchip,pwm")) { ret = clk_enable(pc->clk); if (ret) { clk_unprepare(pc->clk); return ret; } - /* - * Following enables PWM chip, channels would still be - * enabled individually through their control register - */ -#if PWM_CLK -// clk_disable(pc->clk); -#endif -#endif + /* Following enables PWM chip, channels would still be enabled individually through their control register */ DBG("npwm = %d, of_pwm_ncells =%d \n", pc->chip.npwm,pc->chip.of_pwm_n_cells); ret = pwmchip_add(&pc->chip); if (ret < 0) { clk_unprepare(pc->clk); dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); } + DBG("%s end \n",__FUNCTION__); - return ret; } +#if 0 +//(struct platform_device *, pm_message_t state); +static int rk_pwm_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct pwm_device *pwm; + struct rk_pwm_chip *pc; + struct pwm_chip *chip; + + pc = platform_get_drvdata(pdev); + chip = &(pc->chip); + pwm = chip->pwms; + + pc->pwm_suspend(chip,pwm); + + return 0;//pwmchip_remove(&pc->chip); +} +static int rk_pwm_resume(struct platform_device *pdev) +{ + struct pwm_device *pwm; + struct rk_pwm_chip *pc; + struct pwm_chip *chip; + + pc = platform_get_drvdata(pdev); + chip = &(pc->chip); + pwm = chip->pwms; + pc->pwm_resume(chip,pwm); + return 0;//pwmchip_remove(&pc->chip); +} +#endif static int rk_pwm_remove(struct platform_device *pdev) { return 0;//pwmchip_remove(&pc->chip); } - -static const struct of_device_id rk_pwm_of_match[] = { - { .compatible = "rockchip,pwm" }, - { .compatible = "rockchip,rk-pwm"}, - { } -}; - -MODULE_DEVICE_TABLE(of, rk_pwm_of_match); - static struct platform_driver rk_pwm_driver = { .driver = { .name = "rk-pwm", .of_match_table = rk_pwm_of_match, }, .probe = rk_pwm_probe, - .remove = rk_pwm_remove, + .remove = rk_pwm_remove, }; module_platform_driver(rk_pwm_driver); -- 2.34.1