diff options
author | David Brownell <dbrownell@users.sourceforge.net> | 2009-06-21 17:50:12 -0400 |
---|---|---|
committer | Kevin Hilman <khilman@deeprootsystems.com> | 2009-08-26 03:56:59 -0400 |
commit | ff255c6caa389c90c68f5421f60ebfc40b68ea1b (patch) | |
tree | 9ef641a7b606219099ad5aa53e80ad162da1d193 | |
parent | 37b798da5df377521a8cd32a82467aa2d431f33e (diff) |
davinci: dm365 evm cpld support: leds, card detect, other setup
Add basic support for the CPLD on the DM365 EVM board:
- Read SW5 to set up NAND and keypad vs (someday) OneNAND
- Export MMC/SD card detect and writeprotect signals
- LED support (same layout as on DM355 EVM)
- Static config for video input:
* external HD imager precludes MMC1, Ethernet, audio
* else either tvp5146 (SD/default) or tvp7002 (HD)
The video input could actually be switched around dynamically;
change that if/when that's needed (and after those other video
inputs have driver support).
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
-rw-r--r-- | arch/arm/mach-davinci/board-dm365-evm.c | 263 |
1 files changed, 253 insertions, 10 deletions
diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index 3675e844b9b1..a1d5e7dac741 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/io.h> | 20 | #include <linux/io.h> |
21 | #include <linux/clk.h> | 21 | #include <linux/clk.h> |
22 | #include <linux/i2c/at24.h> | 22 | #include <linux/i2c/at24.h> |
23 | #include <linux/leds.h> | ||
23 | #include <linux/mtd/mtd.h> | 24 | #include <linux/mtd/mtd.h> |
24 | #include <linux/mtd/partitions.h> | 25 | #include <linux/mtd/partitions.h> |
25 | #include <linux/mtd/nand.h> | 26 | #include <linux/mtd/nand.h> |
@@ -33,18 +34,71 @@ | |||
33 | #include <mach/psc.h> | 34 | #include <mach/psc.h> |
34 | #include <mach/common.h> | 35 | #include <mach/common.h> |
35 | #include <mach/i2c.h> | 36 | #include <mach/i2c.h> |
36 | #include <linux/i2c.h> | ||
37 | #include <mach/serial.h> | 37 | #include <mach/serial.h> |
38 | #include <mach/common.h> | 38 | #include <mach/common.h> |
39 | #include <mach/mmc.h> | 39 | #include <mach/mmc.h> |
40 | #include <mach/nand.h> | 40 | #include <mach/nand.h> |
41 | 41 | ||
42 | |||
43 | static inline int have_imager(void) | ||
44 | { | ||
45 | /* REVISIT when it's supported, trigger via Kconfig */ | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static inline int have_tvp7002(void) | ||
50 | { | ||
51 | /* REVISIT when it's supported, trigger via Kconfig */ | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | |||
42 | #define DM365_ASYNC_EMIF_CONTROL_BASE 0x01d10000 | 56 | #define DM365_ASYNC_EMIF_CONTROL_BASE 0x01d10000 |
43 | #define DM365_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 | 57 | #define DM365_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 |
58 | #define DM365_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 | ||
44 | 59 | ||
45 | #define DM365_EVM_PHY_MASK (0x2) | 60 | #define DM365_EVM_PHY_MASK (0x2) |
46 | #define DM365_EVM_MDIO_FREQUENCY (2200000) /* PHY bus frequency */ | 61 | #define DM365_EVM_MDIO_FREQUENCY (2200000) /* PHY bus frequency */ |
47 | 62 | ||
63 | /* | ||
64 | * A MAX-II CPLD is used for various board control functions. | ||
65 | */ | ||
66 | #define CPLD_OFFSET(a13a8,a2a1) (((a13a8) << 10) + ((a2a1) << 3)) | ||
67 | |||
68 | #define CPLD_VERSION CPLD_OFFSET(0,0) /* r/o */ | ||
69 | #define CPLD_TEST CPLD_OFFSET(0,1) | ||
70 | #define CPLD_LEDS CPLD_OFFSET(0,2) | ||
71 | #define CPLD_MUX CPLD_OFFSET(0,3) | ||
72 | #define CPLD_SWITCH CPLD_OFFSET(1,0) /* r/o */ | ||
73 | #define CPLD_POWER CPLD_OFFSET(1,1) | ||
74 | #define CPLD_VIDEO CPLD_OFFSET(1,2) | ||
75 | #define CPLD_CARDSTAT CPLD_OFFSET(1,3) /* r/o */ | ||
76 | |||
77 | #define CPLD_DILC_OUT CPLD_OFFSET(2,0) | ||
78 | #define CPLD_DILC_IN CPLD_OFFSET(2,1) /* r/o */ | ||
79 | |||
80 | #define CPLD_IMG_DIR0 CPLD_OFFSET(2,2) | ||
81 | #define CPLD_IMG_MUX0 CPLD_OFFSET(2,3) | ||
82 | #define CPLD_IMG_MUX1 CPLD_OFFSET(3,0) | ||
83 | #define CPLD_IMG_DIR1 CPLD_OFFSET(3,1) | ||
84 | #define CPLD_IMG_MUX2 CPLD_OFFSET(3,2) | ||
85 | #define CPLD_IMG_MUX3 CPLD_OFFSET(3,3) | ||
86 | #define CPLD_IMG_DIR2 CPLD_OFFSET(4,0) | ||
87 | #define CPLD_IMG_MUX4 CPLD_OFFSET(4,1) | ||
88 | #define CPLD_IMG_MUX5 CPLD_OFFSET(4,2) | ||
89 | |||
90 | #define CPLD_RESETS CPLD_OFFSET(4,3) | ||
91 | |||
92 | #define CPLD_CCD_DIR1 CPLD_OFFSET(0x3e,0) | ||
93 | #define CPLD_CCD_IO1 CPLD_OFFSET(0x3e,1) | ||
94 | #define CPLD_CCD_DIR2 CPLD_OFFSET(0x3e,2) | ||
95 | #define CPLD_CCD_IO2 CPLD_OFFSET(0x3e,3) | ||
96 | #define CPLD_CCD_DIR3 CPLD_OFFSET(0x3f,0) | ||
97 | #define CPLD_CCD_IO3 CPLD_OFFSET(0x3f,1) | ||
98 | |||
99 | static void __iomem *cpld; | ||
100 | |||
101 | |||
48 | /* NOTE: this is geared for the standard config, with a socketed | 102 | /* NOTE: this is geared for the standard config, with a socketed |
49 | * 2 GByte Micron NAND (MT29F16G08FAA) using 128KB sectors. If you | 103 | * 2 GByte Micron NAND (MT29F16G08FAA) using 128KB sectors. If you |
50 | * swap chips with a different block size, partitioning will | 104 | * swap chips with a different block size, partitioning will |
@@ -135,7 +189,27 @@ static struct davinci_i2c_platform_data i2c_pdata = { | |||
135 | .bus_delay = 0 /* usec */, | 189 | .bus_delay = 0 /* usec */, |
136 | }; | 190 | }; |
137 | 191 | ||
192 | static int cpld_mmc_get_cd(int module) | ||
193 | { | ||
194 | if (!cpld) | ||
195 | return -ENXIO; | ||
196 | |||
197 | /* low == card present */ | ||
198 | return !(__raw_readb(cpld + CPLD_CARDSTAT) & BIT(module ? 4 : 0)); | ||
199 | } | ||
200 | |||
201 | static int cpld_mmc_get_ro(int module) | ||
202 | { | ||
203 | if (!cpld) | ||
204 | return -ENXIO; | ||
205 | |||
206 | /* high == card's write protect switch active */ | ||
207 | return !!(__raw_readb(cpld + CPLD_CARDSTAT) & BIT(module ? 5 : 1)); | ||
208 | } | ||
209 | |||
138 | static struct davinci_mmc_config dm365evm_mmc_config = { | 210 | static struct davinci_mmc_config dm365evm_mmc_config = { |
211 | .get_cd = cpld_mmc_get_cd, | ||
212 | .get_ro = cpld_mmc_get_ro, | ||
139 | .wires = 4, | 213 | .wires = 4, |
140 | .max_freq = 50000000, | 214 | .max_freq = 50000000, |
141 | .caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED, | 215 | .caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED, |
@@ -199,10 +273,185 @@ static void __init evm_init_i2c(void) | |||
199 | i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info)); | 273 | i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info)); |
200 | } | 274 | } |
201 | 275 | ||
202 | static struct platform_device *dm365_evm_devices[] __initdata = { | 276 | static struct platform_device *dm365_evm_nand_devices[] __initdata = { |
203 | &davinci_nand_device, | 277 | &davinci_nand_device, |
204 | }; | 278 | }; |
205 | 279 | ||
280 | static inline int have_leds(void) | ||
281 | { | ||
282 | #ifdef CONFIG_LEDS_CLASS | ||
283 | return 1; | ||
284 | #else | ||
285 | return 0; | ||
286 | #endif | ||
287 | } | ||
288 | |||
289 | struct cpld_led { | ||
290 | struct led_classdev cdev; | ||
291 | u8 mask; | ||
292 | }; | ||
293 | |||
294 | static const struct { | ||
295 | const char *name; | ||
296 | const char *trigger; | ||
297 | } cpld_leds[] = { | ||
298 | { "dm365evm::ds2", }, | ||
299 | { "dm365evm::ds3", }, | ||
300 | { "dm365evm::ds4", }, | ||
301 | { "dm365evm::ds5", }, | ||
302 | { "dm365evm::ds6", "nand-disk", }, | ||
303 | { "dm365evm::ds7", "mmc1", }, | ||
304 | { "dm365evm::ds8", "mmc0", }, | ||
305 | { "dm365evm::ds9", "heartbeat", }, | ||
306 | }; | ||
307 | |||
308 | static void cpld_led_set(struct led_classdev *cdev, enum led_brightness b) | ||
309 | { | ||
310 | struct cpld_led *led = container_of(cdev, struct cpld_led, cdev); | ||
311 | u8 reg = __raw_readb(cpld + CPLD_LEDS); | ||
312 | |||
313 | if (b != LED_OFF) | ||
314 | reg &= ~led->mask; | ||
315 | else | ||
316 | reg |= led->mask; | ||
317 | __raw_writeb(reg, cpld + CPLD_LEDS); | ||
318 | } | ||
319 | |||
320 | static enum led_brightness cpld_led_get(struct led_classdev *cdev) | ||
321 | { | ||
322 | struct cpld_led *led = container_of(cdev, struct cpld_led, cdev); | ||
323 | u8 reg = __raw_readb(cpld + CPLD_LEDS); | ||
324 | |||
325 | return (reg & led->mask) ? LED_OFF : LED_FULL; | ||
326 | } | ||
327 | |||
328 | static int __init cpld_leds_init(void) | ||
329 | { | ||
330 | int i; | ||
331 | |||
332 | if (!have_leds() || !cpld) | ||
333 | return 0; | ||
334 | |||
335 | /* setup LEDs */ | ||
336 | __raw_writeb(0xff, cpld + CPLD_LEDS); | ||
337 | for (i = 0; i < ARRAY_SIZE(cpld_leds); i++) { | ||
338 | struct cpld_led *led; | ||
339 | |||
340 | led = kzalloc(sizeof(*led), GFP_KERNEL); | ||
341 | if (!led) | ||
342 | break; | ||
343 | |||
344 | led->cdev.name = cpld_leds[i].name; | ||
345 | led->cdev.brightness_set = cpld_led_set; | ||
346 | led->cdev.brightness_get = cpld_led_get; | ||
347 | led->cdev.default_trigger = cpld_leds[i].trigger; | ||
348 | led->mask = BIT(i); | ||
349 | |||
350 | if (led_classdev_register(NULL, &led->cdev) < 0) { | ||
351 | kfree(led); | ||
352 | break; | ||
353 | } | ||
354 | } | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | /* run after subsys_initcall() for LEDs */ | ||
359 | fs_initcall(cpld_leds_init); | ||
360 | |||
361 | |||
362 | static void __init evm_init_cpld(void) | ||
363 | { | ||
364 | u8 mux, resets; | ||
365 | const char *label; | ||
366 | struct clk *aemif_clk; | ||
367 | |||
368 | /* Make sure we can configure the CPLD through CS1. Then | ||
369 | * leave it on for later access to MMC and LED registers. | ||
370 | */ | ||
371 | aemif_clk = clk_get(NULL, "aemif"); | ||
372 | if (IS_ERR(aemif_clk)) | ||
373 | return; | ||
374 | clk_enable(aemif_clk); | ||
375 | |||
376 | if (request_mem_region(DM365_ASYNC_EMIF_DATA_CE1_BASE, SECTION_SIZE, | ||
377 | "cpld") == NULL) | ||
378 | goto fail; | ||
379 | cpld = ioremap(DM365_ASYNC_EMIF_DATA_CE1_BASE, SECTION_SIZE); | ||
380 | if (!cpld) { | ||
381 | release_mem_region(DM365_ASYNC_EMIF_DATA_CE1_BASE, | ||
382 | SECTION_SIZE); | ||
383 | fail: | ||
384 | pr_err("ERROR: can't map CPLD\n"); | ||
385 | clk_disable(aemif_clk); | ||
386 | return; | ||
387 | } | ||
388 | |||
389 | /* External muxing for some signals */ | ||
390 | mux = 0; | ||
391 | |||
392 | /* Read SW5 to set up NAND + keypad _or_ OneNAND (sync read). | ||
393 | * NOTE: SW4 bus width setting must match! | ||
394 | */ | ||
395 | if ((__raw_readb(cpld + CPLD_SWITCH) & BIT(5)) == 0) { | ||
396 | /* external keypad mux */ | ||
397 | mux |= BIT(7); | ||
398 | |||
399 | platform_add_devices(dm365_evm_nand_devices, | ||
400 | ARRAY_SIZE(dm365_evm_nand_devices)); | ||
401 | } else { | ||
402 | /* no OneNAND support yet */ | ||
403 | } | ||
404 | |||
405 | /* Leave external chips in reset when unused. */ | ||
406 | resets = BIT(3) | BIT(2) | BIT(1) | BIT(0); | ||
407 | |||
408 | /* Static video input config with SN74CBT16214 1-of-3 mux: | ||
409 | * - port b1 == tvp7002 (mux lowbits == 1 or 6) | ||
410 | * - port b2 == imager (mux lowbits == 2 or 7) | ||
411 | * - port b3 == tvp5146 (mux lowbits == 5) | ||
412 | * | ||
413 | * Runtime switching could work too, with limitations. | ||
414 | */ | ||
415 | if (have_imager()) { | ||
416 | label = "HD imager"; | ||
417 | mux |= 1; | ||
418 | |||
419 | /* externally mux MMC1/ENET/AIC33 to imager */ | ||
420 | mux |= BIT(6) | BIT(5) | BIT(3); | ||
421 | } else { | ||
422 | struct davinci_soc_info *soc_info = &davinci_soc_info; | ||
423 | |||
424 | /* we can use MMC1 ... */ | ||
425 | dm365evm_mmc_configure(); | ||
426 | davinci_setup_mmc(1, &dm365evm_mmc_config); | ||
427 | |||
428 | /* ... and ENET ... */ | ||
429 | dm365evm_emac_configure(); | ||
430 | soc_info->emac_pdata->phy_mask = DM365_EVM_PHY_MASK; | ||
431 | soc_info->emac_pdata->mdio_max_freq = DM365_EVM_MDIO_FREQUENCY; | ||
432 | resets &= ~BIT(3); | ||
433 | |||
434 | /* ... and AIC33 */ | ||
435 | resets &= ~BIT(1); | ||
436 | |||
437 | if (have_tvp7002()) { | ||
438 | mux |= 2; | ||
439 | resets &= ~BIT(2); | ||
440 | label = "tvp7002 HD"; | ||
441 | } else { | ||
442 | /* default to tvp5146 */ | ||
443 | mux |= 5; | ||
444 | resets &= ~BIT(0); | ||
445 | label = "tvp5146 SD"; | ||
446 | } | ||
447 | } | ||
448 | __raw_writeb(mux, cpld + CPLD_MUX); | ||
449 | __raw_writeb(resets, cpld + CPLD_RESETS); | ||
450 | pr_info("EVM: %s video input\n", label); | ||
451 | |||
452 | /* REVISIT export switches: NTSC/PAL (SW5.6), EXTRA1 (SW5.2), etc */ | ||
453 | } | ||
454 | |||
206 | static struct davinci_uart_config uart_config __initdata = { | 455 | static struct davinci_uart_config uart_config __initdata = { |
207 | .enabled_uarts = (1 << 0), | 456 | .enabled_uarts = (1 << 0), |
208 | }; | 457 | }; |
@@ -214,11 +463,6 @@ static void __init dm365_evm_map_io(void) | |||
214 | 463 | ||
215 | static __init void dm365_evm_init(void) | 464 | static __init void dm365_evm_init(void) |
216 | { | 465 | { |
217 | struct davinci_soc_info *soc_info = &davinci_soc_info; | ||
218 | |||
219 | platform_add_devices(dm365_evm_devices, | ||
220 | ARRAY_SIZE(dm365_evm_devices)); | ||
221 | |||
222 | evm_init_i2c(); | 466 | evm_init_i2c(); |
223 | davinci_serial_init(&uart_config); | 467 | davinci_serial_init(&uart_config); |
224 | 468 | ||
@@ -226,10 +470,9 @@ static __init void dm365_evm_init(void) | |||
226 | dm365evm_mmc_configure(); | 470 | dm365evm_mmc_configure(); |
227 | 471 | ||
228 | davinci_setup_mmc(0, &dm365evm_mmc_config); | 472 | davinci_setup_mmc(0, &dm365evm_mmc_config); |
229 | davinci_setup_mmc(1, &dm365evm_mmc_config); | ||
230 | 473 | ||
231 | soc_info->emac_pdata->phy_mask = DM365_EVM_PHY_MASK; | 474 | /* maybe setup mmc1/etc ... _after_ mmc0 */ |
232 | soc_info->emac_pdata->mdio_max_freq = DM365_EVM_MDIO_FREQUENCY; | 475 | evm_init_cpld(); |
233 | } | 476 | } |
234 | 477 | ||
235 | static __init void dm365_evm_irq_init(void) | 478 | static __init void dm365_evm_irq_init(void) |