summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-mvf/board-twr-vf700.c18
-rwxr-xr-xarch/arm/plat-mxc/Kconfig8
-rwxr-xr-xarch/arm/plat-mxc/devices/platform-mxc_pwm.c30
-rw-r--r--arch/arm/plat-mxc/include/mach/iomux-mvf.h22
-rwxr-xr-xarch/arm/plat-mxc/pwm.c212
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;