| Tony Lindgren | 1a48e15 | 2009-05-28 13:23:52 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * linux/arch/arm/mach-omap2/gpmc-smc91x.c | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2009 Nokia Corporation | 
|  | 5 | * Contact:	Tony Lindgren | 
|  | 6 | * | 
|  | 7 | * This program is free software; you can redistribute it and/or modify | 
|  | 8 | * it under the terms of the GNU General Public License version 2 as | 
|  | 9 | * published by the Free Software Foundation. | 
|  | 10 | */ | 
|  | 11 |  | 
|  | 12 | #include <linux/kernel.h> | 
|  | 13 | #include <linux/platform_device.h> | 
|  | 14 | #include <linux/gpio.h> | 
|  | 15 | #include <linux/delay.h> | 
|  | 16 | #include <linux/interrupt.h> | 
|  | 17 | #include <linux/io.h> | 
|  | 18 | #include <linux/smc91x.h> | 
|  | 19 |  | 
| Tony Lindgren | ce491cf | 2009-10-20 09:40:47 -0700 | [diff] [blame] | 20 | #include <plat/board.h> | 
|  | 21 | #include <plat/gpmc.h> | 
|  | 22 | #include <plat/gpmc-smc91x.h> | 
| Tony Lindgren | 1a48e15 | 2009-05-28 13:23:52 -0700 | [diff] [blame] | 23 |  | 
|  | 24 | static struct omap_smc91x_platform_data *gpmc_cfg; | 
|  | 25 |  | 
|  | 26 | static struct resource gpmc_smc91x_resources[] = { | 
|  | 27 | [0] = { | 
|  | 28 | .flags		= IORESOURCE_MEM, | 
|  | 29 | }, | 
|  | 30 | [1] = { | 
|  | 31 | .flags		= IORESOURCE_IRQ, | 
|  | 32 | }, | 
|  | 33 | }; | 
|  | 34 |  | 
|  | 35 | static struct smc91x_platdata gpmc_smc91x_info = { | 
| Ladislav Michl | 3bc4801 | 2009-12-11 16:16:33 -0800 | [diff] [blame] | 36 | .flags	= SMC91X_USE_16BIT | SMC91X_NOWAIT | SMC91X_IO_SHIFT_0, | 
|  | 37 | .leda	= RPC_LED_100_10, | 
|  | 38 | .ledb	= RPC_LED_TX_RX, | 
| Tony Lindgren | 1a48e15 | 2009-05-28 13:23:52 -0700 | [diff] [blame] | 39 | }; | 
|  | 40 |  | 
|  | 41 | static struct platform_device gpmc_smc91x_device = { | 
|  | 42 | .name		= "smc91x", | 
|  | 43 | .id		= -1, | 
| Tony Lindgren | 1a48e15 | 2009-05-28 13:23:52 -0700 | [diff] [blame] | 44 | .dev		= { | 
|  | 45 | .platform_data = &gpmc_smc91x_info, | 
|  | 46 | }, | 
| Ladislav Michl | 3bc4801 | 2009-12-11 16:16:33 -0800 | [diff] [blame] | 47 | .num_resources	= ARRAY_SIZE(gpmc_smc91x_resources), | 
|  | 48 | .resource	= gpmc_smc91x_resources, | 
| Tony Lindgren | 1a48e15 | 2009-05-28 13:23:52 -0700 | [diff] [blame] | 49 | }; | 
|  | 50 |  | 
|  | 51 | /* | 
|  | 52 | * Set the gpmc timings for smc91c96. The timings are taken | 
|  | 53 | * from the data sheet available at: | 
|  | 54 | * http://www.smsc.com/main/catalog/lan91c96.html | 
|  | 55 | * REVISIT: Level shifters can add at least to the access latency. | 
|  | 56 | */ | 
|  | 57 | static int smc91c96_gpmc_retime(void) | 
|  | 58 | { | 
|  | 59 | struct gpmc_timings t; | 
|  | 60 | const int t3 = 10;	/* Figure 12.2 read and 12.4 write */ | 
|  | 61 | const int t4_r = 20;	/* Figure 12.2 read */ | 
|  | 62 | const int t4_w = 5;	/* Figure 12.4 write */ | 
|  | 63 | const int t5 = 25;	/* Figure 12.2 read */ | 
|  | 64 | const int t6 = 15;	/* Figure 12.2 read */ | 
|  | 65 | const int t7 = 5;	/* Figure 12.4 write */ | 
|  | 66 | const int t8 = 5;	/* Figure 12.4 write */ | 
|  | 67 | const int t20 = 185;	/* Figure 12.2 read and 12.4 write */ | 
|  | 68 | u32 l; | 
|  | 69 |  | 
|  | 70 | memset(&t, 0, sizeof(t)); | 
|  | 71 |  | 
|  | 72 | /* Read timings */ | 
|  | 73 | t.cs_on = 0; | 
|  | 74 | t.adv_on = t.cs_on; | 
|  | 75 | t.oe_on = t.adv_on + t3; | 
|  | 76 | t.access = t.oe_on + t5; | 
|  | 77 | t.oe_off = t.access; | 
|  | 78 | t.adv_rd_off = t.oe_off + max(t4_r, t6); | 
|  | 79 | t.cs_rd_off = t.oe_off; | 
|  | 80 | t.rd_cycle = t20 - t.oe_on; | 
|  | 81 |  | 
|  | 82 | /* Write timings */ | 
|  | 83 | t.we_on = t.adv_on + t3; | 
|  | 84 |  | 
|  | 85 | if (cpu_is_omap34xx() && (gpmc_cfg->flags & GPMC_MUX_ADD_DATA)) { | 
|  | 86 | t.wr_data_mux_bus = t.we_on; | 
|  | 87 | t.we_off = t.wr_data_mux_bus + t7; | 
|  | 88 | } else | 
|  | 89 | t.we_off = t.we_on + t7; | 
|  | 90 | if (cpu_is_omap34xx()) | 
|  | 91 | t.wr_access = t.we_off; | 
|  | 92 | t.adv_wr_off = t.we_off + max(t4_w, t8); | 
|  | 93 | t.cs_wr_off = t.we_off + t4_w; | 
|  | 94 | t.wr_cycle = t20 - t.we_on; | 
|  | 95 |  | 
|  | 96 | l = GPMC_CONFIG1_DEVICESIZE_16; | 
|  | 97 | if (gpmc_cfg->flags & GPMC_MUX_ADD_DATA) | 
|  | 98 | l |= GPMC_CONFIG1_MUXADDDATA; | 
|  | 99 | if (gpmc_cfg->flags & GPMC_READ_MON) | 
|  | 100 | l |= GPMC_CONFIG1_WAIT_READ_MON; | 
|  | 101 | if (gpmc_cfg->flags & GPMC_WRITE_MON) | 
|  | 102 | l |= GPMC_CONFIG1_WAIT_WRITE_MON; | 
|  | 103 | if (gpmc_cfg->wait_pin) | 
|  | 104 | l |= GPMC_CONFIG1_WAIT_PIN_SEL(gpmc_cfg->wait_pin); | 
|  | 105 | gpmc_cs_write_reg(gpmc_cfg->cs, GPMC_CS_CONFIG1, l); | 
|  | 106 |  | 
|  | 107 | /* | 
|  | 108 | * FIXME: Calculate the address and data bus muxed timings. | 
|  | 109 | * Note that at least adv_rd_off needs to be changed according | 
|  | 110 | * to omap3430 TRM Figure 11-11. Are the sdp boards using the | 
|  | 111 | * FPGA in between smc91x and omap as the timings are different | 
|  | 112 | * from above? | 
|  | 113 | */ | 
|  | 114 | if (gpmc_cfg->flags & GPMC_MUX_ADD_DATA) | 
|  | 115 | return 0; | 
|  | 116 |  | 
|  | 117 | return gpmc_cs_set_timings(gpmc_cfg->cs, &t); | 
|  | 118 | } | 
|  | 119 |  | 
|  | 120 | /* | 
|  | 121 | * Initialize smc91x device connected to the GPMC. Note that we | 
|  | 122 | * assume that pin multiplexing is done in the board-*.c file, | 
|  | 123 | * or in the bootloader. | 
|  | 124 | */ | 
|  | 125 | void __init gpmc_smc91x_init(struct omap_smc91x_platform_data *board_data) | 
|  | 126 | { | 
|  | 127 | unsigned long cs_mem_base; | 
|  | 128 | int ret; | 
|  | 129 |  | 
|  | 130 | gpmc_cfg = board_data; | 
|  | 131 |  | 
|  | 132 | if (gpmc_cfg->flags & GPMC_TIMINGS_SMC91C96) | 
|  | 133 | gpmc_cfg->retime = smc91c96_gpmc_retime; | 
|  | 134 |  | 
|  | 135 | if (gpmc_cs_request(gpmc_cfg->cs, SZ_16M, &cs_mem_base) < 0) { | 
|  | 136 | printk(KERN_ERR "Failed to request GPMC mem for smc91x\n"); | 
|  | 137 | return; | 
|  | 138 | } | 
|  | 139 |  | 
|  | 140 | gpmc_smc91x_resources[0].start = cs_mem_base + 0x300; | 
|  | 141 | gpmc_smc91x_resources[0].end = cs_mem_base + 0x30f; | 
|  | 142 | gpmc_smc91x_resources[1].flags |= (gpmc_cfg->flags & IRQF_TRIGGER_MASK); | 
|  | 143 |  | 
|  | 144 | if (gpmc_cfg->retime) { | 
|  | 145 | ret = gpmc_cfg->retime(); | 
|  | 146 | if (ret != 0) | 
|  | 147 | goto free1; | 
|  | 148 | } | 
|  | 149 |  | 
|  | 150 | if (gpio_request(gpmc_cfg->gpio_irq, "SMC91X irq") < 0) | 
|  | 151 | goto free1; | 
|  | 152 |  | 
|  | 153 | gpio_direction_input(gpmc_cfg->gpio_irq); | 
|  | 154 | gpmc_smc91x_resources[1].start = gpio_to_irq(gpmc_cfg->gpio_irq); | 
|  | 155 |  | 
|  | 156 | if (gpmc_cfg->gpio_pwrdwn) { | 
|  | 157 | ret = gpio_request(gpmc_cfg->gpio_pwrdwn, "SMC91X powerdown"); | 
|  | 158 | if (ret) | 
|  | 159 | goto free2; | 
|  | 160 | gpio_direction_output(gpmc_cfg->gpio_pwrdwn, 0); | 
|  | 161 | } | 
|  | 162 |  | 
|  | 163 | if (gpmc_cfg->gpio_reset) { | 
|  | 164 | ret = gpio_request(gpmc_cfg->gpio_reset, "SMC91X reset"); | 
|  | 165 | if (ret) | 
|  | 166 | goto free3; | 
|  | 167 |  | 
|  | 168 | gpio_direction_output(gpmc_cfg->gpio_reset, 0); | 
|  | 169 | gpio_set_value(gpmc_cfg->gpio_reset, 1); | 
|  | 170 | msleep(100); | 
|  | 171 | gpio_set_value(gpmc_cfg->gpio_reset, 0); | 
|  | 172 | } | 
|  | 173 |  | 
|  | 174 | if (platform_device_register(&gpmc_smc91x_device) < 0) { | 
|  | 175 | printk(KERN_ERR "Unable to register smc91x device\n"); | 
|  | 176 | gpio_free(gpmc_cfg->gpio_reset); | 
|  | 177 | goto free3; | 
|  | 178 | } | 
|  | 179 |  | 
|  | 180 | return; | 
|  | 181 |  | 
|  | 182 | free3: | 
|  | 183 | if (gpmc_cfg->gpio_pwrdwn) | 
|  | 184 | gpio_free(gpmc_cfg->gpio_pwrdwn); | 
|  | 185 | free2: | 
|  | 186 | gpio_free(gpmc_cfg->gpio_irq); | 
|  | 187 | free1: | 
|  | 188 | gpmc_cs_free(gpmc_cfg->cs); | 
|  | 189 |  | 
|  | 190 | printk(KERN_ERR "Could not initialize smc91x\n"); | 
|  | 191 | } |