From d09971d1f6971b3035599d77bfcf4374084bb3d9 Mon Sep 17 00:00:00 2001
From: chraac <chraac@gmail.com>
Date: Thu, 15 Aug 2024 23:38:44 +0800
Subject: drivers: pwm: Add pwm-sunxi-enhance driver for h616

---
 drivers/pwm/Kconfig             |    9 +
 drivers/pwm/Makefile            |    1 +
 drivers/pwm/pwm-sunxi-enhance.c | 1194 +++++++++++++++++++++++++++++++
 drivers/pwm/pwm-sunxi-enhance.h |   60 ++
 4 files changed, 1264 insertions(+)
 create mode 100644 drivers/pwm/pwm-sunxi-enhance.c
 create mode 100644 drivers/pwm/pwm-sunxi-enhance.h

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 0915c1e7df16..1f1ff7af5b5a 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -652,6 +652,15 @@ config PWM_SUN4I
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-sun4i.
 
+config PWM_SUNXI_ENHANCE
+	tristate "Sunxi Enhance PWM support"
+	depends on PWM && ARCH_SUNXI
+	help
+	Enhance PWM framework driver for sunxi.
+
+	To compile this driver as a module, choose M here: the module
+	will be called pwm-sunxi.
+
 config PWM_SUNPLUS
 	tristate "Sunplus PWM support"
 	depends on ARCH_SUNPLUS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 9081e0c0e9e0..b729ae59db56 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_PWM_STM32)		+= pwm-stm32.o
 obj-$(CONFIG_PWM_STM32_LP)	+= pwm-stm32-lp.o
 obj-$(CONFIG_PWM_STMPE)		+= pwm-stmpe.o
 obj-$(CONFIG_PWM_SUN4I)		+= pwm-sun4i.o
+obj-$(CONFIG_PWM_SUNXI_ENHANCE)		+= pwm-sunxi-enhance.o
 obj-$(CONFIG_PWM_SUNPLUS)	+= pwm-sunplus.o
 obj-$(CONFIG_PWM_TEGRA)		+= pwm-tegra.o
 obj-$(CONFIG_PWM_TIECAP)	+= pwm-tiecap.o
diff --git a/drivers/pwm/pwm-sunxi-enhance.c b/drivers/pwm/pwm-sunxi-enhance.c
new file mode 100644
index 000000000000..6604c447b3b0
--- /dev/null
+++ b/drivers/pwm/pwm-sunxi-enhance.c
@@ -0,0 +1,1194 @@
+/*
+ * Allwinnertech pulse-width-modulation controller driver
+ *
+ * Copyright (C) 2015 AllWinner
+ *
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cdev.h>
+#include <linux/pwm.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_iommu.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/printk.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include "pwm-sunxi-enhance.h"
+
+#define PWM_NUM_MAX 4
+#define PWM_BIND_NUM 2
+#define PWM_PIN_STATE_ACTIVE "default"
+//#define PWM_PIN_STATE_SLEEP "sleep"
+
+#define SETMASK(width, shift) ((width ? ((-1U) >> (32 - width)) : 0) << (shift))
+#define CLRMASK(width, shift) (~(SETMASK(width, shift)))
+#define GET_BITS(shift, width, reg) \
+    (((reg)&SETMASK(width, shift)) >> (shift))
+#define SET_BITS(shift, width, reg, val) \
+    (((reg)&CLRMASK(width, shift)) | (val << (shift)))
+
+static int pwm_debug = 0;
+module_param(pwm_debug, int, 0644);
+MODULE_PARM_DESC(pwm_debug, "enable pwm debug");
+
+#define pwm_debug(fmt, ...) \
+    if (pwm_debug)          \
+    printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
+
+struct sunxi_pwm_config
+{
+    unsigned int dead_time;
+    unsigned int bind_pwm;
+
+    unsigned int clk_bypass_output;
+};
+
+struct sunxi_pwm_chip
+{
+    void __iomem *base;
+    struct sunxi_pwm_config *config;
+    struct clk *bus_clk;
+    struct clk *clk;
+    struct reset_control *pwm_rst_clk;
+};
+
+static inline struct sunxi_pwm_chip *to_sunxi_pwm_chip(struct pwm_chip *chip)
+{
+    return pwmchip_get_drvdata(chip);
+}
+
+static inline u32 sunxi_pwm_readl(struct pwm_chip *chip, u32 offset)
+{
+    struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
+    u32 value = 0;
+
+    value = readl(pc->base + offset);
+
+    return value;
+}
+
+static inline u32 sunxi_pwm_writel(struct pwm_chip *chip, u32 offset, u32 value)
+{
+    struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
+
+    writel(value, pc->base + offset);
+
+    return 0;
+}
+
+static int sunxi_pwm_pin_set_state(struct device *dev, char *name)
+{
+    struct pinctrl *pctl;
+    struct pinctrl_state *state;
+    int ret = -1;
+
+    pctl = devm_pinctrl_get(dev);
+    if (IS_ERR(pctl))
+    {
+        dev_err(dev, "pinctrl_get failed!\n");
+        ret = PTR_ERR(pctl);
+        goto exit;
+    }
+
+    state = pinctrl_lookup_state(pctl, name);
+    if (IS_ERR(state))
+    {
+        dev_err(dev, "pinctrl_lookup_state(%s) failed!\n", name);
+        ret = PTR_ERR(state);
+        goto exit;
+    }
+
+    ret = pinctrl_select_state(pctl, state);
+    if (ret < 0)
+    {
+        dev_err(dev, "pinctrl_select_state(%s) failed!\n", name);
+        goto exit;
+    }
+    ret = 0;
+
+exit:
+    return ret;
+}
+
+static int sunxi_pwm_get_config(struct platform_device *pdev, struct sunxi_pwm_config *config)
+{
+    struct device_node *np = pdev->dev.of_node;
+    int ret = 0;
+
+    /* read register config */
+    ret = of_property_read_u32(np, "bind_pwm", &config->bind_pwm);
+    if (ret < 0)
+    {
+        /*if there is no bind pwm,set 255, dual pwm invalid!*/
+        config->bind_pwm = 255;
+        ret = 0;
+    }
+
+    ret = of_property_read_u32(np, "dead_time", &config->dead_time);
+    if (ret < 0)
+    {
+        /*if there is  bind pwm, but not set dead time,set bind pwm 255,dual pwm invalid!*/
+        config->bind_pwm = 255;
+        ret = 0;
+    }
+
+    ret = of_property_read_u32(np, "clk_bypass_output", &config->clk_bypass_output);
+    if (ret < 0)
+    {
+        /*if use pwm as the internal clock source!*/
+        config->clk_bypass_output = 0;
+        ret = 0;
+    }
+
+    of_node_put(np);
+
+    return ret;
+}
+
+static int sunxi_pwm_set_polarity_single(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity)
+{
+    u32 temp;
+    unsigned int reg_offset, reg_shift, reg_width;
+    u32 sel = 0;
+
+    sel = pwm->hwpwm;
+    reg_offset = PWM_PCR_BASE + sel * 0x20;
+    reg_shift = PWM_ACT_STA_SHIFT;
+    reg_width = PWM_ACT_STA_WIDTH;
+    temp = sunxi_pwm_readl(chip, reg_offset);
+    if (polarity == PWM_POLARITY_NORMAL)
+        temp = SET_BITS(reg_shift, 1, temp, 1);
+    else
+        temp = SET_BITS(reg_shift, 1, temp, 0);
+
+    sunxi_pwm_writel(chip, reg_offset, temp);
+
+    return 0;
+}
+
+static int sunxi_pwm_set_polarity_dual(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity, int bind_num)
+{
+    u32 temp[2];
+    unsigned int reg_offset[2], reg_shift[2], reg_width[2];
+    u32 sel[2] = {0};
+
+    sel[0] = pwm->hwpwm;
+    sel[1] = bind_num - (int)chip->id;
+    /* config current pwm*/
+    reg_offset[0] = PWM_PCR_BASE + sel[0] * 0x20;
+    reg_shift[0] = PWM_ACT_STA_SHIFT;
+    reg_width[0] = PWM_ACT_STA_WIDTH;
+    temp[0] = sunxi_pwm_readl(chip, reg_offset[0]);
+    if (polarity == PWM_POLARITY_NORMAL)
+        temp[0] = SET_BITS(reg_shift[0], 1, temp[0], 1);
+    else
+        temp[0] = SET_BITS(reg_shift[0], 1, temp[0], 0);
+
+    /* config bind pwm*/
+    reg_offset[1] = PWM_PCR_BASE + sel[1] * 0x20;
+    reg_shift[1] = PWM_ACT_STA_SHIFT;
+    reg_width[1] = PWM_ACT_STA_WIDTH;
+    temp[1] = sunxi_pwm_readl(chip, reg_offset[1]);
+
+    /*bind pwm's polarity is reverse compare with the  current pwm*/
+    if (polarity == PWM_POLARITY_NORMAL)
+        temp[1] = SET_BITS(reg_shift[0], 1, temp[1], 0);
+    else
+        temp[1] = SET_BITS(reg_shift[0], 1, temp[1], 1);
+
+    /*config register at the same time*/
+    sunxi_pwm_writel(chip, reg_offset[0], temp[0]);
+    sunxi_pwm_writel(chip, reg_offset[1], temp[1]);
+
+    return 0;
+}
+
+static int sunxi_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity)
+{
+    int bind_num;
+    struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
+
+    bind_num = pc->config[pwm->hwpwm].bind_pwm;
+    if (bind_num == 255)
+        sunxi_pwm_set_polarity_single(chip, pwm, polarity);
+    else
+        sunxi_pwm_set_polarity_dual(chip, pwm, polarity, bind_num);
+
+    return 0;
+}
+
+static u32 get_pccr_reg_offset(u32 sel, u32 *reg_offset)
+{
+    switch (sel)
+    {
+    case 0:
+    case 1:
+        *reg_offset = PWM_PCCR01;
+        break;
+    case 2:
+    case 3:
+        *reg_offset = PWM_PCCR23;
+        break;
+    case 4:
+    case 5:
+        *reg_offset = PWM_PCCR45;
+        break;
+    case 6:
+    case 7:
+        *reg_offset = PWM_PCCR67;
+        break;
+    case 8:
+        *reg_offset = PWM_PCCR8;
+        break;
+    default:
+        pr_err("%s:Not supported!\n", __func__);
+        break;
+    }
+    return 0;
+}
+
+static u32 get_pdzcr_reg_offset(u32 sel, u32 *reg_offset)
+{
+    switch (sel)
+    {
+    case 0:
+    case 1:
+        *reg_offset = PWM_PDZCR01;
+        break;
+    case 2:
+    case 3:
+        *reg_offset = PWM_PDZCR23;
+        break;
+    case 4:
+    case 5:
+        *reg_offset = PWM_PDZCR45;
+        break;
+    case 6:
+    case 7:
+        *reg_offset = PWM_PDZCR67;
+        break;
+    default:
+        pr_err("%s:Not supported!\n", __func__);
+        break;
+    }
+    return 0;
+}
+
+#define PRESCALE_MAX 256
+
+static int sunxi_pwm_config_single(struct pwm_chip *chip, struct pwm_device *pwm,
+                                   int duty_ns, int period_ns)
+{
+    unsigned int temp;
+    unsigned long long c = 0;
+    unsigned long entire_cycles = 256, active_cycles = 192;
+    struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
+    unsigned int reg_offset, reg_shift, reg_width;
+    unsigned int reg_bypass_shift;
+    unsigned int reg_clk_src_shift, reg_clk_src_width;
+    unsigned int reg_div_m_shift, reg_div_m_width;
+    unsigned int pre_scal_id = 0, div_m = 0, prescale = 0;
+    u32 sel = 0;
+    u32 pre_scal[][2] = {
+
+        /* reg_value  clk_pre_div */
+        {0, 1},
+        {1, 2},
+        {2, 4},
+        {3, 8},
+        {4, 16},
+        {5, 32},
+        {6, 64},
+        {7, 128},
+        {8, 256},
+    };
+
+    sel = pwm->hwpwm;
+
+    get_pccr_reg_offset(sel, &reg_offset);
+    if ((sel % 2) == 0)
+        reg_bypass_shift = 0x5;
+    else
+        reg_bypass_shift = 0x6;
+    /*src clk reg*/
+    reg_clk_src_shift = PWM_CLK_SRC_SHIFT;
+    reg_clk_src_width = PWM_CLK_SRC_WIDTH;
+
+    if (period_ns > 0 && period_ns <= 10)
+    {
+        /* if freq lt 100M, then direct output 100M clock,set by pass. */
+        c = 100000000;
+        /*bypass reg*/
+        temp = sunxi_pwm_readl(chip, reg_offset);
+        temp = SET_BITS(reg_bypass_shift, 1, temp, 1);
+        sunxi_pwm_writel(chip, reg_offset, temp);
+
+        /*clk_src_reg*/
+        temp = sunxi_pwm_readl(chip, reg_offset);
+        temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1);
+        sunxi_pwm_writel(chip, reg_offset, temp);
+
+        return 0;
+    }
+    else if (period_ns > 10 && period_ns <= 334)
+    {
+        /* if freq between 3M~100M, then select 100M as clock */
+        c = 100000000;
+        /*set clk bypass_output reg to 1 when pwm is used as the internal clock source.*/
+        if (pc->config[pwm->hwpwm].clk_bypass_output == 1) {
+            temp = sunxi_pwm_readl(chip, reg_offset);
+            temp = SET_BITS(reg_bypass_shift, 1, temp, 1);
+            sunxi_pwm_writel(chip, reg_offset, temp);
+        }
+        /*clk_src_reg*/
+        temp = sunxi_pwm_readl(chip, reg_offset);
+        temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1);
+        sunxi_pwm_writel(chip, reg_offset, temp);
+    }
+    else if (period_ns > 334)
+    {
+        /* if freq < 3M, then select 24M clock */
+        c = 24000000;
+        /*set clk bypass_output reg to 1 when pwm is used as the internal clock source.*/
+        if (pc->config[pwm->hwpwm].clk_bypass_output == 1) {
+            temp = sunxi_pwm_readl(chip, reg_offset);
+            temp = SET_BITS(reg_bypass_shift, 1, temp, 1);
+            sunxi_pwm_writel(chip, reg_offset, temp);
+        }
+        /*clk_src_reg*/
+        temp = sunxi_pwm_readl(chip, reg_offset);
+        temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 0);
+        sunxi_pwm_writel(chip, reg_offset, temp);
+    }
+    pwm_debug("duty_ns=%d period_ns=%d c =%llu.\n", duty_ns, period_ns, c);
+
+    c = c * period_ns;
+    do_div(c, 1000000000);
+    entire_cycles = (unsigned long)c;
+
+    for (pre_scal_id = 0; pre_scal_id < 9; pre_scal_id++)
+    {
+        if (entire_cycles <= 65536)
+            break;
+        for (prescale = 0; prescale < PRESCALE_MAX + 1; prescale++)
+        {
+            entire_cycles = ((unsigned long)c / pre_scal[pre_scal_id][1]) / (prescale + 1);
+            if (entire_cycles <= 65536)
+            {
+                div_m = pre_scal[pre_scal_id][0];
+                break;
+            }
+        }
+    }
+
+    c = (unsigned long long)entire_cycles * duty_ns;
+    do_div(c, period_ns);
+    active_cycles = c;
+    if (entire_cycles == 0)
+        entire_cycles++;
+
+    /* config  clk div_m*/
+    reg_div_m_shift = PWM_DIV_M_SHIFT;
+    reg_div_m_width = PWM_DIV_M_WIDTH;
+    temp = sunxi_pwm_readl(chip, reg_offset);
+    temp = SET_BITS(reg_div_m_shift, reg_div_m_width, temp, div_m);
+    sunxi_pwm_writel(chip, reg_offset, temp);
+
+    /* config prescal */
+    reg_offset = PWM_PCR_BASE + 0x20 * sel;
+    reg_shift = PWM_PRESCAL_SHIFT;
+    reg_width = PWM_PRESCAL_WIDTH;
+    temp = sunxi_pwm_readl(chip, reg_offset);
+    temp = SET_BITS(reg_shift, reg_width, temp, prescale);
+    sunxi_pwm_writel(chip, reg_offset, temp);
+
+    /* config active cycles */
+    reg_offset = PWM_PPR_BASE + 0x20 * sel;
+    reg_shift = PWM_ACT_CYCLES_SHIFT;
+    reg_width = PWM_ACT_CYCLES_WIDTH;
+    temp = sunxi_pwm_readl(chip, reg_offset);
+    temp = SET_BITS(reg_shift, reg_width, temp, active_cycles);
+    sunxi_pwm_writel(chip, reg_offset, temp);
+
+    /* config period cycles */
+    reg_offset = PWM_PPR_BASE + 0x20 * sel;
+    reg_shift = PWM_PERIOD_CYCLES_SHIFT;
+    reg_width = PWM_PERIOD_CYCLES_WIDTH;
+    temp = sunxi_pwm_readl(chip, reg_offset);
+    temp = SET_BITS(reg_shift, reg_width, temp, (entire_cycles - 1));
+
+    sunxi_pwm_writel(chip, reg_offset, temp);
+
+    pwm_debug("active_cycles=%lu entire_cycles=%lu prescale=%u div_m=%u\n",
+              active_cycles, entire_cycles, prescale, div_m);
+    return 0;
+}
+
+static int sunxi_pwm_config_dual(struct pwm_chip *chip, struct pwm_device *pwm,
+                                 int duty_ns, int period_ns, int bind_num)
+{
+    u32 value[2] = {0};
+    unsigned int temp;
+    unsigned long long c = 0, clk = 0, clk_temp = 0;
+    unsigned long entire_cycles = 256, active_cycles = 192;
+    unsigned int reg_offset[2], reg_shift[2], reg_width[2];
+    unsigned int reg_bypass_shift;
+    unsigned int reg_dz_en_offset[2], reg_dz_en_shift[2], reg_dz_en_width[2];
+    unsigned int pre_scal_id = 0, div_m = 0, prescale = 0;
+    int src_clk_sel = 0;
+    int i = 0;
+    unsigned int dead_time = 0, duty = 0;
+    u32 pre_scal[][2] = {
+
+        /* reg_value  clk_pre_div */
+        {0, 1},
+        {1, 2},
+        {2, 4},
+        {3, 8},
+        {4, 16},
+        {5, 32},
+        {6, 64},
+        {7, 128},
+        {8, 256},
+    };
+    unsigned int pwm_index[2] = {0};
+    struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
+
+    pwm_index[0] = pwm->hwpwm;
+    pwm_index[1] = bind_num - (int)chip->id;
+
+    /* if duty time < dead time,it is wrong. */
+    dead_time = pc->config[pwm_index[0]].dead_time;
+    duty = (unsigned int)duty_ns;
+    /* judge if the pwm eanble dead zone */
+    get_pdzcr_reg_offset(pwm_index[0], &reg_dz_en_offset[0]);
+    reg_dz_en_shift[0] = PWM_DZ_EN_SHIFT;
+    reg_dz_en_width[0] = PWM_DZ_EN_WIDTH;
+
+    value[0] = sunxi_pwm_readl(chip, reg_dz_en_offset[0]);
+    value[0] = SET_BITS(reg_dz_en_shift[0], reg_dz_en_width[0], value[0], 1);
+    sunxi_pwm_writel(chip, reg_dz_en_offset[0], value[0]);
+    temp = sunxi_pwm_readl(chip, reg_dz_en_offset[0]);
+    temp &= (1u << reg_dz_en_shift[0]);
+    if (duty < dead_time || temp == 0)
+    {
+        pr_err("[PWM]duty time or dead zone error.\n");
+        return -EINVAL;
+    }
+
+    for (i = 0; i < PWM_BIND_NUM; i++)
+    {
+        if ((i % 2) == 0)
+            reg_bypass_shift = 0x5;
+        else
+            reg_bypass_shift = 0x6;
+        get_pccr_reg_offset(pwm_index[i], &reg_offset[i]);
+        reg_shift[i] = reg_bypass_shift;
+        reg_width[i] = PWM_BYPASS_WIDTH;
+    }
+
+    if (period_ns > 0 && period_ns <= 10)
+    {
+        /* if freq lt 100M, then direct output 100M clock,set by pass */
+        clk = 100000000;
+        src_clk_sel = 1;
+
+        /* config the two pwm bypass */
+        for (i = 0; i < PWM_BIND_NUM; i++)
+        {
+            temp = sunxi_pwm_readl(chip, reg_offset[i]);
+            temp = SET_BITS(reg_shift[i], reg_width[i], temp, 1);
+            sunxi_pwm_writel(chip, reg_offset[i], temp);
+
+            reg_shift[i] = PWM_CLK_SRC_SHIFT;
+            reg_width[i] = PWM_CLK_SRC_WIDTH;
+            temp = sunxi_pwm_readl(chip, reg_offset[i]);
+            temp = SET_BITS(reg_shift[i], reg_width[i], temp, 1);
+            sunxi_pwm_writel(chip, reg_offset[i], temp);
+        }
+
+        return 0;
+    }
+    else if (period_ns > 10 && period_ns <= 334)
+    {
+        clk = 100000000;
+        src_clk_sel = 1;
+    }
+    else if (period_ns > 334)
+    {
+        /* if freq < 3M, then select 24M clock */
+        clk = 24000000;
+        src_clk_sel = 0;
+    }
+
+    for (i = 0; i < PWM_BIND_NUM; i++)
+    {
+        reg_shift[i] = PWM_CLK_SRC_SHIFT;
+        reg_width[i] = PWM_CLK_SRC_WIDTH;
+
+        temp = sunxi_pwm_readl(chip, reg_offset[i]);
+        temp = SET_BITS(reg_shift[i], reg_width[i], temp, src_clk_sel);
+        sunxi_pwm_writel(chip, reg_offset[i], temp);
+    }
+
+    c = clk;
+    c *= period_ns;
+    do_div(c, 1000000000);
+    entire_cycles = (unsigned long)c;
+
+    /* get div_m and prescale,which satisfy: deat_val <= 256, entire <= 65536 */
+    for (pre_scal_id = 0; pre_scal_id < 9; pre_scal_id++)
+    {
+        for (prescale = 0; prescale < PRESCALE_MAX + 1; prescale++)
+        {
+            entire_cycles = ((unsigned long)c / pre_scal[pre_scal_id][1]) / (prescale + 1);
+            clk_temp = clk;
+            do_div(clk_temp, pre_scal[pre_scal_id][1] * (prescale + 1));
+            clk_temp *= dead_time;
+            do_div(clk_temp, 1000000000);
+            if (entire_cycles <= 65536 && clk_temp <= 256)
+            {
+                div_m = pre_scal[pre_scal_id][0];
+                break;
+            }
+        }
+        if (entire_cycles <= 65536 && clk_temp <= 256)
+            break;
+        else
+        {
+            pr_err("%s:config dual err.entire_cycles=%lu, dead_zone_val=%llu",
+                   __func__, entire_cycles, clk_temp);
+            return -EINVAL;
+        }
+    }
+
+    c = (unsigned long long)entire_cycles * duty_ns;
+    do_div(c, period_ns);
+    active_cycles = c;
+    if (entire_cycles == 0)
+        entire_cycles++;
+
+    /* config  clk div_m*/
+    for (i = 0; i < PWM_BIND_NUM; i++)
+    {
+        reg_shift[i] = PWM_DIV_M_SHIFT;
+        reg_width[i] = PWM_DIV_M_SHIFT;
+        temp = sunxi_pwm_readl(chip, reg_offset[i]);
+        temp = SET_BITS(reg_shift[i], reg_width[i], temp, div_m);
+        sunxi_pwm_writel(chip, reg_offset[i], temp);
+    }
+
+    /* config prescal */
+    for (i = 0; i < PWM_BIND_NUM; i++)
+    {
+        reg_offset[i] = PWM_PCR_BASE + 0x20 * pwm_index[i];
+        reg_shift[i] = PWM_PRESCAL_SHIFT;
+        reg_width[i] = PWM_PRESCAL_WIDTH;
+        temp = sunxi_pwm_readl(chip, reg_offset[i]);
+        temp = SET_BITS(reg_shift[i], reg_width[i], temp, prescale);
+        sunxi_pwm_writel(chip, reg_offset[i], temp);
+    }
+
+    /* config active cycles */
+    for (i = 0; i < PWM_BIND_NUM; i++)
+    {
+        reg_offset[i] = PWM_PPR_BASE + 0x20 * pwm_index[i];
+        reg_shift[i] = PWM_ACT_CYCLES_SHIFT;
+        reg_width[i] = PWM_ACT_CYCLES_WIDTH;
+        temp = sunxi_pwm_readl(chip, reg_offset[i]);
+        temp = SET_BITS(reg_shift[i], reg_width[i], temp, active_cycles);
+        sunxi_pwm_writel(chip, reg_offset[i], temp);
+    }
+
+    /* config period cycles */
+    for (i = 0; i < PWM_BIND_NUM; i++)
+    {
+        reg_offset[i] = PWM_PPR_BASE + 0x20 * pwm_index[i];
+        reg_shift[i] = PWM_PERIOD_CYCLES_SHIFT;
+        reg_width[i] = PWM_PERIOD_CYCLES_WIDTH;
+        temp = sunxi_pwm_readl(chip, reg_offset[i]);
+        temp = SET_BITS(reg_shift[i], reg_width[i], temp, (entire_cycles - 1));
+        sunxi_pwm_writel(chip, reg_offset[i], temp);
+    }
+
+    pwm_debug("active_cycles=%lu entire_cycles=%lu prescale=%u div_m=%u\n",
+              active_cycles, entire_cycles, prescale, div_m);
+
+    /* config dead zone, one config for two pwm */
+    reg_offset[0] = reg_dz_en_offset[0];
+    reg_shift[0] = PWM_PDZINTV_SHIFT;
+    reg_width[0] = PWM_PDZINTV_WIDTH;
+    temp = sunxi_pwm_readl(chip, reg_offset[0]);
+    temp = SET_BITS(reg_shift[0], reg_width[0], temp, (unsigned int)clk_temp);
+    sunxi_pwm_writel(chip, reg_offset[0], temp);
+
+    return 0;
+}
+
+static int sunxi_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+                            int duty_ns, int period_ns)
+{
+    int bind_num;
+
+    struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
+
+    bind_num = pc->config[pwm->hwpwm].bind_pwm;
+    if (bind_num == 255) {
+        sunxi_pwm_config_single(chip, pwm, duty_ns, period_ns);
+    }
+    else {
+        sunxi_pwm_config_dual(chip, pwm, duty_ns, period_ns, bind_num);
+    }
+
+    return 0;
+}
+
+static int sunxi_pwm_enable_single(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+    unsigned int value = 0, index = 0;
+    unsigned int reg_offset, reg_shift;
+    struct device_node *sub_np;
+    struct platform_device *pwm_pdevice;
+    int ret;
+
+    index = pwm->hwpwm;
+    sub_np = of_parse_phandle(pwmchip_parent(chip)->of_node, "sunxi-pwms", index);
+    if (IS_ERR_OR_NULL(sub_np))
+    {
+        pr_err("%s: can't parse \"sunxi-pwms\" property\n", __func__);
+        return -ENODEV;
+    }
+    pwm_pdevice = of_find_device_by_node(sub_np);
+    if (IS_ERR_OR_NULL(pwm_pdevice))
+    {
+        pr_err("%s: can't parse pwm device\n", __func__);
+        return -ENODEV;
+    }
+    ret = sunxi_pwm_pin_set_state(&pwm_pdevice->dev, PWM_PIN_STATE_ACTIVE);
+    if (ret != 0)
+        return ret;
+
+    /* enable clk for pwm controller */
+    get_pccr_reg_offset(index, &reg_offset);
+    reg_shift = PWM_CLK_GATING_SHIFT;
+    value = sunxi_pwm_readl(chip, reg_offset);
+    value = SET_BITS(reg_shift, 1, value, 1);
+    sunxi_pwm_writel(chip, reg_offset, value);
+
+    /* enable pwm controller */
+    reg_offset = PWM_PER;
+    reg_shift = index;
+    value = sunxi_pwm_readl(chip, reg_offset);
+    value = SET_BITS(reg_shift, 1, value, 1);
+    sunxi_pwm_writel(chip, reg_offset, value);
+
+    return 0;
+}
+
+static int sunxi_pwm_enable_dual(struct pwm_chip *chip, struct pwm_device *pwm, int bind_num)
+{
+    u32 value[2] = {0};
+    unsigned int reg_offset[2], reg_shift[2], reg_width[2];
+    struct device_node *sub_np[2];
+    struct platform_device *pwm_pdevice[2];
+    int i = 0, ret = 0;
+    unsigned int pwm_index[2] = {0};
+
+    pwm_index[0] = pwm->hwpwm;
+    pwm_index[1] = bind_num - (int)chip->id;
+
+    /*set current pwm pin state*/
+    sub_np[0] = of_parse_phandle(pwmchip_parent(chip)->of_node, "sunxi-pwms", pwm_index[0]);
+    if (IS_ERR_OR_NULL(sub_np[0]))
+    {
+        pr_err("%s: can't parse \"sunxi-pwms\" property\n", __func__);
+        return -ENODEV;
+    }
+    pwm_pdevice[0] = of_find_device_by_node(sub_np[0]);
+    if (IS_ERR_OR_NULL(pwm_pdevice[0]))
+    {
+        pr_err("%s: can't parse pwm device\n", __func__);
+        return -ENODEV;
+    }
+
+    /*set bind pwm pin state*/
+    sub_np[1] = of_parse_phandle(pwmchip_parent(chip)->of_node, "sunxi-pwms", pwm_index[1]);
+    if (IS_ERR_OR_NULL(sub_np[1]))
+    {
+        pr_err("%s: can't parse \"sunxi-pwms\" property\n", __func__);
+        return -ENODEV;
+    }
+    pwm_pdevice[1] = of_find_device_by_node(sub_np[1]);
+    if (IS_ERR_OR_NULL(pwm_pdevice[1]))
+    {
+        pr_err("%s: can't parse pwm device\n", __func__);
+        return -ENODEV;
+    }
+
+    ret = sunxi_pwm_pin_set_state(&pwm_pdevice[0]->dev, PWM_PIN_STATE_ACTIVE);
+    if (ret != 0)
+        return ret;
+    ret = sunxi_pwm_pin_set_state(&pwm_pdevice[1]->dev, PWM_PIN_STATE_ACTIVE);
+    if (ret != 0)
+        return ret;
+
+    /* enable clk for pwm controller */
+    for (i = 0; i < PWM_BIND_NUM; i++)
+    {
+        get_pccr_reg_offset(pwm_index[i], &reg_offset[i]);
+        reg_shift[i] = PWM_CLK_GATING_SHIFT;
+        reg_width[i] = PWM_CLK_GATING_WIDTH;
+        value[i] = sunxi_pwm_readl(chip, reg_offset[i]);
+        value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 1);
+        sunxi_pwm_writel(chip, reg_offset[i], value[i]);
+    }
+
+    /* enable pwm controller */
+    for (i = 0; i < PWM_BIND_NUM; i++)
+    {
+        reg_offset[i] = PWM_PER;
+        reg_shift[i] = pwm_index[i];
+        reg_width[i] = 0x1;
+        value[i] = sunxi_pwm_readl(chip, reg_offset[i]);
+        value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 1);
+        sunxi_pwm_writel(chip, reg_offset[i], value[i]);
+    }
+
+    return 0;
+}
+
+static int sunxi_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+    int bind_num;
+    int ret = 0;
+    struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
+
+    bind_num = pc->config[pwm->hwpwm].bind_pwm;
+    if (bind_num == 255)
+        ret = sunxi_pwm_enable_single(chip, pwm);
+    else
+        ret = sunxi_pwm_enable_dual(chip, pwm, bind_num);
+
+    sunxi_pwm_set_polarity(chip, pwm, PWM_POLARITY_NORMAL);
+
+    return ret;
+}
+
+static void sunxi_pwm_disable_single(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+    u32 value = 0, index = 0;
+    unsigned int reg_offset, reg_shift, reg_width;
+    struct device_node *sub_np;
+    struct platform_device *pwm_pdevice;
+
+    index = pwm->hwpwm;
+    sub_np = of_parse_phandle(pwmchip_parent(chip)->of_node, "sunxi-pwms", index);
+    if (IS_ERR_OR_NULL(sub_np))
+    {
+        pr_err("%s: can't parse \"sunxi-pwms\" property\n", __func__);
+        return;
+    }
+    pwm_pdevice = of_find_device_by_node(sub_np);
+    if (IS_ERR_OR_NULL(pwm_pdevice))
+    {
+        pr_err("%s: can't parse pwm device\n", __func__);
+        return;
+    }
+
+    /* disable pwm controller */
+    reg_offset = PWM_PER;
+    reg_shift = index;
+    reg_width = 0x1;
+    value = sunxi_pwm_readl(chip, reg_offset);
+    value = SET_BITS(reg_shift, reg_width, value, 0);
+    sunxi_pwm_writel(chip, reg_offset, value);
+
+    /*
+     * 0 , 1 --> 0
+     * 2 , 3 --> 2
+     * 4 , 5 --> 4
+     * 6 , 7 --> 6
+     */
+    reg_shift &= ~(1);
+
+    if (GET_BITS(reg_shift, 2, value) == 0)
+    {
+        /* disable clk for pwm controller. */
+        get_pccr_reg_offset(index, &reg_offset);
+        reg_shift = PWM_CLK_GATING_SHIFT;
+        reg_width = 0x1;
+        value = sunxi_pwm_readl(chip, reg_offset);
+        value = SET_BITS(reg_shift, reg_width, value, 0);
+        sunxi_pwm_writel(chip, reg_offset, value);
+    }
+
+    // sunxi_pwm_pin_set_state(&pwm_pdevice->dev, PWM_PIN_STATE_SLEEP);
+}
+
+static void sunxi_pwm_disable_dual(struct pwm_chip *chip, struct pwm_device *pwm, int bind_num)
+{
+    u32 value[2] = {0};
+    unsigned int reg_offset[2], reg_shift[2], reg_width[2];
+    struct device_node *sub_np[2];
+    struct platform_device *pwm_pdevice[2];
+    int i = 0;
+    unsigned int pwm_index[2] = {0};
+
+    pwm_index[0] = pwm->hwpwm;
+    pwm_index[1] = bind_num - (int)chip->id;
+
+    /* get current index pwm device */
+    sub_np[0] = of_parse_phandle(pwmchip_parent(chip)->of_node, "pwms", pwm_index[0]);
+    if (IS_ERR_OR_NULL(sub_np[0]))
+    {
+        pr_err("%s: can't parse \"pwms\" property\n", __func__);
+        return;
+    }
+    pwm_pdevice[0] = of_find_device_by_node(sub_np[0]);
+    if (IS_ERR_OR_NULL(pwm_pdevice[0]))
+    {
+        pr_err("%s: can't parse pwm device\n", __func__);
+        return;
+    }
+    /* get bind pwm device */
+    sub_np[1] = of_parse_phandle(pwmchip_parent(chip)->of_node, "pwms", pwm_index[1]);
+    if (IS_ERR_OR_NULL(sub_np[1]))
+    {
+        pr_err("%s: can't parse \"pwms\" property\n", __func__);
+        return;
+    }
+    pwm_pdevice[1] = of_find_device_by_node(sub_np[1]);
+    if (IS_ERR_OR_NULL(pwm_pdevice[1]))
+    {
+        pr_err("%s: can't parse pwm device\n", __func__);
+        return;
+    }
+
+    /* disable pwm controller */
+    for (i = 0; i < PWM_BIND_NUM; i++)
+    {
+        reg_offset[i] = PWM_PER;
+        reg_shift[i] = pwm_index[i];
+        reg_width[i] = 0x1;
+        value[i] = sunxi_pwm_readl(chip, reg_offset[i]);
+        value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 0);
+        sunxi_pwm_writel(chip, reg_offset[i], value[i]);
+    }
+
+    /* disable pwm clk gating */
+    for (i = 0; i < PWM_BIND_NUM; i++)
+    {
+        get_pccr_reg_offset(pwm_index[i], &reg_offset[i]);
+        reg_shift[i] = PWM_CLK_GATING_SHIFT;
+        reg_width[i] = 0x1;
+        value[i] = sunxi_pwm_readl(chip, reg_offset[i]);
+        value[i] = SET_BITS(reg_shift[i], reg_width[i], value[i], 0);
+        sunxi_pwm_writel(chip, reg_offset[i], value[i]);
+    }
+
+    /* disable pwm dead zone,one for the two pwm */
+    get_pdzcr_reg_offset(pwm_index[0], &reg_offset[0]);
+    reg_shift[0] = PWM_DZ_EN_SHIFT;
+    reg_width[0] = PWM_DZ_EN_WIDTH;
+    value[0] = sunxi_pwm_readl(chip, reg_offset[0]);
+    value[0] = SET_BITS(reg_shift[0], reg_width[0], value[0], 0);
+    sunxi_pwm_writel(chip, reg_offset[0], value[0]);
+
+    /* config pin sleep */
+    // sunxi_pwm_pin_set_state(&pwm_pdevice[0]->dev, PWM_PIN_STATE_SLEEP);
+    // sunxi_pwm_pin_set_state(&pwm_pdevice[1]->dev, PWM_PIN_STATE_SLEEP);
+}
+
+static void sunxi_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+    int bind_num;
+    struct sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
+
+    bind_num = pc->config[pwm->hwpwm].bind_pwm;
+    if (bind_num == 255)
+        sunxi_pwm_disable_single(chip, pwm);
+    else
+        sunxi_pwm_disable_dual(chip, pwm, bind_num);
+}
+
+static int sunxi_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			    const struct pwm_state *state)
+{
+	int err;
+	bool enabled = pwm->state.enabled;
+
+	if (state->polarity != pwm->state.polarity) {
+		/*
+		 * Changing the polarity of a running PWM is only allowed when
+		 * the PWM driver implements ->apply().
+		 */
+		if (enabled) {
+			sunxi_pwm_disable(chip, pwm);
+
+			enabled = false;
+		}
+
+		err = sunxi_pwm_set_polarity(chip, pwm, state->polarity);
+		if (err)
+			return err;
+	}
+
+
+	if (!state->enabled) {
+		if (enabled) {
+			sunxi_pwm_disable(chip, pwm);
+        }
+
+		return 0;
+	}
+
+	/*
+	 * We cannot skip calling ->config even if state->period ==
+	 * pwm->state.period && state->duty_cycle == pwm->state.duty_cycle
+	 * because we might have exited early in the last call to
+	 * pwm_apply_state because of !state->enabled and so the two values in
+	 * pwm->state might not be configured in hardware.
+	 */
+	err = sunxi_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
+	if (err) {
+		return err;
+    }
+
+	if (!enabled)
+		err = sunxi_pwm_enable(chip, pwm);
+
+	return err;
+}
+
+static const struct pwm_ops sunxi_pwm_ops = {
+	.apply = sunxi_pwm_apply,
+};
+
+static int sunxi_pwm_probe(struct platform_device *pdev)
+{
+    int ret;
+    unsigned int npwm;
+    struct pwm_chip *chip;
+    struct sunxi_pwm_chip *pwm;
+    struct device_node *np = pdev->dev.of_node;
+    int i;
+    struct platform_device *pwm_pdevice;
+    struct device_node *sub_np;
+
+    /* read property pwm-number */
+    ret = of_property_read_u32(np, "pwm-number", &npwm);
+    if (ret < 0)
+    {
+        dev_err(&pdev->dev, "failed to get pwm number: %d, force to one!\n", ret);
+        /* force to one pwm if read property fail */
+        npwm = 1;
+    }
+
+    chip = devm_pwmchip_alloc(&pdev->dev, npwm, sizeof(*pwm));
+    if (IS_ERR(chip)) {
+        return PTR_ERR(chip);
+    }
+
+    pwm = to_sunxi_pwm_chip(chip);
+
+    /* io map pwm base */
+    pwm->base = (void __iomem *)of_iomap(pdev->dev.of_node, 0);
+    if (!pwm->base)
+    {
+        dev_err(&pdev->dev, "unable to map pwm registers\n");
+        ret = -EINVAL;
+        goto err_iomap;
+    }
+
+    /* read property pwm-base */
+    ret = of_property_read_u32(np, "pwm-base", &chip->id);
+    if (ret < 0)
+    {
+        dev_err(&pdev->dev, "failed to get pwm-base: %d, force to -1 !\n", ret);
+        /* force to one pwm if read property fail */
+        chip->id = -1;
+    }
+
+    chip->dev.parent = &pdev->dev;
+    chip->ops = &sunxi_pwm_ops;
+
+    /* add pwm chip to pwm-core */
+    ret = pwmchip_add(chip);
+    if (ret < 0)
+    {
+        dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+        goto err_add;
+    }
+    platform_set_drvdata(pdev, chip);
+
+    pwm->config = devm_kzalloc(&pdev->dev, sizeof(*pwm->config) * npwm, GFP_KERNEL);
+    if (!pwm->config)
+    {
+        dev_err(&pdev->dev, "failed to allocate memory!\n");
+        goto err_alloc;
+    }
+
+    for (i = 0; i < npwm; i++)
+    {
+        sub_np = of_parse_phandle(np, "sunxi-pwms", i);
+        if (IS_ERR_OR_NULL(sub_np))
+        {
+            pr_err("%s: can't parse \"sunxi-pwms\" property\n", __func__);
+            return -EINVAL;
+        }
+
+        pwm_pdevice = of_find_device_by_node(sub_np);
+        ret = sunxi_pwm_get_config(pwm_pdevice, &pwm->config[i]);
+        if (ret)
+        {
+            pr_err("Get config failed,exit!\n");
+            goto err_get_config;
+        }
+    }
+
+    pwm->clk = devm_clk_get_optional(&pdev->dev, "mod");
+    if (IS_ERR(pwm->clk))
+        return dev_err_probe(&pdev->dev, PTR_ERR(pwm->clk),
+                             "get mod clock failed\n");
+
+    pwm->bus_clk = devm_clk_get_optional(&pdev->dev, "bus");
+    if (IS_ERR(pwm->bus_clk))
+        return dev_err_probe(&pdev->dev, PTR_ERR(pwm->bus_clk),
+                             "get bus clock failed\n");
+
+    pwm->pwm_rst_clk = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
+    if (IS_ERR(pwm->pwm_rst_clk))
+        return dev_err_probe(&pdev->dev, PTR_ERR(pwm->pwm_rst_clk),
+                             "get reset failed\n");
+
+    /* Deassert reset */
+    ret = reset_control_deassert(pwm->pwm_rst_clk);
+    if (ret)
+    {
+        dev_err(&pdev->dev, "cannot deassert reset control: %pe\n",
+                ERR_PTR(ret));
+        return ret;
+    }
+
+    ret = clk_prepare_enable(pwm->clk);
+    if (ret)
+    {
+        dev_err(&pdev->dev, "cannot prepare and enable clk %pe\n",
+                ERR_PTR(ret));
+        goto err_alloc;
+    }
+
+    ret = clk_prepare_enable(pwm->bus_clk);
+    if (ret)
+    {
+        dev_err(&pdev->dev, "cannot prepare and enable bus_clk %pe\n",
+                ERR_PTR(ret));
+        goto err_alloc;
+    }
+
+    return 0;
+
+err_get_config:
+err_alloc:
+    pwmchip_remove(chip);
+err_add:
+    iounmap(pwm->base);
+err_iomap:
+    return ret;
+}
+
+static void sunxi_pwm_remove(struct platform_device *pdev)
+{
+    struct pwm_chip *chip = platform_get_drvdata(pdev);
+    struct sunxi_pwm_chip *pwm = to_sunxi_pwm_chip(chip);
+    clk_disable(pwm->clk);
+    clk_disable(pwm->bus_clk);
+    reset_control_assert(pwm->pwm_rst_clk);
+    pwmchip_remove(chip);
+}
+
+static int sunxi_pwm_suspend(struct platform_device *pdev, pm_message_t state)
+{
+    return 0;
+}
+
+static int sunxi_pwm_resume(struct platform_device *pdev)
+{
+    return 0;
+}
+
+#if !IS_ENABLED(CONFIG_OF)
+struct platform_device sunxi_pwm_device = {
+    .name = "sunxi_pwm",
+    .id = -1,
+};
+#else
+static const struct of_device_id sunxi_pwm_match[] = {
+    {
+        .compatible = "allwinner,sun50i-h616-pwm",
+    },
+    {},
+};
+MODULE_DEVICE_TABLE(of, sunxi_pwm_match);
+#endif
+
+static struct platform_driver sunxi_pwm_driver = {
+    .probe = sunxi_pwm_probe,
+    .remove = sunxi_pwm_remove,
+    .suspend = sunxi_pwm_suspend,
+    .resume = sunxi_pwm_resume,
+    .driver = {
+        .name = "sunxi_pwm",
+        .owner = THIS_MODULE,
+        .of_match_table = sunxi_pwm_match,
+    },
+};
+
+static int __init pwm_module_init(void)
+{
+    int ret = 0;
+
+#if !IS_ENABLED(CONFIG_OF)
+    ret = platform_device_register(&sunxi_pwm_device);
+#endif
+    if (ret == 0)
+    {
+        ret = platform_driver_register(&sunxi_pwm_driver);
+    }
+
+    return ret;
+}
+
+static void __exit pwm_module_exit(void)
+{
+    platform_driver_unregister(&sunxi_pwm_driver);
+#if !IS_ENABLED(CONFIG_OF)
+    platform_device_unregister(&sunxi_pwm_device);
+#endif
+}
+
+subsys_initcall(pwm_module_init);
+module_exit(pwm_module_exit);
+
+MODULE_AUTHOR("zengqi");
+MODULE_AUTHOR("liuli");
+MODULE_DESCRIPTION("pwm driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sunxi-pwm");
diff --git a/drivers/pwm/pwm-sunxi-enhance.h b/drivers/pwm/pwm-sunxi-enhance.h
new file mode 100644
index 000000000000..e25e10bf5a3d
--- /dev/null
+++ b/drivers/pwm/pwm-sunxi-enhance.h
@@ -0,0 +1,60 @@
+/*
+ * drivers/pwm/pwm-sunxi-new.h
+ *
+ * Allwinnertech pulse-width-modulation controller driver
+ *
+ * Copyright (C) 2018 AllWinner
+ *
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef __PWM_SUNXI_NEW__H__
+#define __PWM_SUNXI_NEW__H__
+
+#define PWM_PIER	(0x0000)
+#define PWM_PISR	(0x0004)
+#define	PWM_CIER	(0x0010)
+#define	PWM_CISR	(0x0014)
+#define PWM_PCCR01	(0x0020)
+#define PWM_PCCR23	(0x0024)
+#define PWM_PCCR45	(0x0028)
+#define PWM_PCCR67	(0x002c)
+#define PWM_PDZCR01	(0x0030)
+#define PWM_PDZCR23	(0x0034)
+#define	PWM_PDZCR45	(0x0038)
+#define	PWM_PDZCR67	(0x003c)
+#define PWM_PER		(0x0040)
+#define PWM_CER		(0x0044)
+
+#define PWM_PCR_BASE	(0x0060 + 0x0000)
+#define PWM_PPR_BASE	(0x0060 + 0x0004)
+#define PWM_PCNTR_BASE	(0x0060 + 0x0008)
+#define PWM_CCR_BASE	(0x0060 + 0x000c)
+#define	PWM_CRLR_BASE	(0x0060 + 0x0010)
+#define PWM_CFLR_BASE	(0x0060 + 0x0014)
+#define PWM_PCCR8	(0x0300)
+
+#define PWM_ACT_STA_SHIFT	0x8
+#define PWM_ACT_STA_WIDTH	0x1
+#define PWM_CLK_SRC_SHIFT	0x7
+#define PWM_CLK_SRC_WIDTH	0x2
+#define PWM_DIV_M_SHIFT		0x0
+#define PWM_DIV_M_WIDTH		0x4
+#define PWM_PRESCAL_SHIFT	0x0
+#define PWM_PRESCAL_WIDTH	0x8
+#define PWM_ACT_CYCLES_SHIFT	0x0
+#define PWM_ACT_CYCLES_WIDTH	0x10
+#define PWM_PERIOD_CYCLES_SHIFT	0x10
+#define PWM_PERIOD_CYCLES_WIDTH	0x10
+#define PWM_DZ_EN_SHIFT		0x0
+#define PWM_DZ_EN_WIDTH		0x1
+#define PWM_PDZINTV_SHIFT	0x8
+#define PWM_PDZINTV_WIDTH	0x8
+#define PWM_BYPASS_WIDTH	0x1
+#define PWM_CLK_GATING_SHIFT	0x4
+#define PWM_CLK_GATING_WIDTH	0x1
+
+#endif
+
-- 
2.35.3

