pwm: support vop-pwm function
author许盛飞 <xsf@rock-chips.com>
Fri, 25 Apr 2014 03:25:59 +0000 (11:25 +0800)
committer许盛飞 <xsf@rock-chips.com>
Fri, 25 Apr 2014 03:25:59 +0000 (11:25 +0800)
arch/arm/boot/dts/rk3288-pinctrl.dtsi
arch/arm/boot/dts/rk3288.dtsi
drivers/pwm/pwm-rockchip.c

index 3816ea611b78f689ff992963334a6b69e2280d91..1eff2dcb475bd7894dcf519cd49732393e388d6a 100755 (executable)
                };
 
                gpio7_pwm {
+                        vop0_pwm_pin:vop0-pwm {
+                                rockchip,pins = <VOP0_PWM>;
+                                rockchip,pull = <VALUE_PULL_DISABLE>;
+                                rockchip,drive = <VALUE_DRV_DEFAULT>;
+                                //rockchip,tristate = <VALUE_TRI_DEFAULT>;
+
+                        };
+
+                        vop1_pwm_pin:vop1-pwm {
+                                rockchip,pins = <VOP1_PWM>;
+                                rockchip,pull = <VALUE_PULL_DISABLE>;
+                                rockchip,drive = <VALUE_DRV_DEFAULT>;
+                                //rockchip,tristate = <VALUE_TRI_DEFAULT>;
+                        };
+
                        pwm0_pin:pwm0 {
                                rockchip,pins = <PWM0>;
                                rockchip,pull = <VALUE_PULL_DISABLE>;
index adc54761e11a4f2dddcb62723f8d6551f84b37ca..a577ba0cadeeca2070983459bfb413e3bf4d4fc5 100755 (executable)
                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>;
                pinctrl-0 = <&pwm0_pin>;
                clocks = <&clk_gates11 11>;
                clock-names = "pclk_pwm";
-               status = "okay";
+               status = "disabled";
        };
 
        pwm1: pwm@ff680010 {
index bdad0138f310e3a2ddacf1aa05b172a6d00ec954..6ac796bbab97f5cf13a6f3f0ef7c50e25000a86f 100644 (file)
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/spinlock.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/rockchip/grf.h>
+#include <linux/rockchip/iomap.h>
 
-#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);