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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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");