firmware/br-ext-chip-ingenic/board/t40/kernel/patches/00000-drivers_pwm_pwm-ingen...

926 lines
27 KiB
Diff

diff -drupN a/drivers/pwm/pwm-ingenic-v2.c b/drivers/pwm/pwm-ingenic-v2.c
--- a/drivers/pwm/pwm-ingenic-v2.c 1970-01-01 03:00:00.000000000 +0300
+++ b/drivers/pwm/pwm-ingenic-v2.c 2022-06-09 05:02:33.000000000 +0300
@@ -0,0 +1,921 @@
+/* drivers/pwm/pwm-ingenic-x2000.c
+ * PWM driver of Ingenic's SoC X2000
+ *
+ * Copyright (C) 2015 Ingenic Semiconductor Co., Ltd.
+ * http://www.ingenic.com
+ * Author: sihui.liu <sihui.liu@ingenic.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/pwm.h>
+#include <linux/clk.h>
+#include <linux/ctype.h>
+#include <linux/stat.h>
+#include <linux/pwm.h>
+#include <linux/time.h>
+#include <asm/io.h>
+#include <asm-generic/div64.h>
+#include <linux/sysfs.h>
+#include <irq.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+
+#define INGENIC_PWM_NUM (16)
+#define NS_IN_HZ (1000000000UL)
+#define DEFAULT_PWM_CLK_RATE (50000000)
+
+struct ingenic_pwm_channel {
+ u32 period_ns;
+ u32 duty_ns;
+};
+
+enum pwm_mode_sel{
+ COMMON_MODE,
+ DMA_MODE
+};
+#define PWM_INIT_HIGH (1)
+#define PWM_INIT_LOW (0)
+
+
+struct ingenic_pwm_chip {
+ struct pwm_chip chip;
+ struct clk *clk_pwm;
+ struct clk *clk_gate;
+ void __iomem *iomem;
+ int init_level;
+ struct mutex mutex;
+ unsigned int output_mask;
+
+ struct pwm_device *debug_pwm[INGENIC_PWM_NUM];
+ unsigned int vir_addr[INGENIC_PWM_NUM];
+ unsigned int phy_addr[INGENIC_PWM_NUM];
+ int debug_current_id;
+ int mode_sel[INGENIC_PWM_NUM];
+ int irq;
+};
+
+
+
+
+
+/**-----------------------------------------------------------------------------
+ ** reg offset
+ **-----------------------------------------------------------------------------*/
+
+
+#define PWM_CCFG0 (0x0)
+#define PWM_CCFG1 (0x4)
+#define PWM_ENS (0x10)
+#define PWM_ENC (0x14)
+#define PWM_EN (0x18)
+#define PWM_UP (0x20)
+#define PWM_BUSY (0x24)
+#define PWM_INITR (0x30)
+#define PWM_WCFG (0xb0)
+#define PWM_DES (0x100)
+#define PWM_DEC (0x104)
+#define PWM_DE (0x108)
+#define PWM_DCR0 (0x110)
+#define PWM_DCR1 (0x114)
+#define PWM_DTRIG (0x120)
+#define PWM_DFER (0x124)
+#define PWM_DFSM (0x128)
+#define PWM_DSR (0x130)
+#define PWM_DSCR (0x134)
+#define PWM_DINTC (0x138)
+#define PWM_DMADDR (0x140)
+#define PWM_DTLR (0x190)
+#define PWM_OEN (0x300)
+
+
+#define PRESCALE_MSK (0xf)
+#define PRESCALE (0x2)
+#define TRANS_LEN (32)
+#define PWM_PER (0x30)
+
+
+/**-----------------------------------------------------------------------------
+ ** reg bit field
+ **-----------------------------------------------------------------------------*/
+
+/**
+ ** PWM Waveform Configuration Register (PWMWCFG)
+ **/
+#define PWM_WCFG_HIGH (16)
+#define PWM_WCFG_LOW (0)
+
+static void pwm_writel(struct ingenic_pwm_chip *chip, unsigned int value, unsigned int offset)
+{
+ writel(value, chip->iomem + offset);
+}
+
+static unsigned int pwm_readl(struct ingenic_pwm_chip *chip, unsigned int offset)
+{
+ return readl(chip->iomem + offset);
+}
+
+static void pwm_clk_config(struct ingenic_pwm_chip *chip, unsigned int channel, unsigned int prescale)
+{
+ int tmp = 0;
+ if(channel < 8) {
+ tmp = pwm_readl(chip, PWM_CCFG0) & ~(PRESCALE_MSK << (channel *4));
+ tmp |= prescale << (channel * 4);
+ pwm_writel(chip, tmp, PWM_CCFG0);
+ } else {
+ tmp = pwm_readl(chip, PWM_CCFG1) & ~(PRESCALE_MSK << ((channel - 8) * 4));
+ tmp |= prescale << ((channel - 8) * 4);
+ pwm_writel(chip, tmp, PWM_CCFG1);
+ }
+}
+
+static unsigned int pwm_get_prescale(struct ingenic_pwm_chip *chip, unsigned int channel)
+{
+ int tmp = 0;
+ int prescale = 0;
+ if(channel < 8) {
+ tmp = pwm_readl(chip, PWM_CCFG0);
+ prescale = tmp & (0xf << channel * 4);
+ } else {
+ tmp = pwm_readl(chip, PWM_CCFG1);
+ prescale = tmp & (0xf << (channel - 8) * 4);
+ }
+ return prescale;
+}
+
+static void pwm_enable_hw(struct ingenic_pwm_chip *chip, unsigned int channel)
+{
+ int tmp = 0;
+ tmp = pwm_readl(chip, PWM_EN);
+ tmp |= (1 << channel);
+ pwm_writel(chip, tmp, PWM_ENS);
+}
+
+static void pwm_disable_hw(struct ingenic_pwm_chip *chip, unsigned int channel)
+{
+ pwm_writel(chip, 1 << channel, PWM_ENC);
+}
+
+static int pwm_enable_status(struct ingenic_pwm_chip *chip)
+{
+ return pwm_readl(chip, PWM_EN);
+}
+
+static void pwm_update(struct ingenic_pwm_chip *chip, unsigned int channel)
+{
+ pwm_writel(chip, 1 << channel, PWM_UP);
+}
+
+static int pwm_busy(struct ingenic_pwm_chip *chip, unsigned int channel)
+{
+ int tmp = 0;
+ tmp = pwm_readl(chip, PWM_BUSY);
+ return tmp & (1 << channel);
+}
+
+static void pwm_set_period_hw(struct ingenic_pwm_chip *chip, unsigned int channel, unsigned int period)
+{
+ pwm_writel(chip, period, PWM_PER + channel * 4);
+}
+
+static void pwm_set_init_level(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel, unsigned int level)
+{
+ int tmp;
+ tmp = pwm_readl(ingenic_pwm,PWM_INITR);
+ if(level)
+ tmp |= 1 << channel;
+ else
+ tmp &= ~(1 << channel);
+ pwm_writel(ingenic_pwm,tmp,PWM_INITR);
+}
+
+static void pwm_set_finish_level(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel, unsigned int level)
+{
+ int tmp;
+ tmp = pwm_readl(ingenic_pwm,PWM_INITR);
+ if(level)
+ tmp |= 1 << (channel + 16);
+ else
+ tmp &= ~(1 << (channel + 16));
+ pwm_writel(ingenic_pwm,tmp,PWM_INITR);
+}
+
+static void pwm_waveform_high(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel, unsigned int high_num)
+{
+ int tmp = 0;
+ tmp = pwm_readl(ingenic_pwm, PWM_WCFG + channel * 4);
+ tmp &= ~(0xffff << PWM_WCFG_HIGH);
+ tmp |= high_num << PWM_WCFG_HIGH;
+ pwm_writel(ingenic_pwm, tmp, PWM_WCFG + channel * 4);
+}
+
+static void pwm_waveform_low(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel, unsigned int low_num)
+{
+ int tmp = 0;
+ tmp = pwm_readl(ingenic_pwm, PWM_WCFG + channel * 4);
+ tmp &= ~(0xffff);
+ tmp |= low_num << PWM_WCFG_LOW;
+ pwm_writel(ingenic_pwm, tmp, PWM_WCFG + channel * 4);
+}
+
+/*pwm DMA mode control register operation*/
+static void pwm_dma_enable_hw(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel)
+{
+ int tmp = 0;
+ tmp = pwm_readl(ingenic_pwm, PWM_DES);
+ tmp |= (1 << channel);
+ pwm_writel(ingenic_pwm, tmp, PWM_DES);
+}
+
+static void pwm_dma_disable_hw(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel)
+{
+ pwm_writel(ingenic_pwm, 1 << channel, PWM_DEC);
+}
+
+static int pwm_dma_enable_status(struct ingenic_pwm_chip *ingenic_pwm)
+{
+ return pwm_readl(ingenic_pwm, PWM_DE);
+}
+
+static void pwm_set_update_mode(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel, int mode_sel)
+{
+ int tmp;
+ tmp = pwm_readl(ingenic_pwm,PWM_DCR0);
+ if(mode_sel == DMA_MODE)
+ tmp |= 1 << channel;
+ if(mode_sel == COMMON_MODE)
+ tmp &= ~(1 << channel);
+ pwm_writel(ingenic_pwm,tmp,PWM_DCR0);
+}
+
+static void pwm_dma_set_loop(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel)
+{
+ int tmp;
+ tmp = pwm_readl(ingenic_pwm,PWM_DCR1);
+ tmp |= 1 << channel;
+ pwm_writel(ingenic_pwm,tmp,PWM_DCR1);
+}
+
+static void pwm_dma_clear_loop(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel)
+{
+ int tmp;
+ tmp = pwm_readl(ingenic_pwm,PWM_DCR1);
+ tmp &= ~(1 << channel);
+ pwm_writel(ingenic_pwm,tmp,PWM_DCR1);
+}
+
+static unsigned int pwm_dma_get_loop_status(struct ingenic_pwm_chip *ingenic_pwm,unsigned int channel)
+{
+ return pwm_readl(ingenic_pwm,PWM_DCR1) & (1 << channel);
+}
+
+static void pwm_dma_trans_trigger(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel)
+{
+ int tmp;
+ tmp = pwm_readl(ingenic_pwm,PWM_DTRIG);
+ tmp |= 1 << channel;
+ pwm_writel(ingenic_pwm,tmp,PWM_DTRIG);
+}
+
+static unsigned int pwm_dma_fifo_empty(struct ingenic_pwm_chip *ingenic_pwm,unsigned int channel)
+{
+ return pwm_readl(ingenic_pwm,PWM_DFER) & (1 << channel);
+}
+
+static unsigned int pwm_dma_get_dfsm(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel)
+{
+ int tmp;
+ tmp = pwm_readl(ingenic_pwm,PWM_DFSM);
+ tmp &= 1 << channel;
+ return tmp;
+}
+
+static int pwm_dma_get_status(struct ingenic_pwm_chip *ingenic_pwm)
+{
+ return pwm_readl(ingenic_pwm,PWM_DSR);
+}
+
+static void pwm_dma_clear_status(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel)
+{
+ int tmp;
+ tmp = pwm_readl(ingenic_pwm,PWM_DSCR);
+ tmp |= 1 << channel;
+ pwm_writel(ingenic_pwm,tmp,PWM_DSCR);
+}
+
+static void pwm_dma_interrupt_mask(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel)
+{
+ int tmp;
+ tmp = pwm_readl(ingenic_pwm,PWM_DINTC);
+ tmp |= 1 << channel;
+ pwm_writel(ingenic_pwm,tmp,PWM_DINTC);
+}
+
+static void pwm_dma_interrupt_unmask(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel)
+{
+ int tmp;
+ tmp = pwm_readl(ingenic_pwm,PWM_DINTC);
+ tmp &= ~(1 << channel);
+ pwm_writel(ingenic_pwm,tmp,PWM_DINTC);
+}
+
+static void pwm_dma_set_mem_addr(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel, unsigned int mem_addr)
+{
+ pwm_writel(ingenic_pwm,mem_addr,PWM_DMADDR + channel * 4);
+}
+
+/*DMA transfer length must 4 word align*/
+static void pwm_dma_set_trans_length(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel, int tran_len)
+{
+ pwm_writel(ingenic_pwm,tran_len,PWM_DTLR + channel * 4);
+}
+
+static void pwm_set_io_output_enable(struct ingenic_pwm_chip *ingenic_pwm, unsigned int channel, int enable)
+{
+ int tmp;
+ tmp = pwm_readl(ingenic_pwm, PWM_OEN);
+ if(enable)
+ tmp |= 1 << channel;
+ else
+ tmp &= ~(1 << channel);
+ pwm_writel(ingenic_pwm, tmp, PWM_OEN);
+}
+
+static void dump_pwm_reg(struct ingenic_pwm_chip *ingenic_pwm)
+{
+ int i;
+ printk("PWM_CCFG0 addr %08x = %08x\n", ingenic_pwm->iomem + PWM_CCFG0,pwm_readl(ingenic_pwm, PWM_CCFG0));
+ printk("PWM_CCFG1 addr %08x = %08x\n", ingenic_pwm->iomem + PWM_CCFG1,pwm_readl(ingenic_pwm, PWM_CCFG1));
+ printk("PWM_ENS addr %08x = %08x\n", ingenic_pwm->iomem + PWM_ENS, pwm_readl(ingenic_pwm, PWM_ENS));
+ printk("PWM_ENC addr %08x = %08x\n", ingenic_pwm->iomem + PWM_ENC,pwm_readl(ingenic_pwm, PWM_ENC));
+ printk("PWM_EN addr %08x = %08x\n", ingenic_pwm->iomem + PWM_EN,pwm_readl(ingenic_pwm, PWM_EN));
+ printk("PWM_UP addr %08x = %08x\n", ingenic_pwm->iomem + PWM_UP,pwm_readl(ingenic_pwm, PWM_UP));
+ printk("PWM_LOOP addr %08x = %08x\n", ingenic_pwm->iomem + PWM_DCR1,pwm_readl(ingenic_pwm, PWM_DCR1));
+ printk("PWM_MASK addr %08x = %08x\n", ingenic_pwm->iomem + PWM_DINTC,pwm_readl(ingenic_pwm, PWM_DINTC));
+ printk("PWM_BUSY addr %08x = %08x\n", ingenic_pwm->iomem + PWM_BUSY,pwm_readl(ingenic_pwm, PWM_BUSY));
+ printk("----------------------------------------------------------------------\n");
+ for(i = 0; i < 16; i++) {
+ printk("PWM_DAR %d addr %08x = %08x\n", i,ingenic_pwm->iomem + i * 4, pwm_readl(ingenic_pwm, PWM_DMADDR + i * 4));
+ }
+ printk("----------------------------------------------------------------------\n");
+ for(i = 0; i < 16; i++) {
+ printk("PWM_WCFG%d addr %08x = %08x\n", i,ingenic_pwm->iomem + i * 4, pwm_readl(ingenic_pwm, PWM_WCFG + i * 4));
+ }
+ printk("----------------------------------------------------------------------\n");
+ for(i = 0; i < 16; i++) {
+ printk("PWM_DTLR%d addr %08x = %08x\n", i, ingenic_pwm->iomem + i * 4,pwm_readl(ingenic_pwm, PWM_DTLR + i * 4));
+ }
+}
+
+
+static irqreturn_t ingenic_pwm_interrupt(int irq, void *dev_id)
+{
+ int tmp;
+ struct ingenic_pwm_chip *ingenic_pwm = (struct ingenic_pwm_chip *)(dev_id);
+ tmp = pwm_dma_get_status(ingenic_pwm);
+ /*clear end flag*/
+ pwm_writel(ingenic_pwm,tmp,PWM_DSCR);
+
+ /*restart DMA transfer*/
+ // pwm_writel(ingenic_pwm,tmp,PWM_DTRIG);
+
+ return IRQ_HANDLED;
+}
+
+
+//--------------------------add end----------------------
+
+
+static inline struct ingenic_pwm_chip *to_ingenic_chip(struct pwm_chip *chip)
+{
+ return container_of(chip, struct ingenic_pwm_chip, chip);
+}
+
+static int ingenic_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+ int duty_ns, int period_ns)
+{
+ struct ingenic_pwm_chip *ingenic_pwm = to_ingenic_chip(chip);
+ int channel = pwm->hwpwm;
+ unsigned int period = 0;
+ unsigned int duty = 0;
+ unsigned short start = 0;
+ unsigned short end = 0;
+ unsigned int pwm_freq = 0;
+ unsigned int clk_in = 0;
+ unsigned int prescale = 0;
+ unsigned int tmp = 1;
+ int i = 0;
+ int update_flag = 1;
+ int mode = 0;
+ unsigned int dma_coherent;
+ dma_addr_t dma_coherent_handle;
+ unsigned long long clk_ns = 1000000000ULL;
+
+ mutex_lock(&ingenic_pwm->mutex);
+ printk("duty_ns=%d period_ns=%d\n", duty_ns, period_ns);
+
+ if (duty_ns < 0 || duty_ns > period_ns) {
+ pr_err("%s, duty_ns(%d)< 0 or duty_ns > period_ns(%d)\n", __func__, duty_ns, period_ns);
+ return -EINVAL;
+ }
+
+ if(period_ns > NS_IN_HZ || duty_ns > NS_IN_HZ)
+ return -ERANGE;
+
+ /*select mode */
+ mode = ingenic_pwm->mode_sel[channel];
+
+ /*set prescale*/
+ pwm_clk_config(ingenic_pwm, channel, PRESCALE);
+ clk_in = clk_get_rate(ingenic_pwm->clk_pwm);
+ prescale = pwm_get_prescale(ingenic_pwm, channel);
+ printk("prescale=%08x\n", prescale);
+
+ for (i = 0; i < prescale; i++) {
+ tmp = 2 * tmp;
+ }
+
+ pwm_freq = clk_in / tmp;
+ printk("pwm_freq=%d tmp=%d\n", pwm_freq, tmp);
+ do_div(clk_ns, pwm_freq);
+ printk("clk_ns=%lld\n", clk_ns);
+
+ period = (period_ns / (unsigned int)clk_ns) - 1;
+ printk("period=%d\n", period);
+ // pwm_set_period_hw(ingenic_pwm, channel, period);
+ duty = duty_ns / (unsigned int)clk_ns;
+
+ /*set init level*/
+ if (ingenic_pwm->init_level == PWM_INIT_HIGH)
+ pwm_set_init_level(ingenic_pwm,channel,1);
+ else if (ingenic_pwm->init_level == PWM_INIT_LOW)
+ pwm_set_init_level(ingenic_pwm,channel,0);
+ else
+ pr_err("%s, pwm init level error\n", __func__);
+
+ printk("duty=%d level_init=%d low=%d\n", duty, ingenic_pwm->init_level, period - duty);
+
+ /*set waveform high_num and low_num*/
+ if(COMMON_MODE == mode){
+ //*****************************test**(duty**period)**********
+ pwm_waveform_high(ingenic_pwm, channel, duty_ns);
+ pwm_waveform_low(ingenic_pwm, channel, period_ns - duty_ns);
+ }
+
+ /*Set the level state at the end of the waveform*/
+ pwm_set_finish_level(ingenic_pwm,channel,0);
+
+ /*pwm output enable*/
+ pwm_set_io_output_enable(ingenic_pwm, channel, 1);
+
+ /*Update bit according to mode setting*/
+ pwm_set_update_mode(ingenic_pwm, channel, mode);
+
+ /*Whether to update*/
+ if(ingenic_pwm->chip.pwms[channel].flags & (1 << PWMF_ENABLED)) {
+ if(COMMON_MODE == mode){
+ pwm_update(ingenic_pwm, channel);
+ while(pwm_busy(ingenic_pwm, channel));
+ }
+ if(DMA_MODE == mode){
+ update_flag = 0;
+ if(!pwm_dma_get_loop_status(ingenic_pwm, channel)){
+ while(!pwm_dma_fifo_empty(ingenic_pwm, channel));
+ if(pwm_dma_fifo_empty(ingenic_pwm, channel))
+ update_flag = 1;
+ }
+ }
+ }
+ /*DMA mode related configuration*/
+ if(DMA_MODE == mode && update_flag){
+
+ dma_coherent = dma_alloc_coherent(ingenic_pwm->chip.dev, sizeof(int) * TRANS_LEN, &dma_coherent_handle, GFP_KERNEL);
+ printk("--%s-------%d-dma_coherent = %x--dma_coherent_handle = %x---write value = %x----\n",__func__,__LINE__,dma_coherent,dma_coherent_handle,(0x1111ffff));
+ dma_cache_wback_inv(dma_coherent,sizeof(int) * TRANS_LEN);
+ for(i = 0; i < TRANS_LEN; i++ ){
+ printk("-----------befor-addr = %x--value ----%x --- \n",dma_coherent + i * 4,*(volatile unsigned int *)(dma_coherent + i * 4));
+ *(volatile unsigned int *)(dma_coherent + i * 4) = (0x1111ffff + i * 5);
+ printk("-----------after-addr = %x--value ----%x --- \n",dma_coherent + i * 4,*(volatile unsigned int *)(dma_coherent + i * 4));
+ }
+ ingenic_pwm->vir_addr[channel] = dma_coherent;
+ ingenic_pwm->phy_addr[channel] = dma_coherent_handle;
+
+ /*set addr and trans_len*/
+ pwm_dma_set_mem_addr(ingenic_pwm, channel, dma_coherent_handle);
+ pwm_dma_set_trans_length(ingenic_pwm, channel, TRANS_LEN);
+
+ /*set interrupt */
+ pwm_dma_clear_loop(ingenic_pwm, channel);
+ pwm_dma_interrupt_unmask(ingenic_pwm, channel);
+ }
+ mutex_unlock(&ingenic_pwm->mutex);
+ return 0;
+}
+
+
+static int ingenic_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ingenic_pwm_chip *ingenic_pwm = to_ingenic_chip(chip);
+ int channel = pwm->hwpwm;
+ int count = 0;
+ mutex_lock(&ingenic_pwm->mutex);
+
+ if(ingenic_pwm->mode_sel[channel] == DMA_MODE){
+ /*enable DMA mode*/
+ while(pwm_dma_get_dfsm(ingenic_pwm,channel)){
+ count ++;
+ if(count == 0x10000000){
+ printk("pwm %d channel is working\n",channel);
+ return 0;
+ }
+ }
+ pwm_dma_enable_hw(ingenic_pwm, channel);
+ pwm_dma_trans_trigger(ingenic_pwm, channel);
+ }
+ pwm_enable_hw(ingenic_pwm, channel);
+ mutex_unlock(&ingenic_pwm->mutex);
+
+ return 0;
+}
+
+static void ingenic_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct ingenic_pwm_chip *ingenic_pwm = to_ingenic_chip(chip);
+ int channel = pwm->hwpwm;
+
+ mutex_lock(&ingenic_pwm->mutex);
+ if(ingenic_pwm->mode_sel[channel] == DMA_MODE){
+ pwm_dma_disable_hw(ingenic_pwm,channel);
+ dma_free_coherent(ingenic_pwm->chip.dev,sizeof(int) * TRANS_LEN, ingenic_pwm->vir_addr[channel], ingenic_pwm->phy_addr[channel]);
+ }
+
+ pwm_disable_hw(ingenic_pwm, channel);
+ mutex_unlock(&ingenic_pwm->mutex);
+}
+
+
+
+static const struct pwm_ops ingenic_pwm_ops = {
+ .config = ingenic_pwm_config,
+ .enable = ingenic_pwm_enable,
+ .disable = ingenic_pwm_disable,
+ .owner = THIS_MODULE,
+};
+
+
+static ssize_t pwm_show_enable(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ingenic_pwm_chip *ingenic_pwm = dev_get_drvdata(dev);
+ return sprintf(buf, "%x", pwm_enable_status(ingenic_pwm));
+}
+
+static ssize_t pwm_store_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ingenic_pwm_chip *ingenic_pwm = dev_get_drvdata(dev);
+ int enable;
+ const char *str = buf;
+ int ret_count = 0;
+
+ while (!isxdigit(*str)) {
+ str++;
+ if(++ret_count >= count)
+ return count;
+ }
+ enable = simple_strtoul(str, (char **)&str, 10);
+
+ printk("input enable value %d,current_id %d\n",enable,ingenic_pwm->debug_current_id);
+ if (enable == 1) {
+ pwm_enable(ingenic_pwm->debug_pwm[ingenic_pwm->debug_current_id]);
+ } else if (enable == 0) {
+ pwm_disable(ingenic_pwm->debug_pwm[ingenic_pwm->debug_current_id]);
+ } else {
+ printk("enable or disable error!\n");
+ }
+ return count;
+}
+
+
+static ssize_t pwm_show_config(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ingenic_pwm_chip *ingenic_pwm = dev_get_drvdata(dev);
+ dump_pwm_reg(ingenic_pwm);
+ printk("\n");
+ return 0;
+}
+
+static ssize_t pwm_store_config(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ingenic_pwm_chip *ingenic_pwm = dev_get_drvdata(dev);
+ const char *str = buf;
+ unsigned int period_ns, duty_ns;
+ int mode = 0;
+ int ret_count = 0;
+
+ while (!isxdigit(*str)) {
+ str++;
+ if(++ret_count >= count)
+ return count;
+ }
+ mode = simple_strtoul(str, (char **)&str, 10);
+ if(DMA_MODE == mode)
+ ingenic_pwm->mode_sel[ingenic_pwm->debug_current_id] = DMA_MODE;
+
+ while (!isxdigit(*str)) {
+ str++;
+ if(++ret_count >= count)
+ return count;
+ }
+ duty_ns = simple_strtoul(str, (char **)&str, 10);
+
+ while (!isxdigit(*str)) {
+ str++;
+ if(++ret_count >= count)
+ return count;
+ }
+ period_ns = simple_strtoul(str, (char **)&str, 10);
+
+ printk("debug_current_id = %d \n", ingenic_pwm->debug_current_id);
+ printk("period_ns = %d , duty_ns = %d mode = %s\n", period_ns,duty_ns,mode? "DMA_MODE":"COMMON_MODE");
+ pwm_config(ingenic_pwm->debug_pwm[ingenic_pwm->debug_current_id], duty_ns, period_ns);
+ printk("pwm_config finish\n");
+
+ printk("\n");
+ return count;
+}
+
+static ssize_t pwm_show_channel(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ingenic_pwm_chip *ingenic_pwm = dev_get_drvdata(dev);
+
+ return sprintf(buf, "channel = %x", ingenic_pwm->debug_current_id);
+
+}
+
+static ssize_t pwm_store_channel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ingenic_pwm_chip *ingenic_pwm = dev_get_drvdata(dev);
+ const char *str = buf;
+ int ret_count = 0;
+ int pwm_id = 0;
+ struct pwm_device *pwm = NULL;
+
+ while (!isxdigit(*str)) {
+ str++;
+ if(++ret_count >= count)
+ return count;
+ }
+ pwm_id = simple_strtoul(str, (char **)&str, 10);
+ pwm = pwm_request(pwm_id, "pwm_request_test");
+ if (IS_ERR(pwm)) {
+ printk("unable to request pwm\n");
+ return -1;
+ }
+ ingenic_pwm->debug_pwm[pwm_id] = pwm;
+ ingenic_pwm->debug_current_id = pwm_id;
+ printk("channel = %d\n", ingenic_pwm->debug_current_id);
+ printk("\n");
+ return count;
+}
+
+static ssize_t pwm_store_free(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ingenic_pwm_chip *ingenic_pwm = dev_get_drvdata(dev);
+ const char *str = buf;
+ int ret_count = 0;
+ int pwm_id = 0;
+
+ while (!isxdigit(*str)) {
+ str++;
+ if(++ret_count >= count)
+ return count;
+ }
+ pwm_id = simple_strtoul(str, (char **)&str, 10);
+
+ pwm_put(ingenic_pwm->debug_pwm[pwm_id]);
+ ingenic_pwm->debug_pwm[pwm_id] = NULL;
+ return count;
+}
+
+static ssize_t pwm_show_requested_channel(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ingenic_pwm_chip *ingenic_pwm = dev_get_drvdata(dev);
+ int i = 0;
+ int ret = 0;
+
+ for (i = 0; i <INGENIC_PWM_NUM ; i++) {
+ if (ingenic_pwm->debug_pwm[i] == &(ingenic_pwm->chip.pwms[i]))
+ ret += sprintf(buf + ret, "ch: %d requested\n", i);
+ else
+ ret += sprintf(buf + ret, "ch: %d unrequested\n", i);
+ }
+ return ret;
+}
+
+static struct device_attribute pwm_device_attributes[] = {
+ __ATTR(enable, S_IRUGO|S_IWUSR, pwm_show_enable, pwm_store_enable),
+ __ATTR(config, S_IRUGO|S_IWUSR, pwm_show_config, pwm_store_config),
+ __ATTR(request, S_IRUGO|S_IWUSR, pwm_show_channel, pwm_store_channel),
+ __ATTR(free, S_IWUSR, NULL, pwm_store_free),
+ __ATTR(channels, S_IRUGO, pwm_show_requested_channel, NULL),
+};
+
+static int ingenic_pwm_probe(struct platform_device *pdev)
+{
+ struct ingenic_pwm_chip *chip;
+ struct resource *res;
+ int err = 0;
+ int ret = 0;
+ int i = 0;
+
+ chip = devm_kzalloc(&pdev->dev,
+ sizeof(struct ingenic_pwm_chip), GFP_KERNEL);
+ if (!chip) {
+ pr_err("%s %d,malloc ingenic_pwm_chip error\n",
+ __func__, __LINE__);
+ return -ENOMEM;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
+ return -ENOENT;
+ }
+
+ chip->irq = platform_get_irq(pdev, 0);
+ if(chip->irq < 0){
+ dev_err(&pdev->dev, "Cannot get %d IORESOURCE_IRQ\n",chip->irq);
+ return -ENOENT;
+ }
+
+ chip->iomem = ioremap(res->start, (res->end - res->start) + 1);
+ if (chip->iomem == NULL) {
+ dev_err(&pdev->dev, "Cannot map IO\n");
+ err = -ENXIO;
+ goto err_no_iomap;
+ }
+
+ chip->clk_gate = devm_clk_get(&pdev->dev, "gate_pwm");
+ if (IS_ERR(chip->clk_gate)) {
+ dev_err(&pdev->dev, "get pwm clk gate failed %ld\n", PTR_ERR(chip->clk_gate));
+ return PTR_ERR(chip->clk_gate);
+ }
+
+ chip->clk_pwm = devm_clk_get(&pdev->dev, "cgu_pwm");
+ if(IS_ERR(chip->clk_pwm)){
+ dev_err(&pdev->dev, "get pwm clk failed %ld\n", PTR_ERR(chip->clk_pwm));
+ return PTR_ERR(chip->clk_pwm);
+ }
+
+ if(chip->clk_gate) {
+ ret = clk_prepare_enable(chip->clk_gate);
+ if(ret) {
+ dev_err(&pdev->dev, "enable pwm clock gate failed!\n");
+ }
+ }
+
+ if(chip->clk_pwm) {
+ ret = clk_set_rate(chip->clk_pwm, DEFAULT_PWM_CLK_RATE);
+ if(ret) {
+ dev_err(&pdev->dev, "set pwm clock rate failed!\n");
+ }
+ ret = clk_prepare_enable(chip->clk_pwm);
+ if(ret) {
+ dev_err(&pdev->dev, "enable pwm clock failed!\n");
+ }
+ }
+
+ chip->chip.dev = &pdev->dev;
+ chip->chip.ops = &ingenic_pwm_ops;
+ chip->chip.base = 0;
+ chip->chip.npwm = INGENIC_PWM_NUM;
+ chip->init_level = PWM_INIT_HIGH;
+
+ ret = pwmchip_add(&chip->chip);
+ if (ret < 0) {
+ devm_kfree(&pdev->dev, chip);
+ return ret;
+ }
+
+ ret = request_irq(chip->irq,ingenic_pwm_interrupt,
+ IRQF_SHARED | IRQF_TRIGGER_LOW,"ingenic-interrupt",chip);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq failed !! %d-\n",chip->irq);
+ goto err_no_iomap;
+ }
+
+ platform_set_drvdata(pdev, chip);
+ mutex_init(&chip->mutex);
+
+ for (i = 0; i < ARRAY_SIZE(pwm_device_attributes); i++) {
+ ret = device_create_file(&pdev->dev, &pwm_device_attributes[i]);
+ if (ret)
+ dev_warn(&pdev->dev, "attribute %d create failed", i);
+ }
+
+ dev_info(&pdev->dev, "ingenic-x2000 Probe of pwm success!\n");
+ return 0;
+
+err_no_iomap:
+ iounmap(chip->iomem);
+
+ return err;
+}
+
+static int ingenic_pwm_remove(struct platform_device *pdev)
+{
+ struct ingenic_pwm_chip *chip;
+ chip =platform_get_drvdata(pdev);
+ if (!chip)
+ return -ENODEV;
+
+ free_irq(chip->irq,chip);
+ pwmchip_remove(&chip->chip);
+ devm_clk_put(&pdev->dev,chip->clk_pwm);
+ devm_clk_put(&pdev->dev,chip->clk_gate);
+ devm_kfree(&pdev->dev, chip);
+ return 0;
+}
+
+static const struct of_device_id ingenic_pwm_matches[] = {
+ { .compatible = "ingenic,x2000-pwm", .data = NULL },
+ {},
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int ingenic_pwm_suspend(struct device *dev)
+{
+ struct ingenic_pwm_chip *chip = dev_get_drvdata(dev);
+ unsigned int i;
+
+ /*
+ * No one preserves these values during suspend so reset them.
+ * Otherwise driver leaves PWM unconfigured if same values are
+ * passed to pwm_config() next time.
+ */
+ for (i = 0; i < INGENIC_PWM_NUM; ++i) {
+ struct pwm_device *pwm = &chip->chip.pwms[i];
+ struct ingenic_pwm_channel *chan = pwm_get_chip_data(pwm);
+
+ if (!chan)
+ continue;
+ chan->period_ns = 0;
+ chan->duty_ns = 0;
+ }
+
+ return 0;
+}
+
+static int ingenic_pwm_resume(struct device *dev)
+{
+ struct ingenic_pwm_chip *chip = dev_get_drvdata(dev);
+ unsigned int chan;
+
+ /*
+ * Inverter setting must be preserved across suspend/resume
+ * as nobody really seems to configure it more than once.
+ */
+ for (chan = 0; chan < INGENIC_PWM_NUM; ++chan) {
+ if (chip->output_mask & BIT(chan)) {
+ /*TODO: ??*/
+ }
+ }
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ingenic_pwm_pm_ops, ingenic_pwm_suspend, ingenic_pwm_resume);
+
+static struct platform_driver ingenic_pwm_driver = {
+ .driver = {
+ .name = "ingenic-pwm",
+ .pm = &ingenic_pwm_pm_ops,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ingenic_pwm_matches),
+ },
+ .probe = ingenic_pwm_probe,
+ .remove = ingenic_pwm_remove,
+};
+module_platform_driver(ingenic_pwm_driver);
+
+MODULE_DESCRIPTION("Ingenic SoC PWM driver");
+MODULE_ALIAS("platform:ingenic-pwm");
+MODULE_LICENSE("GPL");