From 7524aaeff2fef8a41ee3d4c91fde72b013d8bab5 Mon Sep 17 00:00:00 2001 From: Eric Lee Date: Tue, 16 Jun 2015 01:02:24 +0800 Subject: [PATCH] Add U-Boot LVDS Backlight PWM Support --- README | 4 ++ arch/arm/include/asm/arch-mx6/imx-regs.h | 17 +++++ board/embedian/smarcfimx6/smarcfimx6.c | 25 +++++-- drivers/Makefile | 1 + drivers/pwm/Makefile | 13 ++++ drivers/pwm/pwm-imx-util.c | 73 +++++++++++++++++++++ drivers/pwm/pwm-imx-util.h | 16 +++++ drivers/pwm/pwm-imx.c | 109 +++++++++++++++++++++++++++++++ include/configs/smarcfimx6.h | 4 ++ 9 files changed, 255 insertions(+), 7 deletions(-) create mode 100644 drivers/pwm/Makefile create mode 100644 drivers/pwm/pwm-imx-util.c create mode 100644 drivers/pwm/pwm-imx-util.h create mode 100644 drivers/pwm/pwm-imx.c diff --git a/README b/README index 25b22a3..857b5b7 100644 --- a/README +++ b/README @@ -1314,6 +1314,10 @@ The following options need to be configured: CONFIG_SH_ETHER_CACHE_WRITEBACK If this option is set, the driver enables cache flush. +- PWM Support: + CONFIG_PWM_IMX + Support for PWM modul on the imx6. + - TPM Support: CONFIG_TPM Support TPM devices. diff --git a/arch/arm/include/asm/arch-mx6/imx-regs.h b/arch/arm/include/asm/arch-mx6/imx-regs.h index fd85803..57e10af 100644 --- a/arch/arm/include/asm/arch-mx6/imx-regs.h +++ b/arch/arm/include/asm/arch-mx6/imx-regs.h @@ -679,6 +679,23 @@ struct wdog_regs { u16 wmcr; /* Miscellaneous Control */ }; +#define PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4) +#define PWMCR_DOZEEN (1 << 24) +#define PWMCR_WAITEN (1 << 23) +#define PWMCR_DBGEN (1 << 22) +#define PWMCR_CLKSRC_IPG_HIGH (2 << 16) +#define PWMCR_CLKSRC_IPG (1 << 16) +#define PWMCR_EN (1 << 0) + +struct pwm_regs { + u32 cr; + u32 sr; + u32 ir; + u32 sar; + u32 pr; + u32 cnr; +}; + struct dbg_monitor_regs { u32 ctrl[4]; /* Control */ u32 master_en[4]; /* Master enable */ diff --git a/board/embedian/smarcfimx6/smarcfimx6.c b/board/embedian/smarcfimx6/smarcfimx6.c index 1f4b626..0a158af 100644 --- a/board/embedian/smarcfimx6/smarcfimx6.c +++ b/board/embedian/smarcfimx6/smarcfimx6.c @@ -32,6 +32,7 @@ #include #include #include +#include #ifdef CONFIG_SYS_I2C_MXC #include #include @@ -835,11 +836,9 @@ static iomux_v3_cfg_t const backlight_pads[] = { MX6_PAD_GPIO_0__GPIO1_IO00 | MUX_PAD_CTRL(NO_PAD_CTRL), #define BACKLIGHT_EN IMX_GPIO_NR(1, 00) /* PWM Backlight Control: S141 */ - MX6_PAD_GPIO_1__GPIO1_IO01 | MUX_PAD_CTRL(NO_PAD_CTRL), -#define BACKLIGHT_PWM IMX_GPIO_NR(1, 01) + /* Backlight Enable for LVDS: S127 */ - /*MX6_PAD_GPIO_0__GPIO1_IO00 | MUX_PAD_CTRL(NO_PAD_CTRL), -#define LVDS_BACKLIGHT_EN IMX_GPIO_NR(1, 00)*/ + MX6_PAD_GPIO_1__PWM2_OUT | MUX_PAD_CTRL(NO_PAD_CTRL), /* LCD VDD Enable(for parallel LCD): S133 */ MX6_PAD_GPIO_2__GPIO1_IO02 | MUX_PAD_CTRL(NO_PAD_CTRL), #define LCD_VDD_EN IMX_GPIO_NR(1, 02) @@ -1075,13 +1074,25 @@ static void setup_display(void) writel(reg, &iomux->gpr[3]); /* backlights off until needed */ - /*imx_iomux_v3_setup_multiple_pads(backlight_pads, + imx_iomux_v3_setup_multiple_pads(backlight_pads, ARRAY_SIZE(backlight_pads)); - gpio_direction_input(BACKLIGHT_EN);*/ + /*gpio_direction_input(BACKLIGHT_EN);*/ /* turn on backlight */ gpio_direction_output(BACKLIGHT_EN, 1); - gpio_direction_output(BACKLIGHT_PWM, 1); gpio_direction_output(LCD_VDD_EN, 1); + /* enable backlight PWM 2 */ + if (pwm_init(1, 0, 0)) + goto error; + /* duty cycle 500ns, period: 3000ns */ + if (pwm_config(1, 1000, 3000)) + goto error; + if (pwm_enable(1)) + goto error; + return; + +error: + puts("error init pwm for backlight\n"); + return; } #endif /* CONFIG_VIDEO_IPUV3 */ diff --git a/drivers/Makefile b/drivers/Makefile index e103896..99d8b81 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -15,3 +15,4 @@ obj-y += video/ obj-y += watchdog/ obj-y += fastboot/ obj-$(CONFIG_QE) += qe/ +obj-y += pwm/ diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile new file mode 100644 index 0000000..dd3a09a --- /dev/null +++ b/drivers/pwm/Makefile @@ -0,0 +1,13 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd at denx.de. +# +# (C) Copyright 2001 +# Erik Theisen, Wave 7 Optics, etheisen at mindspring.com. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +#ccflags-y += -DDEBUG + +obj-$(CONFIG_PWM_IMX) += pwm-imx.o diff --git a/drivers/pwm/pwm-imx-util.c b/drivers/pwm/pwm-imx-util.c new file mode 100644 index 0000000..f1d0b35 --- /dev/null +++ b/drivers/pwm/pwm-imx-util.c @@ -0,0 +1,73 @@ +/* + * (C) Copyright 2014 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Basic support for the pwm modul on imx6. + * + * Based on linux:drivers/pwm/pwm-imx.c + * from + * Sascha Hauer + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include + +/* pwm_id from 0..3 */ +struct pwm_regs *pwm_id_to_reg(int pwm_id) +{ + switch (pwm_id) { + case 0: + return (struct pwm_regs *)PWM1_BASE_ADDR; + break; + case 1: + return (struct pwm_regs *)PWM2_BASE_ADDR; + break; + case 2: + return (struct pwm_regs *)PWM3_BASE_ADDR; + break; + case 3: + return (struct pwm_regs *)PWM4_BASE_ADDR; + break; + default: + printf("unknown pwm_id: %d\n", pwm_id); + break; + } + return NULL; +} + +int pwm_imx_get_parms(int period_ns, int duty_ns, unsigned long *period_c, + unsigned long *duty_c, unsigned long *prescale) +{ + unsigned long long c; + + /* + * we have not yet a clock framework for imx6, so add the clock + * value here as a define. Replace it when we have the clock + * framework. + */ + c = CONFIG_IMX6_PWM_PER_CLK; + c = c * period_ns; + do_div(c, 1000000000); + *period_c = c; + + *prescale = *period_c / 0x10000 + 1; + + *period_c /= *prescale; + c = (unsigned long long)(*period_c * duty_ns); + do_div(c, period_ns); + *duty_c = c; + + /* + * according to imx pwm RM, the real period value should be + * PERIOD value in PWMPR plus 2. + */ + if (*period_c > 2) + *period_c -= 2; + else + *period_c = 0; + + return 0; +} diff --git a/drivers/pwm/pwm-imx-util.h b/drivers/pwm/pwm-imx-util.h new file mode 100644 index 0000000..45465c4 --- /dev/null +++ b/drivers/pwm/pwm-imx-util.h @@ -0,0 +1,16 @@ +/* + * (C) Copyright 2014 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Basic support for the pwm modul on imx6. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _pwm_imx_util_h_ +#define _pwm_imx_util_h_ + +struct pwm_regs *pwm_id_to_reg(int pwm_id); +int pwm_imx_get_parms(int period_ns, int duty_ns, unsigned long *period_c, + unsigned long *duty_c, unsigned long *prescale); +#endif diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c new file mode 100644 index 0000000..cb8f89f --- /dev/null +++ b/drivers/pwm/pwm-imx.c @@ -0,0 +1,109 @@ +/* + * (C) Copyright 2014 + * Heiko Schocher, DENX Software Engineering, hs at denx.de. + * + * Basic support for the pwm modul on imx6. + * + * Based on linux:drivers/pwm/pwm-imx.c + * from + * Sascha Hauer + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +/* pwm_id from 0..3 */ +static struct pwm_regs *pwm_id_to_reg(int pwm_id) +{ + switch (pwm_id) { + case 0: + return (struct pwm_regs *)PWM1_BASE_ADDR; + break; + case 1: + return (struct pwm_regs *)PWM2_BASE_ADDR; + break; + case 2: + return (struct pwm_regs *)PWM3_BASE_ADDR; + break; + case 3: + return (struct pwm_regs *)PWM4_BASE_ADDR; + break; + default: + printf("unknown pwm_id: %d\n", pwm_id); + break; + } + return NULL; +} + +int pwm_init(int pwm_id, int div, int invert) +{ + struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id); + + writel(0, &pwm->ir); + return 0; +} + +int pwm_config(int pwm_id, int duty_ns, int period_ns) +{ + struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id); + unsigned long long c; + unsigned long period_cycles, duty_cycles, prescale; + u32 cr; + + /* + * we have not yet a clock framework for imx6, so add the clock + * value here as a define. Replace it when we have the clock + * framework. + */ + c = CONFIG_IMX6_PWM_PER_CLK; + c = c * period_ns; + do_div(c, 1000000000); + period_cycles = c; + + prescale = period_cycles / 0x10000 + 1; + + period_cycles /= prescale; + c = (unsigned long long)period_cycles * duty_ns; + do_div(c, period_ns); + duty_cycles = c; + + /* + * according to imx pwm RM, the real period value should be + * PERIOD value in PWMPR plus 2. + */ + if (period_cycles > 2) + period_cycles -= 2; + else + period_cycles = 0; + + cr = PWMCR_PRESCALER(prescale) | + PWMCR_DOZEEN | PWMCR_WAITEN | + PWMCR_DBGEN | PWMCR_CLKSRC_IPG_HIGH; + + writel(cr, &pwm->cr); + /* set duty cycles */ + writel(duty_cycles, &pwm->sar); + /* set period cycles */ + writel(period_cycles, &pwm->pr); + return 0; +} + +int pwm_enable(int pwm_id) +{ + struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id); + + setbits_le32(&pwm->cr, PWMCR_EN); + return 0; +} + +void pwm_disable(int pwm_id) +{ + struct pwm_regs *pwm = (struct pwm_regs *)pwm_id_to_reg(pwm_id); + + clrbits_le32(&pwm->cr, PWMCR_EN); +} diff --git a/include/configs/smarcfimx6.h b/include/configs/smarcfimx6.h index 2774ca2..6940377 100644 --- a/include/configs/smarcfimx6.h +++ b/include/configs/smarcfimx6.h @@ -40,6 +40,10 @@ #include "smarcfimx6_common.h" +/* PWM Configs */ +#define CONFIG_PWM_IMX +#define CONFIG_IMX6_PWM_PER_CLK 66000000 + /* USB Configs */ #define CONFIG_CMD_USB #define CONFIG_USB_EHCI -- 1.9.1