diff options
author | Alison Wang <b18965@freescale.com> | 2012-10-10 09:31:08 +0800 |
---|---|---|
committer | Andy Voltz <andy.voltz@timesys.com> | 2012-10-17 14:37:22 -0400 |
commit | d69fab521df6877782ed38b11677057a5400cbc4 (patch) | |
tree | 6a25b7355a7ad0f9f2f97492cdf51f35966205c4 /arch | |
parent | 4663b72fb93a520b6455579d76ed4c1dff46fb05 (diff) |
ENGR00180956-2: Add FlexTimer PWM support on Faraday
The FlexTimer work on PWM mode with EPWM and CPWM supported.
The API configures each FTM channels the same due to pwm subsystem
interface restriction.
Signed-off-by: Jingchang Lu <b35083@freescale.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-mvf/board-twr-vf700.c | 18 | ||||
-rwxr-xr-x | arch/arm/plat-mxc/Kconfig | 8 | ||||
-rwxr-xr-x | arch/arm/plat-mxc/devices/platform-mxc_pwm.c | 30 | ||||
-rw-r--r-- | arch/arm/plat-mxc/include/mach/iomux-mvf.h | 22 | ||||
-rwxr-xr-x | arch/arm/plat-mxc/pwm.c | 212 |
5 files changed, 277 insertions, 13 deletions
diff --git a/arch/arm/mach-mvf/board-twr-vf700.c b/arch/arm/mach-mvf/board-twr-vf700.c index f1848177196e..187293ec369a 100644 --- a/arch/arm/mach-mvf/board-twr-vf700.c +++ b/arch/arm/mach-mvf/board-twr-vf700.c @@ -46,6 +46,7 @@ #include <linux/ipu.h> #include <linux/mxcfb.h> #include <linux/pwm_backlight.h> +#include <linux/leds_pwm.h> #include <linux/fec.h> #include <linux/memblock.h> #include <linux/gpio.h> @@ -170,6 +171,21 @@ static iomux_v3_cfg_t mvf600_pads[] = { /*USB0/1 VBUS_EN*/ MVF600_PAD85_PTD6__USB0_VBUS_EN, MVF600_PAD92_PTD13__USB1_VBUS_EN, + + /* + * FlexTimer PWM channels + * FTM0 CH0~3 are connected to demo LED0~3 + * PAD30 mux with LCD enable signal + */ + MVF600_PAD22_PTB0_FTM0CH0, + MVF600_PAD23_PTB1_FTM0CH1, + MVF600_PAD24_PTB2_FTM0CH2, + MVF600_PAD25_PTB3_FTM0CH3, + + MVF600_PAD28_PTB6_FTM0CH6, + MVF600_PAD29_PTB7_FTM0CH7, + /*MVF600_PAD30_PTB8_FTM1CH0,*/ + MVF600_PAD31_PTB9_FTM1CH1, }; static struct mxc_audio_platform_data mvf_twr_audio_data; @@ -365,6 +381,8 @@ static void __init mvf_board_init(void) mvf_twr_init_usb(); mvf_add_nand(&mvf_data); + + mvf_add_mxc_pwm(0); } static void __init mvf_timer_init(void) diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig index fb9f6e962f45..9f40120e63e1 100755 --- a/arch/arm/plat-mxc/Kconfig +++ b/arch/arm/plat-mxc/Kconfig @@ -102,6 +102,14 @@ config MXC_PWM help Enable support for the i.MX PWM controller(s). +config MXC_PWM_CPWM + bool "Center-Aligned PWM mode" + depends on MXC_PWM && ARCH_MVF + help + Select Center-aligned PWM mode, otherwise the PWM will be + Edge-alinged(EPWM). + Say Y here if Center-aligned PWM signal required. + config MXC_DEBUG_BOARD bool "Enable MXC debug board(for 3-stack)" help diff --git a/arch/arm/plat-mxc/devices/platform-mxc_pwm.c b/arch/arm/plat-mxc/devices/platform-mxc_pwm.c index a8521b978676..c157cf9c782c 100755 --- a/arch/arm/plat-mxc/devices/platform-mxc_pwm.c +++ b/arch/arm/plat-mxc/devices/platform-mxc_pwm.c @@ -69,6 +69,36 @@ const struct imx_mxc_pwm_data imx6q_mxc_pwm_data[] __initconst = { }; #endif /* ifdef CONFIG_SOC_IMX6Q */ +#ifdef CONFIG_SOC_MVFA5 +const struct imx_mxc_pwm_data mvf_mxc_pwm_data[] __initdata = { + [0] = { + 1, + MVF_FTM0_BASE_ADDR, + SZ_4K, + MVF_INT_FLEXTIMER0, + }, + [1] = { + 2, + MVF_FTM1_BASE_ADDR, + SZ_4K, + MVF_INT_FLEXTIMER1, + }, + [2] = { + 3, + MVF_FTM2_BASE_ADDR, + SZ_4K, + MVF_INT_FLEXTIMER2, + }, + [3] = { + 4, + MVF_FTM3_BASE_ADDR, + SZ_4K, + MVF_INT_FLEXTIMER3, + }, +}; + +#endif + struct platform_device *__init imx_add_mxc_pwm( const struct imx_mxc_pwm_data *data) { diff --git a/arch/arm/plat-mxc/include/mach/iomux-mvf.h b/arch/arm/plat-mxc/include/mach/iomux-mvf.h index faf455e74952..775b0eb5d484 100644 --- a/arch/arm/plat-mxc/include/mach/iomux-mvf.h +++ b/arch/arm/plat-mxc/include/mach/iomux-mvf.h @@ -70,6 +70,10 @@ typedef enum iomux_config { #define MVF600_GPIO_GENERAL_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | PAD_CTL_SPEED_MED | PAD_CTL_PUS_47K_UP | \ PAD_CTL_DSE_25ohm) +#define MVF600_FTM0_CH_CTRL (PAD_CTL_SPEED_LOW | PAD_CTL_OBE_ENABLE | \ + PAD_CTL_ODE | PAD_CTL_DSE_25ohm) +#define MVF600_FTM1_CH_CTRL (PAD_CTL_SPEED_LOW | PAD_CTL_OBE_ENABLE | \ + PAD_CTL_DSE_25ohm) /*SDHC1*/ #define MVF600_PAD14_PTA24__SDHC1_CLK \ IOMUX_PAD(0x0038, 0x0038, 5, 0x0000, 0, MVF600_SDHC_PAD_CTRL) @@ -282,4 +286,22 @@ typedef enum iomux_config { IOMUX_PAD(0x0084, 0x0084, 1, 0x0000, 0, \ MVF600_UART_PAD_CTRL | PAD_CTL_IBE_ENABLE) +/* FlexTimer channel pin */ +#define MVF600_PAD22_PTB0_FTM0CH0 \ + IOMUX_PAD(0x0058, 0x0058, 1, 0x0000, 0, MVF600_FTM0_CH_CTRL) +#define MVF600_PAD23_PTB1_FTM0CH1 \ + IOMUX_PAD(0x005c, 0x005c, 1, 0x0000, 0, MVF600_FTM0_CH_CTRL) +#define MVF600_PAD24_PTB2_FTM0CH2 \ + IOMUX_PAD(0x0060, 0x0060, 1, 0x0000, 0, MVF600_FTM0_CH_CTRL) +#define MVF600_PAD25_PTB3_FTM0CH3 \ + IOMUX_PAD(0x0064, 0x0064, 1, 0x0000, 0, MVF600_FTM0_CH_CTRL) +#define MVF600_PAD28_PTB6_FTM0CH6 \ + IOMUX_PAD(0x0070, 0x0070, 1, 0x0000, 0, MVF600_FTM0_CH_CTRL) +#define MVF600_PAD29_PTB7_FTM0CH7 \ + IOMUX_PAD(0x0074, 0x0074, 1, 0x0000, 0, MVF600_FTM0_CH_CTRL) +/* PAD30 mux with LCD enable signal */ +#define MVF600_PAD30_PTB8_FTM1CH0 \ + IOMUX_PAD(0x0078, 0x0078, 1, 0x032C, 0, MVF600_FTM1_CH_CTRL) +#define MVF600_PAD31_PTB9_FTM1CH1 \ + IOMUX_PAD(0x007C, 0x007C, 1, 0x0330, 0, MVF600_FTM1_CH_CTRL) #endif diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c index 4a67f4de0e4d..048efb788c3d 100755 --- a/arch/arm/plat-mxc/pwm.c +++ b/arch/arm/plat-mxc/pwm.c @@ -6,7 +6,7 @@ * published by the Free Software Foundation. * * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com> - * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2009-2012 Freescale Semiconductor, Inc. */ #include <linux/module.h> @@ -45,6 +45,84 @@ #define MX3_PWMCR_CLKSRC_IPG (1 << 16) #define MX3_PWMCR_CLKSRC_IPG_32k (3 << 16) +/* + * Vybrid(CONFIG_ARCH_MVF) FlexTimer PWM registers defination + */ +#define MVF_PWM_FTM_SC 0x00 /* status and controls */ +#define MVF_PWM_FTM_CNT 0x04 /* counter */ +#define MVF_PWM_FTM_MOD 0x08 /* modulo */ + +#define MVF_PWM_FTM_C0SC 0x0C /* channel(0) status and control */ +#define MVF_PWM_FTM_C0V 0x10 /* channel(0) value */ +#define MVF_PWM_FTM_C1SC 0x14 /* channel(1) */ +#define MVF_PWM_FTM_C1V 0x18 /* channel(1) */ +#define MVF_PWM_FTM_C2SC 0x1C /* channel(2) */ +#define MVF_PWM_FTM_C2V 0x20 /* channel(2) */ +#define MVF_PWM_FTM_C3SC 0x24 /* channel(3) */ +#define MVF_PWM_FTM_C3V 0x28 /* channel(3) */ +#define MVF_PWM_FTM_C4SC 0x2C /* channel(4) */ +#define MVF_PWM_FTM_C4V 0x30 /* channel(4) */ +#define MVF_PWM_FTM_C5SC 0x34 /* channel(5) */ +#define MVF_PWM_FTM_C5V 0x38 /* channel(5) */ +#define MVF_PWM_FTM_C6SC 0x3C /* channel(6) */ +#define MVF_PWM_FTM_C6V 0x40 /* channel(6) */ +#define MVF_PWM_FTM_C7SC 0x44 /* channel(7) */ +#define MVF_PWM_FTM_C7V 0x48 /* channel(7) */ + +#define MVF_PWM_FTM_CNTIN 0x4C /* counter initial value */ +#define MVF_PWM_FTM_STATUS 0x50 /* capture and compare status */ +#define MVF_PWM_FTM_MODE 0x54 /* mode select */ +#define MVF_PWM_FTM_SYNC 0x58 /* synchronization */ +#define MVF_PWM_FTM_OUTINIT 0x5C /* initial state for channels output */ +#define MVF_PWM_FTM_OUTMASK 0x60 /* output mask */ +#define MVF_PWM_FTM_COMBINE 0x64 /* function for linked channels */ +#define MVF_PWM_FTM_DEADTIME 0x68 /* deadtime insertion control */ +#define MVF_PWM_FTM_EXTTRIG 0x6C /* external trigger */ +#define MVF_PWM_FTM_POL 0x70 /* channels polarity */ +#define MVF_PWM_FTM_FMS 0x74 /* fault mode status */ +#define MVF_PWM_FTM_FILTER 0x78 /* input capture filter control */ +#define MVF_PWM_FTM_FLTCTRL 0x7C /* fault control */ +#define MVF_PWM_FTM_QDCTRL 0x80 /* quadrature decoder ctrl and status */ +#define MVF_PWM_FTM_CONF 0x84 /* configuration */ +#define MVF_PWM_FTM_FLTPOL 0x88 /* fault input polarity */ +#define MVF_PWM_FTM_SYNCONF 0x8C /* synchronization configuration */ +#define MVF_PWM_FTM_INVCTRL 0x90 /* inverting control */ +#define MVF_PWM_FTM_SWOCTRL 0x94 /* software output control */ +#define MVF_PWM_FTM_PWMLOAD 0x98 /* PWM load */ + +#define PWM_TYPE_EPWM 0x01 /* Edge-aligned pwm */ +#define PWM_TYPE_CPWM 0x02 /* Center-aligned pwm */ + +#define PWM_FTMSC_CPWMS (0x01 << 5) +#define PWM_FTMSC_CLK_MASK 0x3 +#define PWM_FTMSC_CLK_OFFSET 3 +#define PWM_FTMSC_CLKSYS (0x1 << 3) +#define PWM_FTMSC_CLKFIX (0x2 << 3) +#define PWM_FTMSC_CLKEXT (0x3 << 3) +#define PWM_FTMSC_PS_MASK 0x7 +#define PWM_FTMSC_PS_OFFSET 0 +#define PWM_FTMSC_PS1 0x0 +#define PWM_FTMSC_PS2 0x1 +#define PWM_FTMSC_PS4 0x2 +#define PWM_FTMSC_PS8 0x3 +#define PWM_FTMSC_PS16 0x4 +#define PWM_FTMSC_PS32 0x5 +#define PWM_FTMSC_PS64 0x6 +#define PWM_FTMSC_PS128 0x7 + +#define PWM_FTMCnSC_MSB (0x1 << 5) +#define PWM_FTMCnSC_MSA (0x1 << 4) +#define PWM_FTMCnSC_ELSB (0x1 << 3) +#define PWM_FTMCnSC_ELSA (0x1 << 2) + +#define FTM_PWMMODE (PWM_FTMCnSC_MSB) +#define FTM_PWM_HIGH_TRUE (PWM_FTMCnSC_ELSB) +#define FTM_PWM_LOW_TRUE (PWM_FTMCnSC_ELSA) + +#define PWM_FTMMODE_FTMEN 0x01 +#define PWM_FTMMODE_INIT 0x02 +#define PWM_FTMMODE_PWMSYNC (0x01 << 3) + struct pwm_device { struct list_head node; struct platform_device *pdev; @@ -58,6 +136,8 @@ struct pwm_device { unsigned int use_count; unsigned int pwm_id; int pwmo_invert; + unsigned int cpwm; /* CPWM mode */ + unsigned int clk_ps; /* clock prescaler:1/2/4/8/16/32/64/128 */ void (*enable_pwm_pad)(void); void (*disable_pwm_pad)(void); }; @@ -67,7 +147,7 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) if (pwm == NULL || period_ns == 0 || duty_ns > period_ns) return -EINVAL; - if (!(cpu_is_mx1() || cpu_is_mx21())) { + if (!(cpu_is_mx1() || cpu_is_mx21() || cpu_is_mvf())) { unsigned long long c; unsigned long period_cycles, duty_cycles, prescale; u32 cr; @@ -127,6 +207,91 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) duty_ns = period_ns - duty_ns; p = max * duty_ns / period_ns; writel(max - p, pwm->mmio_base + MX1_PWMS); + } else if (cpu_is_mvf()) { + unsigned long long c; + unsigned long period_cycles, duty_cycles; + /* FTM clock source prescaler */ + u32 ps = (0x1 << pwm->clk_ps) * 1000; + /* IPS bus clock source */ + c = clk_get_rate(pwm->clk) / 1000000UL; + + c = c * period_ns; + do_div(c, ps); + period_cycles = (unsigned long)c; + + c = clk_get_rate(pwm->clk) / 1000000UL; + c = c * duty_ns; + do_div(c, ps); + duty_cycles = (unsigned long)c; + + if (period_cycles > 0xFFFF) { + dev_warn(&pwm->pdev->dev, + "required PWM period cycles(%lu) overflow" + "16-bits counter!\n", period_cycles); + period_cycles = 0xFFFF; + } + if (duty_cycles >= 0xFFFF) { + dev_warn(&pwm->pdev->dev, + "required PWM duty cycles(%lu) overflow" + "16-bits counter!\n", duty_cycles); + duty_cycles = 0xFFFF - 1; + } + if (duty_cycles >= period_cycles) + duty_cycles = (period_cycles/10) * 9; + + /* configure channel to pwm mode */ + /* enable FTMEN */ + if (pwm->cpwm) { + u32 reg; + reg = __raw_readl(pwm->mmio_base + MVF_PWM_FTM_SC); + reg |= 0x01 << 5; + __raw_writel(reg, pwm->mmio_base + MVF_PWM_FTM_SC); + } + __raw_writel(FTM_PWMMODE | FTM_PWM_HIGH_TRUE, + pwm->mmio_base + MVF_PWM_FTM_C0SC); + __raw_writel(FTM_PWMMODE | FTM_PWM_HIGH_TRUE, + pwm->mmio_base + MVF_PWM_FTM_C1SC); + __raw_writel(FTM_PWMMODE | FTM_PWM_HIGH_TRUE, + pwm->mmio_base + MVF_PWM_FTM_C2SC); + __raw_writel(FTM_PWMMODE | FTM_PWM_HIGH_TRUE, + pwm->mmio_base + MVF_PWM_FTM_C3SC); + + __raw_writel(0xF0, pwm->mmio_base + MVF_PWM_FTM_OUTMASK); + __raw_writel(0x0F, pwm->mmio_base + MVF_PWM_FTM_OUTINIT); + __raw_writel(0x0, pwm->mmio_base + MVF_PWM_FTM_CNTIN); + if (pwm->cpwm) { + /* + * Center-aligned PWM: + * period = 2*(MOD - CNTIN) + * duty = 2*(CnV - CNTIN) + */ + __raw_writel(period_cycles / 2, + pwm->mmio_base + MVF_PWM_FTM_MOD); + __raw_writel(duty_cycles / 2, + pwm->mmio_base + MVF_PWM_FTM_C0V); + __raw_writel(duty_cycles / 2, + pwm->mmio_base + MVF_PWM_FTM_C1V); + __raw_writel(duty_cycles / 2, + pwm->mmio_base + MVF_PWM_FTM_C2V); + __raw_writel(duty_cycles / 2, + pwm->mmio_base + MVF_PWM_FTM_C3V); + + } else { + /* Edge-aligend PWM + * period = MOD - CNTIN + 1 + * duty = CnV - CNTIN + */ + __raw_writel(period_cycles - 1, + pwm->mmio_base + MVF_PWM_FTM_MOD); + __raw_writel(duty_cycles, + pwm->mmio_base + MVF_PWM_FTM_C0V); + __raw_writel(duty_cycles, + pwm->mmio_base + MVF_PWM_FTM_C1V); + __raw_writel(duty_cycles, + pwm->mmio_base + MVF_PWM_FTM_C2V); + __raw_writel(duty_cycles, + pwm->mmio_base + MVF_PWM_FTM_C3V); + } } else { BUG(); } @@ -145,25 +310,41 @@ int pwm_enable(struct pwm_device *pwm) if (!rc) pwm->clk_enabled = 1; } + if (cpu_is_mvf()) { + reg = __raw_readl(pwm->mmio_base + MVF_PWM_FTM_SC); + reg &= ~((PWM_FTMSC_CLK_MASK << PWM_FTMSC_CLK_OFFSET) | + (PWM_FTMSC_PS_MASK << PWM_FTMSC_PS_OFFSET)); + /* select IPS bus clock source + * prescale 128 + */ + reg |= (PWM_FTMSC_CLKSYS | pwm->clk_ps); + __raw_writel(reg, pwm->mmio_base + MVF_PWM_FTM_SC); + } else { + reg = readl(pwm->mmio_base + MX3_PWMCR); + reg |= MX3_PWMCR_EN; + writel(reg, pwm->mmio_base + MX3_PWMCR); - reg = readl(pwm->mmio_base + MX3_PWMCR); - reg |= MX3_PWMCR_EN; - writel(reg, pwm->mmio_base + MX3_PWMCR); - - if (pwm->enable_pwm_pad) - pwm->enable_pwm_pad(); - + if (pwm->enable_pwm_pad) + pwm->enable_pwm_pad(); + } return rc; } EXPORT_SYMBOL(pwm_enable); void pwm_disable(struct pwm_device *pwm) { - if (pwm->disable_pwm_pad) - pwm->disable_pwm_pad(); - - writel(0, pwm->mmio_base + MX3_PWMCR); + u32 reg; + if (cpu_is_mvf()) { + __raw_writel(0xFF, pwm->mmio_base + MVF_PWM_FTM_OUTMASK); + reg = __raw_readl(pwm->mmio_base + MVF_PWM_FTM_SC); + reg &= ~(PWM_FTMSC_CLK_MASK << PWM_FTMSC_CLK_OFFSET); + __raw_writel(reg, pwm->mmio_base + MVF_PWM_FTM_SC); + } else { + if (pwm->disable_pwm_pad) + pwm->disable_pwm_pad(); + writel(0, pwm->mmio_base + MX3_PWMCR); + } if (pwm->clk_enabled) { clk_disable(pwm->clk); pwm->clk_enabled = 0; @@ -241,6 +422,11 @@ static int __devinit mxc_pwm_probe(struct platform_device *pdev) pwm->use_count = 0; pwm->pwm_id = pdev->id; pwm->pdev = pdev; + /* default select IPS bus clock, and divided by 128 for MVF platform */ + pwm->clk_ps = PWM_FTMSC_PS128; +#ifdef CONFIG_MXC_PWM_CPWM + pwm->cpwm = 1; +#endif if (plat_data != NULL) { pwm->pwmo_invert = plat_data->pwmo_invert; pwm->enable_pwm_pad = plat_data->enable_pwm_pad; |