armbian_build/patch/kernel/archive/sunxi-6.13/patches.armbian/drivers-pwm-Add-pwm-sunxi-enhance-driver-for-h616.patch
2025-03-21 10:24:35 +01:00

1316 lines
38 KiB
Diff

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