diff options
Diffstat (limited to 'arch/arm/mach-omap2')
-rw-r--r-- | arch/arm/mach-omap2/Makefile | 15 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-2430sdp.c | 48 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-apollon.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-generic.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-h4.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-ldp.c | 40 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-omap3beagle.c | 90 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-omap3pandora.c | 32 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-overo.c | 43 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock24xx.h | 9 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock34xx.h | 10 | ||||
-rw-r--r-- | arch/arm/mach-omap2/devices.c | 169 | ||||
-rw-r--r-- | arch/arm/mach-omap2/mmc-twl4030.c | 408 | ||||
-rw-r--r-- | arch/arm/mach-omap2/mmc-twl4030.h | 29 |
14 files changed, 875 insertions, 51 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index f12c43e4932..bbd12bc10fd 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -27,10 +27,15 @@ obj-$(CONFIG_ARCH_OMAP3) += clock34xx.o # Specific board support obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o obj-$(CONFIG_MACH_OMAP_H4) += board-h4.o -obj-$(CONFIG_MACH_OMAP_2430SDP) += board-2430sdp.o +obj-$(CONFIG_MACH_OMAP_2430SDP) += board-2430sdp.o \ + mmc-twl4030.o obj-$(CONFIG_MACH_OMAP_APOLLON) += board-apollon.o -obj-$(CONFIG_MACH_OMAP3_BEAGLE) += board-omap3beagle.o -obj-$(CONFIG_MACH_OMAP_LDP) += board-ldp.o -obj-$(CONFIG_MACH_OVERO) += board-overo.o -obj-$(CONFIG_MACH_OMAP3_PANDORA) += board-omap3pandora.o +obj-$(CONFIG_MACH_OMAP3_BEAGLE) += board-omap3beagle.o \ + mmc-twl4030.o +obj-$(CONFIG_MACH_OMAP_LDP) += board-ldp.o \ + mmc-twl4030.o +obj-$(CONFIG_MACH_OVERO) += board-overo.o \ + mmc-twl4030.o +obj-$(CONFIG_MACH_OMAP3_PANDORA) += board-omap3pandora.o \ + mmc-twl4030.o diff --git a/arch/arm/mach-omap2/board-2430sdp.c b/arch/arm/mach-omap2/board-2430sdp.c index 6748de6e19a..83fa37211d7 100644 --- a/arch/arm/mach-omap2/board-2430sdp.c +++ b/arch/arm/mach-omap2/board-2430sdp.c @@ -19,6 +19,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/delay.h> +#include <linux/i2c/twl4030.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/io.h> @@ -35,6 +36,7 @@ #include <mach/common.h> #include <mach/gpmc.h> +#include "mmc-twl4030.h" #define SDP2430_FLASH_CS 0 #define SDP2430_SMC91X_CS 5 @@ -197,12 +199,58 @@ static struct omap_board_config_kernel sdp2430_config[] = { {OMAP_TAG_UART, &sdp2430_uart_config}, }; + +static struct twl4030_gpio_platform_data sdp2430_gpio_data = { + .gpio_base = OMAP_MAX_GPIO_LINES, + .irq_base = TWL4030_GPIO_IRQ_BASE, + .irq_end = TWL4030_GPIO_IRQ_END, +}; + +static struct twl4030_platform_data sdp2430_twldata = { + .irq_base = TWL4030_IRQ_BASE, + .irq_end = TWL4030_IRQ_END, + + /* platform_data for children goes here */ + .gpio = &sdp2430_gpio_data, +}; + +static struct i2c_board_info __initdata sdp2430_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("twl4030", 0x48), + .flags = I2C_CLIENT_WAKE, + .irq = INT_24XX_SYS_NIRQ, + .platform_data = &sdp2430_twldata, + }, +}; + +static int __init omap2430_i2c_init(void) +{ + omap_register_i2c_bus(1, 400, NULL, 0); + omap_register_i2c_bus(2, 2600, sdp2430_i2c_boardinfo, + ARRAY_SIZE(sdp2430_i2c_boardinfo)); + return 0; +} + +static struct twl4030_hsmmc_info mmc[] __initdata = { + { + .mmc = 1, + .wires = 4, + .gpio_cd = -EINVAL, + .gpio_wp = -EINVAL, + .ext_clock = 1, + }, + {} /* Terminator */ +}; + static void __init omap_2430sdp_init(void) { + omap2430_i2c_init(); + platform_add_devices(sdp2430_devices, ARRAY_SIZE(sdp2430_devices)); omap_board_config = sdp2430_config; omap_board_config_size = ARRAY_SIZE(sdp2430_config); omap_serial_init(); + twl4030_mmc_init(mmc); } static void __init omap_2430sdp_map_io(void) diff --git a/arch/arm/mach-omap2/board-apollon.c b/arch/arm/mach-omap2/board-apollon.c index d83035b436d..bf1e5d32c2a 100644 --- a/arch/arm/mach-omap2/board-apollon.c +++ b/arch/arm/mach-omap2/board-apollon.c @@ -261,16 +261,6 @@ static struct omap_uart_config apollon_uart_config __initdata = { .enabled_uarts = (1 << 0) | (0 << 1) | (0 << 2), }; -static struct omap_mmc_config apollon_mmc_config __initdata = { - .mmc [0] = { - .enabled = 1, - .wire4 = 1, - .wp_pin = -1, - .power_pin = -1, - .switch_pin = -1, - }, -}; - static struct omap_usb_config apollon_usb_config __initdata = { .register_dev = 1, .hmc_mode = 0x14, /* 0:dev 1:host1 2:disable */ @@ -284,7 +274,6 @@ static struct omap_lcd_config apollon_lcd_config __initdata = { static struct omap_board_config_kernel apollon_config[] = { { OMAP_TAG_UART, &apollon_uart_config }, - { OMAP_TAG_MMC, &apollon_mmc_config }, { OMAP_TAG_USB, &apollon_usb_config }, { OMAP_TAG_LCD, &apollon_lcd_config }, }; diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c index 9ba097868e7..3b34c20d1df 100644 --- a/arch/arm/mach-omap2/board-generic.c +++ b/arch/arm/mach-omap2/board-generic.c @@ -41,19 +41,8 @@ static struct omap_uart_config generic_uart_config __initdata = { .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), }; -static struct omap_mmc_config generic_mmc_config __initdata = { - .mmc [0] = { - .enabled = 0, - .wire4 = 0, - .wp_pin = -1, - .power_pin = -1, - .switch_pin = -1, - }, -}; - static struct omap_board_config_kernel generic_config[] = { { OMAP_TAG_UART, &generic_uart_config }, - { OMAP_TAG_MMC, &generic_mmc_config }, }; static void __init omap_generic_init(void) diff --git a/arch/arm/mach-omap2/board-h4.c b/arch/arm/mach-omap2/board-h4.c index 7de0506e1e2..5e9b14675b1 100644 --- a/arch/arm/mach-omap2/board-h4.c +++ b/arch/arm/mach-omap2/board-h4.c @@ -373,23 +373,12 @@ static struct omap_uart_config h4_uart_config __initdata = { .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), }; -static struct omap_mmc_config h4_mmc_config __initdata = { - .mmc [0] = { - .enabled = 1, - .wire4 = 1, - .wp_pin = -1, - .power_pin = -1, - .switch_pin = -1, - }, -}; - static struct omap_lcd_config h4_lcd_config __initdata = { .ctrl_name = "internal", }; static struct omap_board_config_kernel h4_config[] = { { OMAP_TAG_UART, &h4_uart_config }, - { OMAP_TAG_MMC, &h4_mmc_config }, { OMAP_TAG_LCD, &h4_lcd_config }, }; diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c index 43c7ac4b7f8..aa6972781e4 100644 --- a/arch/arm/mach-omap2/board-ldp.c +++ b/arch/arm/mach-omap2/board-ldp.c @@ -21,6 +21,7 @@ #include <linux/clk.h> #include <linux/spi/spi.h> #include <linux/spi/ads7846.h> +#include <linux/i2c/twl4030.h> #include <mach/hardware.h> #include <asm/mach-types.h> @@ -38,6 +39,8 @@ #include <asm/delay.h> #include <mach/control.h> +#include "mmc-twl4030.h" + #define SDP3430_SMC91X_CS 3 static struct resource ldp_smc911x_resources[] = { @@ -109,14 +112,48 @@ static struct omap_board_config_kernel ldp_config[] __initdata = { { OMAP_TAG_UART, &ldp_uart_config }, }; +static struct twl4030_gpio_platform_data ldp_gpio_data = { + .gpio_base = OMAP_MAX_GPIO_LINES, + .irq_base = TWL4030_GPIO_IRQ_BASE, + .irq_end = TWL4030_GPIO_IRQ_END, +}; + +static struct twl4030_platform_data ldp_twldata = { + .irq_base = TWL4030_IRQ_BASE, + .irq_end = TWL4030_IRQ_END, + + /* platform_data for children goes here */ + .gpio = &ldp_gpio_data, +}; + +static struct i2c_board_info __initdata ldp_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("twl4030", 0x48), + .flags = I2C_CLIENT_WAKE, + .irq = INT_34XX_SYS_NIRQ, + .platform_data = &ldp_twldata, + }, +}; + static int __init omap_i2c_init(void) { - omap_register_i2c_bus(1, 2600, NULL, 0); + omap_register_i2c_bus(1, 2600, ldp_i2c_boardinfo, + ARRAY_SIZE(ldp_i2c_boardinfo)); omap_register_i2c_bus(2, 400, NULL, 0); omap_register_i2c_bus(3, 400, NULL, 0); return 0; } +static struct twl4030_hsmmc_info mmc[] __initdata = { + { + .mmc = 1, + .wires = 4, + .gpio_cd = -EINVAL, + .gpio_wp = -EINVAL, + }, + {} /* Terminator */ +}; + static void __init omap_ldp_init(void) { omap_i2c_init(); @@ -124,6 +161,7 @@ static void __init omap_ldp_init(void) omap_board_config = ldp_config; omap_board_config_size = ARRAY_SIZE(ldp_config); omap_serial_init(); + twl4030_mmc_init(mmc); } static void __init omap_ldp_map_io(void) diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index baa79674e9d..9e5ada01b5f 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -38,7 +38,9 @@ #include <mach/common.h> #include <mach/gpmc.h> #include <mach/nand.h> +#include <mach/mux.h> +#include "mmc-twl4030.h" #define GPMC_CS0_BASE 0x60 #define GPMC_CS_SIZE 0x30 @@ -103,6 +105,78 @@ static struct omap_uart_config omap3_beagle_uart_config __initdata = { .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), }; +static struct twl4030_hsmmc_info mmc[] = { + { + .mmc = 1, + .wires = 8, + .gpio_wp = 29, + }, + {} /* Terminator */ +}; + +static struct gpio_led gpio_leds[]; + +static int beagle_twl_gpio_setup(struct device *dev, + unsigned gpio, unsigned ngpio) +{ + /* gpio + 0 is "mmc0_cd" (input/IRQ) */ + + /* REVISIT: need ehci-omap hooks for external VBUS + * power switch and overcurrent detect + */ + + gpio_request(gpio + 1, "EHCI_nOC"); + gpio_direction_input(gpio + 1); + + /* TWL4030_GPIO_MAX + 0 == ledA, EHCI nEN_USB_PWR (out, active low) */ + gpio_request(gpio + TWL4030_GPIO_MAX, "nEN_USB_PWR"); + gpio_direction_output(gpio + TWL4030_GPIO_MAX, 1); + + /* TWL4030_GPIO_MAX + 1 == ledB, PMU_STAT (out, active low LED) */ + gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1; + + return 0; +} + +static struct twl4030_gpio_platform_data beagle_gpio_data = { + .gpio_base = OMAP_MAX_GPIO_LINES, + .irq_base = TWL4030_GPIO_IRQ_BASE, + .irq_end = TWL4030_GPIO_IRQ_END, + .use_leds = true, + .pullups = BIT(1), + .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) | BIT(13) + | BIT(15) | BIT(16) | BIT(17), + .setup = beagle_twl_gpio_setup, +}; + +static struct twl4030_platform_data beagle_twldata = { + .irq_base = TWL4030_IRQ_BASE, + .irq_end = TWL4030_IRQ_END, + + /* platform_data for children goes here */ + .gpio = &beagle_gpio_data, +}; + +static struct i2c_board_info __initdata beagle_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("twl4030", 0x48), + .flags = I2C_CLIENT_WAKE, + .irq = INT_34XX_SYS_NIRQ, + .platform_data = &beagle_twldata, + }, +}; + +static int __init omap3_beagle_i2c_init(void) +{ + omap_register_i2c_bus(1, 2600, beagle_i2c_boardinfo, + ARRAY_SIZE(beagle_i2c_boardinfo)); +#ifdef CONFIG_I2C2_OMAP_BEAGLE + omap_register_i2c_bus(2, 400, NULL, 0); +#endif + omap_register_i2c_bus(3, 400, NULL, 0); + return 0; +} + static void __init omap3_beagle_init_irq(void) { omap2_init_common_hw(); @@ -130,6 +204,11 @@ static struct gpio_led gpio_leds[] = { .default_trigger = "mmc0", .gpio = 149, }, + { + .name = "beagleboard::pmu_stat", + .gpio = -EINVAL, /* gets replaced */ + .active_low = true, + }, }; static struct gpio_led_platform_data gpio_led_info = { @@ -218,11 +297,22 @@ static void __init omap3beagle_flash_init(void) static void __init omap3_beagle_init(void) { + omap3_beagle_i2c_init(); platform_add_devices(omap3_beagle_devices, ARRAY_SIZE(omap3_beagle_devices)); omap_board_config = omap3_beagle_config; omap_board_config_size = ARRAY_SIZE(omap3_beagle_config); omap_serial_init(); + + omap_cfg_reg(AH8_34XX_GPIO29); + mmc[0].gpio_cd = gpio + 0; + twl4030_mmc_init(mmc); + + omap_cfg_reg(J25_34XX_GPIO170); + gpio_request(170, "DVI_nPD"); + /* REVISIT leave DVI powered down until it's needed ... */ + gpio_direction_output(170, true); + omap3beagle_flash_init(); } diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index 7236c7be05b..b3196107afd 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -35,16 +35,48 @@ #include <mach/hardware.h> #include <mach/mcspi.h> +#include "mmc-twl4030.h" + #define OMAP3_PANDORA_TS_GPIO 94 +static struct twl4030_hsmmc_info omap3pandora_mmc[] = { + { + .mmc = 1, + .wires = 4, + .gpio_cd = -EINVAL, + .gpio_wp = 126, + .ext_clock = 0, + }, + { + .mmc = 2, + .wires = 4, + .gpio_cd = -EINVAL, + .gpio_wp = 127, + .ext_clock = 1, + }, + {} /* Terminator */ +}; + static struct omap_uart_config omap3pandora_uart_config __initdata = { .enabled_uarts = (1 << 2), /* UART3 */ }; +static int omap3pandora_twl_gpio_setup(struct device *dev, + unsigned gpio, unsigned ngpio) +{ + /* gpio + {0,1} is "mmc{0,1}_cd" (input/IRQ) */ + omap3pandora_mmc[0].gpio_cd = gpio + 0; + omap3pandora_mmc[1].gpio_cd = gpio + 1; + twl4030_mmc_init(omap3pandora_mmc); + + return 0; +} + static struct twl4030_gpio_platform_data omap3pandora_gpio_data = { .gpio_base = OMAP_MAX_GPIO_LINES, .irq_base = TWL4030_GPIO_IRQ_BASE, .irq_end = TWL4030_GPIO_IRQ_END, + .setup = omap3pandora_twl_gpio_setup, }; static struct twl4030_usb_data omap3pandora_usb_data = { diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c index e09aa59a399..82b3dc557c9 100644 --- a/arch/arm/mach-omap2/board-overo.c +++ b/arch/arm/mach-omap2/board-overo.c @@ -26,6 +26,7 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/platform_device.h> +#include <linux/i2c/twl4030.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> @@ -44,6 +45,8 @@ #include <mach/hardware.h> #include <mach/nand.h> +#include "mmc-twl4030.h" + #define NAND_BLOCK_SIZE SZ_128K #define GPMC_CS0_BASE 0x60 #define GPMC_CS_SIZE 0x30 @@ -139,8 +142,31 @@ static struct omap_uart_config overo_uart_config __initdata = { .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), }; +static struct twl4030_gpio_platform_data overo_gpio_data = { + .gpio_base = OMAP_MAX_GPIO_LINES, + .irq_base = TWL4030_GPIO_IRQ_BASE, + .irq_end = TWL4030_GPIO_IRQ_END, +}; + +static struct twl4030_platform_data overo_twldata = { + .irq_base = TWL4030_IRQ_BASE, + .irq_end = TWL4030_IRQ_END, + .gpio = &overo_gpio_data, +}; + +static struct i2c_board_info __initdata overo_i2c_boardinfo[] = { + { + I2C_BOARD_INFO("twl4030", 0x48), + .flags = I2C_CLIENT_WAKE, + .irq = INT_34XX_SYS_NIRQ, + .platform_data = &overo_twldata, + }, +}; + static int __init overo_i2c_init(void) { + omap_register_i2c_bus(1, 2600, overo_i2c_boardinfo, + ARRAY_SIZE(overo_i2c_boardinfo)); /* i2c2 pins are used for gpio */ omap_register_i2c_bus(3, 400, NULL, 0); return 0; @@ -171,6 +197,22 @@ static struct platform_device *overo_devices[] __initdata = { &overo_lcd_device, }; +static struct twl4030_hsmmc_info mmc[] __initdata = { + { + .mmc = 1, + .wires = 4, + .gpio_cd = -EINVAL, + .gpio_wp = -EINVAL, + }, + { + .mmc = 2, + .wires = 4, + .gpio_cd = -EINVAL, + .gpio_wp = -EINVAL, + }, + {} /* Terminator */ +}; + static void __init overo_init(void) { overo_i2c_init(); @@ -178,6 +220,7 @@ static void __init overo_init(void) omap_board_config = overo_config; omap_board_config_size = ARRAY_SIZE(overo_config); omap_serial_init(); + twl4030_mmc_init(mmc); overo_flash_init(); if ((gpio_request(OVERO_GPIO_W2W_NRESET, diff --git a/arch/arm/mach-omap2/clock24xx.h b/arch/arm/mach-omap2/clock24xx.h index 242a19d86cc..ff6cd14d254 100644 --- a/arch/arm/mach-omap2/clock24xx.h +++ b/arch/arm/mach-omap2/clock24xx.h @@ -2522,7 +2522,6 @@ static struct clk usbhs_ick = { static struct clk mmchs1_ick = { .name = "mmchs_ick", - .id = 1, .parent = &l4_ck, .flags = CLOCK_IN_OMAP243X, .clkdm_name = "core_l4_clkdm", @@ -2533,7 +2532,6 @@ static struct clk mmchs1_ick = { static struct clk mmchs1_fck = { .name = "mmchs_fck", - .id = 1, .parent = &func_96m_ck, .flags = CLOCK_IN_OMAP243X, .clkdm_name = "core_l3_clkdm", @@ -2544,7 +2542,7 @@ static struct clk mmchs1_fck = { static struct clk mmchs2_ick = { .name = "mmchs_ick", - .id = 2, + .id = 1, .parent = &l4_ck, .flags = CLOCK_IN_OMAP243X, .clkdm_name = "core_l4_clkdm", @@ -2555,7 +2553,7 @@ static struct clk mmchs2_ick = { static struct clk mmchs2_fck = { .name = "mmchs_fck", - .id = 2, + .id = 1, .parent = &func_96m_ck, .flags = CLOCK_IN_OMAP243X, .enable_reg = OMAP_CM_REGADDR(CORE_MOD, OMAP24XX_CM_FCLKEN2), @@ -2595,7 +2593,6 @@ static struct clk mdm_intc_ick = { static struct clk mmchsdb1_fck = { .name = "mmchsdb_fck", - .id = 1, .parent = &func_32k_ck, .flags = CLOCK_IN_OMAP243X, .clkdm_name = "core_l4_clkdm", @@ -2606,7 +2603,7 @@ static struct clk mmchsdb1_fck = { static struct clk mmchsdb2_fck = { .name = "mmchsdb_fck", - .id = 2, + .id = 1, .parent = &func_32k_ck, .flags = CLOCK_IN_OMAP243X, .clkdm_name = "core_l4_clkdm", diff --git a/arch/arm/mach-omap2/clock34xx.h b/arch/arm/mach-omap2/clock34xx.h index 7217a0824ec..a826094d89b 100644 --- a/arch/arm/mach-omap2/clock34xx.h +++ b/arch/arm/mach-omap2/clock34xx.h @@ -1374,7 +1374,7 @@ static struct clk core_96m_fck = { static struct clk mmchs3_fck = { .name = "mmchs_fck", - .id = 3, + .id = 2, .parent = &core_96m_fck, .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_FCLKEN1), .enable_bit = OMAP3430ES2_EN_MMC3_SHIFT, @@ -1385,7 +1385,7 @@ static struct clk mmchs3_fck = { static struct clk mmchs2_fck = { .name = "mmchs_fck", - .id = 2, + .id = 1, .parent = &core_96m_fck, .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_FCLKEN1), .enable_bit = OMAP3430_EN_MMC2_SHIFT, @@ -1406,7 +1406,6 @@ static struct clk mspro_fck = { static struct clk mmchs1_fck = { .name = "mmchs_fck", - .id = 1, .parent = &core_96m_fck, .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_FCLKEN1), .enable_bit = OMAP3430_EN_MMC1_SHIFT, @@ -1722,7 +1721,7 @@ static struct clk usbtll_ick = { static struct clk mmchs3_ick = { .name = "mmchs_ick", - .id = 3, + .id = 2, .parent = &core_l4_ick, .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_ICLKEN1), .enable_bit = OMAP3430ES2_EN_MMC3_SHIFT, @@ -1774,7 +1773,7 @@ static struct clk des2_ick = { static struct clk mmchs2_ick = { .name = "mmchs_ick", - .id = 2, + .id = 1, .parent = &core_l4_ick, .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_ICLKEN1), .enable_bit = OMAP3430_EN_MMC2_SHIFT, @@ -1785,7 +1784,6 @@ static struct clk mmchs2_ick = { static struct clk mmchs1_ick = { .name = "mmchs_ick", - .id = 1, .parent = &core_l4_ick, .enable_reg = OMAP_CM_REGADDR(CORE_MOD, CM_ICLKEN1), .enable_bit = OMAP3430_EN_MMC1_SHIFT, diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 90af2ac469a..9d7216ff6c9 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -14,16 +14,19 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/io.h> +#include <linux/clk.h> #include <mach/hardware.h> #include <asm/mach-types.h> #include <asm/mach/map.h> +#include <mach/control.h> #include <mach/tc.h> #include <mach/board.h> #include <mach/mux.h> #include <mach/gpio.h> #include <mach/eac.h> +#include <mach/mmc.h> #if defined(CONFIG_OMAP_DSP) || defined(CONFIG_OMAP_DSP_MODULE) #define OMAP2_MBOX_BASE IO_ADDRESS(OMAP24XX_MAILBOX_BASE) @@ -295,6 +298,171 @@ static void omap_init_sha1_md5(void) static inline void omap_init_sha1_md5(void) { } #endif +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_ARCH_OMAP3 + +#define MMCHS_SYSCONFIG 0x0010 +#define MMCHS_SYSCONFIG_SWRESET (1 << 1) +#define MMCHS_SYSSTATUS 0x0014 +#define MMCHS_SYSSTATUS_RESETDONE (1 << 0) + +static struct platform_device dummy_pdev = { + .dev = { + .bus = &platform_bus_type, + }, +}; + +/** + * omap_hsmmc_reset() - Full reset of each HS-MMC controller + * + * Ensure that each MMC controller is fully reset. Controllers + * left in an unknown state (by bootloader) may prevent retention + * or OFF-mode. This is especially important in cases where the + * MMC driver is not enabled, _or_ built as a module. + * + * In order for reset to work, interface, functional and debounce + * clocks must be enabled. The debounce clock comes from func_32k_clk + * and is not under SW control, so we only enable i- and f-clocks. + **/ +static void __init omap_hsmmc_reset(void) +{ + u32 i, nr_controllers = cpu_is_omap34xx() ? OMAP34XX_NR_MMC : + OMAP24XX_NR_MMC; + + for (i = 0; i < nr_controllers; i++) { + u32 v, base = 0; + struct clk *iclk, *fclk; + struct device *dev = &dummy_pdev.dev; + + switch (i) { + case 0: + base = OMAP2_MMC1_BASE; + break; + case 1: + base = OMAP2_MMC2_BASE; + break; + case 2: + base = OMAP3_MMC3_BASE; + break; + } + + dummy_pdev.id = i; + iclk = clk_get(dev, "mmchs_ick"); + if (iclk && clk_enable(iclk)) + iclk = NULL; + + fclk = clk_get(dev, "mmchs_fck"); + if (fclk && clk_enable(fclk)) + fclk = NULL; + + if (!iclk || !fclk) { + printk(KERN_WARNING + "%s: Unable to enable clocks for MMC%d, " + "cannot reset.\n", __func__, i); + break; + } + + omap_writel(MMCHS_SYSCONFIG_SWRESET, base + MMCHS_SYSCONFIG); + v = omap_readl(base + MMCHS_SYSSTATUS); + while (!(omap_readl(base + MMCHS_SYSSTATUS) & + MMCHS_SYSSTATUS_RESETDONE)) + cpu_relax(); + + if (fclk) { + clk_disable(fclk); + clk_put(fclk); + } + if (iclk) { + clk_disable(iclk); + clk_put(iclk); + } + } +} +#else +static inline void omap_hsmmc_reset(void) {} +#endif + +#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \ + defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE) + +static inline void omap2_mmc_mux(struct omap_mmc_platform_data *mmc_controller, + int controller_nr) +{ + if (cpu_is_omap2420() && controller_nr == 0) { + omap_cfg_reg(H18_24XX_MMC_CMD); + omap_cfg_reg(H15_24XX_MMC_CLKI); + omap_cfg_reg(G19_24XX_MMC_CLKO); + omap_cfg_reg(F20_24XX_MMC_DAT0); + omap_cfg_reg(F19_24XX_MMC_DAT_DIR0); + omap_cfg_reg(G18_24XX_MMC_CMD_DIR); + if (mmc_controller->slots[0].wires == 4) { + omap_cfg_reg(H14_24XX_MMC_DAT1); + omap_cfg_reg(E19_24XX_MMC_DAT2); + omap_cfg_reg(D19_24XX_MMC_DAT3); + omap_cfg_reg(E20_24XX_MMC_DAT_DIR1); + omap_cfg_reg(F18_24XX_MMC_DAT_DIR2); + omap_cfg_reg(E18_24XX_MMC_DAT_DIR3); + } + + /* + * Use internal loop-back in MMC/SDIO Module Input Clock + * selection + */ + if (mmc_controller->slots[0].internal_clock) { + u32 v = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0); + v |= (1 << 24); + omap_ctrl_writel(v, OMAP2_CONTROL_DEVCONF0); + } + } +} + +void __init omap2_init_mmc(struct omap_mmc_platform_data **mmc_data, + int nr_controllers) +{ + int i; + + for (i = 0; i < nr_controllers; i++) { + unsigned long base, size; + unsigned int irq = 0; + + if (!mmc_data[i]) + continue; + + omap2_mmc_mux(mmc_data[i], i); + + switch (i) { + case 0: + base = OMAP2_MMC1_BASE; + irq = INT_24XX_MMC_IRQ; + break; + case 1: + base = OMAP2_MMC2_BASE; + irq = INT_24XX_MMC2_IRQ; + break; + case 2: + if (!cpu_is_omap34xx()) + return; + base = OMAP3_MMC3_BASE; + irq = INT_34XX_MMC3_IRQ; + break; + default: + continue; + } + + if (cpu_is_omap2420()) + size = OMAP2420_MMC_SIZE; + else + size = HSMMC_SIZE; + + omap_mmc_add(i, base, size, irq, mmc_data[i]); + }; +} + +#endif + +/*-------------------------------------------------------------------------*/ + #if defined(CONFIG_HDQ_MASTER_OMAP) || defined(CONFIG_HDQ_MASTER_OMAP_MODULE) #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3430) #define OMAP_HDQ_BASE 0x480B2000 @@ -334,6 +502,7 @@ static int __init omap2_init_devices(void) /* please keep these calls, and their implementations above, * in alphabetical order so they're easier to sort through. */ + omap_hsmmc_reset(); omap_init_mbox(); omap_init_mcspi(); omap_hdq_init(); diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c new file mode 100644 index 00000000000..437f52073f6 --- /dev/null +++ b/arch/arm/mach-omap2/mmc-twl4030.c @@ -0,0 +1,408 @@ +/* + * linux/arch/arm/mach-omap2/mmc-twl4030.c + * + * Copyright (C) 2007-2008 Texas Instruments + * Copyright (C) 2008 Nokia Corporation + * Author: Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/i2c/twl4030.h> + +#include <mach/hardware.h> +#include <mach/control.h> +#include <mach/mmc.h> +#include <mach/board.h> + +#include "mmc-twl4030.h" + +#if defined(CONFIG_TWL4030_CORE) && \ + (defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)) + +#define LDO_CLR 0x00 +#define VSEL_S2_CLR 0x40 + +#define VMMC1_DEV_GRP 0x27 +#define VMMC1_CLR 0x00 +#define VMMC1_315V 0x03 +#define VMMC1_300V 0x02 +#define VMMC1_285V 0x01 +#define VMMC1_185V 0x00 +#define VMMC1_DEDICATED 0x2A + +#define VMMC2_DEV_GRP 0x2B +#define VMMC2_CLR 0x40 +#define VMMC2_315V 0x0c +#define VMMC2_300V 0x0b +#define VMMC2_285V 0x0a +#define VMMC2_260V 0x08 +#define VMMC2_185V 0x06 +#define VMMC2_DEDICATED 0x2E + +#define VMMC_DEV_GRP_P1 0x20 + +static u16 control_pbias_offset; +static u16 control_devconf1_offset; + +#define HSMMC_NAME_LEN 9 + +static struct twl_mmc_controller { + struct omap_mmc_platform_data *mmc; + u8 twl_vmmc_dev_grp; + u8 twl_mmc_dedicated; + char name[HSMMC_NAME_LEN]; +} hsmmc[] = { + { + .twl_vmmc_dev_grp = VMMC1_DEV_GRP, + .twl_mmc_dedicated = VMMC1_DEDICATED, + }, + { + .twl_vmmc_dev_grp = VMMC2_DEV_GRP, + .twl_mmc_dedicated = VMMC2_DEDICATED, + }, +}; + +static int twl_mmc_card_detect(int irq) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(hsmmc); i++) { + struct omap_mmc_platform_data *mmc; + + mmc = hsmmc[i].mmc; + if (!mmc) + continue; + if (irq != mmc->slots[0].card_detect_irq) + continue; + + /* NOTE: assumes card detect signal is active-low */ + return !gpio_get_value_cansleep(mmc->slots[0].switch_pin); + } + return -ENOSYS; +} + +static int twl_mmc_get_ro(struct device *dev, int slot) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + /* NOTE: assumes write protect signal is active-high */ + return gpio_get_value_cansleep(mmc->slots[0].gpio_wp); +} + +/* + * MMC Slot Initialization. + */ +static int twl_mmc_late_init(struct device *dev) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + int ret = 0; + int i; + + ret = gpio_request(mmc->slots[0].switch_pin, "mmc_cd"); + if (ret) + goto done; + ret = gpio_direction_input(mmc->slots[0].switch_pin); + if (ret) + goto err; + + for (i = 0; i < ARRAY_SIZE(hsmmc); i++) { + if (hsmmc[i].name == mmc->slots[0].name) { + hsmmc[i].mmc = mmc; + break; + } + } + + return 0; + +err: + gpio_free(mmc->slots[0].switch_pin); +done: + mmc->slots[0].card_detect_irq = 0; + mmc->slots[0].card_detect = NULL; + + dev_err(dev, "err %d configuring card detect\n", ret); + return ret; +} + +static void twl_mmc_cleanup(struct device *dev) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + gpio_free(mmc->slots[0].switch_pin); +} + +#ifdef CONFIG_PM + +static int twl_mmc_suspend(struct device *dev, int slot) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + disable_irq(mmc->slots[0].card_detect_irq); + return 0; +} + +static int twl_mmc_resume(struct device *dev, int slot) +{ + struct omap_mmc_platform_data *mmc = dev->platform_data; + + enable_irq(mmc->slots[0].card_detect_irq); + return 0; +} + +#else +#define twl_mmc_suspend NULL +#define twl_mmc_resume NULL +#endif + +/* + * Sets the MMC voltage in twl4030 + */ +static int twl_mmc_set_voltage(struct twl_mmc_controller *c, int vdd) +{ + int ret; + u8 vmmc, dev_grp_val; + + switch (1 << vdd) { + case MMC_VDD_35_36: + case MMC_VDD_34_35: + case MMC_VDD_33_34: + case MMC_VDD_32_33: + case MMC_VDD_31_32: + case MMC_VDD_30_31: + if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) + vmmc = VMMC1_315V; + else + vmmc = VMMC2_315V; + break; + case MMC_VDD_29_30: + if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) + vmmc = VMMC1_315V; + else + vmmc = VMMC2_300V; + break; + case MMC_VDD_27_28: + case MMC_VDD_26_27: + if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) + vmmc = VMMC1_285V; + else + vmmc = VMMC2_285V; + break; + case MMC_VDD_25_26: + case MMC_VDD_24_25: + case MMC_VDD_23_24: + case MMC_VDD_22_23: + case MMC_VDD_21_22: + case MMC_VDD_20_21: + if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) + vmmc = VMMC1_285V; + else + vmmc = VMMC2_260V; + break; + case MMC_VDD_165_195: + if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) + vmmc = VMMC1_185V; + else + vmmc = VMMC2_185V; + break; + default: + vmmc = 0; + break; + } + + if (vmmc) + dev_grp_val = VMMC_DEV_GRP_P1; /* Power up */ + else + dev_grp_val = LDO_CLR; /* Power down */ + + ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + dev_grp_val, c->twl_vmmc_dev_grp); + if (ret) + return ret; + + ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + vmmc, c->twl_mmc_dedicated); + + return ret; +} + +static int twl_mmc1_set_power(struct device *dev, int slot, int power_on, + int vdd) +{ + u32 reg; + int ret = 0; + struct twl_mmc_controller *c = &hsmmc[0]; + struct omap_mmc_platform_data *mmc = dev->platform_data; + + if (power_on) { + if (cpu_is_omap2430()) { + reg = omap_ctrl_readl(OMAP243X_CONTROL_DEVCONF1); + if ((1 << vdd) >= MMC_VDD_30_31) + reg |= OMAP243X_MMC1_ACTIVE_OVERWRITE; + else + reg &= ~OMAP243X_MMC1_ACTIVE_OVERWRITE; + omap_ctrl_writel(reg, OMAP243X_CONTROL_DEVCONF1); + } + + if (mmc->slots[0].internal_clock) { + reg = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0); + reg |= OMAP2_MMCSDIO1ADPCLKISEL; + omap_ctrl_writel(reg, OMAP2_CONTROL_DEVCONF0); + } + + reg = omap_ctrl_readl(control_pbias_offset); + reg |= OMAP2_PBIASSPEEDCTRL0; + reg &= ~OMAP2_PBIASLITEPWRDNZ0; + omap_ctrl_writel(reg, control_pbias_offset); + + ret = twl_mmc_set_voltage(c, vdd); + + /* 100ms delay required for PBIAS configuration */ + msleep(100); + reg = omap_ctrl_readl(control_pbias_offset); + reg |= (OMAP2_PBIASLITEPWRDNZ0 | OMAP2_PBIASSPEEDCTRL0); + if ((1 << vdd) <= MMC_VDD_165_195) + reg &= ~OMAP2_PBIASLITEVMODE0; + else + reg |= OMAP2_PBIASLITEVMODE0; + omap_ctrl_writel(reg, control_pbias_offset); + } else { + reg = omap_ctrl_readl(control_pbias_offset); + reg &= ~OMAP2_PBIASLITEPWRDNZ0; + omap_ctrl_writel(reg, control_pbias_offset); + + ret = twl_mmc_set_voltage(c, 0); + + /* 100ms delay required for PBIAS configuration */ + msleep(100); + reg = omap_ctrl_readl(control_pbias_offset); + reg |= (OMAP2_PBIASSPEEDCTRL0 | OMAP2_PBIASLITEPWRDNZ0 | + OMAP2_PBIASLITEVMODE0); + omap_ctrl_writel(reg, control_pbias_offset); + } + + return ret; +} + +static int twl_mmc2_set_power(struct device *dev, int slot, int power_on, int vdd) +{ + int ret; + struct twl_mmc_controller *c = &hsmmc[1]; + struct omap_mmc_platform_data *mmc = dev->platform_data; + + if (power_on) { + if (mmc->slots[0].internal_clock) { + u32 reg; + + reg = omap_ctrl_readl(control_devconf1_offset); + reg |= OMAP2_MMCSDIO2ADPCLKISEL; + omap_ctrl_writel(reg, control_devconf1_offset); + } + ret = twl_mmc_set_voltage(c, vdd); + } else { + ret = twl_mmc_set_voltage(c, 0); + } + + return ret; +} + +static struct omap_mmc_platform_data *hsmmc_data[OMAP34XX_NR_MMC] __initdata; + +void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers) +{ + struct twl4030_hsmmc_info *c; + int nr_hsmmc = ARRAY_SIZE(hsmmc_data); + + if (cpu_is_omap2430()) { + control_pbias_offset = OMAP243X_CONTROL_PBIAS_LITE; + control_devconf1_offset = OMAP243X_CONTROL_DEVCONF1; + nr_hsmmc = 2; + } else { + control_pbias_offset = OMAP343X_CONTROL_PBIAS_LITE; + control_devconf1_offset = OMAP343X_CONTROL_DEVCONF1; + } + + for (c = controllers; c->mmc; c++) { + struct twl_mmc_controller *twl = hsmmc + c->mmc - 1; + struct omap_mmc_platform_data *mmc = hsmmc_data[c->mmc - 1]; + + if (!c->mmc || c->mmc > nr_hsmmc) { + pr_debug("MMC%d: no such controller\n", c->mmc); + continue; + } + if (mmc) { + pr_debug("MMC%d: already configured\n", c->mmc); + continue; + } + + mmc = kzalloc(sizeof(struct omap_mmc_platform_data), GFP_KERNEL); + if (!mmc) { + pr_err("Cannot allocate memory for mmc device!\n"); + return; + } + + sprintf(twl->name, "mmc%islot%i", c->mmc, 1); + mmc->slots[0].name = twl->name; + mmc->nr_slots = 1; + mmc->slots[0].ocr_mask = MMC_VDD_165_195 | + MMC_VDD_26_27 | MMC_VDD_27_28 | + MMC_VDD_29_30 | + MMC_VDD_30_31 | MMC_VDD_31_32; + mmc->slots[0].wires = c->wires; + mmc->slots[0].internal_clock = !c->ext_clock; + mmc->dma_mask = 0xffffffff; + + /* note: twl4030 card detect GPIOs normally switch VMMCx ... */ + if (gpio_is_valid(c->gpio_cd)) { + mmc->init = twl_mmc_late_init; + mmc->cleanup = twl_mmc_cleanup; + mmc->suspend = twl_mmc_suspend; + mmc->resume = twl_mmc_resume; + + mmc->slots[0].switch_pin = c->gpio_cd; + mmc->slots[0].card_detect_irq = gpio_to_irq(c->gpio_cd); + mmc->slots[0].card_detect = twl_mmc_card_detect; + } else + mmc->slots[0].switch_pin = -EINVAL; + + /* write protect normally uses an OMAP gpio */ + if (gpio_is_valid(c->gpio_wp)) { + gpio_request(c->gpio_wp, "mmc_wp"); + gpio_direction_input(c->gpio_wp); + + mmc->slots[0].gpio_wp = c->gpio_wp; + mmc->slots[0].get_ro = twl_mmc_get_ro; + } else + mmc->slots[0].gpio_wp = -EINVAL; + + /* NOTE: we assume OMAP's MMC1 and MMC2 use + * the TWL4030's VMMC1 and VMMC2, respectively; + * and that OMAP's MMC3 isn't used. + */ + + switch (c->mmc) { + case 1: + mmc->slots[0].set_power = twl_mmc1_set_power; + break; + case 2: + mmc->slots[0].set_power = twl_mmc2_set_power; + break; + default: + pr_err("MMC%d configuration not supported!\n", c->mmc); + continue; + } + hsmmc_data[c->mmc - 1] = mmc; + } + + omap2_init_mmc(hsmmc_data, OMAP34XX_NR_MMC); +} + +#endif diff --git a/arch/arm/mach-omap2/mmc-twl4030.h b/arch/arm/mach-omap2/mmc-twl4030.h new file mode 100644 index 00000000000..e1c8076400c --- /dev/null +++ b/arch/arm/mach-omap2/mmc-twl4030.h @@ -0,0 +1,29 @@ +/* + * MMC definitions for OMAP2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +struct twl4030_hsmmc_info { + u8 mmc; /* controller 1/2/3 */ + u8 wires; /* 1/4/8 wires */ + int gpio_cd; /* or -EINVAL */ + int gpio_wp; /* or -EINVAL */ + int ext_clock:1; /* use external pin for input clock */ +}; + +#if defined(CONFIG_TWL4030_CORE) && \ + (defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \ + defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)) + +void twl4030_mmc_init(struct twl4030_hsmmc_info *); + +#else + +static inline void twl4030_mmc_init(struct twl4030_hsmmc_info *info) +{ +} + +#endif |