| /* |
| * TI DaVinci EVM board support |
| * |
| * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com> |
| * |
| * 2007 (c) MontaVista Software, Inc. This file is licensed under |
| * the terms of the GNU General Public License version 2. This program |
| * is licensed "as is" without any warranty of any kind, whether express |
| * or implied. |
| */ |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/platform_device.h> |
| #include <linux/gpio.h> |
| #include <linux/leds.h> |
| |
| #include <linux/i2c.h> |
| #include <linux/i2c/pcf857x.h> |
| #include <linux/i2c/at24.h> |
| |
| #include <linux/mtd/mtd.h> |
| #include <linux/mtd/partitions.h> |
| #include <linux/mtd/physmap.h> |
| #include <linux/io.h> |
| |
| #include <asm/setup.h> |
| #include <asm/mach-types.h> |
| |
| #include <asm/mach/arch.h> |
| #include <asm/mach/map.h> |
| #include <asm/mach/flash.h> |
| |
| #include <mach/hardware.h> |
| #include <mach/common.h> |
| #include <mach/i2c.h> |
| |
| /* other misc. init functions */ |
| void __init davinci_psc_init(void); |
| void __init davinci_irq_init(void); |
| void __init davinci_map_common_io(void); |
| void __init davinci_init_common_hw(void); |
| |
| #if defined(CONFIG_MTD_PHYSMAP) || \ |
| defined(CONFIG_MTD_PHYSMAP_MODULE) |
| |
| static struct mtd_partition davinci_evm_norflash_partitions[] = { |
| /* bootloader (U-Boot, etc) in first 4 sectors */ |
| { |
| .name = "bootloader", |
| .offset = 0, |
| .size = 4 * SZ_64K, |
| .mask_flags = MTD_WRITEABLE, /* force read-only */ |
| }, |
| /* bootloader params in the next 1 sectors */ |
| { |
| .name = "params", |
| .offset = MTDPART_OFS_APPEND, |
| .size = SZ_64K, |
| .mask_flags = 0, |
| }, |
| /* kernel */ |
| { |
| .name = "kernel", |
| .offset = MTDPART_OFS_APPEND, |
| .size = SZ_2M, |
| .mask_flags = 0 |
| }, |
| /* file system */ |
| { |
| .name = "filesystem", |
| .offset = MTDPART_OFS_APPEND, |
| .size = MTDPART_SIZ_FULL, |
| .mask_flags = 0 |
| } |
| }; |
| |
| static struct physmap_flash_data davinci_evm_norflash_data = { |
| .width = 2, |
| .parts = davinci_evm_norflash_partitions, |
| .nr_parts = ARRAY_SIZE(davinci_evm_norflash_partitions), |
| }; |
| |
| /* NOTE: CFI probe will correctly detect flash part as 32M, but EMIF |
| * limits addresses to 16M, so using addresses past 16M will wrap */ |
| static struct resource davinci_evm_norflash_resource = { |
| .start = DAVINCI_ASYNC_EMIF_DATA_CE0_BASE, |
| .end = DAVINCI_ASYNC_EMIF_DATA_CE0_BASE + SZ_16M - 1, |
| .flags = IORESOURCE_MEM, |
| }; |
| |
| static struct platform_device davinci_evm_norflash_device = { |
| .name = "physmap-flash", |
| .id = 0, |
| .dev = { |
| .platform_data = &davinci_evm_norflash_data, |
| }, |
| .num_resources = 1, |
| .resource = &davinci_evm_norflash_resource, |
| }; |
| |
| #endif |
| |
| #if defined(CONFIG_BLK_DEV_PALMCHIP_BK3710) || \ |
| defined(CONFIG_BLK_DEV_PALMCHIP_BK3710_MODULE) |
| |
| static struct resource ide_resources[] = { |
| { |
| .start = DAVINCI_CFC_ATA_BASE, |
| .end = DAVINCI_CFC_ATA_BASE + 0x7ff, |
| .flags = IORESOURCE_MEM, |
| }, |
| { |
| .start = IRQ_IDE, |
| .end = IRQ_IDE, |
| .flags = IORESOURCE_IRQ, |
| }, |
| }; |
| |
| static u64 ide_dma_mask = DMA_32BIT_MASK; |
| |
| static struct platform_device ide_dev = { |
| .name = "palm_bk3710", |
| .id = -1, |
| .resource = ide_resources, |
| .num_resources = ARRAY_SIZE(ide_resources), |
| .dev = { |
| .dma_mask = &ide_dma_mask, |
| .coherent_dma_mask = DMA_32BIT_MASK, |
| }, |
| }; |
| |
| #endif |
| |
| /*----------------------------------------------------------------------*/ |
| |
| /* |
| * I2C GPIO expanders |
| */ |
| |
| #define PCF_Uxx_BASE(x) (DAVINCI_N_GPIO + ((x) * 8)) |
| |
| |
| /* U2 -- LEDs */ |
| |
| static struct gpio_led evm_leds[] = { |
| { .name = "DS8", .active_low = 1, |
| .default_trigger = "heartbeat", }, |
| { .name = "DS7", .active_low = 1, }, |
| { .name = "DS6", .active_low = 1, }, |
| { .name = "DS5", .active_low = 1, }, |
| { .name = "DS4", .active_low = 1, }, |
| { .name = "DS3", .active_low = 1, }, |
| { .name = "DS2", .active_low = 1, |
| .default_trigger = "mmc0", }, |
| { .name = "DS1", .active_low = 1, |
| .default_trigger = "ide-disk", }, |
| }; |
| |
| static const struct gpio_led_platform_data evm_led_data = { |
| .num_leds = ARRAY_SIZE(evm_leds), |
| .leds = evm_leds, |
| }; |
| |
| static struct platform_device *evm_led_dev; |
| |
| static int |
| evm_led_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *c) |
| { |
| struct gpio_led *leds = evm_leds; |
| int status; |
| |
| while (ngpio--) { |
| leds->gpio = gpio++; |
| leds++; |
| } |
| |
| /* what an extremely annoying way to be forced to handle |
| * device unregistration ... |
| */ |
| evm_led_dev = platform_device_alloc("leds-gpio", 0); |
| platform_device_add_data(evm_led_dev, |
| &evm_led_data, sizeof evm_led_data); |
| |
| evm_led_dev->dev.parent = &client->dev; |
| status = platform_device_add(evm_led_dev); |
| if (status < 0) { |
| platform_device_put(evm_led_dev); |
| evm_led_dev = NULL; |
| } |
| return status; |
| } |
| |
| static int |
| evm_led_teardown(struct i2c_client *client, int gpio, unsigned ngpio, void *c) |
| { |
| if (evm_led_dev) { |
| platform_device_unregister(evm_led_dev); |
| evm_led_dev = NULL; |
| } |
| return 0; |
| } |
| |
| static struct pcf857x_platform_data pcf_data_u2 = { |
| .gpio_base = PCF_Uxx_BASE(0), |
| .setup = evm_led_setup, |
| .teardown = evm_led_teardown, |
| }; |
| |
| |
| /* U18 - A/V clock generator and user switch */ |
| |
| static int sw_gpio; |
| |
| static ssize_t |
| sw_show(struct device *d, struct device_attribute *a, char *buf) |
| { |
| char *s = gpio_get_value_cansleep(sw_gpio) ? "on\n" : "off\n"; |
| |
| strcpy(buf, s); |
| return strlen(s); |
| } |
| |
| static DEVICE_ATTR(user_sw, S_IRUGO, sw_show, NULL); |
| |
| static int |
| evm_u18_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *c) |
| { |
| int status; |
| |
| /* export dip switch option */ |
| sw_gpio = gpio + 7; |
| status = gpio_request(sw_gpio, "user_sw"); |
| if (status == 0) |
| status = gpio_direction_input(sw_gpio); |
| if (status == 0) |
| status = device_create_file(&client->dev, &dev_attr_user_sw); |
| else |
| gpio_free(sw_gpio); |
| if (status != 0) |
| sw_gpio = -EINVAL; |
| |
| /* audio PLL: 48 kHz (vs 44.1 or 32), single rate (vs double) */ |
| gpio_request(gpio + 3, "pll_fs2"); |
| gpio_direction_output(gpio + 3, 0); |
| |
| gpio_request(gpio + 2, "pll_fs1"); |
| gpio_direction_output(gpio + 2, 0); |
| |
| gpio_request(gpio + 1, "pll_sr"); |
| gpio_direction_output(gpio + 1, 0); |
| |
| return 0; |
| } |
| |
| static int |
| evm_u18_teardown(struct i2c_client *client, int gpio, unsigned ngpio, void *c) |
| { |
| gpio_free(gpio + 1); |
| gpio_free(gpio + 2); |
| gpio_free(gpio + 3); |
| |
| if (sw_gpio > 0) { |
| device_remove_file(&client->dev, &dev_attr_user_sw); |
| gpio_free(sw_gpio); |
| } |
| return 0; |
| } |
| |
| static struct pcf857x_platform_data pcf_data_u18 = { |
| .gpio_base = PCF_Uxx_BASE(1), |
| .n_latch = (1 << 3) | (1 << 2) | (1 << 1), |
| .setup = evm_u18_setup, |
| .teardown = evm_u18_teardown, |
| }; |
| |
| |
| /* U35 - various I/O signals used to manage USB, CF, ATA, etc */ |
| |
| static int |
| evm_u35_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *c) |
| { |
| /* p0 = nDRV_VBUS (initial: don't supply it) */ |
| gpio_request(gpio + 0, "nDRV_VBUS"); |
| gpio_direction_output(gpio + 0, 1); |
| |
| /* p1 = VDDIMX_EN */ |
| gpio_request(gpio + 1, "VDDIMX_EN"); |
| gpio_direction_output(gpio + 1, 1); |
| |
| /* p2 = VLYNQ_EN */ |
| gpio_request(gpio + 2, "VLYNQ_EN"); |
| gpio_direction_output(gpio + 2, 1); |
| |
| /* p3 = n3V3_CF_RESET (initial: stay in reset) */ |
| gpio_request(gpio + 3, "nCF_RESET"); |
| gpio_direction_output(gpio + 3, 0); |
| |
| /* (p4 unused) */ |
| |
| /* p5 = 1V8_WLAN_RESET (initial: stay in reset) */ |
| gpio_request(gpio + 5, "WLAN_RESET"); |
| gpio_direction_output(gpio + 5, 1); |
| |
| /* p6 = nATA_SEL (initial: select) */ |
| gpio_request(gpio + 6, "nATA_SEL"); |
| gpio_direction_output(gpio + 6, 0); |
| |
| /* p7 = nCF_SEL (initial: deselect) */ |
| gpio_request(gpio + 7, "nCF_SEL"); |
| gpio_direction_output(gpio + 7, 1); |
| |
| return 0; |
| } |
| |
| static int |
| evm_u35_teardown(struct i2c_client *client, int gpio, unsigned ngpio, void *c) |
| { |
| gpio_free(gpio + 7); |
| gpio_free(gpio + 6); |
| gpio_free(gpio + 5); |
| gpio_free(gpio + 3); |
| gpio_free(gpio + 2); |
| gpio_free(gpio + 1); |
| gpio_free(gpio + 0); |
| return 0; |
| } |
| |
| static struct pcf857x_platform_data pcf_data_u35 = { |
| .gpio_base = PCF_Uxx_BASE(2), |
| .setup = evm_u35_setup, |
| .teardown = evm_u35_teardown, |
| }; |
| |
| /*----------------------------------------------------------------------*/ |
| |
| /* Most of this EEPROM is unused, but U-Boot uses some data: |
| * - 0x7f00, 6 bytes Ethernet Address |
| * - 0x0039, 1 byte NTSC vs PAL (bit 0x80 == PAL) |
| * - ... newer boards may have more |
| */ |
| static struct at24_platform_data eeprom_info = { |
| .byte_len = (256*1024) / 8, |
| .page_size = 64, |
| .flags = AT24_FLAG_ADDR16, |
| }; |
| |
| static struct i2c_board_info __initdata i2c_info[] = { |
| { |
| I2C_BOARD_INFO("pcf8574", 0x38), |
| .platform_data = &pcf_data_u2, |
| }, |
| { |
| I2C_BOARD_INFO("pcf8574", 0x39), |
| .platform_data = &pcf_data_u18, |
| }, |
| { |
| I2C_BOARD_INFO("pcf8574", 0x3a), |
| .platform_data = &pcf_data_u35, |
| }, |
| { |
| I2C_BOARD_INFO("24c256", 0x50), |
| .platform_data = &eeprom_info, |
| }, |
| /* ALSO: |
| * - tvl320aic33 audio codec (0x1b) |
| * - msp430 microcontroller (0x23) |
| * - tvp5146 video decoder (0x5d) |
| */ |
| }; |
| |
| /* The msp430 uses a slow bitbanged I2C implementation (ergo 20 KHz), |
| * which requires 100 usec of idle bus after i2c writes sent to it. |
| */ |
| static struct davinci_i2c_platform_data i2c_pdata = { |
| .bus_freq = 20 /* kHz */, |
| .bus_delay = 100 /* usec */, |
| }; |
| |
| static void __init evm_init_i2c(void) |
| { |
| davinci_init_i2c(&i2c_pdata); |
| i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info)); |
| } |
| |
| static struct platform_device *davinci_evm_devices[] __initdata = { |
| #if defined(CONFIG_MTD_PHYSMAP) || \ |
| defined(CONFIG_MTD_PHYSMAP_MODULE) |
| &davinci_evm_norflash_device, |
| #endif |
| #if defined(CONFIG_BLK_DEV_PALMCHIP_BK3710) || \ |
| defined(CONFIG_BLK_DEV_PALMCHIP_BK3710_MODULE) |
| &ide_dev, |
| #endif |
| }; |
| |
| static void __init |
| davinci_evm_map_io(void) |
| { |
| davinci_map_common_io(); |
| } |
| |
| static __init void davinci_evm_init(void) |
| { |
| davinci_psc_init(); |
| |
| #if defined(CONFIG_BLK_DEV_PALMCHIP_BK3710) || \ |
| defined(CONFIG_BLK_DEV_PALMCHIP_BK3710_MODULE) |
| #if defined(CONFIG_MTD_PHYSMAP) || \ |
| defined(CONFIG_MTD_PHYSMAP_MODULE) |
| printk(KERN_WARNING "WARNING: both IDE and NOR flash are enabled, " |
| "but share pins.\n\t Disable IDE for NOR support.\n"); |
| #endif |
| #endif |
| |
| platform_add_devices(davinci_evm_devices, |
| ARRAY_SIZE(davinci_evm_devices)); |
| evm_init_i2c(); |
| |
| /* irlml6401 sustains over 3A, switches 5V in under 8 msec */ |
| setup_usb(500, 8); |
| } |
| |
| static __init void davinci_evm_irq_init(void) |
| { |
| davinci_init_common_hw(); |
| davinci_irq_init(); |
| } |
| |
| MACHINE_START(DAVINCI_EVM, "DaVinci EVM") |
| /* Maintainer: MontaVista Software <source@mvista.com> */ |
| .phys_io = IO_PHYS, |
| .io_pg_offst = (__IO_ADDRESS(IO_PHYS) >> 18) & 0xfffc, |
| .boot_params = (DAVINCI_DDR_BASE + 0x100), |
| .map_io = davinci_evm_map_io, |
| .init_irq = davinci_evm_irq_init, |
| .timer = &davinci_timer, |
| .init_machine = davinci_evm_init, |
| MACHINE_END |