1/*
2 *  linux/arch/arm/mach-pxa/idp.c
3 *
4 *  This program is free software; you can redistribute it and/or modify
5 *  it under the terms of the GNU General Public License version 2 as
6 *  published by the Free Software Foundation.
7 *
8 *  Copyright (c) 2001 Cliff Brake, Accelent Systems Inc.
9 *
10 *  2001-09-13: Cliff Brake <cbrake@accelent.com>
11 *              Initial code
12 *
13 *  2005-02-15: Cliff Brake <cliff.brake@gmail.com>
14 *  		<http://www.vibren.com> <http://bec-systems.com>
15 *              Updated for 2.6 kernel
16 *
17 */
18
19#include <linux/init.h>
20#include <linux/interrupt.h>
21#include <linux/irq.h>
22#include <linux/platform_device.h>
23#include <linux/fb.h>
24
25#include <asm/setup.h>
26#include <asm/memory.h>
27#include <asm/mach-types.h>
28#include <mach/hardware.h>
29#include <asm/irq.h>
30
31#include <asm/mach/arch.h>
32#include <asm/mach/map.h>
33
34#include <mach/pxa25x.h>
35#include <mach/idp.h>
36#include <linux/platform_data/video-pxafb.h>
37#include <mach/bitfield.h>
38#include <linux/platform_data/mmc-pxamci.h>
39#include <linux/smc91x.h>
40
41#include "generic.h"
42#include "devices.h"
43
44/* TODO:
45 * - add pxa2xx_audio_ops_t device structure
46 * - Ethernet interrupt
47 */
48
49static unsigned long idp_pin_config[] __initdata = {
50	/* LCD */
51	GPIOxx_LCD_DSTN_16BPP,
52
53	/* BTUART */
54	GPIO42_BTUART_RXD,
55	GPIO43_BTUART_TXD,
56	GPIO44_BTUART_CTS,
57	GPIO45_BTUART_RTS,
58
59	/* STUART */
60	GPIO46_STUART_RXD,
61	GPIO47_STUART_TXD,
62
63	/* MMC */
64	GPIO6_MMC_CLK,
65	GPIO8_MMC_CS0,
66
67	/* Ethernet */
68	GPIO33_nCS_5,	/* Ethernet CS */
69	GPIO4_GPIO,	/* Ethernet IRQ */
70};
71
72static struct resource smc91x_resources[] = {
73	[0] = {
74		.start	= (IDP_ETH_PHYS + 0x300),
75		.end	= (IDP_ETH_PHYS + 0xfffff),
76		.flags	= IORESOURCE_MEM,
77	},
78	[1] = {
79		.start	= PXA_GPIO_TO_IRQ(4),
80		.end	= PXA_GPIO_TO_IRQ(4),
81		.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
82	}
83};
84
85static struct smc91x_platdata smc91x_platdata = {
86	.flags = SMC91X_USE_32BIT | SMC91X_USE_DMA | SMC91X_NOWAIT,
87};
88
89static struct platform_device smc91x_device = {
90	.name		= "smc91x",
91	.id		= 0,
92	.num_resources	= ARRAY_SIZE(smc91x_resources),
93	.resource	= smc91x_resources,
94	.dev.platform_data = &smc91x_platdata,
95};
96
97static void idp_backlight_power(int on)
98{
99	if (on) {
100		IDP_CPLD_LCD |= (1<<1);
101	} else {
102		IDP_CPLD_LCD &= ~(1<<1);
103	}
104}
105
106static void idp_vlcd(int on)
107{
108	if (on) {
109		IDP_CPLD_LCD |= (1<<2);
110	} else {
111		IDP_CPLD_LCD &= ~(1<<2);
112	}
113}
114
115static void idp_lcd_power(int on, struct fb_var_screeninfo *var)
116{
117	if (on) {
118		IDP_CPLD_LCD |= (1<<0);
119	} else {
120		IDP_CPLD_LCD &= ~(1<<0);
121	}
122
123	/* call idp_vlcd for now as core driver does not support
124	 * both power and vlcd hooks.  Note, this is not technically
125	 * the correct sequence, but seems to work.  Disclaimer:
126	 * this may eventually damage the display.
127	 */
128
129	idp_vlcd(on);
130}
131
132static struct pxafb_mode_info sharp_lm8v31_mode = {
133	.pixclock	= 270000,
134	.xres		= 640,
135	.yres		= 480,
136	.bpp		= 16,
137	.hsync_len	= 1,
138	.left_margin	= 3,
139	.right_margin	= 3,
140	.vsync_len	= 1,
141	.upper_margin	= 0,
142	.lower_margin	= 0,
143	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
144	.cmap_greyscale	= 0,
145};
146
147static struct pxafb_mach_info sharp_lm8v31 = {
148	.modes          = &sharp_lm8v31_mode,
149	.num_modes      = 1,
150	.cmap_inverse	= 0,
151	.cmap_static	= 0,
152	.lcd_conn	= LCD_COLOR_DSTN_16BPP | LCD_PCLK_EDGE_FALL |
153			  LCD_AC_BIAS_FREQ(255),
154	.pxafb_backlight_power = &idp_backlight_power,
155	.pxafb_lcd_power = &idp_lcd_power
156};
157
158static struct pxamci_platform_data idp_mci_platform_data = {
159	.ocr_mask		= MMC_VDD_32_33|MMC_VDD_33_34,
160	.gpio_card_detect	= -1,
161	.gpio_card_ro		= -1,
162	.gpio_power		= -1,
163};
164
165static void __init idp_init(void)
166{
167	printk("idp_init()\n");
168
169	pxa2xx_mfp_config(ARRAY_AND_SIZE(idp_pin_config));
170	pxa_set_ffuart_info(NULL);
171	pxa_set_btuart_info(NULL);
172	pxa_set_stuart_info(NULL);
173
174	platform_device_register(&smc91x_device);
175	//platform_device_register(&mst_audio_device);
176	pxa_set_fb_info(NULL, &sharp_lm8v31);
177	pxa_set_mci_info(&idp_mci_platform_data);
178}
179
180static struct map_desc idp_io_desc[] __initdata = {
181  	{
182		.virtual	=  IDP_COREVOLT_VIRT,
183		.pfn		= __phys_to_pfn(IDP_COREVOLT_PHYS),
184		.length		= IDP_COREVOLT_SIZE,
185		.type		= MT_DEVICE
186	}, {
187		.virtual	=  IDP_CPLD_VIRT,
188		.pfn		= __phys_to_pfn(IDP_CPLD_PHYS),
189		.length		= IDP_CPLD_SIZE,
190		.type		= MT_DEVICE
191	}
192};
193
194static void __init idp_map_io(void)
195{
196	pxa25x_map_io();
197	iotable_init(idp_io_desc, ARRAY_SIZE(idp_io_desc));
198}
199
200/* LEDs */
201#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
202struct idp_led {
203	struct led_classdev     cdev;
204	u8                      mask;
205};
206
207/*
208 * The triggers lines up below will only be used if the
209 * LED triggers are compiled in.
210 */
211static const struct {
212	const char *name;
213	const char *trigger;
214} idp_leds[] = {
215	{ "idp:green", "heartbeat", },
216	{ "idp:red", "cpu0", },
217};
218
219static void idp_led_set(struct led_classdev *cdev,
220		enum led_brightness b)
221{
222	struct idp_led *led = container_of(cdev,
223			struct idp_led, cdev);
224	u32 reg = IDP_CPLD_LED_CONTROL;
225
226	if (b != LED_OFF)
227		reg &= ~led->mask;
228	else
229		reg |= led->mask;
230
231	IDP_CPLD_LED_CONTROL = reg;
232}
233
234static enum led_brightness idp_led_get(struct led_classdev *cdev)
235{
236	struct idp_led *led = container_of(cdev,
237			struct idp_led, cdev);
238
239	return (IDP_CPLD_LED_CONTROL & led->mask) ? LED_OFF : LED_FULL;
240}
241
242static int __init idp_leds_init(void)
243{
244	int i;
245
246	if (!machine_is_pxa_idp())
247		return -ENODEV;
248
249	for (i = 0; i < ARRAY_SIZE(idp_leds); i++) {
250		struct idp_led *led;
251
252		led = kzalloc(sizeof(*led), GFP_KERNEL);
253		if (!led)
254			break;
255
256		led->cdev.name = idp_leds[i].name;
257		led->cdev.brightness_set = idp_led_set;
258		led->cdev.brightness_get = idp_led_get;
259		led->cdev.default_trigger = idp_leds[i].trigger;
260
261		if (i == 0)
262			led->mask = IDP_HB_LED;
263		else
264			led->mask = IDP_BUSY_LED;
265
266		if (led_classdev_register(NULL, &led->cdev) < 0) {
267			kfree(led);
268			break;
269		}
270	}
271
272	return 0;
273}
274
275/*
276 * Since we may have triggers on any subsystem, defer registration
277 * until after subsystem_init.
278 */
279fs_initcall(idp_leds_init);
280#endif
281
282MACHINE_START(PXA_IDP, "Vibren PXA255 IDP")
283	/* Maintainer: Vibren Technologies */
284	.map_io		= idp_map_io,
285	.nr_irqs	= PXA_NR_IRQS,
286	.init_irq	= pxa25x_init_irq,
287	.handle_irq	= pxa25x_handle_irq,
288	.init_time	= pxa_timer_init,
289	.init_machine	= idp_init,
290	.restart	= pxa_restart,
291MACHINE_END
292