diff options
Diffstat (limited to 'arch/arm/mach-omap2/gpmc-smc91x.c')
-rw-r--r-- | arch/arm/mach-omap2/gpmc-smc91x.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/gpmc-smc91x.c b/arch/arm/mach-omap2/gpmc-smc91x.c new file mode 100644 index 000000000000..df99d31d8b64 --- /dev/null +++ b/arch/arm/mach-omap2/gpmc-smc91x.c | |||
@@ -0,0 +1,189 @@ | |||
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 | |||
20 | #include <mach/board.h> | ||
21 | #include <mach/gpmc.h> | ||
22 | #include <mach/gpmc-smc91x.h> | ||
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 = { | ||
36 | .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT | SMC91X_IO_SHIFT_0, | ||
37 | }; | ||
38 | |||
39 | static struct platform_device gpmc_smc91x_device = { | ||
40 | .name = "smc91x", | ||
41 | .id = -1, | ||
42 | .num_resources = ARRAY_SIZE(gpmc_smc91x_resources), | ||
43 | .resource = gpmc_smc91x_resources, | ||
44 | .dev = { | ||
45 | .platform_data = &gpmc_smc91x_info, | ||
46 | }, | ||
47 | }; | ||
48 | |||
49 | /* | ||
50 | * Set the gpmc timings for smc91c96. The timings are taken | ||
51 | * from the data sheet available at: | ||
52 | * http://www.smsc.com/main/catalog/lan91c96.html | ||
53 | * REVISIT: Level shifters can add at least to the access latency. | ||
54 | */ | ||
55 | static int smc91c96_gpmc_retime(void) | ||
56 | { | ||
57 | struct gpmc_timings t; | ||
58 | const int t3 = 10; /* Figure 12.2 read and 12.4 write */ | ||
59 | const int t4_r = 20; /* Figure 12.2 read */ | ||
60 | const int t4_w = 5; /* Figure 12.4 write */ | ||
61 | const int t5 = 25; /* Figure 12.2 read */ | ||
62 | const int t6 = 15; /* Figure 12.2 read */ | ||
63 | const int t7 = 5; /* Figure 12.4 write */ | ||
64 | const int t8 = 5; /* Figure 12.4 write */ | ||
65 | const int t20 = 185; /* Figure 12.2 read and 12.4 write */ | ||
66 | u32 l; | ||
67 | |||
68 | memset(&t, 0, sizeof(t)); | ||
69 | |||
70 | /* Read timings */ | ||
71 | t.cs_on = 0; | ||
72 | t.adv_on = t.cs_on; | ||
73 | t.oe_on = t.adv_on + t3; | ||
74 | t.access = t.oe_on + t5; | ||
75 | t.oe_off = t.access; | ||
76 | t.adv_rd_off = t.oe_off + max(t4_r, t6); | ||
77 | t.cs_rd_off = t.oe_off; | ||
78 | t.rd_cycle = t20 - t.oe_on; | ||
79 | |||
80 | /* Write timings */ | ||
81 | t.we_on = t.adv_on + t3; | ||
82 | |||
83 | if (cpu_is_omap34xx() && (gpmc_cfg->flags & GPMC_MUX_ADD_DATA)) { | ||
84 | t.wr_data_mux_bus = t.we_on; | ||
85 | t.we_off = t.wr_data_mux_bus + t7; | ||
86 | } else | ||
87 | t.we_off = t.we_on + t7; | ||
88 | if (cpu_is_omap34xx()) | ||
89 | t.wr_access = t.we_off; | ||
90 | t.adv_wr_off = t.we_off + max(t4_w, t8); | ||
91 | t.cs_wr_off = t.we_off + t4_w; | ||
92 | t.wr_cycle = t20 - t.we_on; | ||
93 | |||
94 | l = GPMC_CONFIG1_DEVICESIZE_16; | ||
95 | if (gpmc_cfg->flags & GPMC_MUX_ADD_DATA) | ||
96 | l |= GPMC_CONFIG1_MUXADDDATA; | ||
97 | if (gpmc_cfg->flags & GPMC_READ_MON) | ||
98 | l |= GPMC_CONFIG1_WAIT_READ_MON; | ||
99 | if (gpmc_cfg->flags & GPMC_WRITE_MON) | ||
100 | l |= GPMC_CONFIG1_WAIT_WRITE_MON; | ||
101 | if (gpmc_cfg->wait_pin) | ||
102 | l |= GPMC_CONFIG1_WAIT_PIN_SEL(gpmc_cfg->wait_pin); | ||
103 | gpmc_cs_write_reg(gpmc_cfg->cs, GPMC_CS_CONFIG1, l); | ||
104 | |||
105 | /* | ||
106 | * FIXME: Calculate the address and data bus muxed timings. | ||
107 | * Note that at least adv_rd_off needs to be changed according | ||
108 | * to omap3430 TRM Figure 11-11. Are the sdp boards using the | ||
109 | * FPGA in between smc91x and omap as the timings are different | ||
110 | * from above? | ||
111 | */ | ||
112 | if (gpmc_cfg->flags & GPMC_MUX_ADD_DATA) | ||
113 | return 0; | ||
114 | |||
115 | return gpmc_cs_set_timings(gpmc_cfg->cs, &t); | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Initialize smc91x device connected to the GPMC. Note that we | ||
120 | * assume that pin multiplexing is done in the board-*.c file, | ||
121 | * or in the bootloader. | ||
122 | */ | ||
123 | void __init gpmc_smc91x_init(struct omap_smc91x_platform_data *board_data) | ||
124 | { | ||
125 | unsigned long cs_mem_base; | ||
126 | int ret; | ||
127 | |||
128 | gpmc_cfg = board_data; | ||
129 | |||
130 | if (gpmc_cfg->flags & GPMC_TIMINGS_SMC91C96) | ||
131 | gpmc_cfg->retime = smc91c96_gpmc_retime; | ||
132 | |||
133 | if (gpmc_cs_request(gpmc_cfg->cs, SZ_16M, &cs_mem_base) < 0) { | ||
134 | printk(KERN_ERR "Failed to request GPMC mem for smc91x\n"); | ||
135 | return; | ||
136 | } | ||
137 | |||
138 | gpmc_smc91x_resources[0].start = cs_mem_base + 0x300; | ||
139 | gpmc_smc91x_resources[0].end = cs_mem_base + 0x30f; | ||
140 | gpmc_smc91x_resources[1].flags |= (gpmc_cfg->flags & IRQF_TRIGGER_MASK); | ||
141 | |||
142 | if (gpmc_cfg->retime) { | ||
143 | ret = gpmc_cfg->retime(); | ||
144 | if (ret != 0) | ||
145 | goto free1; | ||
146 | } | ||
147 | |||
148 | if (gpio_request(gpmc_cfg->gpio_irq, "SMC91X irq") < 0) | ||
149 | goto free1; | ||
150 | |||
151 | gpio_direction_input(gpmc_cfg->gpio_irq); | ||
152 | gpmc_smc91x_resources[1].start = gpio_to_irq(gpmc_cfg->gpio_irq); | ||
153 | |||
154 | if (gpmc_cfg->gpio_pwrdwn) { | ||
155 | ret = gpio_request(gpmc_cfg->gpio_pwrdwn, "SMC91X powerdown"); | ||
156 | if (ret) | ||
157 | goto free2; | ||
158 | gpio_direction_output(gpmc_cfg->gpio_pwrdwn, 0); | ||
159 | } | ||
160 | |||
161 | if (gpmc_cfg->gpio_reset) { | ||
162 | ret = gpio_request(gpmc_cfg->gpio_reset, "SMC91X reset"); | ||
163 | if (ret) | ||
164 | goto free3; | ||
165 | |||
166 | gpio_direction_output(gpmc_cfg->gpio_reset, 0); | ||
167 | gpio_set_value(gpmc_cfg->gpio_reset, 1); | ||
168 | msleep(100); | ||
169 | gpio_set_value(gpmc_cfg->gpio_reset, 0); | ||
170 | } | ||
171 | |||
172 | if (platform_device_register(&gpmc_smc91x_device) < 0) { | ||
173 | printk(KERN_ERR "Unable to register smc91x device\n"); | ||
174 | gpio_free(gpmc_cfg->gpio_reset); | ||
175 | goto free3; | ||
176 | } | ||
177 | |||
178 | return; | ||
179 | |||
180 | free3: | ||
181 | if (gpmc_cfg->gpio_pwrdwn) | ||
182 | gpio_free(gpmc_cfg->gpio_pwrdwn); | ||
183 | free2: | ||
184 | gpio_free(gpmc_cfg->gpio_irq); | ||
185 | free1: | ||
186 | gpmc_cs_free(gpmc_cfg->cs); | ||
187 | |||
188 | printk(KERN_ERR "Could not initialize smc91x\n"); | ||
189 | } | ||