diff options
-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; |