From 4cfc6c4fa7611e79e6434e97db56b6072ad4d05c Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 23 Oct 2012 06:34:50 +0000 Subject: pmic_fsl: Introduce FSL_PMIC_I2C_LENGTH Introduce FSL_PMIC_I2C_LENGTH to configure the number of bytes that are used to communicate with the PMIC via I2C. Instead of hardcoding the value, pass the number via a configurable option per PMIC type. This will be useful for adding support for PMIC MC34704 from Freescale, which uses only one byte in its I2C protocol. Signed-off-by: Fabio Estevam --- drivers/misc/pmic_fsl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/pmic_fsl.c b/drivers/misc/pmic_fsl.c index 0ff75ed..9d80b55 100644 --- a/drivers/misc/pmic_fsl.c +++ b/drivers/misc/pmic_fsl.c @@ -26,6 +26,10 @@ #include #include +#if defined(CONFIG_PMIC_FSL_MC13892) +#define FSL_PMIC_I2C_LENGTH 3 +#endif + #if defined(CONFIG_PMIC_SPI) static u32 pmic_spi_prepare_tx(u32 reg, u32 *val, u32 write) { @@ -53,7 +57,7 @@ int pmic_init(void) #elif defined(CONFIG_PMIC_I2C) p->interface = PMIC_I2C; p->hw.i2c.addr = CONFIG_SYS_FSL_PMIC_I2C_ADDR; - p->hw.i2c.tx_num = 3; + p->hw.i2c.tx_num = FSL_PMIC_I2C_LENGTH; p->bus = I2C_PMIC; #else #error "You must select CONFIG_PMIC_SPI or CONFIG_PMIC_I2C" -- cgit v1.1 From 787f4f30823a15b1e45c0a0b901cfa5bd415e251 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 23 Oct 2012 06:36:18 +0000 Subject: pmic: Add support for mc34704 Add the register layout for the MC34704 PMIC from Freescale. Signed-off-by: Fabio Estevam --- drivers/misc/pmic_fsl.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/pmic_fsl.c b/drivers/misc/pmic_fsl.c index 9d80b55..c8d4c8d 100644 --- a/drivers/misc/pmic_fsl.c +++ b/drivers/misc/pmic_fsl.c @@ -28,6 +28,8 @@ #if defined(CONFIG_PMIC_FSL_MC13892) #define FSL_PMIC_I2C_LENGTH 3 +#elif defined(CONFIG_PMIC_FSL_MC34704) +#define FSL_PMIC_I2C_LENGTH 1 #endif #if defined(CONFIG_PMIC_SPI) -- cgit v1.1 From 5ea6d7c8fc05ad73d930a6fdb430188ff17a70e5 Mon Sep 17 00:00:00 2001 From: Troy Kisky Date: Tue, 23 Oct 2012 10:57:47 +0000 Subject: mx6: use CONFIG_MX6 instead of CONFIG_MX6Q Use CONFIG_MX6 when the particular processor variant isn't important. Reserve the use of CONFIG_MX6Q to specifically test for quad cores variant. Signed-off-by: Troy Kisky --- drivers/gpio/mxc_gpio.c | 6 +++--- drivers/video/ipu_regs.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/mxc_gpio.c b/drivers/gpio/mxc_gpio.c index 2c79bff..a388064 100644 --- a/drivers/gpio/mxc_gpio.c +++ b/drivers/gpio/mxc_gpio.c @@ -42,14 +42,14 @@ static unsigned long gpio_ports[] = { [1] = GPIO2_BASE_ADDR, [2] = GPIO3_BASE_ADDR, #if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \ - defined(CONFIG_MX53) || defined(CONFIG_MX6Q) + defined(CONFIG_MX53) || defined(CONFIG_MX6) [3] = GPIO4_BASE_ADDR, #endif -#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6Q) +#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) [4] = GPIO5_BASE_ADDR, [5] = GPIO6_BASE_ADDR, #endif -#if defined(CONFIG_MX53) || defined(CONFIG_MX6Q) +#if defined(CONFIG_MX53) || defined(CONFIG_MX6) [6] = GPIO7_BASE_ADDR, #endif }; diff --git a/drivers/video/ipu_regs.h b/drivers/video/ipu_regs.h index a43aa03..982e252 100644 --- a/drivers/video/ipu_regs.h +++ b/drivers/video/ipu_regs.h @@ -55,7 +55,7 @@ #define IPU_TPM_REG_BASE 0x01060000 #define IPU_DC_TMPL_REG_BASE 0x01080000 #define IPU_ISP_TBPR_REG_BASE 0x010C0000 -#elif defined(CONFIG_MX6Q) +#elif defined(CONFIG_MX6) #define IPU_CPMEM_REG_BASE 0x00100000 #define IPU_LUT_REG_BASE 0x00120000 #define IPU_SRM_REG_BASE 0x00140000 -- cgit v1.1 From 822593f02890f10e08c39ed82f926c9a80f7d533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Majewski?= Date: Tue, 4 Sep 2012 21:47:46 +0000 Subject: gpio:fix: Proper handling of GPIO subsystem parts at Samsung devices Now proper GPIO parts numbering is handled at Samsung devices. This fix is necessary for code using GPIO located at other banks than first. Test HW: - Exynos4210 - Trats - S5PC110 - goni Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park Signed-off-by: Minkyu Kang --- drivers/gpio/s5p_gpio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/s5p_gpio.c b/drivers/gpio/s5p_gpio.c index 47f3213..656bf4a 100644 --- a/drivers/gpio/s5p_gpio.c +++ b/drivers/gpio/s5p_gpio.c @@ -144,9 +144,11 @@ void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode) struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio) { - int bank = gpio / GPIO_PER_BANK; - bank *= sizeof(struct s5p_gpio_bank); + int bank; + unsigned g = gpio - s5p_gpio_part_max(gpio); + bank = g / GPIO_PER_BANK; + bank *= sizeof(struct s5p_gpio_bank); return (struct s5p_gpio_bank *) (s5p_gpio_base(gpio) + bank); } -- cgit v1.1 From 2b5fdd07c51cc4f1c99f70d4753bffa45070bf5a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 21 Jul 2012 05:02:24 +0000 Subject: dm: wdt: Move s5p watchdog timer to drivers/watchdog/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Vasut Cc: David Müller Cc: Wolfgang Denk Cc: Albert Aribaud Cc: U-Boot DM Signed-off-by: Minkyu Kang --- drivers/watchdog/Makefile | 1 + drivers/watchdog/s5p_wdt.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 drivers/watchdog/s5p_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 923acb9..bc09123 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -28,6 +28,7 @@ LIB := $(obj)libwatchdog.o COBJS-$(CONFIG_AT91SAM9_WATCHDOG) += at91sam9_wdt.o COBJS-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o COBJS-$(CONFIG_TNETV107X_WATCHDOG) += tnetv107x_wdt.o +COBJS-$(CONFIG_S5P) += s5p_wdt.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/watchdog/s5p_wdt.c b/drivers/watchdog/s5p_wdt.c new file mode 100644 index 0000000..94acc1e --- /dev/null +++ b/drivers/watchdog/s5p_wdt.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Minkyu Kang + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +#define PRESCALER_VAL 255 + +void wdt_stop(void) +{ + struct s5p_watchdog *wdt = + (struct s5p_watchdog *)samsung_get_base_watchdog(); + unsigned int wtcon; + + wtcon = readl(&wdt->wtcon); + wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET); + + writel(wtcon, &wdt->wtcon); +} + +void wdt_start(unsigned int timeout) +{ + struct s5p_watchdog *wdt = + (struct s5p_watchdog *)samsung_get_base_watchdog(); + unsigned int wtcon; + + wdt_stop(); + + wtcon = readl(&wdt->wtcon); + wtcon |= (WTCON_EN | WTCON_CLK(WTCON_CLK_128)); + wtcon &= ~WTCON_INT; + wtcon |= WTCON_RESET; + wtcon |= WTCON_PRESCALER(PRESCALER_VAL); + + writel(timeout, &wdt->wtdat); + writel(timeout, &wdt->wtcnt); + writel(wtcon, &wdt->wtcon); +} -- cgit v1.1 From 588945240364aafea27c6e618ae126a6d461dc20 Mon Sep 17 00:00:00 2001 From: Rajeshwari Shinde Date: Fri, 24 Aug 2012 00:39:23 +0000 Subject: PMIC: MAX77686: Add support for MAX77686 This patch adds driver and register definitions for PMIC chip MAX77686. Signed-off-by: Rajeshwari Shinde Acked-by: Lukasz Majewski Acked-by: Simon Glass Signed-off-by: Minkyu Kang --- drivers/misc/Makefile | 1 + drivers/misc/pmic_max77686.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 drivers/misc/pmic_max77686.c (limited to 'drivers') diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 271463c..9fac190 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -39,6 +39,7 @@ COBJS-$(CONFIG_DIALOG_PMIC) += pmic_dialog.o COBJS-$(CONFIG_PMIC_FSL) += pmic_fsl.o COBJS-$(CONFIG_PMIC_I2C) += pmic_i2c.o COBJS-$(CONFIG_PMIC_SPI) += pmic_spi.o +COBJS-$(CONFIG_PMIC_MAX77686) += pmic_max77686.o COBJS-$(CONFIG_PMIC_MAX8998) += pmic_max8998.o COBJS-$(CONFIG_PMIC_MAX8997) += pmic_max8997.o diff --git a/drivers/misc/pmic_max77686.c b/drivers/misc/pmic_max77686.c new file mode 100644 index 0000000..36f7f4d --- /dev/null +++ b/drivers/misc/pmic_max77686.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Rajeshwari Shinde + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +int pmic_init(void) +{ + struct pmic *p = get_pmic(); + static const char name[] = "MAX77686_PMIC"; + + puts("Board PMIC init\n"); + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX77686_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = I2C_PMIC; + + return 0; +} -- cgit v1.1 From a6f173073b04f193e83bccf39569e56233c9edf0 Mon Sep 17 00:00:00 2001 From: Minkyu Kang Date: Thu, 25 Oct 2012 22:15:33 +0000 Subject: s3c64xx: fix the compiler error and warning This patch is fixing the following errors s3c64xx.c:175: error: variable 's3c64xx_serial_drv' has initializer but incomplete type s3c64xx.c:176: error: unknown field 'name' specified in initializer s3c64xx.c:176: warning: excess elements in struct initializer s3c64xx.c:176: warning: (near initialization for 's3c64xx_serial_drv') s3c64xx.c:177: error: unknown field 'start' specified in initializer s3c64xx.c:177: warning: excess elements in struct initializer s3c64xx.c:177: warning: (near initialization for 's3c64xx_serial_drv') s3c64xx.c:178: error: unknown field 'stop' specified in initializer s3c64xx.c:178: warning: excess elements in struct initializer s3c64xx.c:178: warning: (near initialization for 's3c64xx_serial_drv') s3c64xx.c:179: error: unknown field 'setbrg' specified in initializer s3c64xx.c:179: warning: excess elements in struct initializer s3c64xx.c:179: warning: (near initialization for 's3c64xx_serial_drv') s3c64xx.c:180: error: unknown field 'putc' specified in initializer s3c64xx.c:180: warning: excess elements in struct initializer s3c64xx.c:180: warning: (near initialization for 's3c64xx_serial_drv') s3c64xx.c:181: error: unknown field 'puts' specified in initializer s3c64xx.c:181: warning: excess elements in struct initializer s3c64xx.c:181: warning: (near initialization for 's3c64xx_serial_drv') s3c64xx.c:182: error: unknown field 'getc' specified in initializer s3c64xx.c:182: warning: excess elements in struct initializer s3c64xx.c:182: warning: (near initialization for 's3c64xx_serial_drv') s3c64xx.c:183: error: unknown field 'tstc' specified in initializer s3c64xx.c:183: warning: excess elements in struct initializer s3c64xx.c:183: warning: (near initialization for 's3c64xx_serial_drv') s3c64xx.c: In function 's3c64xx_serial_initialize': s3c64xx.c:188: warning: implicit declaration of function 'serial_register' s3c64xx.c: At top level: s3c64xx.c:191: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'struct' Signed-off-by: Minkyu Kang Acked-by: Marek Vasut --- drivers/serial/s3c64xx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/s3c64xx.c b/drivers/serial/s3c64xx.c index f53c2bf..b590992 100644 --- a/drivers/serial/s3c64xx.c +++ b/drivers/serial/s3c64xx.c @@ -22,7 +22,8 @@ */ #include - +#include +#include #include DECLARE_GLOBAL_DATA_PTR; -- cgit v1.1 From eed29742183b5a462f2e8fd56efb004f317b4fa7 Mon Sep 17 00:00:00 2001 From: Piotr Wilczek Date: Fri, 19 Oct 2012 05:34:05 +0000 Subject: drivers: video: Add ld9040 video driver Signed-off-by: Piotr Wilczek Signed-off-by: Kyungmin Park CC: Donghwa Lee Signed-off-by: Minkyu Kang --- drivers/video/Makefile | 1 + drivers/video/ld9040.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 drivers/video/ld9040.c (limited to 'drivers') diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ebb6da8..247b867 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -37,6 +37,7 @@ COBJS-$(CONFIG_EXYNOS_PWM_BL) += exynos_pwm_bl.o COBJS-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o COBJS-$(CONFIG_S6E8AX0) += s6e8ax0.o COBJS-$(CONFIG_S6E63D6) += s6e63d6.o +COBJS-$(CONFIG_LD9040) += ld9040.o COBJS-$(CONFIG_SED156X) += sed156x.o COBJS-$(CONFIG_VIDEO_AMBA) += amba.o COBJS-$(CONFIG_VIDEO_CT69000) += ct69000.o videomodes.o diff --git a/drivers/video/ld9040.c b/drivers/video/ld9040.c new file mode 100644 index 0000000..c01ae12 --- /dev/null +++ b/drivers/video/ld9040.c @@ -0,0 +1,144 @@ +/* + * ld9040 AMOLED LCD panel driver. + * + * Copyright (C) 2012 Samsung Electronics + * Donghwa Lee + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include + +static const unsigned char SEQ_SWRESET[] = { + 0x01, +}; + +static const unsigned char SEQ_USER_SETTING[] = { + 0xF0, 0x5A, 0x5A +}; + +static const unsigned char SEQ_ELVSS_ON[] = { + 0xB1, 0x0D, 0x00, 0x16, +}; + +static const unsigned char SEQ_TEMP_SWIRE[] = { + 0xB2, 0x06, 0x06, 0x06, 0x06, +}; + +static const unsigned char SEQ_GTCON[] = { + 0xF7, 0x09, 0x00, 0x00, +}; + +static const unsigned char SEQ_PANEL_CONDITION[] = { + 0xF8, 0x05, 0x65, 0x96, 0x71, 0x7D, 0x19, 0x3B, + 0x0D, 0x19, 0x7E, 0x0D, 0xE2, 0x00, 0x00, 0x7E, + 0x7D, 0x07, 0x07, 0x20, 0x20, 0x20, 0x02, 0x02, +}; + +static const unsigned char SEQ_GAMMA_SET1[] = { + 0xF9, 0x00, 0xA7, 0xB4, 0xAE, 0xBF, 0x00, 0x91, + 0x00, 0xB2, 0xB4, 0xAA, 0xBB, 0x00, 0xAC, 0x00, + 0xB3, 0xB1, 0xAA, 0xBC, 0x00, 0xB3, +}; + +static const unsigned char SEQ_GAMMA_CTRL[] = { + 0xFB, 0x02, 0x5A, +}; + +static const unsigned char SEQ_APON[] = { + 0xF3, 0x00, 0x00, 0x00, 0x0A, 0x02, +}; + +static const unsigned char SEQ_DISPCTL[] = { + 0xF2, 0x02, 0x08, 0x08, 0x10, 0x10, +}; + +static const unsigned char SEQ_MANPWR[] = { + 0xB0, 0x04, +}; + +static const unsigned char SEQ_PWR_CTRL[] = { + 0xF4, 0x0A, 0x87, 0x25, 0x6A, 0x44, 0x02, 0x88, +}; + +static const unsigned char SEQ_SLPOUT[] = { + 0x11, +}; + +static const unsigned char SEQ_SLPIN[] = { + 0x10, +}; + +static const unsigned char SEQ_DISPON[] = { + 0x29, +}; + +static const unsigned char SEQ_DISPOFF[] = { + 0x28, +}; + +static void ld9040_spi_write(const unsigned char *wbuf, unsigned int size_cmd) +{ + int i = 0; + + /* + * Data are transmitted in 9-bit words: + * the first bit is command/parameter, the other are the value. + * The value's LSB is shifted to MSB position, to be sent as 9th bit + */ + + unsigned int data_out = 0, data_in = 0; + for (i = 0; i < size_cmd; i++) { + data_out = wbuf[i] >> 1; + if (i != 0) + data_out += 0x0080; + if (wbuf[i] & 0x01) + data_out += 0x8000; + spi_xfer(NULL, 9, &data_out, &data_in, SPI_XFER_BEGIN); + } +} + +void ld9040_cfg_ldo(void) +{ + udelay(10); + + ld9040_spi_write(SEQ_USER_SETTING, + ARRAY_SIZE(SEQ_USER_SETTING)); + ld9040_spi_write(SEQ_PANEL_CONDITION, + ARRAY_SIZE(SEQ_PANEL_CONDITION)); + ld9040_spi_write(SEQ_DISPCTL, ARRAY_SIZE(SEQ_DISPCTL)); + ld9040_spi_write(SEQ_MANPWR, ARRAY_SIZE(SEQ_MANPWR)); + ld9040_spi_write(SEQ_PWR_CTRL, ARRAY_SIZE(SEQ_PWR_CTRL)); + ld9040_spi_write(SEQ_ELVSS_ON, ARRAY_SIZE(SEQ_ELVSS_ON)); + ld9040_spi_write(SEQ_GTCON, ARRAY_SIZE(SEQ_GTCON)); + ld9040_spi_write(SEQ_GAMMA_SET1, ARRAY_SIZE(SEQ_GAMMA_SET1)); + ld9040_spi_write(SEQ_GAMMA_CTRL, ARRAY_SIZE(SEQ_GAMMA_CTRL)); + ld9040_spi_write(SEQ_SLPOUT, ARRAY_SIZE(SEQ_SLPOUT)); + + udelay(120); +} + +void ld9040_enable_ldo(unsigned int onoff) +{ + if (onoff) + ld9040_spi_write(SEQ_DISPON, ARRAY_SIZE(SEQ_DISPON)); + else + ld9040_spi_write(SEQ_DISPOFF, ARRAY_SIZE(SEQ_DISPOFF)); +} -- cgit v1.1 From 28c9e34827372a0d243e04c7d4859cd7e8dd51d7 Mon Sep 17 00:00:00 2001 From: Piotr Wilczek Date: Fri, 19 Oct 2012 05:34:06 +0000 Subject: drivers: video: fix image position This patch fixes image position on screen when images's height or width is biger then the lcd's. Signed-off-by: Piotr Wilczek Signed-off-by: Kyungmin Park CC: Minkyu Kang Signed-off-by: Minkyu Kang --- drivers/video/exynos_fb.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/video/exynos_fb.c b/drivers/video/exynos_fb.c index e31a0fd..d9a3f9a 100644 --- a/drivers/video/exynos_fb.c +++ b/drivers/video/exynos_fb.c @@ -70,8 +70,19 @@ static void draw_logo(void) int x, y; ulong addr; - x = ((panel_width - panel_info.logo_width) >> 1); - y = ((panel_height - panel_info.logo_height) >> 1) - 4; + if (panel_width >= panel_info.logo_width) { + x = ((panel_width - panel_info.logo_width) >> 1); + } else { + x = 0; + printf("Warning: image width is bigger than display width\n"); + } + + if (panel_height >= panel_info.logo_height) { + y = ((panel_height - panel_info.logo_height) >> 1) - 4; + } else { + y = 0; + printf("Warning: image height is bigger than display height\n"); + } addr = panel_info.logo_addr; bmp_display(addr, x, y); -- cgit v1.1 From 511ed5fdd3892c6e4a42b14c7d80db15eee7fc4f Mon Sep 17 00:00:00 2001 From: Rajeshwari Shinde Date: Thu, 25 Oct 2012 19:49:22 +0000 Subject: SOUND: SAMSUNG: Add I2S driver This patch adds driver for I2S interface specific to samsung. Signed-off-by: R. Chandrasekar Signed-off-by: Rajeshwari Shinde Acked-by: Simon Glass Signed-off-by: Minkyu Kang --- drivers/sound/Makefile | 47 ++++++ drivers/sound/samsung-i2s.c | 358 ++++++++++++++++++++++++++++++++++++++++++++ drivers/sound/sound.c | 228 ++++++++++++++++++++++++++++ 3 files changed, 633 insertions(+) create mode 100644 drivers/sound/Makefile create mode 100644 drivers/sound/samsung-i2s.c create mode 100644 drivers/sound/sound.c (limited to 'drivers') diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile new file mode 100644 index 0000000..18ad2c9 --- /dev/null +++ b/drivers/sound/Makefile @@ -0,0 +1,47 @@ +# +# Copyright (C) 2012 Samsung Electronics +# R. Chandrasekar +# +# See file CREDITS for list of people who contributed to this +# project. +# +# 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., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libsound.o + +COBJS-$(CONFIG_SOUND) += sound.o +COBJS-$(CONFIG_I2S) += samsung-i2s.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +# diff --git a/drivers/sound/samsung-i2s.c b/drivers/sound/samsung-i2s.c new file mode 100644 index 0000000..9f3117d --- /dev/null +++ b/drivers/sound/samsung-i2s.c @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * R. Chandrasekar + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) +#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) +#define FIC_TXCOUNT(x) (((x) >> 8) & 0xf) +#define FIC_RXCOUNT(x) (((x) >> 0) & 0xf) +#define FICS_TXCOUNT(x) (((x) >> 8) & 0x7f) + +#define TIMEOUT_I2S_TX 100 /* i2s transfer timeout */ + +/* + * Sets the frame size for I2S LR clock + * + * @param i2s_reg i2s regiter address + * @param rfs Frame Size + */ +static void i2s_set_lr_framesize(struct i2s_reg *i2s_reg, unsigned int rfs) +{ + unsigned int mod = readl(&i2s_reg->mod); + + mod &= ~MOD_RCLK_MASK; + + switch (rfs) { + case 768: + mod |= MOD_RCLK_768FS; + break; + case 512: + mod |= MOD_RCLK_512FS; + break; + case 384: + mod |= MOD_RCLK_384FS; + break; + default: + mod |= MOD_RCLK_256FS; + break; + } + + writel(mod, &i2s_reg->mod); +} + +/* + * Sets the i2s transfer control + * + * @param i2s_reg i2s regiter address + * @param on 1 enable tx , 0 disable tx transfer + */ +static void i2s_txctrl(struct i2s_reg *i2s_reg, int on) +{ + unsigned int con = readl(&i2s_reg->con); + unsigned int mod = readl(&i2s_reg->mod) & ~MOD_MASK; + + if (on) { + con |= CON_ACTIVE; + con &= ~CON_TXCH_PAUSE; + + } else { + + con |= CON_TXCH_PAUSE; + con &= ~CON_ACTIVE; + } + + writel(mod, &i2s_reg->mod); + writel(con, &i2s_reg->con); +} + +/* + * set the bit clock frame size (in multiples of LRCLK) + * + * @param i2s_reg i2s regiter address + * @param bfs bit Frame Size + */ +static void i2s_set_bitclk_framesize(struct i2s_reg *i2s_reg, unsigned bfs) +{ + unsigned int mod = readl(&i2s_reg->mod); + + mod &= ~MOD_BCLK_MASK; + + switch (bfs) { + case 48: + mod |= MOD_BCLK_48FS; + break; + case 32: + mod |= MOD_BCLK_32FS; + break; + case 24: + mod |= MOD_BCLK_24FS; + break; + case 16: + mod |= MOD_BCLK_16FS; + break; + default: + return; + } + writel(mod, &i2s_reg->mod); +} + +/* + * flushes the i2stx fifo + * + * @param i2s_reg i2s regiter address + * @param flush Tx fifo flush command (0x00 - do not flush + * 0x80 - flush tx fifo) + */ +void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush) +{ + /* Flush the FIFO */ + setbits_le32(&i2s_reg->fic, flush); + clrbits_le32(&i2s_reg->fic, flush); +} + +/* + * Set System Clock direction + * + * @param i2s_reg i2s regiter address + * @param dir Clock direction + * + * @return int value 0 for success, -1 in case of error + */ +int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir) +{ + unsigned int mod = readl(&i2s_reg->mod); + + if (dir == SND_SOC_CLOCK_IN) + mod |= MOD_CDCLKCON; + else + mod &= ~MOD_CDCLKCON; + + writel(mod, &i2s_reg->mod); + + return 0; +} + +/* + * Sets I2S Clcok format + * + * @param fmt i2s clock properties + * @param i2s_reg i2s regiter address + * + * @return int value 0 for success, -1 in case of error + */ +int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) +{ + unsigned int mod = readl(&i2s_reg->mod); + unsigned int tmp = 0; + unsigned int ret = 0; + + /* Format is priority */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_RIGHT_J: + tmp |= MOD_LR_RLOW; + tmp |= MOD_SDF_MSB; + break; + case SND_SOC_DAIFMT_LEFT_J: + tmp |= MOD_LR_RLOW; + tmp |= MOD_SDF_LSB; + break; + case SND_SOC_DAIFMT_I2S: + tmp |= MOD_SDF_IIS; + break; + default: + debug("%s: Invalid format priority [0x%x]\n", __func__, + (fmt & SND_SOC_DAIFMT_FORMAT_MASK)); + return -1; + } + + /* + * INV flag is relative to the FORMAT flag - if set it simply + * flips the polarity specified by the Standard + */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + if (tmp & MOD_LR_RLOW) + tmp &= ~MOD_LR_RLOW; + else + tmp |= MOD_LR_RLOW; + break; + default: + debug("%s: Invalid clock ploarity input [0x%x]\n", __func__, + (fmt & SND_SOC_DAIFMT_INV_MASK)); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + tmp |= MOD_SLAVE; + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set default source clock in Master mode */ + ret = i2s_set_sysclk_dir(i2s_reg, SND_SOC_CLOCK_OUT); + if (ret != 0) { + debug("%s:set i2s clock direction failed\n", __func__); + return -1; + } + break; + default: + debug("%s: Invalid master selection [0x%x]\n", __func__, + (fmt & SND_SOC_DAIFMT_MASTER_MASK)); + return -1; + } + + mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE); + mod |= tmp; + writel(mod, &i2s_reg->mod); + + return 0; +} + +/* + * Sets the sample width in bits + * + * @param blc samplewidth (size of sample in bits) + * @param i2s_reg i2s regiter address + * + * @return int value 0 for success, -1 in case of error + */ +int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) +{ + unsigned int mod = readl(&i2s_reg->mod); + + mod &= ~MOD_BLCP_MASK; + mod &= ~MOD_BLC_MASK; + + switch (blc) { + case 8: + mod |= MOD_BLCP_8BIT; + mod |= MOD_BLC_8BIT; + break; + case 16: + mod |= MOD_BLCP_16BIT; + mod |= MOD_BLC_16BIT; + break; + case 24: + mod |= MOD_BLCP_24BIT; + mod |= MOD_BLC_24BIT; + break; + default: + debug("%s: Invalid sample size input [0x%x]\n", + __func__, blc); + return -1; + } + writel(mod, &i2s_reg->mod); + + return 0; +} + +int i2s_transfer_tx_data(struct i2stx_info *pi2s_tx, unsigned int *data, + unsigned long data_size) +{ + int i; + int start; + struct i2s_reg *i2s_reg = + (struct i2s_reg *)pi2s_tx->base_address; + + if (data_size < FIFO_LENGTH) { + debug("%s : Invalid data size\n", __func__); + return -1; /* invalid pcm data size */ + } + + /* fill the tx buffer before stating the tx transmit */ + for (i = 0; i < FIFO_LENGTH; i++) + writel(*data++, &i2s_reg->txd); + + data_size -= FIFO_LENGTH; + i2s_txctrl(i2s_reg, I2S_TX_ON); + + while (data_size > 0) { + start = get_timer(0); + if (!(CON_TXFIFO_FULL & (readl(&i2s_reg->con)))) { + writel(*data++, &i2s_reg->txd); + data_size--; + } else { + if (get_timer(start) > TIMEOUT_I2S_TX) { + i2s_txctrl(i2s_reg, I2S_TX_OFF); + debug("%s: I2S Transfer Timeout\n", __func__); + return -1; + } + } + } + i2s_txctrl(i2s_reg, I2S_TX_OFF); + + return 0; +} + +int i2s_tx_init(struct i2stx_info *pi2s_tx) +{ + int ret; + struct i2s_reg *i2s_reg = + (struct i2s_reg *)pi2s_tx->base_address; + + /* Initialize GPIO for I2s */ + exynos_pinmux_config(PERIPH_ID_I2S1, 0); + + /* Set EPLL Clock */ + ret = set_epll_clk(pi2s_tx->audio_pll_clk); + if (ret != 0) { + debug("%s: epll clock set rate falied\n", __func__); + return -1; + } + + /* Select Clk Source for Audio1 */ + set_i2s_clk_source(); + + /* Set Prescaler to get MCLK */ + set_i2s_clk_prescaler(pi2s_tx->audio_pll_clk, + (pi2s_tx->samplingrate * (pi2s_tx->rfs))); + + /* Configure I2s format */ + ret = i2s_set_fmt(i2s_reg, (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM)); + if (ret == 0) { + i2s_set_lr_framesize(i2s_reg, pi2s_tx->rfs); + ret = i2s_set_samplesize(i2s_reg, pi2s_tx->bitspersample); + if (ret != 0) { + debug("%s:set sample rate failed\n", __func__); + return -1; + } + + i2s_set_bitclk_framesize(i2s_reg, pi2s_tx->bfs); + /* disable i2s transfer flag and flush the fifo */ + i2s_txctrl(i2s_reg, I2S_TX_OFF); + i2s_fifo(i2s_reg, FIC_TXFLUSH); + } else { + debug("%s: failed\n", __func__); + } + + return ret; +} diff --git a/drivers/sound/sound.c b/drivers/sound/sound.c new file mode 100644 index 0000000..4c74534 --- /dev/null +++ b/drivers/sound/sound.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * R. Chandrasekar + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "wm8994.h" +#include + +/* defines */ +#define SOUND_400_HZ 400 +#define SOUND_BITS_IN_BYTE 8 + +static struct i2stx_info g_i2stx_pri; +static struct sound_codec_info g_codec_info; + +/* + * get_sound_fdt_values gets fdt values for i2s parameters + * + * @param i2stx_info i2s transmitter transfer param structure + * @param blob FDT blob + */ +static void get_sound_i2s_values(struct i2stx_info *i2s) +{ + i2s->base_address = samsung_get_base_i2s(); + i2s->audio_pll_clk = I2S_PLL_CLK; + i2s->samplingrate = I2S_SAMPLING_RATE; + i2s->bitspersample = I2S_BITS_PER_SAMPLE; + i2s->channels = I2S_CHANNELS; + i2s->rfs = I2S_RFS; + i2s->bfs = I2S_BFS; +} + +/* + * Gets fdt values for wm8994 config parameters + * + * @param pcodec_info codec information structure + * @param blob FDT blob + * @return int value, 0 for success + */ +static int get_sound_wm8994_values(struct sound_codec_info *pcodec_info) +{ + int error = 0; + + switch (AUDIO_COMPAT) { + case AUDIO_COMPAT_SPI: + debug("%s: Support not added for SPI interface\n", __func__); + return -1; + break; + case AUDIO_COMPAT_I2C: + pcodec_info->i2c_bus = AUDIO_I2C_BUS; + pcodec_info->i2c_dev_addr = AUDIO_I2C_REG; + debug("i2c dev addr = %d\n", pcodec_info->i2c_dev_addr); + break; + default: + debug("%s: Unknown compat id %d\n", __func__, AUDIO_COMPAT); + return -1; + } + + if (error == -1) { + debug("fail to get wm8994 codec node properties\n"); + return -1; + } + + return 0; +} + +/* + * Gets fdt values for codec config parameters + * + * @param pcodec_info codec information structure + * @param blob FDT blob + * @return int value, 0 for success + */ +static int get_sound_codec_values(struct sound_codec_info *pcodec_info) +{ + int error = 0; + const char *codectype; + + codectype = AUDIO_CODEC; + + if (!strcmp(codectype, "wm8994")) { + pcodec_info->codec_type = CODEC_WM_8994; + error = get_sound_wm8994_values(pcodec_info); + } else { + error = -1; + } + + if (error == -1) { + debug("fail to get sound codec node properties\n"); + return -1; + } + + return 0; +} + +int sound_init(void) +{ + int ret; + struct i2stx_info *pi2s_tx = &g_i2stx_pri; + struct sound_codec_info *pcodec_info = &g_codec_info; + + /* Get the I2S Values */ + get_sound_i2s_values(pi2s_tx); + + /* Get the codec Values */ + if (get_sound_codec_values(pcodec_info) < 0) + return -1; + + ret = i2s_tx_init(pi2s_tx); + if (ret) { + debug("%s: Failed to init i2c transmit: ret=%d\n", __func__, + ret); + return ret; + } + + /* Check the codec type and initialise the same */ + if (pcodec_info->codec_type == CODEC_WM_8994) { + ret = wm8994_init(pcodec_info, WM8994_AIF2, + pi2s_tx->samplingrate, + (pi2s_tx->samplingrate * (pi2s_tx->rfs)), + pi2s_tx->bitspersample, pi2s_tx->channels); + } else { + debug("%s: Unknown code type %d\n", __func__, + pcodec_info->codec_type); + return -1; + } + if (ret) { + debug("%s: Codec init failed\n", __func__); + return -1; + } + + return ret; +} + +/* + * Generates square wave sound data for 1 second + * + * @param data data buffer pointer + * @param size size of the buffer + * @param freq frequency of the wave + */ +static void sound_prepare_buffer(unsigned short *data, int size, uint32_t freq) +{ + const int sample = 48000; + const unsigned short amplitude = 16000; /* between 1 and 32767 */ + const int period = freq ? sample / freq : 0; + const int half = period / 2; + + assert(freq); + + /* Make sure we don't overflow our buffer */ + if (size % 2) + size--; + + while (size) { + int i; + for (i = 0; size && i < half; i++) { + size -= 2; + *data++ = amplitude; + *data++ = amplitude; + } + for (i = 0; size && i < period - half; i++) { + size -= 2; + *data++ = -amplitude; + *data++ = -amplitude; + } + } +} + +int sound_play(uint32_t msec, uint32_t frequency) +{ + unsigned int *data; + unsigned long data_size; + unsigned int ret = 0; + + /*Buffer length computation */ + data_size = g_i2stx_pri.samplingrate * g_i2stx_pri.channels; + data_size *= (g_i2stx_pri.bitspersample / SOUND_BITS_IN_BYTE); + data = malloc(data_size); + + if (data == NULL) { + debug("%s: malloc failed\n", __func__); + return -1; + } + + sound_prepare_buffer((unsigned short *)data, + data_size / sizeof(unsigned short), frequency); + + while (msec >= 1000) { + ret = i2s_transfer_tx_data(&g_i2stx_pri, data, + (data_size / sizeof(int))); + msec -= 1000; + } + if (msec) { + unsigned long size = + (data_size * msec) / (sizeof(int) * 1000); + + ret = i2s_transfer_tx_data(&g_i2stx_pri, data, size); + } + + free(data); + + return ret; +} -- cgit v1.1 From a2d8e0a717b01995eac162de205c90b3ea782440 Mon Sep 17 00:00:00 2001 From: Rajeshwari Shinde Date: Thu, 25 Oct 2012 19:49:23 +0000 Subject: SOUND: Add WM8994 codec This patch adds driver for audio codec WM8994 Signed-off-by: R. Chandrasekar Signed-off-by: Rajeshwari Shinde Acked-by: Simon Glass Signed-off-by: Minkyu Kang --- drivers/sound/Makefile | 1 + drivers/sound/wm8994.c | 792 +++++++++++++++++++++++++++++++++++++++ drivers/sound/wm8994.h | 87 +++++ drivers/sound/wm8994_registers.h | 299 +++++++++++++++ 4 files changed, 1179 insertions(+) create mode 100644 drivers/sound/wm8994.c create mode 100644 drivers/sound/wm8994.h create mode 100644 drivers/sound/wm8994_registers.h (limited to 'drivers') diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 18ad2c9..8fdffb1 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -27,6 +27,7 @@ LIB := $(obj)libsound.o COBJS-$(CONFIG_SOUND) += sound.o COBJS-$(CONFIG_I2S) += samsung-i2s.o +COBJS-$(CONFIG_SOUND_WM8994) += wm8994.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/sound/wm8994.c b/drivers/sound/wm8994.c new file mode 100644 index 0000000..293903a --- /dev/null +++ b/drivers/sound/wm8994.c @@ -0,0 +1,792 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * R. Chandrasekar + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wm8994.h" +#include "wm8994_registers.h" + +/* defines for wm8994 system clock selection */ +#define SEL_MCLK1 0x00 +#define SEL_MCLK2 0x08 +#define SEL_FLL1 0x10 +#define SEL_FLL2 0x18 + +/* fll config to configure fll */ +struct wm8994_fll_config { + int src; /* Source */ + int in; /* Input frequency in Hz */ + int out; /* output frequency in Hz */ +}; + +/* codec private data */ +struct wm8994_priv { + enum wm8994_type type; /* codec type of wolfson */ + int revision; /* Revision */ + int sysclk[WM8994_MAX_AIF]; /* System clock frequency in Hz */ + int mclk[WM8994_MAX_AIF]; /* master clock frequency in Hz */ + int aifclk[WM8994_MAX_AIF]; /* audio interface clock in Hz */ + struct wm8994_fll_config fll[2]; /* fll config to configure fll */ +}; + +/* wm 8994 supported sampling rate values */ +static unsigned int src_rate[] = { + 8000, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 88200, 96000 +}; + +/* op clock divisions */ +static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 }; + +/* lr clock frame size ratio */ +static int fs_ratios[] = { + 64, 128, 192, 256, 348, 512, 768, 1024, 1408, 1536 +}; + +/* bit clock divisors */ +static int bclk_divs[] = { + 10, 15, 20, 30, 40, 50, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480, + 640, 880, 960, 1280, 1760, 1920 +}; + +static struct wm8994_priv g_wm8994_info; +static unsigned char g_wm8994_i2c_dev_addr; + +/* + * Initialise I2C for wm 8994 + * + * @param bus no i2c bus number in which wm8994 is connected + */ +static void wm8994_i2c_init(int bus_no) +{ + i2c_set_bus_num(bus_no); +} + +/* + * Writes value to a device register through i2c + * + * @param reg reg number to be write + * @param data data to be writen to the above registor + * + * @return int value 1 for change, 0 for no change or negative error code. + */ +static int wm8994_i2c_write(unsigned int reg, unsigned short data) +{ + unsigned char val[2]; + + val[0] = (unsigned char)((data >> 8) & 0xff); + val[1] = (unsigned char)(data & 0xff); + debug("Write Addr : 0x%04X, Data : 0x%04X\n", reg, data); + + return i2c_write(g_wm8994_i2c_dev_addr, reg, 2, val, 2); +} + +/* + * Read a value from a device register through i2c + * + * @param reg reg number to be read + * @param data address of read data to be stored + * + * @return int value 0 for success, -1 in case of error. + */ +static unsigned int wm8994_i2c_read(unsigned int reg , unsigned short *data) +{ + unsigned char val[2]; + int ret; + + ret = i2c_read(g_wm8994_i2c_dev_addr, reg, 2, val, 2); + if (ret != 0) { + debug("%s: Error while reading register %#04x\n", + __func__, reg); + return -1; + } + + *data = val[0]; + *data <<= 8; + *data |= val[1]; + + return 0; +} + +/* + * update device register bits through i2c + * + * @param reg codec register + * @param mask register mask + * @param value new value + * + * @return int value 1 if change in the register value, + * 0 for no change or negative error code. + */ +static int wm8994_update_bits(unsigned int reg, unsigned short mask, + unsigned short value) +{ + int change , ret = 0; + unsigned short old, new; + + if (wm8994_i2c_read(reg, &old) != 0) + return -1; + new = (old & ~mask) | (value & mask); + change = (old != new) ? 1 : 0; + if (change) + ret = wm8994_i2c_write(reg, new); + if (ret < 0) + return ret; + + return change; +} + +/* + * Sets i2s set format + * + * @param aif_id Interface ID + * @param fmt i2S format + * + * @return -1 for error and 0 Success. + */ +int wm8994_set_fmt(int aif_id, unsigned int fmt) +{ + int ms_reg; + int aif_reg; + int ms = 0; + int aif = 0; + int aif_clk = 0; + int error = 0; + + switch (aif_id) { + case 1: + ms_reg = WM8994_AIF1_MASTER_SLAVE; + aif_reg = WM8994_AIF1_CONTROL_1; + aif_clk = WM8994_AIF1_CLOCKING_1; + break; + case 2: + ms_reg = WM8994_AIF2_MASTER_SLAVE; + aif_reg = WM8994_AIF2_CONTROL_1; + aif_clk = WM8994_AIF2_CLOCKING_1; + break; + default: + debug("%s: Invalid audio interface selection\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + ms = WM8994_AIF1_MSTR; + break; + default: + debug("%s: Invalid i2s master selection\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_B: + aif |= WM8994_AIF1_LRCLK_INV; + case SND_SOC_DAIFMT_DSP_A: + aif |= 0x18; + break; + case SND_SOC_DAIFMT_I2S: + aif |= 0x10; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + aif |= 0x8; + break; + default: + debug("%s: Invalid i2s format selection\n", __func__); + return -1; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + /* frame inversion not valid for DSP modes */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8994_AIF1_BCLK_INV; + break; + default: + debug("%s: Invalid i2s frame inverse selection\n", + __func__); + return -1; + } + break; + + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_RIGHT_J: + case SND_SOC_DAIFMT_LEFT_J: + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + aif |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV; + break; + case SND_SOC_DAIFMT_IB_NF: + aif |= WM8994_AIF1_BCLK_INV; + break; + case SND_SOC_DAIFMT_NB_IF: + aif |= WM8994_AIF1_LRCLK_INV; + break; + default: + debug("%s: Invalid i2s clock polarity selection\n", + __func__); + return -1; + } + break; + default: + debug("%s: Invalid i2s format selection\n", __func__); + return -1; + } + + error = wm8994_update_bits(aif_reg, WM8994_AIF1_BCLK_INV | + WM8994_AIF1_LRCLK_INV_MASK | WM8994_AIF1_FMT_MASK, aif); + + error |= wm8994_update_bits(ms_reg, WM8994_AIF1_MSTR_MASK, ms); + error |= wm8994_update_bits(aif_clk, WM8994_AIF1CLK_ENA_MASK, + WM8994_AIF1CLK_ENA); + if (error < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Sets hw params FOR WM8994 + * + * @param wm8994 wm8994 information pointer + * @param aif_id Audio interface ID + * @param sampling_rate Sampling rate + * @param bits_per_sample Bits per sample + * @param Channels Channels in the given audio input + * + * @return -1 for error and 0 Success. + */ +static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id, + unsigned int sampling_rate, unsigned int bits_per_sample, + unsigned int channels) +{ + int aif1_reg; + int aif2_reg; + int bclk_reg; + int bclk = 0; + int rate_reg; + int aif1 = 0; + int aif2 = 0; + int rate_val = 0; + int id = aif_id - 1; + int i, cur_val, best_val, bclk_rate, best; + unsigned short reg_data; + int ret = 0; + + switch (aif_id) { + case 1: + aif1_reg = WM8994_AIF1_CONTROL_1; + aif2_reg = WM8994_AIF1_CONTROL_2; + bclk_reg = WM8994_AIF1_BCLK; + rate_reg = WM8994_AIF1_RATE; + break; + case 2: + aif1_reg = WM8994_AIF2_CONTROL_1; + aif2_reg = WM8994_AIF2_CONTROL_2; + bclk_reg = WM8994_AIF2_BCLK; + rate_reg = WM8994_AIF2_RATE; + break; + default: + return -1; + } + + bclk_rate = sampling_rate * 32; + switch (bits_per_sample) { + case 16: + bclk_rate *= 16; + break; + case 20: + bclk_rate *= 20; + aif1 |= 0x20; + break; + case 24: + bclk_rate *= 24; + aif1 |= 0x40; + break; + case 32: + bclk_rate *= 32; + aif1 |= 0x60; + break; + default: + return -1; + } + + /* Try to find an appropriate sample rate; look for an exact match. */ + for (i = 0; i < ARRAY_SIZE(src_rate); i++) + if (src_rate[i] == sampling_rate) + break; + + if (i == ARRAY_SIZE(src_rate)) { + debug("%s: Could not get the best matching samplingrate\n", + __func__); + return -1; + } + + rate_val |= i << WM8994_AIF1_SR_SHIFT; + + /* AIFCLK/fs ratio; look for a close match in either direction */ + best = 0; + best_val = abs((fs_ratios[0] * sampling_rate) + - wm8994->aifclk[id]); + + for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) { + cur_val = abs((fs_ratios[i] * sampling_rate) + - wm8994->aifclk[id]); + if (cur_val >= best_val) + continue; + best = i; + best_val = cur_val; + } + + rate_val |= best; + + /* + * We may not get quite the right frequency if using + * approximate clocks so look for the closest match that is + * higher than the target (we need to ensure that there enough + * BCLKs to clock out the samples). + */ + best = 0; + for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { + cur_val = (wm8994->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate; + if (cur_val < 0) /* BCLK table is sorted */ + break; + best = i; + } + + if (i == ARRAY_SIZE(bclk_divs)) { + debug("%s: Could not get the best matching bclk division\n", + __func__); + return -1; + } + + bclk_rate = wm8994->aifclk[id] * 10 / bclk_divs[best]; + bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT; + + if (wm8994_i2c_read(aif1_reg, ®_data) != 0) { + debug("%s: AIF1 register read Failed\n", __func__); + return -1; + } + + if ((channels == 1) && ((reg_data & 0x18) == 0x18)) + aif2 |= WM8994_AIF1_MONO; + + if (wm8994->aifclk[id] == 0) { + debug("%s:Audio interface clock not set\n", __func__); + return -1; + } + + ret = wm8994_update_bits(aif1_reg, WM8994_AIF1_WL_MASK, aif1); + ret |= wm8994_update_bits(aif2_reg, WM8994_AIF1_MONO, aif2); + ret |= wm8994_update_bits(bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk); + ret |= wm8994_update_bits(rate_reg, WM8994_AIF1_SR_MASK | + WM8994_AIF1CLK_RATE_MASK, rate_val); + + debug("rate vale = %x , bclk val= %x\n", rate_val, bclk); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Configures Audio interface Clock + * + * @param wm8994 wm8994 information pointer + * @param aif Audio Interface ID + * + * @return -1 for error and 0 Success. + */ +static int configure_aif_clock(struct wm8994_priv *wm8994, int aif) +{ + int rate; + int reg1 = 0; + int offset; + int ret; + + /* AIF(1/0) register adress offset calculated */ + if (aif) + offset = 4; + else + offset = 0; + + switch (wm8994->sysclk[aif]) { + case WM8994_SYSCLK_MCLK1: + reg1 |= SEL_MCLK1; + rate = wm8994->mclk[0]; + break; + + case WM8994_SYSCLK_MCLK2: + reg1 |= SEL_MCLK2; + rate = wm8994->mclk[1]; + break; + + case WM8994_SYSCLK_FLL1: + reg1 |= SEL_FLL1; + rate = wm8994->fll[0].out; + break; + + case WM8994_SYSCLK_FLL2: + reg1 |= SEL_FLL2; + rate = wm8994->fll[1].out; + break; + + default: + debug("%s: Invalid input clock selection [%d]\n", + __func__, wm8994->sysclk[aif]); + return -1; + } + + /* if input clock frequenct is more than 135Mhz then divide */ + if (rate >= WM8994_MAX_INPUT_CLK_FREQ) { + rate /= 2; + reg1 |= WM8994_AIF1CLK_DIV; + } + + wm8994->aifclk[aif] = rate; + + ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_1 + offset, + WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV, + reg1); + + ret |= wm8994_update_bits(WM8994_CLOCKING_1, + WM8994_SYSCLK_SRC | WM8994_AIF2DSPCLK_ENA_MASK | + WM8994_SYSDSPCLK_ENA_MASK, WM8994_SYSCLK_SRC | + WM8994_AIF2DSPCLK_ENA | WM8994_SYSDSPCLK_ENA); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Configures Audio interface for the given frequency + * + * @param wm8994 wm8994 information + * @param aif_id Audio Interface + * @param clk_id Input Clock ID + * @param freq Sampling frequency in Hz + * + * @return -1 for error and 0 success. + */ +static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id, + int clk_id, unsigned int freq) +{ + int i; + int ret = 0; + + wm8994->sysclk[aif_id - 1] = clk_id; + + switch (clk_id) { + case WM8994_SYSCLK_MCLK1: + wm8994->mclk[0] = freq; + if (aif_id == 2) { + ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_2 , + WM8994_AIF2DAC_DIV_MASK , 0); + } + break; + + case WM8994_SYSCLK_MCLK2: + /* TODO: Set GPIO AF */ + wm8994->mclk[1] = freq; + break; + + case WM8994_SYSCLK_FLL1: + case WM8994_SYSCLK_FLL2: + break; + + case WM8994_SYSCLK_OPCLK: + /* + * Special case - a division (times 10) is given and + * no effect on main clocking. + */ + if (freq) { + for (i = 0; i < ARRAY_SIZE(opclk_divs); i++) + if (opclk_divs[i] == freq) + break; + if (i == ARRAY_SIZE(opclk_divs)) { + debug("%s frequency divisor not found\n", + __func__); + return -1; + } + ret = wm8994_update_bits(WM8994_CLOCKING_2, + WM8994_OPCLK_DIV_MASK, i); + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2, + WM8994_OPCLK_ENA, WM8994_OPCLK_ENA); + } else { + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2, + WM8994_OPCLK_ENA, 0); + } + + default: + debug("%s Invalid input clock selection [%d]\n", + __func__, clk_id); + return -1; + } + + ret |= configure_aif_clock(wm8994, aif_id - 1); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Initializes Volume for AIF2 to HP path + * + * @returns -1 for error and 0 Success. + * + */ +static int wm8994_init_volume_aif2_dac1(void) +{ + int ret; + + /* Unmute AIF2DAC */ + ret = wm8994_update_bits(WM8994_AIF2_DAC_FILTERS_1, + WM8994_AIF2DAC_MUTE_MASK, 0); + + + ret |= wm8994_update_bits(WM8994_AIF2_DAC_LEFT_VOLUME, + WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACL_VOL_MASK, + WM8994_AIF2DAC_VU | 0xff); + + ret |= wm8994_update_bits(WM8994_AIF2_DAC_RIGHT_VOLUME, + WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACR_VOL_MASK, + WM8994_AIF2DAC_VU | 0xff); + + + ret |= wm8994_update_bits(WM8994_DAC1_LEFT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK | + WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + + ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK | + WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + /* Head Phone Volume */ + ret |= wm8994_i2c_write(WM8994_LEFT_OUTPUT_VOLUME, 0x12D); + ret |= wm8994_i2c_write(WM8994_RIGHT_OUTPUT_VOLUME, 0x12D); + + if (ret < 0) { + debug("%s: codec register access error\n", __func__); + return -1; + } + + return 0; +} + +/* + * Intialise wm8994 codec device + * + * @param wm8994 wm8994 information + * + * @returns -1 for error and 0 Success. + */ +static int wm8994_device_init(struct wm8994_priv *wm8994) +{ + const char *devname; + unsigned short reg_data; + int ret; + + wm8994_i2c_write(WM8994_SOFTWARE_RESET, WM8994_SW_RESET);/* Reset */ + + ret = wm8994_i2c_read(WM8994_SOFTWARE_RESET, ®_data); + if (ret < 0) { + debug("Failed to read ID register\n"); + goto err; + } + + if (reg_data == WM8994_ID) { + devname = "WM8994"; + debug("Device registered as type %d\n", wm8994->type); + wm8994->type = WM8994; + } else { + debug("Device is not a WM8994, ID is %x\n", ret); + ret = -1; + goto err; + } + + ret = wm8994_i2c_read(WM8994_CHIP_REVISION, ®_data); + if (ret < 0) { + debug("Failed to read revision register: %d\n", ret); + goto err; + } + wm8994->revision = reg_data; + debug("%s revision %c\n", devname, 'A' + wm8994->revision); + + /* VMID Selection */ + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, + WM8994_VMID_SEL_MASK | WM8994_BIAS_ENA_MASK, 0x3); + + /* Charge Pump Enable */ + ret |= wm8994_update_bits(WM8994_CHARGE_PUMP_1, WM8994_CP_ENA_MASK, + WM8994_CP_ENA); + + /* Head Phone Power Enable */ + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, + WM8994_HPOUT1L_ENA_MASK, WM8994_HPOUT1L_ENA); + + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, + WM8994_HPOUT1R_ENA_MASK, WM8994_HPOUT1R_ENA); + + /* Power enable for AIF2 and DAC1 */ + ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_5, + WM8994_AIF2DACL_ENA_MASK | WM8994_AIF2DACR_ENA_MASK | + WM8994_DAC1L_ENA_MASK | WM8994_DAC1R_ENA_MASK, + WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | WM8994_DAC1L_ENA | + WM8994_DAC1R_ENA); + + /* Head Phone Initialisation */ + ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1, + WM8994_HPOUT1L_DLY_MASK | WM8994_HPOUT1R_DLY_MASK, + WM8994_HPOUT1L_DLY | WM8994_HPOUT1R_DLY); + + ret |= wm8994_update_bits(WM8994_DC_SERVO_1, + WM8994_DCS_ENA_CHAN_0_MASK | + WM8994_DCS_ENA_CHAN_1_MASK , WM8994_DCS_ENA_CHAN_0 | + WM8994_DCS_ENA_CHAN_1); + + ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1, + WM8994_HPOUT1L_DLY_MASK | + WM8994_HPOUT1R_DLY_MASK | WM8994_HPOUT1L_OUTP_MASK | + WM8994_HPOUT1R_OUTP_MASK | + WM8994_HPOUT1L_RMV_SHORT_MASK | + WM8994_HPOUT1R_RMV_SHORT_MASK, WM8994_HPOUT1L_DLY | + WM8994_HPOUT1R_DLY | WM8994_HPOUT1L_OUTP | + WM8994_HPOUT1R_OUTP | WM8994_HPOUT1L_RMV_SHORT | + WM8994_HPOUT1R_RMV_SHORT); + + /* MIXER Config DAC1 to HP */ + ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_1, + WM8994_DAC1L_TO_HPOUT1L_MASK, WM8994_DAC1L_TO_HPOUT1L); + + ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_2, + WM8994_DAC1R_TO_HPOUT1R_MASK, WM8994_DAC1R_TO_HPOUT1R); + + /* Routing AIF2 to DAC1 */ + ret |= wm8994_update_bits(WM8994_DAC1_LEFT_MIXER_ROUTING, + WM8994_AIF2DACL_TO_DAC1L_MASK, + WM8994_AIF2DACL_TO_DAC1L); + + ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_MIXER_ROUTING, + WM8994_AIF2DACR_TO_DAC1R_MASK, + WM8994_AIF2DACR_TO_DAC1R); + + /* GPIO Settings for AIF2 */ + /* B CLK */ + ret |= wm8994_update_bits(WM8994_GPIO_3, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK , + WM8994_GPIO_DIR_OUTPUT | + WM8994_GPIO_FUNCTION_I2S_CLK); + + /* LR CLK */ + ret |= wm8994_update_bits(WM8994_GPIO_4, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK, + WM8994_GPIO_DIR_OUTPUT | + WM8994_GPIO_FUNCTION_I2S_CLK); + + /* DATA */ + ret |= wm8994_update_bits(WM8994_GPIO_5, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK, + WM8994_GPIO_DIR_OUTPUT | + WM8994_GPIO_FUNCTION_I2S_CLK); + + ret |= wm8994_init_volume_aif2_dac1(); + if (ret < 0) + goto err; + + debug("%s: Codec chip init ok\n", __func__); + return 0; +err: + debug("%s: Codec chip init error\n", __func__); + return -1; +} + +/*wm8994 Device Initialisation */ +int wm8994_init(struct sound_codec_info *pcodec_info, + enum en_audio_interface aif_id, + int sampling_rate, int mclk_freq, + int bits_per_sample, unsigned int channels) +{ + int ret = 0; + + /* shift the device address by 1 for 7 bit addressing */ + g_wm8994_i2c_dev_addr = pcodec_info->i2c_dev_addr; + wm8994_i2c_init(pcodec_info->i2c_bus); + + if (pcodec_info->codec_type == CODEC_WM_8994) + g_wm8994_info.type = WM8994; + else { + debug("%s: Codec id [%d] not defined\n", __func__, + pcodec_info->codec_type); + return -1; + } + + ret = wm8994_device_init(&g_wm8994_info); + if (ret < 0) { + debug("%s: wm8994 codec chip init failed\n", __func__); + return ret; + } + + ret = wm8994_set_sysclk(&g_wm8994_info, aif_id, WM8994_SYSCLK_MCLK1, + mclk_freq); + if (ret < 0) { + debug("%s: wm8994 codec set sys clock failed\n", __func__); + return ret; + } + + ret = wm8994_hw_params(&g_wm8994_info, aif_id, sampling_rate, + bits_per_sample, channels); + + if (ret == 0) { + ret = wm8994_set_fmt(aif_id, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + } + return ret; +} diff --git a/drivers/sound/wm8994.h b/drivers/sound/wm8994.h new file mode 100644 index 0000000..a8f0de1 --- /dev/null +++ b/drivers/sound/wm8994.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * R. Chadrasekar + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __WM8994_H__ +#define __WM8994_H__ + +/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */ +#define WM8994_SYSCLK_MCLK1 1 +#define WM8994_SYSCLK_MCLK2 2 +#define WM8994_SYSCLK_FLL1 3 +#define WM8994_SYSCLK_FLL2 4 + +/* Avilable audi interface ports in wm8994 codec */ +enum en_audio_interface { + WM8994_AIF1 = 1, + WM8994_AIF2, + WM8994_AIF3 +}; + +/* OPCLK is also configured with set_dai_sysclk, specify division*10 as rate. */ +#define WM8994_SYSCLK_OPCLK 5 + +#define WM8994_FLL1 1 +#define WM8994_FLL2 2 + +#define WM8994_FLL_SRC_MCLK1 1 +#define WM8994_FLL_SRC_MCLK2 2 +#define WM8994_FLL_SRC_LRCLK 3 +#define WM8994_FLL_SRC_BCLK 4 + +/* maximum available digital interfac in the dac to configure */ +#define WM8994_MAX_AIF 2 + +#define WM8994_MAX_INPUT_CLK_FREQ 13500000 +#define WM8994_ID 0x8994 + +enum wm8994_vmid_mode { + WM8994_VMID_NORMAL, + WM8994_VMID_FORCE, +}; + +/* wm 8994 family devices */ +enum wm8994_type { + WM8994 = 0, + WM8958 = 1, + WM1811 = 2, +}; + +/* + * intialise wm8994 sound codec device for the given configuration + * + * @param pcodec_info pointer value of the sound codec info structure + * parsed from device tree + * @param aif_id enum value of codec interface port in which + * soc i2s is connected + * @param sampling_rate Sampling rate ranges between from 8khz to 96khz + * @param mclk_freq Master clock frequency. + * @param bits_per_sample bits per Sample can be 16 or 24 + * @param channels Number of channnels, maximum 2 + * + * @returns -1 for error and 0 Success. + */ +int wm8994_init(struct sound_codec_info *pcodec_info, + enum en_audio_interface aif_id, + int sampling_rate, int mclk_freq, + int bits_per_sample, unsigned int channels); +#endif /*__WM8994_H__ */ diff --git a/drivers/sound/wm8994_registers.h b/drivers/sound/wm8994_registers.h new file mode 100644 index 0000000..f455b11 --- /dev/null +++ b/drivers/sound/wm8994_registers.h @@ -0,0 +1,299 @@ +/* + * (C) Copyright 2012 Samsung Electronics + * + * 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. + * + */ + +#ifndef __WM8994_REGISTERS_H__ +#define __WM8994_REGISTERS_H__ + +/* + * Register values. + */ +#define WM8994_SOFTWARE_RESET 0x00 +#define WM8994_POWER_MANAGEMENT_1 0x01 +#define WM8994_POWER_MANAGEMENT_2 0x02 +#define WM8994_POWER_MANAGEMENT_5 0x05 +#define WM8994_LEFT_OUTPUT_VOLUME 0x1C +#define WM8994_RIGHT_OUTPUT_VOLUME 0x1D +#define WM8994_OUTPUT_MIXER_1 0x2D +#define WM8994_OUTPUT_MIXER_2 0x2E +#define WM8994_CHARGE_PUMP_1 0x4C +#define WM8994_DC_SERVO_1 0x54 +#define WM8994_ANALOGUE_HP_1 0x60 +#define WM8994_CHIP_REVISION 0x100 +#define WM8994_AIF1_CLOCKING_1 0x200 +#define WM8994_AIF1_CLOCKING_2 0x201 +#define WM8994_AIF2_CLOCKING_1 0x204 +#define WM8994_CLOCKING_1 0x208 +#define WM8994_CLOCKING_2 0x209 +#define WM8994_AIF1_RATE 0x210 +#define WM8994_AIF2_RATE 0x211 +#define WM8994_RATE_STATUS 0x212 +#define WM8994_AIF1_CONTROL_1 0x300 +#define WM8994_AIF1_CONTROL_2 0x301 +#define WM8994_AIF1_MASTER_SLAVE 0x302 +#define WM8994_AIF1_BCLK 0x303 +#define WM8994_AIF2_CONTROL_1 0x310 +#define WM8994_AIF2_CONTROL_2 0x311 +#define WM8994_AIF2_MASTER_SLAVE 0x312 +#define WM8994_AIF2_BCLK 0x313 +#define WM8994_AIF2_DAC_LEFT_VOLUME 0x502 +#define WM8994_AIF2_DAC_RIGHT_VOLUME 0x503 +#define WM8994_AIF2_DAC_FILTERS_1 0x520 +#define WM8994_DAC1_LEFT_MIXER_ROUTING 0x601 +#define WM8994_DAC1_RIGHT_MIXER_ROUTING 0x602 +#define WM8994_DAC1_LEFT_VOLUME 0x610 +#define WM8994_DAC1_RIGHT_VOLUME 0x611 +#define WM8994_GPIO_3 0x702 +#define WM8994_GPIO_4 0x703 +#define WM8994_GPIO_5 0x704 + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - Software Reset + */ +/* SW_RESET */ +#define WM8994_SW_RESET 1 +/* + * R1 (0x01) - Power Management (1) + */ +/* HPOUT1L_ENA */ +#define WM8994_HPOUT1L_ENA 0x0200 +/* HPOUT1L_ENA */ +#define WM8994_HPOUT1L_ENA_MASK 0x0200 +/* HPOUT1R_ENA */ +#define WM8994_HPOUT1R_ENA 0x0100 +/* HPOUT1R_ENA */ +#define WM8994_HPOUT1R_ENA_MASK 0x0100 +/* VMID_SEL - [2:1] */ +#define WM8994_VMID_SEL_MASK 0x0006 +/* BIAS_ENA */ +#define WM8994_BIAS_ENA 0x0001 +/* BIAS_ENA */ +#define WM8994_BIAS_ENA_MASK 0x0001 + +/* + * R2 (0x02) - Power Management (2) + */ +/* OPCLK_ENA */ +#define WM8994_OPCLK_ENA 0x0800 + +/* + * R5 (0x05) - Power Management (5) + */ +/* AIF2DACL_ENA */ +#define WM8994_AIF2DACL_ENA 0x2000 +#define WM8994_AIF2DACL_ENA_MASK 0x2000 +/* AIF2DACR_ENA */ +#define WM8994_AIF2DACR_ENA 0x1000 +#define WM8994_AIF2DACR_ENA_MASK 0x1000 +/* DAC1L_ENA */ +#define WM8994_DAC1L_ENA 0x0002 +#define WM8994_DAC1L_ENA_MASK 0x0002 +/* DAC1R_ENA */ +#define WM8994_DAC1R_ENA 0x0001 +#define WM8994_DAC1R_ENA_MASK 0x0001 + +/* + * R45 (0x2D) - Output Mixer (1) + */ +/* DAC1L_TO_HPOUT1L */ +#define WM8994_DAC1L_TO_HPOUT1L 0x0100 +#define WM8994_DAC1L_TO_HPOUT1L_MASK 0x0100 + +/* + * R46 (0x2E) - Output Mixer (2) + */ +/* DAC1R_TO_HPOUT1R */ +#define WM8994_DAC1R_TO_HPOUT1R 0x0100 +#define WM8994_DAC1R_TO_HPOUT1R_MASK 0x0100 + +/* + * R76 (0x4C) - Charge Pump (1) + */ +/* CP_ENA */ +#define WM8994_CP_ENA 0x8000 +#define WM8994_CP_ENA_MASK 0x8000 +/* + * R84 (0x54) - DC Servo (1) + */ +/* DCS_ENA_CHAN_1 */ +#define WM8994_DCS_ENA_CHAN_1 0x0002 +#define WM8994_DCS_ENA_CHAN_1_MASK 0x0002 +/* DCS_ENA_CHAN_0 */ +#define WM8994_DCS_ENA_CHAN_0 0x0001 +#define WM8994_DCS_ENA_CHAN_0_MASK 0x0001 + +/* + * R96 (0x60) - Analogue HP (1) + */ +/* HPOUT1L_RMV_SHORT */ +#define WM8994_HPOUT1L_RMV_SHORT 0x0080 +#define WM8994_HPOUT1L_RMV_SHORT_MASK 0x0080 +/* HPOUT1L_OUTP */ +#define WM8994_HPOUT1L_OUTP 0x0040 +#define WM8994_HPOUT1L_OUTP_MASK 0x0040 +/* HPOUT1L_DLY */ +#define WM8994_HPOUT1L_DLY 0x0020 +#define WM8994_HPOUT1L_DLY_MASK 0x0020 +/* HPOUT1R_RMV_SHORT */ +#define WM8994_HPOUT1R_RMV_SHORT 0x0008 +#define WM8994_HPOUT1R_RMV_SHORT_MASK 0x0008 +/* HPOUT1R_OUTP */ +#define WM8994_HPOUT1R_OUTP 0x0004 +#define WM8994_HPOUT1R_OUTP_MASK 0x0004 +/* HPOUT1R_DLY */ +#define WM8994_HPOUT1R_DLY 0x0002 +#define WM8994_HPOUT1R_DLY_MASK 0x0002 + +/* + * R512 (0x200) - AIF1 Clocking (1) + */ +/* AIF1CLK_SRC - [4:3] */ +#define WM8994_AIF1CLK_SRC_MASK 0x0018 +/* AIF1CLK_DIV */ +#define WM8994_AIF1CLK_DIV 0x0002 +/* AIF1CLK_ENA */ +#define WM8994_AIF1CLK_ENA 0x0001 +#define WM8994_AIF1CLK_ENA_MASK 0x0001 + +/* + * R517 (0x205) - AIF2 Clocking (2) + */ +/* AIF2DAC_DIV - [5:3] */ +#define WM8994_AIF2DAC_DIV_MASK 0x0038 + +/* + * R520 (0x208) - Clocking (1) + */ +/* AIF2DSPCLK_ENA */ +#define WM8994_AIF2DSPCLK_ENA 0x0004 +#define WM8994_AIF2DSPCLK_ENA_MASK 0x0004 +/* SYSDSPCLK_ENA */ +#define WM8994_SYSDSPCLK_ENA 0x0002 +#define WM8994_SYSDSPCLK_ENA_MASK 0x0002 +/* SYSCLK_SRC */ +#define WM8994_SYSCLK_SRC 0x0001 + +/* + * R521 (0x209) - Clocking (2) + */ +/* OPCLK_DIV - [2:0] */ +#define WM8994_OPCLK_DIV_MASK 0x0007 + +/* + * R528 (0x210) - AIF1 Rate + */ +/* AIF1_SR - [7:4] */ +#define WM8994_AIF1_SR_MASK 0x00F0 +#define WM8994_AIF1_SR_SHIFT 4 +/* AIF1CLK_RATE - [3:0] */ +#define WM8994_AIF1CLK_RATE_MASK 0x000F + +/* + * R768 (0x300) - AIF1 Control (1) + */ +/* AIF1_BCLK_INV */ +#define WM8994_AIF1_BCLK_INV 0x0100 +/* AIF1_LRCLK_INV */ +#define WM8994_AIF1_LRCLK_INV 0x0080 +#define WM8994_AIF1_LRCLK_INV_MASK 0x0080 +/* AIF1_WL - [6:5] */ +#define WM8994_AIF1_WL_MASK 0x0060 +/* AIF1_FMT - [4:3] */ +#define WM8994_AIF1_FMT_MASK 0x0018 + +/* + * R769 (0x301) - AIF1 Control (2) + */ +/* AIF1_MONO */ +#define WM8994_AIF1_MONO 0x0100 + +/* + * R770 (0x302) - AIF1 Master/Slave + */ +/* AIF1_MSTR */ +#define WM8994_AIF1_MSTR 0x4000 +#define WM8994_AIF1_MSTR_MASK 0x4000 + +/* + * R771 (0x303) - AIF1 BCLK + */ +/* AIF1_BCLK_DIV - [8:4] */ +#define WM8994_AIF1_BCLK_DIV_MASK 0x01F0 +#define WM8994_AIF1_BCLK_DIV_SHIFT 4 + +/* + * R1282 (0x502) - AIF2 DAC Left Volume + */ +/* AIF2DAC_VU */ +#define WM8994_AIF2DAC_VU 0x0100 +#define WM8994_AIF2DAC_VU_MASK 0x0100 +/* AIF2DACL_VOL - [7:0] */ +#define WM8994_AIF2DACL_VOL_MASK 0x00FF + +/* + * R1283 (0x503) - AIF2 DAC Right Volume + */ +/* AIF2DACR_VOL - [7:0] */ +#define WM8994_AIF2DACR_VOL_MASK 0x00FF + +/* + * R1312 (0x520) - AIF2 DAC Filters (1) + */ +/* AIF2DAC_MUTE */ +#define WM8994_AIF2DAC_MUTE_MASK 0x0200 + +/* + * R1537 (0x601) - DAC1 Left Mixer Routing + */ +/* AIF2DACL_TO_DAC1L */ +#define WM8994_AIF2DACL_TO_DAC1L 0x0004 +#define WM8994_AIF2DACL_TO_DAC1L_MASK 0x0004 + +/* + * R1538 (0x602) - DAC1 Right Mixer Routing + */ +/* AIF2DACR_TO_DAC1R */ +#define WM8994_AIF2DACR_TO_DAC1R 0x0004 +#define WM8994_AIF2DACR_TO_DAC1R_MASK 0x0004 + +/* + * R1552 (0x610) - DAC1 Left Volume + */ +/* DAC1L_MUTE */ +#define WM8994_DAC1L_MUTE_MASK 0x0200 +/* DAC1_VU */ +#define WM8994_DAC1_VU 0x0100 +#define WM8994_DAC1_VU_MASK 0x0100 +/* DAC1L_VOL - [7:0] */ +#define WM8994_DAC1L_VOL_MASK 0x00FF + +/* + * R1553 (0x611) - DAC1 Right Volume + */ +/* DAC1R_MUTE */ +#define WM8994_DAC1R_MUTE_MASK 0x0200 +/* DAC1R_VOL - [7:0] */ +#define WM8994_DAC1R_VOL_MASK 0x00FF + +/* + * GPIO + */ +/* OUTPUT PIN */ +#define WM8994_GPIO_DIR_OUTPUT 0x8000 +/* GPIO PIN MASK */ +#define WM8994_GPIO_DIR_MASK 0xFFE0 +/* I2S CLK */ +#define WM8994_GPIO_FUNCTION_I2S_CLK 0x0000 +/* GPn FN */ +#define WM8994_GPIO_FUNCTION_MASK 0x001F +#endif -- cgit v1.1 From 1bf43b829eae86e70895fb90f0b3a102dfc7ba2c Mon Sep 17 00:00:00 2001 From: Rajeshwari Shinde Date: Fri, 2 Nov 2012 01:15:36 +0000 Subject: SPI: Add SPI Driver for EXYNOS. This patch adds SPI driver for EXYNOS. Signed-off-by: Simon Glass Signed-off-by: Padmavathi Venna Signed-off-by: Gabe Black Signed-off-by: Rajeshwari Shinde Signed-off-by: Hatim Ali Acked-by: Mike Frysinger Acked-by: Simon Glass Tested-by: jy0922.shim@samsung.com Signed-off-by: Minkyu Kang --- drivers/spi/Makefile | 1 + drivers/spi/exynos_spi.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 368 insertions(+) create mode 100644 drivers/spi/exynos_spi.c (limited to 'drivers') diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index f0b82c6..824d357 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -34,6 +34,7 @@ COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o COBJS-$(CONFIG_CF_SPI) += cf_spi.o COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o +COBJS-$(CONFIG_EXYNOS_SPI) += exynos_spi.o COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c new file mode 100644 index 0000000..3e6c18f --- /dev/null +++ b/drivers/spi/exynos_spi.c @@ -0,0 +1,367 @@ +/* + * (C) Copyright 2012 SAMSUNG Electronics + * Padmavathi Venna + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Information about each SPI controller */ +struct spi_bus { + enum periph_id periph_id; + s32 frequency; /* Default clock frequency, -1 for none */ + struct exynos_spi *regs; + int inited; /* 1 if this bus is ready for use */ +}; + +/* A list of spi buses that we know about */ +static struct spi_bus spi_bus[EXYNOS5_SPI_NUM_CONTROLLERS]; + +struct exynos_spi_slave { + struct spi_slave slave; + struct exynos_spi *regs; + unsigned int freq; /* Default frequency */ + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + unsigned int fifo_size; +}; + +static struct spi_bus *spi_get_bus(unsigned dev_index) +{ + if (dev_index < EXYNOS5_SPI_NUM_CONTROLLERS) + return &spi_bus[dev_index]; + debug("%s: invalid bus %d", __func__, dev_index); + + return NULL; +} + +static inline struct exynos_spi_slave *to_exynos_spi(struct spi_slave *slave) +{ + return container_of(slave, struct exynos_spi_slave, slave); +} + +/** + * Setup the driver private data + * + * @param bus ID of the bus that the slave is attached to + * @param cs ID of the chip select connected to the slave + * @param max_hz Required spi frequency + * @param mode Required spi mode (clk polarity, clk phase and + * master or slave) + * @return new device or NULL + */ +struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct exynos_spi_slave *spi_slave; + struct spi_bus *bus; + + if (!spi_cs_is_valid(busnum, cs)) { + debug("%s: Invalid bus/chip select %d, %d\n", __func__, + busnum, cs); + return NULL; + } + + spi_slave = malloc(sizeof(*spi_slave)); + if (!spi_slave) { + debug("%s: Could not allocate spi_slave\n", __func__); + return NULL; + } + + bus = &spi_bus[busnum]; + spi_slave->slave.bus = busnum; + spi_slave->slave.cs = cs; + spi_slave->regs = bus->regs; + spi_slave->mode = mode; + spi_slave->periph_id = bus->periph_id; + if (bus->periph_id == PERIPH_ID_SPI1 || + bus->periph_id == PERIPH_ID_SPI2) + spi_slave->fifo_size = 64; + else + spi_slave->fifo_size = 256; + + spi_slave->freq = bus->frequency; + if (max_hz) + spi_slave->freq = min(max_hz, spi_slave->freq); + + return &spi_slave->slave; +} + +/** + * Free spi controller + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_free_slave(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + free(spi_slave); +} + +/** + * Flush spi tx, rx fifos and reset the SPI controller + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +static void spi_flush_fifo(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + struct exynos_spi *regs = spi_slave->regs; + + clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); +} + +/** + * Initialize the spi base registers, set the required clock frequency and + * initialize the gpios + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + * @return zero on success else a negative value + */ +int spi_claim_bus(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + struct exynos_spi *regs = spi_slave->regs; + u32 reg = 0; + int ret; + + ret = set_spi_clk(spi_slave->periph_id, + spi_slave->freq); + if (ret < 0) { + debug("%s: Failed to setup spi clock\n", __func__); + return ret; + } + + exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE); + + spi_flush_fifo(slave); + + reg = readl(®s->ch_cfg); + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); + + if (spi_slave->mode & SPI_CPHA) + reg |= SPI_CH_CPHA_B; + + if (spi_slave->mode & SPI_CPOL) + reg |= SPI_CH_CPOL_L; + + writel(reg, ®s->ch_cfg); + writel(SPI_FB_DELAY_180, ®s->fb_clk); + + return 0; +} + +/** + * Reset the spi H/W and flush the tx and rx fifos + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_release_bus(struct spi_slave *slave) +{ + spi_flush_fifo(slave); +} + +static void spi_get_fifo_levels(struct exynos_spi *regs, + int *rx_lvl, int *tx_lvl) +{ + uint32_t spi_sts = readl(®s->spi_sts); + + *rx_lvl = (spi_sts >> SPI_RX_LVL_OFFSET) & SPI_FIFO_LVL_MASK; + *tx_lvl = (spi_sts >> SPI_TX_LVL_OFFSET) & SPI_FIFO_LVL_MASK; +} + +/** + * If there's something to transfer, do a software reset and set a + * transaction size. + * + * @param regs SPI peripheral registers + * @param count Number of bytes to transfer + */ +static void spi_request_bytes(struct exynos_spi *regs, int count) +{ + assert(count && count < (1 << 16)); + setbits_le32(®s->ch_cfg, SPI_CH_RST); + clrbits_le32(®s->ch_cfg, SPI_CH_RST); + writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt); +} + +static void spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, + void **dinp, void const **doutp) +{ + struct exynos_spi *regs = spi_slave->regs; + uchar *rxp = *dinp; + const uchar *txp = *doutp; + int rx_lvl, tx_lvl; + uint out_bytes, in_bytes; + + out_bytes = in_bytes = todo; + + /* + * If there's something to send, do a software reset and set a + * transaction size. + */ + spi_request_bytes(regs, todo); + + /* + * Bytes are transmitted/received in pairs. Wait to receive all the + * data because then transmission will be done as well. + */ + while (in_bytes) { + int temp; + + /* Keep the fifos full/empty. */ + spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl); + if (tx_lvl < spi_slave->fifo_size && out_bytes) { + temp = txp ? *txp++ : 0xff; + writel(temp, ®s->tx_data); + out_bytes--; + } + if (rx_lvl > 0 && in_bytes) { + temp = readl(®s->rx_data); + if (rxp) + *rxp++ = temp; + in_bytes--; + } + } + *dinp = rxp; + *doutp = txp; +} + +/** + * Transfer and receive data + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + * @param bitlen No of bits to tranfer or receive + * @param dout Pointer to transfer buffer + * @param din Pointer to receive buffer + * @param flags Flags for transfer begin and end + * @return zero on success else a negative value + */ +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + int upto, todo; + int bytelen; + + /* spi core configured to do 8 bit transfers */ + if (bitlen % 8) { + debug("Non byte aligned SPI transfer.\n"); + return -1; + } + + /* Start the transaction, if necessary. */ + if ((flags & SPI_XFER_BEGIN)) + spi_cs_activate(slave); + + /* Exynos SPI limits each transfer to 65535 bytes */ + bytelen = bitlen / 8; + for (upto = 0; upto < bytelen; upto += todo) { + todo = min(bytelen - upto, (1 << 16) - 1); + spi_rx_tx(spi_slave, todo, &din, &dout); + } + + /* Stop the transaction, if necessary. */ + if ((flags & SPI_XFER_END)) + spi_cs_deactivate(slave); + + return 0; +} + +/** + * Validates the bus and chip select numbers + * + * @param bus ID of the bus that the slave is attached to + * @param cs ID of the chip select connected to the slave + * @return one on success else zero + */ +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return spi_get_bus(bus) && cs == 0; +} + +/** + * Activate the CS by driving it LOW + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_cs_activate(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Activate CS, bus %d\n", spi_slave->slave.bus); +} + +/** + * Deactivate the CS by driving it HIGH + * + * @param slave Pointer to spi_slave to which controller has to + * communicate with + */ +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); + + setbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); + debug("Deactivate CS, bus %d\n", spi_slave->slave.bus); +} + +static inline struct exynos_spi *get_spi_base(int dev_index) +{ + if (dev_index < 3) + return (struct exynos_spi *)samsung_get_base_spi() + dev_index; + else + return (struct exynos_spi *)samsung_get_base_spi_isp() + + (dev_index - 3); +} + +/* Sadly there is no error return from this function */ +void spi_init(void) +{ + int i; + struct spi_bus *bus; + + for (i = 0; i < EXYNOS5_SPI_NUM_CONTROLLERS; i++) { + bus = &spi_bus[i]; + bus->regs = get_spi_base(i); + bus->periph_id = PERIPH_ID_SPI0 + i; + + /* Although Exynos5 supports upto 50Mhz speed, + * we are setting it to 10Mhz for safe side + */ + bus->frequency = 10000000; + bus->inited = 1; + } +} -- cgit v1.1 From 164738e9403c3a6a84eca7a2c2b9b78a62a3f57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 13 Nov 2012 09:55:57 +0000 Subject: ehci-mxc: Clean up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up ehci-mxc: - Remove useless #if's. - Fix identation. - Issue a #error if used with an unsupported platform. Signed-off-by: Benoît Thébaudeau Cc: Marek Vasut Cc: Stefano Babic --- drivers/usb/host/ehci-mxc.c | 86 +++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index a38bc9c..e21f2c5 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -28,14 +28,11 @@ #define USBCTRL_OTGBASE_OFFSET 0x600 -#ifdef CONFIG_MX25 #define MX25_USB_CTRL_IP_PUE_DOWN_BIT (1<<6) #define MX25_USB_CTRL_HSTD_BIT (1<<5) #define MX25_USB_CTRL_USBTE_BIT (1<<4) #define MX25_USB_CTRL_OCPOL_OTG_BIT (1<<3) -#endif -#ifdef CONFIG_MX31 #define MX31_OTG_SIC_SHIFT 29 #define MX31_OTG_SIC_MASK (0x3 << MX31_OTG_SIC_SHIFT) #define MX31_OTG_PM_BIT (1 << 24) @@ -49,59 +46,56 @@ #define MX31_H1_SIC_MASK (0x3 << MX31_H1_SIC_SHIFT) #define MX31_H1_PM_BIT (1 << 8) #define MX31_H1_DT_BIT (1 << 4) -#endif static int mxc_set_usbcontrol(int port, unsigned int flags) { unsigned int v; -#ifdef CONFIG_MX25 +#if defined(CONFIG_MX25) v = MX25_USB_CTRL_IP_PUE_DOWN_BIT | MX25_USB_CTRL_HSTD_BIT | MX25_USB_CTRL_USBTE_BIT | MX25_USB_CTRL_OCPOL_OTG_BIT; -#endif +#elif defined(CONFIG_MX31) + v = readl(IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET); -#ifdef CONFIG_MX31 - v = readl(IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET); - - switch (port) { - case 0: /* OTG port */ - v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT); - v |= (flags & MXC_EHCI_INTERFACE_MASK) - << MX31_OTG_SIC_SHIFT; - if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) - v |= MX31_OTG_PM_BIT; - - break; - case 1: /* H1 port */ - v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT | - MX31_H1_DT_BIT); - v |= (flags & MXC_EHCI_INTERFACE_MASK) - << MX31_H1_SIC_SHIFT; - if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) - v |= MX31_H1_PM_BIT; - - if (!(flags & MXC_EHCI_TTL_ENABLED)) - v |= MX31_H1_DT_BIT; - - break; - case 2: /* H2 port */ - v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT | - MX31_H2_DT_BIT); - v |= (flags & MXC_EHCI_INTERFACE_MASK) - << MX31_H2_SIC_SHIFT; - if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) - v |= MX31_H2_PM_BIT; - - if (!(flags & MXC_EHCI_TTL_ENABLED)) - v |= MX31_H2_DT_BIT; - - break; - default: - return -EINVAL; - } -#endif + switch (port) { + case 0: /* OTG port */ + v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_OTG_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX31_OTG_PM_BIT; + + break; + case 1: /* H1 port */ + v &= ~(MX31_H1_SIC_MASK | MX31_H1_PM_BIT | MX31_H1_DT_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H1_SIC_SHIFT; + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX31_H1_PM_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX31_H1_DT_BIT; + + break; + case 2: /* H2 port */ + v &= ~(MX31_H2_SIC_MASK | MX31_H2_PM_BIT | MX31_H2_DT_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX31_H2_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX31_H2_PM_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX31_H2_DT_BIT; + + break; + default: + return -EINVAL; + } +#else +#error MXC EHCI USB driver not supported on this platform +#endif writel(v, IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET); + return 0; } -- cgit v1.1 From bdc5202068ec778ae2eecb818559ce7c61057109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 13 Nov 2012 09:56:15 +0000 Subject: ehci-mx5: Clean up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clean up ehci-mx5: - Fix column alignments. - Fix comments. Signed-off-by: Benoît Thébaudeau Cc: Marek Vasut Cc: Stefano Babic --- drivers/usb/host/ehci-mx5.c | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index 9a2c295..46973b0 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -29,9 +29,9 @@ #define MX5_USBOTHER_REGS_OFFSET 0x800 -#define MXC_OTG_OFFSET 0 -#define MXC_H1_OFFSET 0x200 -#define MXC_H2_OFFSET 0x400 +#define MXC_OTG_OFFSET 0 +#define MXC_H1_OFFSET 0x200 +#define MXC_H2_OFFSET 0x400 #define MXC_USBCTRL_OFFSET 0 #define MXC_USB_PHY_CTR_FUNC_OFFSET 0x8 @@ -40,23 +40,30 @@ #define MXC_USBH2CTRL_OFFSET 0x14 /* USB_CTRL */ -#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27) /* OTG wakeup intr enable */ -#define MXC_OTG_UCTRL_OPM_BIT (1 << 24) /* OTG power mask */ -#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12) /* Host1 ULPI interrupt enable */ -#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11) /* HOST1 wakeup intr enable */ -#define MXC_H1_UCTRL_H1PM_BIT (1 << 8) /* HOST1 power mask */ +/* OTG wakeup intr enable */ +#define MXC_OTG_UCTRL_OWIE_BIT (1 << 27) +/* OTG power mask */ +#define MXC_OTG_UCTRL_OPM_BIT (1 << 24) +/* Host1 ULPI interrupt enable */ +#define MXC_H1_UCTRL_H1UIE_BIT (1 << 12) +/* HOST1 wakeup intr enable */ +#define MXC_H1_UCTRL_H1WIE_BIT (1 << 11) +/* HOST1 power mask */ +#define MXC_H1_UCTRL_H1PM_BIT (1 << 8) /* USB_PHY_CTRL_FUNC */ -#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) /* OTG Disable Overcurrent Event */ -#define MXC_H1_OC_DIS_BIT (1 << 5) /* UH1 Disable Overcurrent Event */ +/* OTG Disable Overcurrent Event */ +#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) +/* UH1 Disable Overcurrent Event */ +#define MXC_H1_OC_DIS_BIT (1 << 5) /* USBH2CTRL */ -#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8) -#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7) -#define MXC_H2_UCTRL_H2PM_BIT (1 << 4) +#define MXC_H2_UCTRL_H2UIE_BIT (1 << 8) +#define MXC_H2_UCTRL_H2WIE_BIT (1 << 7) +#define MXC_H2_UCTRL_H2PM_BIT (1 << 4) /* USB_CTRL_1 */ -#define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) +#define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) /* USB pin configuration */ #define USB_PAD_CONFIG (PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST | \ @@ -160,7 +167,7 @@ int mxc_set_usbcontrol(int port, unsigned int flags) __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); } break; - case 1: /* Host 1 Host ULPI */ + case 1: /* Host 1 ULPI */ #ifdef CONFIG_MX51 /* The clock for the USBH1 ULPI port will come externally from the PHY. */ @@ -171,9 +178,9 @@ int mxc_set_usbcontrol(int port, unsigned int flags) v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); if (flags & MXC_EHCI_POWER_PINS_ENABLED) - v &= ~MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */ + v &= ~MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask unused */ else - v |= MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */ + v |= MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask used */ __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); @@ -187,9 +194,9 @@ int mxc_set_usbcontrol(int port, unsigned int flags) case 2: /* Host 2 ULPI */ v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET); if (flags & MXC_EHCI_POWER_PINS_ENABLED) - v &= ~MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */ + v &= ~MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask unused */ else - v |= MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */ + v |= MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask used */ __raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET); break; -- cgit v1.1 From 7d42432d38af11f9d05398c3b6d29a54ab3322c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 13 Nov 2012 09:56:30 +0000 Subject: ehci-mx5: Fix OC_DIS usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MXC_OTG_PHYCTRL_OC_DIS_BIT disables the oc pin if set, like MXC_H1_OC_DIS_BIT, not the opposite. Signed-off-by: Benoît Thébaudeau Cc: Marek Vasut Cc: Stefano Babic --- drivers/usb/host/ehci-mx5.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index 46973b0..72cde1a 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -151,11 +151,11 @@ int mxc_set_usbcontrol(int port, unsigned int flags) v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); if (flags & MXC_EHCI_POWER_PINS_ENABLED) - /* OC/USBPWR is not used */ - v |= MXC_OTG_PHYCTRL_OC_DIS_BIT; - else /* OC/USBPWR is used */ v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT; + else + /* OC/USBPWR is not used */ + v |= MXC_OTG_PHYCTRL_OC_DIS_BIT; __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); -- cgit v1.1 From 394c00dcfc9751030cbd9eb87ab2492d234f9ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 13 Nov 2012 09:56:44 +0000 Subject: ehci-mx5: Fix OPM usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MXC_OTG_UCTRL_OPM_BIT disables (masks) the power/oc pins if set, like MXC_H1_UCTRL_H1PM_BIT and MXC_H2_UCTRL_H2PM_BIT, not the opposite. Signed-off-by: Benoît Thébaudeau Cc: Marek Vasut Cc: Stefano Babic --- drivers/usb/host/ehci-mx5.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index 72cde1a..8c1b70a 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -161,9 +161,9 @@ int mxc_set_usbcontrol(int port, unsigned int flags) v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); if (flags & MXC_EHCI_POWER_PINS_ENABLED) - v |= MXC_OTG_UCTRL_OPM_BIT; - else v &= ~MXC_OTG_UCTRL_OPM_BIT; + else + v |= MXC_OTG_UCTRL_OPM_BIT; __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); } break; -- cgit v1.1 From 661052f47622ce234e592622020f69b61f04c649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 13 Nov 2012 09:56:59 +0000 Subject: ehci-mx5: Fix *PM usage for i.MX53 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MXC_*_UCTRL_*PM_BIT bits are available only on i.MX51. Signed-off-by: Benoît Thébaudeau Cc: Marek Vasut Cc: Stefano Babic --- drivers/usb/host/ehci-mx5.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index 8c1b70a..4f4b98a 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -160,10 +160,12 @@ int mxc_set_usbcontrol(int port, unsigned int flags) MXC_USB_PHY_CTR_FUNC_OFFSET); v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); +#ifdef CONFIG_MX51 if (flags & MXC_EHCI_POWER_PINS_ENABLED) v &= ~MXC_OTG_UCTRL_OPM_BIT; else v |= MXC_OTG_UCTRL_OPM_BIT; +#endif __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); } break; @@ -177,10 +179,12 @@ int mxc_set_usbcontrol(int port, unsigned int flags) #endif v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); +#ifdef CONFIG_MX51 if (flags & MXC_EHCI_POWER_PINS_ENABLED) v &= ~MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask unused */ else v |= MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask used */ +#endif __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); @@ -193,11 +197,12 @@ int mxc_set_usbcontrol(int port, unsigned int flags) break; case 2: /* Host 2 ULPI */ v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET); +#ifdef CONFIG_MX51 if (flags & MXC_EHCI_POWER_PINS_ENABLED) v &= ~MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask unused */ else v |= MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask used */ - +#endif __raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET); break; } -- cgit v1.1 From 2cfe0b8f132a04b2f1075355f062a4d05c95f096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 13 Nov 2012 09:57:14 +0000 Subject: ehci-mx5: Add missing OC_DIS for i.MX53 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The i.MX53 has MXC_H*_UCTRL_H*_OC_DIS_BIT bits to disable the oc pin. Signed-off-by: Benoît Thébaudeau Cc: Marek Vasut Cc: Stefano Babic --- drivers/usb/host/ehci-mx5.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index 4f4b98a..6f7fe80 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -32,12 +32,14 @@ #define MXC_OTG_OFFSET 0 #define MXC_H1_OFFSET 0x200 #define MXC_H2_OFFSET 0x400 +#define MXC_H3_OFFSET 0x600 #define MXC_USBCTRL_OFFSET 0 #define MXC_USB_PHY_CTR_FUNC_OFFSET 0x8 #define MXC_USB_PHY_CTR_FUNC2_OFFSET 0xc #define MXC_USB_CTRL_1_OFFSET 0x10 #define MXC_USBH2CTRL_OFFSET 0x14 +#define MXC_USBH3CTRL_OFFSET 0x18 /* USB_CTRL */ /* OTG wakeup intr enable */ @@ -58,10 +60,16 @@ #define MXC_H1_OC_DIS_BIT (1 << 5) /* USBH2CTRL */ +#define MXC_H2_UCTRL_H2_OC_DIS_BIT (1 << 30) #define MXC_H2_UCTRL_H2UIE_BIT (1 << 8) #define MXC_H2_UCTRL_H2WIE_BIT (1 << 7) #define MXC_H2_UCTRL_H2PM_BIT (1 << 4) +/* USBH3CTRL */ +#define MXC_H3_UCTRL_H3_OC_DIS_BIT (1 << 30) +#define MXC_H3_UCTRL_H3UIE_BIT (1 << 8) +#define MXC_H3_UCTRL_H3WIE_BIT (1 << 7) + /* USB_CTRL_1 */ #define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) @@ -203,8 +211,24 @@ int mxc_set_usbcontrol(int port, unsigned int flags) else v |= MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask used */ #endif +#ifdef CONFIG_MX53 + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is used */ + else + v |= MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is not used */ +#endif __raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET); break; +#ifdef CONFIG_MX53 + case 3: /* Host 3 ULPI */ + v = __raw_readl(usbother_base + MXC_USBH3CTRL_OFFSET); + if (flags & MXC_EHCI_POWER_PINS_ENABLED) + v &= ~MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is used */ + else + v |= MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is not used */ + __raw_writel(v, usbother_base + MXC_USBH3CTRL_OFFSET); + break; +#endif } return ret; -- cgit v1.1 From 31ac2d0c6af61b16ab8c3c6cefe61f53a06219d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 13 Nov 2012 09:57:27 +0000 Subject: ehci-mxc: Make EHCI power/oc polarities configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make EHCI power and overcurrent polarities configurable. If not set, these new configurartions keep the default register values so that existing board files do not have to be changed. Signed-off-by: Benoît Thébaudeau Cc: Marek Vasut Cc: Stefano Babic --- drivers/usb/host/ehci-mx5.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index 6f7fe80..7e60c3c 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -46,29 +46,43 @@ #define MXC_OTG_UCTRL_OWIE_BIT (1 << 27) /* OTG power mask */ #define MXC_OTG_UCTRL_OPM_BIT (1 << 24) +/* OTG power pin polarity */ +#define MXC_OTG_UCTRL_O_PWR_POL_BIT (1 << 24) /* Host1 ULPI interrupt enable */ #define MXC_H1_UCTRL_H1UIE_BIT (1 << 12) /* HOST1 wakeup intr enable */ #define MXC_H1_UCTRL_H1WIE_BIT (1 << 11) /* HOST1 power mask */ #define MXC_H1_UCTRL_H1PM_BIT (1 << 8) +/* HOST1 power pin polarity */ +#define MXC_H1_UCTRL_H1_PWR_POL_BIT (1 << 8) /* USB_PHY_CTRL_FUNC */ +/* OTG Polarity of Overcurrent */ +#define MXC_OTG_PHYCTRL_OC_POL_BIT (1 << 9) /* OTG Disable Overcurrent Event */ #define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) +/* UH1 Polarity of Overcurrent */ +#define MXC_H1_OC_POL_BIT (1 << 6) /* UH1 Disable Overcurrent Event */ #define MXC_H1_OC_DIS_BIT (1 << 5) +/* OTG Power Pin Polarity */ +#define MXC_OTG_PHYCTRL_PWR_POL_BIT (1 << 3) /* USBH2CTRL */ +#define MXC_H2_UCTRL_H2_OC_POL_BIT (1 << 31) #define MXC_H2_UCTRL_H2_OC_DIS_BIT (1 << 30) #define MXC_H2_UCTRL_H2UIE_BIT (1 << 8) #define MXC_H2_UCTRL_H2WIE_BIT (1 << 7) #define MXC_H2_UCTRL_H2PM_BIT (1 << 4) +#define MXC_H2_UCTRL_H2_PWR_POL_BIT (1 << 4) /* USBH3CTRL */ +#define MXC_H3_UCTRL_H3_OC_POL_BIT (1 << 31) #define MXC_H3_UCTRL_H3_OC_DIS_BIT (1 << 30) #define MXC_H3_UCTRL_H3UIE_BIT (1 << 8) #define MXC_H3_UCTRL_H3WIE_BIT (1 << 7) +#define MXC_H3_UCTRL_H3_PWR_POL_BIT (1 << 4) /* USB_CTRL_1 */ #define MXC_USB_CTRL_UH1_EXT_CLK_EN (1 << 25) @@ -158,12 +172,22 @@ int mxc_set_usbcontrol(int port, unsigned int flags) if (flags & MXC_EHCI_INTERNAL_PHY) { v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) + v |= MXC_OTG_PHYCTRL_OC_POL_BIT; + else + v &= ~MXC_OTG_PHYCTRL_OC_POL_BIT; if (flags & MXC_EHCI_POWER_PINS_ENABLED) /* OC/USBPWR is used */ v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT; else /* OC/USBPWR is not used */ v |= MXC_OTG_PHYCTRL_OC_DIS_BIT; +#ifdef CONFIG_MX51 + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MXC_OTG_PHYCTRL_PWR_POL_BIT; + else + v &= ~MXC_OTG_PHYCTRL_PWR_POL_BIT; +#endif __raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); @@ -174,6 +198,12 @@ int mxc_set_usbcontrol(int port, unsigned int flags) else v |= MXC_OTG_UCTRL_OPM_BIT; #endif +#ifdef CONFIG_MX53 + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MXC_OTG_UCTRL_O_PWR_POL_BIT; + else + v &= ~MXC_OTG_UCTRL_O_PWR_POL_BIT; +#endif __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); } break; @@ -193,9 +223,19 @@ int mxc_set_usbcontrol(int port, unsigned int flags) else v |= MXC_H1_UCTRL_H1PM_BIT; /* H1 power mask used */ #endif +#ifdef CONFIG_MX53 + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MXC_H1_UCTRL_H1_PWR_POL_BIT; + else + v &= ~MXC_H1_UCTRL_H1_PWR_POL_BIT; +#endif __raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) + v |= MXC_H1_OC_POL_BIT; + else + v &= ~MXC_H1_OC_POL_BIT; if (flags & MXC_EHCI_POWER_PINS_ENABLED) v &= ~MXC_H1_OC_DIS_BIT; /* OC is used */ else @@ -212,20 +252,36 @@ int mxc_set_usbcontrol(int port, unsigned int flags) v |= MXC_H2_UCTRL_H2PM_BIT; /* H2 power mask used */ #endif #ifdef CONFIG_MX53 + if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) + v |= MXC_H2_UCTRL_H2_OC_POL_BIT; + else + v &= ~MXC_H2_UCTRL_H2_OC_POL_BIT; if (flags & MXC_EHCI_POWER_PINS_ENABLED) v &= ~MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is used */ else v |= MXC_H2_UCTRL_H2_OC_DIS_BIT; /* OC is not used */ + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MXC_H2_UCTRL_H2_PWR_POL_BIT; + else + v &= ~MXC_H2_UCTRL_H2_PWR_POL_BIT; #endif __raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET); break; #ifdef CONFIG_MX53 case 3: /* Host 3 ULPI */ v = __raw_readl(usbother_base + MXC_USBH3CTRL_OFFSET); + if (flags & MXC_EHCI_OC_PIN_ACTIVE_LOW) + v |= MXC_H3_UCTRL_H3_OC_POL_BIT; + else + v &= ~MXC_H3_UCTRL_H3_OC_POL_BIT; if (flags & MXC_EHCI_POWER_PINS_ENABLED) v &= ~MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is used */ else v |= MXC_H3_UCTRL_H3_OC_DIS_BIT; /* OC is not used */ + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MXC_H3_UCTRL_H3_PWR_POL_BIT; + else + v &= ~MXC_H3_UCTRL_H3_PWR_POL_BIT; __raw_writel(v, usbother_base + MXC_USBH3CTRL_OFFSET); break; #endif -- cgit v1.1 From 9fa3d093d626b25c64c695e29d381dd2ae7bf8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 13 Nov 2012 09:57:48 +0000 Subject: ehci-mxc: Make i.MX25 EHCI configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use EHCI MXC configuration options for i.MX25. Signed-off-by: Benoît Thébaudeau Cc: Marek Vasut Cc: Stefano Babic Cc: Matthias Weisser --- drivers/usb/host/ehci-mxc.c | 72 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 64 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index e21f2c5..846aa3b 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -28,10 +28,21 @@ #define USBCTRL_OTGBASE_OFFSET 0x600 -#define MX25_USB_CTRL_IP_PUE_DOWN_BIT (1<<6) -#define MX25_USB_CTRL_HSTD_BIT (1<<5) -#define MX25_USB_CTRL_USBTE_BIT (1<<4) -#define MX25_USB_CTRL_OCPOL_OTG_BIT (1<<3) +#define MX25_OTG_SIC_SHIFT 29 +#define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT) +#define MX25_OTG_PM_BIT (1 << 24) +#define MX25_OTG_PP_BIT (1 << 11) +#define MX25_OTG_OCPOL_BIT (1 << 3) + +#define MX25_H1_SIC_SHIFT 21 +#define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT) +#define MX25_H1_PP_BIT (1 << 18) +#define MX25_H1_PM_BIT (1 << 8) +#define MX25_H1_IPPUE_UP_BIT (1 << 7) +#define MX25_H1_IPPUE_DOWN_BIT (1 << 6) +#define MX25_H1_TLL_BIT (1 << 5) +#define MX25_H1_USBTE_BIT (1 << 4) +#define MX25_H1_OCPOL_BIT (1 << 2) #define MX31_OTG_SIC_SHIFT 29 #define MX31_OTG_SIC_MASK (0x3 << MX31_OTG_SIC_SHIFT) @@ -51,12 +62,57 @@ static int mxc_set_usbcontrol(int port, unsigned int flags) { unsigned int v; -#if defined(CONFIG_MX25) - v = MX25_USB_CTRL_IP_PUE_DOWN_BIT | MX25_USB_CTRL_HSTD_BIT | - MX25_USB_CTRL_USBTE_BIT | MX25_USB_CTRL_OCPOL_OTG_BIT; -#elif defined(CONFIG_MX31) v = readl(IMX_USB_BASE + USBCTRL_OTGBASE_OFFSET); +#if defined(CONFIG_MX25) + switch (port) { + case 0: /* OTG port */ + v &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PM_BIT | MX25_OTG_PP_BIT | + MX25_OTG_OCPOL_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX25_OTG_PM_BIT; + + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MX25_OTG_PP_BIT; + + if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) + v |= MX25_OTG_OCPOL_BIT; + + break; + case 1: /* H1 port */ + v &= ~(MX25_H1_SIC_MASK | MX25_H1_PM_BIT | MX25_H1_PP_BIT | + MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT | + MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT | + MX25_H1_IPPUE_UP_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX25_H1_PM_BIT; + + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MX25_H1_PP_BIT; + + if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) + v |= MX25_H1_OCPOL_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX25_H1_TLL_BIT; + + if (flags & MXC_EHCI_INTERNAL_PHY) + v |= MX25_H1_USBTE_BIT; + + if (flags & MXC_EHCI_IPPUE_DOWN) + v |= MX25_H1_IPPUE_DOWN_BIT; + if (flags & MXC_EHCI_IPPUE_UP) + v |= MX25_H1_IPPUE_UP_BIT; + + break; + default: + return -EINVAL; + } +#elif defined(CONFIG_MX31) switch (port) { case 0: /* OTG port */ v &= ~(MX31_OTG_SIC_MASK | MX31_OTG_PM_BIT); -- cgit v1.1 From 34d33b671a03da1c115d83a603fb36da0360b20a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 13 Nov 2012 09:57:59 +0000 Subject: ehci-mxc: Define host offsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some MXC SoCs like the i.MX35 have hosts located at unusual offsets, so prepare to the introduction of i.MX35 support by defining the ehci-mxc hosts offsets at SoC level. Signed-off-by: Benoît Thébaudeau Cc: Marek Vasut Cc: Stefano Babic --- drivers/usb/host/ehci-mxc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 846aa3b..6260a8c 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -169,7 +169,7 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) udelay(80); ehci = (struct usb_ehci *)(IMX_USB_BASE + - (0x200 * CONFIG_MXC_USB_PORT)); + IMX_USB_PORT_OFFSET * CONFIG_MXC_USB_PORT); *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); *hcor = (struct ehci_hcor *)((uint32_t) *hccr + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); -- cgit v1.1 From 71a5c55bfa776b29d11b85e80945b89af06e6546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 13 Nov 2012 09:58:12 +0000 Subject: ehci-mxc: Add support for i.MX35 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Benoît Thébaudeau Cc: Marek Vasut Cc: Stefano Babic --- drivers/usb/host/ehci-mxc.c | 69 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 6260a8c..7c5f71c 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -58,6 +58,22 @@ #define MX31_H1_PM_BIT (1 << 8) #define MX31_H1_DT_BIT (1 << 4) +#define MX35_OTG_SIC_SHIFT 29 +#define MX35_OTG_SIC_MASK (0x3 << MX35_OTG_SIC_SHIFT) +#define MX35_OTG_PM_BIT (1 << 24) +#define MX35_OTG_PP_BIT (1 << 11) +#define MX35_OTG_OCPOL_BIT (1 << 3) + +#define MX35_H1_SIC_SHIFT 21 +#define MX35_H1_SIC_MASK (0x3 << MX35_H1_SIC_SHIFT) +#define MX35_H1_PP_BIT (1 << 18) +#define MX35_H1_PM_BIT (1 << 8) +#define MX35_H1_IPPUE_UP_BIT (1 << 7) +#define MX35_H1_IPPUE_DOWN_BIT (1 << 6) +#define MX35_H1_TLL_BIT (1 << 5) +#define MX35_H1_USBTE_BIT (1 << 4) +#define MX35_H1_OCPOL_BIT (1 << 2) + static int mxc_set_usbcontrol(int port, unsigned int flags) { unsigned int v; @@ -147,6 +163,55 @@ static int mxc_set_usbcontrol(int port, unsigned int flags) default: return -EINVAL; } +#elif defined(CONFIG_MX35) + switch (port) { + case 0: /* OTG port */ + v &= ~(MX35_OTG_SIC_MASK | MX35_OTG_PM_BIT | MX35_OTG_PP_BIT | + MX35_OTG_OCPOL_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_OTG_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX35_OTG_PM_BIT; + + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MX35_OTG_PP_BIT; + + if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) + v |= MX35_OTG_OCPOL_BIT; + + break; + case 1: /* H1 port */ + v &= ~(MX35_H1_SIC_MASK | MX35_H1_PM_BIT | MX35_H1_PP_BIT | + MX35_H1_OCPOL_BIT | MX35_H1_TLL_BIT | + MX35_H1_USBTE_BIT | MX35_H1_IPPUE_DOWN_BIT | + MX35_H1_IPPUE_UP_BIT); + v |= (flags & MXC_EHCI_INTERFACE_MASK) << MX35_H1_SIC_SHIFT; + + if (!(flags & MXC_EHCI_POWER_PINS_ENABLED)) + v |= MX35_H1_PM_BIT; + + if (flags & MXC_EHCI_PWR_PIN_ACTIVE_HIGH) + v |= MX35_H1_PP_BIT; + + if (!(flags & MXC_EHCI_OC_PIN_ACTIVE_LOW)) + v |= MX35_H1_OCPOL_BIT; + + if (!(flags & MXC_EHCI_TTL_ENABLED)) + v |= MX35_H1_TLL_BIT; + + if (flags & MXC_EHCI_INTERNAL_PHY) + v |= MX35_H1_USBTE_BIT; + + if (flags & MXC_EHCI_IPPUE_DOWN) + v |= MX35_H1_IPPUE_DOWN_BIT; + + if (flags & MXC_EHCI_IPPUE_UP) + v |= MX35_H1_IPPUE_UP_BIT; + + break; + default: + return -EINVAL; + } #else #error MXC EHCI USB driver not supported on this platform #endif @@ -176,6 +241,10 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) setbits_le32(&ehci->usbmode, CM_HOST); __raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc); mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS); +#ifdef CONFIG_MX35 + /* Workaround for ENGcm11601 */ + __raw_writel(0, &ehci->sbuscfg); +#endif udelay(10000); -- cgit v1.1 From f22e4fae19c05ae66c83b846fee66a1b8fbc5a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Tue, 13 Nov 2012 09:58:35 +0000 Subject: ehci-mx5/6: Make board_ehci_hcd_init() optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A custom board_ehci_hcd_init() may be unneeded, so add a weak default implementation doing nothing. By the way, use simple __weak from linux/compiler.h for board_ehci_hcd_postinit() instead of weak alias with full attribute. Signed-off-by: Benoît Thébaudeau Cc: Marek Vasut Cc: Stefano Babic --- drivers/usb/host/ehci-mx5.c | 8 +++++--- drivers/usb/host/ehci-mx6.c | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index 7e60c3c..adbed5c 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -290,12 +290,14 @@ int mxc_set_usbcontrol(int port, unsigned int flags) return ret; } -void __board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) +int __weak board_ehci_hcd_init(int port) { + return 0; } -void board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) - __attribute((weak, alias("__board_ehci_hcd_postinit"))); +void __weak board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) +{ +} int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) { diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 9ce25da..1b20e41 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -159,6 +159,11 @@ static void usbh1_oc_config(void) __raw_writel(val, usbother_base + USB_H1_CTRL_OFFSET); } +int __weak board_ehci_hcd_init(int port) +{ + return 0; +} + int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) { struct usb_ehci *ehci; -- cgit v1.1 From de5bf02cb1f61de0c65a539cd0083ac8ab07ec50 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 15 Nov 2012 11:23:23 +0000 Subject: spi: mxc_spi: Fix handling of chip select In decode_cs() function the polarity of the chip select must be taken into account. Also, for the case of low active chip select, the CS was activated too early. Signed-off-by: Fabio Estevam --- drivers/spi/mxc_spi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c index 13bebe8..b6bad98 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -387,7 +387,7 @@ static int decode_cs(struct mxc_spi_slave *mxcs, unsigned int cs) if (cs > 3) { mxcs->gpio = cs >> 8; cs &= 3; - ret = gpio_direction_output(mxcs->gpio, 0); + ret = gpio_direction_output(mxcs->gpio, !(mxcs->ss_pol)); if (ret) { printf("mxc_spi: cannot setup gpio %d\n", mxcs->gpio); return -EINVAL; @@ -414,6 +414,8 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, return NULL; } + mxcs->ss_pol = (mode & SPI_CS_HIGH) ? 1 : 0; + ret = decode_cs(mxcs, cs); if (ret < 0) { free(mxcs); @@ -425,7 +427,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, mxcs->slave.bus = bus; mxcs->slave.cs = cs; mxcs->base = spi_bases[bus]; - mxcs->ss_pol = (mode & SPI_CS_HIGH) ? 1 : 0; ret = spi_cfg_mxc(mxcs, cs, max_hz, mode); if (ret) { -- cgit v1.1 From 3cea335c3410f71524b50263b8af0e7eb69ebbe4 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 15 Nov 2012 11:23:24 +0000 Subject: spi: mxc_spi: Fix spi clock glitch durant reset Measuring the spi clock line on a scope shows a 'glitch' during the reset of the spi. Fix this by toggling only the MXC_CSPICTRL_EN bit, so that the clock line becomes always stable. Signed-off-by: Fabio Estevam Acked-by: Stefano Babic --- drivers/spi/mxc_spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c index b6bad98..859c43f 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -140,8 +140,8 @@ static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs, reg_ctrl = reg_read(®s->ctrl); /* Reset spi */ - reg_write(®s->ctrl, 0); - reg_write(®s->ctrl, (reg_ctrl | 0x1)); + reg_write(®s->ctrl, (reg_ctrl & ~MXC_CSPICTRL_EN)); + reg_write(®s->ctrl, (reg_ctrl | MXC_CSPICTRL_EN)); /* * The following computation is taken directly from Freescale's code. -- cgit v1.1 From ed0a6fcff92ec14947916291a33167188b4218d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Fri, 16 Nov 2012 01:42:49 +0000 Subject: ehci-mxc: Fix host power mask bit for i.MX35 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The correct bit for H1_PM is 16, not 8, which is the DP pull-up impedance selection bit. This issue has been reported by Michael Burkey and fixed by Christoph Fritz on Linux, from which these #define-s had been copied. Signed-off-by: Benoît Thébaudeau Cc: Stefano Babic Cc: Marek Vasut Acked-by: Stefano Babic --- drivers/usb/host/ehci-mxc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 7c5f71c..f2642a0 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -67,7 +67,7 @@ #define MX35_H1_SIC_SHIFT 21 #define MX35_H1_SIC_MASK (0x3 << MX35_H1_SIC_SHIFT) #define MX35_H1_PP_BIT (1 << 18) -#define MX35_H1_PM_BIT (1 << 8) +#define MX35_H1_PM_BIT (1 << 16) #define MX35_H1_IPPUE_UP_BIT (1 << 7) #define MX35_H1_IPPUE_DOWN_BIT (1 << 6) #define MX35_H1_TLL_BIT (1 << 5) -- cgit v1.1 From fa88ddb75f0abeee123dd1547f4e32ec3155d31f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Th=C3=A9baudeau?= Date: Fri, 16 Nov 2012 06:46:24 +0000 Subject: ehci-mxc: Fix host power mask bit for i.MX25 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The correct bit for H1_PM is 16, not 8, which is the DP pull-up impedance selection bit. This issue has been reported by Eric Bénard and fixed by Christoph Fritz on Linux, from which these #define-s had been copied. Signed-off-by: Benoît Thébaudeau Cc: Stefano Babic Cc: Marek Vasut --- drivers/usb/host/ehci-mxc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index f2642a0..8633cab 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -37,7 +37,7 @@ #define MX25_H1_SIC_SHIFT 21 #define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT) #define MX25_H1_PP_BIT (1 << 18) -#define MX25_H1_PM_BIT (1 << 8) +#define MX25_H1_PM_BIT (1 << 16) #define MX25_H1_IPPUE_UP_BIT (1 << 7) #define MX25_H1_IPPUE_DOWN_BIT (1 << 6) #define MX25_H1_TLL_BIT (1 << 5) -- cgit v1.1 From 0be8f203f6aba3c1fc117fd0c651c324977c4a27 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 17 Oct 2012 13:24:51 +0000 Subject: tegra: Add LCD driver This driver supports driving a single LCD and providing a U-Boot console on it. Signed-off-by: Simon Glass Signed-off-by: Tom Warren --- drivers/video/Makefile | 1 + drivers/video/tegra.c | 368 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 369 insertions(+) create mode 100644 drivers/video/tegra.c (limited to 'drivers') diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 247b867..b3207c8 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -50,6 +50,7 @@ COBJS-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o COBJS-$(CONFIG_VIDEO_SED13806) += sed13806.o COBJS-$(CONFIG_VIDEO_SM501) += sm501.o COBJS-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o +COBJS-$(CONFIG_VIDEO_TEGRA) += tegra.o COBJS-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o COBJS := $(sort $(COBJS-y)) diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c new file mode 100644 index 0000000..ab26ad0 --- /dev/null +++ b/drivers/video/tegra.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* These are the stages we go throuh in enabling the LCD */ +enum stage_t { + STAGE_START, + STAGE_PANEL_VDD, + STAGE_LVDS, + STAGE_BACKLIGHT_VDD, + STAGE_PWM, + STAGE_BACKLIGHT_EN, + STAGE_DONE, +}; + +static enum stage_t stage; /* Current stage we are at */ +static unsigned long timer_next; /* Time we can move onto next stage */ + +/* Our LCD config, set up in handle_stage() */ +static struct fdt_panel_config config; +struct fdt_disp_config *disp_config; /* Display controller config */ + +enum { + /* Maximum LCD size we support */ + LCD_MAX_WIDTH = 1366, + LCD_MAX_HEIGHT = 768, + LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */ +}; + +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; + +void *lcd_base; /* Start of framebuffer memory */ +void *lcd_console_address; /* Start of console buffer */ + +short console_col; +short console_row; + +vidinfo_t panel_info = { + /* Insert a value here so that we don't end up in the BSS */ + .vl_col = -1, +}; + +char lcd_cursor_enabled; + +ushort lcd_cursor_width; +ushort lcd_cursor_height; + +#ifndef CONFIG_OF_CONTROL +#error "You must enable CONFIG_OF_CONTROL to get Tegra LCD support" +#endif + +void lcd_cursor_size(ushort width, ushort height) +{ + lcd_cursor_width = width; + lcd_cursor_height = height; +} + +void lcd_toggle_cursor(void) +{ + ushort x, y; + uchar *dest; + ushort row; + + x = console_col * lcd_cursor_width; + y = console_row * lcd_cursor_height; + dest = (uchar *)(lcd_base + y * lcd_line_length + x * (1 << LCD_BPP) / + 8); + + for (row = 0; row < lcd_cursor_height; ++row, dest += lcd_line_length) { + ushort *d = (ushort *)dest; + ushort color; + int i; + + for (i = 0; i < lcd_cursor_width; ++i) { + color = *d; + color ^= lcd_color_fg; + *d = color; + ++d; + } + } +} + +void lcd_cursor_on(void) +{ + lcd_cursor_enabled = 1; + lcd_toggle_cursor(); +} +void lcd_cursor_off(void) +{ + lcd_cursor_enabled = 0; + lcd_toggle_cursor(); +} + +char lcd_is_cursor_enabled(void) +{ + return lcd_cursor_enabled; +} + +static void update_panel_size(struct fdt_disp_config *config) +{ + panel_info.vl_col = config->width; + panel_info.vl_row = config->height; + panel_info.vl_bpix = config->log2_bpp; +} + +/* + * Main init function called by lcd driver. + * Inits and then prints test pattern if required. + */ + +void lcd_ctrl_init(void *lcdbase) +{ + int line_length, size; + + assert(disp_config); + + lcd_base = (void *)disp_config->frame_buffer; + + /* Make sure that we can acommodate the selected LCD */ + assert(disp_config->width <= LCD_MAX_WIDTH); + assert(disp_config->height <= LCD_MAX_HEIGHT); + assert(disp_config->log2_bpp <= LCD_MAX_LOG2_BPP); + if (disp_config->width <= LCD_MAX_WIDTH + && disp_config->height <= LCD_MAX_HEIGHT + && disp_config->log2_bpp <= LCD_MAX_LOG2_BPP) + update_panel_size(disp_config); + size = lcd_get_size(&line_length); + + debug("LCD frame buffer at %p\n", lcd_base); +} + +ulong calc_fbsize(void) +{ + return (panel_info.vl_col * panel_info.vl_row * + NBITS(panel_info.vl_bpix)) / 8; +} + +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ +} + +void tegra_lcd_early_init(const void *blob) +{ + /* + * Go with the maximum size for now. We will fix this up after + * relocation. These values are only used for memory alocation. + */ + panel_info.vl_col = LCD_MAX_WIDTH; + panel_info.vl_row = LCD_MAX_HEIGHT; + panel_info.vl_bpix = LCD_MAX_LOG2_BPP; +} + +/** + * Decode the panel information from the fdt. + * + * @param blob fdt blob + * @param config structure to store fdt config into + * @return 0 if ok, -ve on error + */ +static int fdt_decode_lcd(const void *blob, struct fdt_panel_config *config) +{ + int display_node; + + disp_config = tegra_display_get_config(); + if (!disp_config) { + debug("%s: Display controller is not configured\n", __func__); + return -1; + } + display_node = disp_config->panel_node; + if (display_node < 0) { + debug("%s: No panel configuration available\n", __func__); + return -1; + } + + config->pwm_channel = pwm_request(blob, display_node, "nvidia,pwm"); + if (config->pwm_channel < 0) { + debug("%s: Unable to request PWM channel\n", __func__); + return -1; + } + + config->cache_type = fdtdec_get_int(blob, display_node, + "nvidia,cache-type", + FDT_LCD_CACHE_WRITE_BACK_FLUSH); + + /* These GPIOs are all optional */ + fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-enable-gpios", + &config->backlight_en); + fdtdec_decode_gpio(blob, display_node, "nvidia,lvds-shutdown-gpios", + &config->lvds_shutdown); + fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-vdd-gpios", + &config->backlight_vdd); + fdtdec_decode_gpio(blob, display_node, "nvidia,panel-vdd-gpios", + &config->panel_vdd); + + return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings", + config->panel_timings, FDT_LCD_TIMINGS); +} + +/** + * Handle the next stage of device init + */ +static int handle_stage(const void *blob) +{ + debug("%s: stage %d\n", __func__, stage); + + /* do the things for this stage */ + switch (stage) { + case STAGE_START: + /* Initialize the Tegra display controller */ + if (tegra_display_probe(gd->fdt_blob, (void *)gd->fb_base)) { + printf("%s: Failed to probe display driver\n", + __func__); + return -1; + } + + /* get panel details */ + if (fdt_decode_lcd(blob, &config)) { + printf("No valid LCD information in device tree\n"); + return -1; + } + + /* + * It is possible that the FDT has requested that the LCD be + * disabled. We currently don't support this. It would require + * changes to U-Boot LCD subsystem to have LCD support + * compiled in but not used. An easier option might be to + * still have a frame buffer, but leave the backlight off and + * remove all mention of lcd in the stdout environment + * variable. + */ + + funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); + + fdtdec_setup_gpio(&config.panel_vdd); + fdtdec_setup_gpio(&config.lvds_shutdown); + fdtdec_setup_gpio(&config.backlight_vdd); + fdtdec_setup_gpio(&config.backlight_en); + + /* + * TODO: If fdt includes output flag we can omit this code + * since fdtdec_setup_gpio will do it for us. + */ + if (fdt_gpio_isvalid(&config.panel_vdd)) + gpio_direction_output(config.panel_vdd.gpio, 0); + if (fdt_gpio_isvalid(&config.lvds_shutdown)) + gpio_direction_output(config.lvds_shutdown.gpio, 0); + if (fdt_gpio_isvalid(&config.backlight_vdd)) + gpio_direction_output(config.backlight_vdd.gpio, 0); + if (fdt_gpio_isvalid(&config.backlight_en)) + gpio_direction_output(config.backlight_en.gpio, 0); + break; + case STAGE_PANEL_VDD: + if (fdt_gpio_isvalid(&config.panel_vdd)) + gpio_direction_output(config.panel_vdd.gpio, 1); + break; + case STAGE_LVDS: + if (fdt_gpio_isvalid(&config.lvds_shutdown)) + gpio_set_value(config.lvds_shutdown.gpio, 1); + break; + case STAGE_BACKLIGHT_VDD: + if (fdt_gpio_isvalid(&config.backlight_vdd)) + gpio_set_value(config.backlight_vdd.gpio, 1); + break; + case STAGE_PWM: + /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ + pinmux_set_func(PINGRP_GPU, PMUX_FUNC_PWM); + pinmux_tristate_disable(PINGRP_GPU); + + pwm_enable(config.pwm_channel, 32768, 0xdf, 1); + break; + case STAGE_BACKLIGHT_EN: + if (fdt_gpio_isvalid(&config.backlight_en)) + gpio_set_value(config.backlight_en.gpio, 1); + break; + case STAGE_DONE: + break; + } + + /* set up timer for next stage */ + timer_next = timer_get_us(); + if (stage < FDT_LCD_TIMINGS) + timer_next += config.panel_timings[stage] * 1000; + + /* move to next stage */ + stage++; + return 0; +} + +int tegra_lcd_check_next_stage(const void *blob, int wait) +{ + if (stage == STAGE_DONE) + return 0; + + do { + /* wait if we need to */ + debug("%s: stage %d\n", __func__, stage); + if (stage != STAGE_START) { + int delay = timer_next - timer_get_us(); + + if (delay > 0) { + if (wait) + udelay(delay); + else + return 0; + } + } + + if (handle_stage(blob)) + return -1; + } while (wait && stage != STAGE_DONE); + if (stage == STAGE_DONE) + debug("%s: LCD init complete\n", __func__); + + return 0; +} + +void lcd_enable(void) +{ + /* + * Backlight and power init will be done separately in + * tegra_lcd_check_next_stage(), which should be called in + * board_late_init(). + * + * U-Boot code supports only colour depth, selected at compile time. + * The device tree setting should match this. Otherwise the display + * will not look right, and U-Boot may crash. + */ + if (disp_config->log2_bpp != LCD_BPP) { + printf("%s: Error: LCD depth configured in FDT (%d = %dbpp)" + " must match setting of LCD_BPP (%d)\n", __func__, + disp_config->log2_bpp, disp_config->bpp, LCD_BPP); + } +} -- cgit v1.1 From 44706a87654bae810cc0740e9cafde134c5796f1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 17 Oct 2012 13:24:57 +0000 Subject: tegra: Support control of cache settings for LCD Add support for selecting the required cache mode for the LCD: off, write-through or write-back. Signed-off-by: Simon Glass Signed-off-by: Tom Warren --- drivers/video/tegra.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c index ab26ad0..750a283 100644 --- a/drivers/video/tegra.c +++ b/drivers/video/tegra.c @@ -146,6 +146,7 @@ static void update_panel_size(struct fdt_disp_config *config) void lcd_ctrl_init(void *lcdbase) { int line_length, size; + int type = DCACHE_OFF; assert(disp_config); @@ -161,6 +162,16 @@ void lcd_ctrl_init(void *lcdbase) update_panel_size(disp_config); size = lcd_get_size(&line_length); + /* Set up the LCD caching as requested */ + if (config.cache_type & FDT_LCD_CACHE_WRITE_THROUGH) + type = DCACHE_WRITETHROUGH; + else if (config.cache_type & FDT_LCD_CACHE_WRITE_BACK) + type = DCACHE_WRITEBACK; + mmu_set_region_dcache_behaviour(disp_config->frame_buffer, size, type); + + /* Enable flushing after LCD writes if requested */ + lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH); + debug("LCD frame buffer at %p\n", lcd_base); } -- cgit v1.1 From 1ed0b51b7d58bfcf1901005fb98591bbdb9e30e3 Mon Sep 17 00:00:00 2001 From: Allen Martin Date: Thu, 1 Nov 2012 13:41:16 +0000 Subject: tegra: add CONSOLE_MUX support to tegra-kbc Add support for CONSOLE_MUX to tegra-kbc driver. This requires adding a flag to struct keyb to know the driver has already been initialized so if we try to initialize it again we can just return success. Also call into iomux_doenv() from drv_keyboard_init to re-evaluate the stdin string. Signed-off-by: Allen Martin Signed-off-by: Tom Warren --- drivers/input/tegra-kbc.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/tegra-kbc.c b/drivers/input/tegra-kbc.c index ab7a9e3..88471d3 100644 --- a/drivers/input/tegra-kbc.c +++ b/drivers/input/tegra-kbc.c @@ -63,6 +63,7 @@ static struct keyb { struct kbc_tegra *kbc; /* tegra keyboard controller */ unsigned char inited; /* 1 if keyboard has been inited */ unsigned char first_scan; /* 1 if this is our first key scan */ + unsigned char created; /* 1 if driver has been created */ /* * After init we must wait a short time before polling the keyboard. @@ -306,6 +307,10 @@ static void tegra_kbc_open(void) */ static int init_tegra_keyboard(void) { + /* check if already created */ + if (config.created) + return 0; + #ifdef CONFIG_OF_CONTROL int node; @@ -349,6 +354,7 @@ static int init_tegra_keyboard(void) config_kbc_gpio(config.kbc); tegra_kbc_open(); + config.created = 1; debug("%s: Tegra keyboard ready\n", __func__); return 0; @@ -357,6 +363,8 @@ static int init_tegra_keyboard(void) int drv_keyboard_init(void) { struct stdio_dev dev; + char *stdinname = getenv("stdin"); + int error; if (input_init(&config.input, 0)) { debug("%s: Cannot set up input\n", __func__); @@ -372,5 +380,13 @@ int drv_keyboard_init(void) dev.start = init_tegra_keyboard; /* Register the device. init_tegra_keyboard() will be called soon */ - return input_stdio_register(&dev); + error = input_stdio_register(&dev); + if (error) + return error; +#ifdef CONFIG_CONSOLE_MUX + error = iomux_doenv(stdin, stdinname); + if (error) + return error; +#endif + return 0; } -- cgit v1.1 From 8c0ec0dbc4cce76c6c745858116a404791d28b8b Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 2 Nov 2012 06:56:12 +0000 Subject: mmc: tegra: support 4-bit operation too on 8-bit slots If a board has all 8 data lines routed, the SD/MMC controller can still operate in 4-bit (or presumably even 1-bit) mode. Adjust Tegra's MMC driver to report the 4-bit capability even for 8-bit slots. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren --- drivers/mmc/tegra_mmc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index 8fea6a6..1fd5592 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -547,10 +547,11 @@ int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio) mmc->getcd = tegra_mmc_getcd; mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + mmc->host_caps = 0; if (bus_width == 8) - mmc->host_caps = MMC_MODE_8BIT; - else - mmc->host_caps = MMC_MODE_4BIT; + mmc->host_caps |= MMC_MODE_8BIT; + if (bus_width >= 4) + mmc->host_caps |= MMC_MODE_4BIT; mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; /* -- cgit v1.1 From 857765e9aaeb9b80ae29e4d4e4916979d07040ba Mon Sep 17 00:00:00 2001 From: Rajeshwari Shinde Date: Mon, 10 Dec 2012 01:55:47 +0000 Subject: POWER: MAX77686: Modified as per the latest Implementation Moved the pmic_max77686.c max77686_pmic.h to drivers/power and made required changes accordingly Signed-off-by: Rajeshwari Shinde Reviewed-by: Lukasz Majewski Acked-by: Lukasz Majewski Signed-off-by: Minkyu Kang --- drivers/misc/pmic_max77686.c | 42 --------------------------------- drivers/power/pmic/Makefile | 1 + drivers/power/pmic/pmic_max77686.c | 48 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 42 deletions(-) delete mode 100644 drivers/misc/pmic_max77686.c create mode 100644 drivers/power/pmic/pmic_max77686.c (limited to 'drivers') diff --git a/drivers/misc/pmic_max77686.c b/drivers/misc/pmic_max77686.c deleted file mode 100644 index 36f7f4d..0000000 --- a/drivers/misc/pmic_max77686.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2012 Samsung Electronics - * Rajeshwari Shinde - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ - -#include -#include -#include - -int pmic_init(void) -{ - struct pmic *p = get_pmic(); - static const char name[] = "MAX77686_PMIC"; - - puts("Board PMIC init\n"); - p->name = name; - p->interface = PMIC_I2C; - p->number_of_regs = PMIC_NUM_OF_REGS; - p->hw.i2c.addr = MAX77686_I2C_ADDR; - p->hw.i2c.tx_num = 1; - p->bus = I2C_PMIC; - - return 0; -} diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index e19a9a8..14d426f 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -28,6 +28,7 @@ LIB := $(obj)libpmic.o COBJS-$(CONFIG_POWER_MAX8998) += pmic_max8998.o COBJS-$(CONFIG_POWER_MAX8997) += pmic_max8997.o COBJS-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o +COBJS-$(CONFIG_POWER_MAX77686) += pmic_max77686.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/power/pmic/pmic_max77686.c b/drivers/power/pmic/pmic_max77686.c new file mode 100644 index 0000000..fce0183 --- /dev/null +++ b/drivers/power/pmic/pmic_max77686.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Rajeshwari Shinde + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include + +int pmic_init(unsigned char bus) +{ + static const char name[] = "MAX77686_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + puts("Board PMIC init\n"); + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX77686_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + return 0; +} -- cgit v1.1