armbian_build/patch/kernel/archive/sunxi-6.6/patches.armbian/drivers-pwm-Add-pwm-sunxi-enhance-driver-for-h616.patch
The-going dbdb03ddb8 sunxi-6.6: armbian: delete unused ones, replace changed ones.
Now the names of the patch files correspond to the entry
in the commit message.
Patches that have been transferred (copied) since time
immemorial and require revision have been removed.
No one wants to deal with them.
2024-08-02 17:21:36 +02:00

1318 lines
39 KiB
Diff

From d33be8d15727f4b9f9741744c11df3b22a6e9273 Mon Sep 17 00:00:00 2001
From: chraac <chraac@gmail.com>
Date: Wed, 1 May 2024 14:24:51 +0800
Subject: drivers: pwm: Add pwm-sunxi-enhance driver for h616
linux-orangepi commit:
c68ef342eba3673c7f1f5aa1ab819b06da1f60c6
---
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 8ebcddf91f7b..44adc2b7df0e 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -615,6 +615,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 c822389c2a24..9339710d4177 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -57,6 +57,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..68e22077ce34
--- /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
+{
+ struct pwm_chip 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 container_of(chip, struct sunxi_pwm_chip, 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->pwm - chip->base;
+ 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->pwm - chip->base;
+ sel[1] = bind_num - chip->base;
+ /* 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->pwm - chip->base].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->pwm - chip->base;
+
+ 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->pwm - chip->base].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->pwm - chip->base].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->pwm - chip->base;
+ pwm_index[1] = bind_num - chip->base;
+
+ /* 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->pwm - chip->base].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->pwm - chip->base;
+ sub_np = of_parse_phandle(chip->dev->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->pwm - chip->base;
+ pwm_index[1] = bind_num - chip->base;
+
+ /*set current pwm pin state*/
+ sub_np[0] = of_parse_phandle(chip->dev->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(chip->dev->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->pwm - chip->base].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->pwm - chip->base;
+ sub_np = of_parse_phandle(chip->dev->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->pwm - chip->base;
+ pwm_index[1] = bind_num - chip->base;
+
+ /* get current index pwm device */
+ sub_np[0] = of_parse_phandle(chip->dev->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(chip->dev->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->pwm - chip->base].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,
+ .owner = THIS_MODULE,
+};
+
+static int sunxi_pwm_probe(struct platform_device *pdev)
+{
+ int ret;
+ 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;
+
+ pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
+ if (!pwm)
+ {
+ dev_err(&pdev->dev, "failed to allocate memory!\n");
+ return -ENOMEM;
+ }
+
+ /* 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-number */
+ ret = of_property_read_u32(np, "pwm-number", &pwm->chip.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 */
+ pwm->chip.npwm = 1;
+ }
+
+ /* read property pwm-base */
+ ret = of_property_read_u32(np, "pwm-base", &pwm->chip.base);
+ 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 */
+ pwm->chip.base = -1;
+ }
+ pwm->chip.dev = &pdev->dev;
+ pwm->chip.ops = &sunxi_pwm_ops;
+
+ /* add pwm chip to pwm-core */
+ ret = pwmchip_add(&pwm->chip);
+ if (ret < 0)
+ {
+ dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
+ goto err_add;
+ }
+ platform_set_drvdata(pdev, pwm);
+
+ pwm->config = devm_kzalloc(&pdev->dev, sizeof(*pwm->config) * pwm->chip.npwm, GFP_KERNEL);
+ if (!pwm->config)
+ {
+ dev_err(&pdev->dev, "failed to allocate memory!\n");
+ goto err_alloc;
+ }
+
+ for (i = 0; i < pwm->chip.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(&pwm->chip);
+err_add:
+ iounmap(pwm->base);
+err_iomap:
+ return ret;
+}
+
+static int sunxi_pwm_remove(struct platform_device *pdev)
+{
+ struct sunxi_pwm_chip *pwm = platform_get_drvdata(pdev);
+ clk_disable(pwm->clk);
+ clk_disable(pwm->bus_clk);
+ reset_control_assert(pwm->pwm_rst_clk);
+ pwmchip_remove(&pwm->chip);
+
+ return 0;
+}
+
+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