nas100d-setup.c 8.1 KB
Newer Older
1 2 3 4 5
/*
 * arch/arm/mach-ixp4xx/nas100d-setup.c
 *
 * NAS 100d board-setup
 *
6 7 8
 * Copyright (C) 2008 Rod Whitby <rod@whitby.id.au>
 *
 * based on ixdp425-setup.c:
9
 *      Copyright (C) 2003-2004 MontaVista Software, Inc.
10 11 12 13
 * based on nas100d-power.c:
 *	Copyright (C) 2005 Tower Technologies
 * based on nas100d-io.c
 *	Copyright (C) 2004 Karen Spearel
14 15 16 17 18 19
 *
 * Author: Alessandro Zummo <a.zummo@towertech.it>
 * Author: Rod Whitby <rod@whitby.id.au>
 * Maintainers: http://www.nslu2-linux.org/
 *
 */
20
#include <linux/gpio.h>
21
#include <linux/if_ether.h>
22 23 24
#include <linux/irq.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
25 26
#include <linux/serial.h>
#include <linux/serial_8250.h>
27
#include <linux/leds.h>
28
#include <linux/reboot.h>
29
#include <linux/i2c.h>
M
Michael-Luke Jones 已提交
30
#include <linux/i2c-gpio.h>
31
#include <linux/io.h>
32 33 34 35
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/flash.h>

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
#define NAS100D_SDA_PIN		5
#define NAS100D_SCL_PIN		6

/* Buttons */
#define NAS100D_PB_GPIO         14   /* power button */
#define NAS100D_RB_GPIO         4    /* reset button */

/* Power control */
#define NAS100D_PO_GPIO         12   /* power off */

/* LEDs */
#define NAS100D_LED_WLAN_GPIO	0
#define NAS100D_LED_DISK_GPIO	3
#define NAS100D_LED_PWR_GPIO	15

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
static struct flash_platform_data nas100d_flash_data = {
	.map_name		= "cfi_probe",
	.width			= 2,
};

static struct resource nas100d_flash_resource = {
	.flags			= IORESOURCE_MEM,
};

static struct platform_device nas100d_flash = {
	.name			= "IXP4XX-Flash",
	.id			= 0,
	.dev.platform_data	= &nas100d_flash_data,
	.num_resources		= 1,
	.resource		= &nas100d_flash_resource,
};

68 69
static struct i2c_board_info __initdata nas100d_i2c_board_info [] = {
	{
70
		I2C_BOARD_INFO("pcf8563", 0x51),
71 72 73
	},
};

74
static struct gpio_led nas100d_led_pins[] = {
75
	{
76
		.name		= "nas100d:green:wlan",
77 78
		.gpio		= NAS100D_LED_WLAN_GPIO,
		.active_low	= true,
79 80
	},
	{
81
		.name		= "nas100d:blue:power",  /* (off=flashing) */
82 83
		.gpio		= NAS100D_LED_PWR_GPIO,
		.active_low	= true,
84 85
	},
	{
86
		.name		= "nas100d:yellow:disk",
87 88
		.gpio		= NAS100D_LED_DISK_GPIO,
		.active_low	= true,
89 90 91
	},
};

92 93 94 95 96
static struct gpio_led_platform_data nas100d_led_data = {
	.num_leds		= ARRAY_SIZE(nas100d_led_pins),
	.leds			= nas100d_led_pins,
};

97
static struct platform_device nas100d_leds = {
98
	.name			= "leds-gpio",
99
	.id			= -1,
100
	.dev.platform_data	= &nas100d_led_data,
101 102
};

M
Michael-Luke Jones 已提交
103
static struct i2c_gpio_platform_data nas100d_i2c_gpio_data = {
104 105 106 107
	.sda_pin		= NAS100D_SDA_PIN,
	.scl_pin		= NAS100D_SCL_PIN,
};

M
Michael-Luke Jones 已提交
108 109
static struct platform_device nas100d_i2c_gpio = {
	.name			= "i2c-gpio",
110
	.id			= 0,
M
Michael-Luke Jones 已提交
111 112 113
	.dev	 = {
		.platform_data	= &nas100d_i2c_gpio_data,
	},
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
};

static struct resource nas100d_uart_resources[] = {
	{
		.start		= IXP4XX_UART1_BASE_PHYS,
		.end		= IXP4XX_UART1_BASE_PHYS + 0x0fff,
		.flags		= IORESOURCE_MEM,
	},
	{
		.start		= IXP4XX_UART2_BASE_PHYS,
		.end		= IXP4XX_UART2_BASE_PHYS + 0x0fff,
		.flags		= IORESOURCE_MEM,
	}
};

static struct plat_serial8250_port nas100d_uart_data[] = {
	{
		.mapbase	= IXP4XX_UART1_BASE_PHYS,
		.membase	= (char *)IXP4XX_UART1_BASE_VIRT + REG_OFFSET,
		.irq		= IRQ_IXP4XX_UART1,
		.flags		= UPF_BOOT_AUTOCONF,
		.iotype		= UPIO_MEM,
		.regshift	= 2,
		.uartclk	= IXP4XX_UART_XTAL,
	},
	{
		.mapbase	= IXP4XX_UART2_BASE_PHYS,
		.membase	= (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET,
		.irq		= IRQ_IXP4XX_UART2,
		.flags		= UPF_BOOT_AUTOCONF,
		.iotype		= UPIO_MEM,
		.regshift	= 2,
		.uartclk	= IXP4XX_UART_XTAL,
	},
	{ }
};

static struct platform_device nas100d_uart = {
	.name			= "serial8250",
	.id			= PLAT8250_DEV_PLATFORM,
	.dev.platform_data	= nas100d_uart_data,
	.num_resources		= 2,
	.resource		= nas100d_uart_resources,
};

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
/* Built-in 10/100 Ethernet MAC interfaces */
static struct eth_plat_info nas100d_plat_eth[] = {
	{
		.phy		= 0,
		.rxq		= 3,
		.txreadyq	= 20,
	}
};

static struct platform_device nas100d_eth[] = {
	{
		.name			= "ixp4xx_eth",
		.id			= IXP4XX_ETH_NPEB,
		.dev.platform_data	= nas100d_plat_eth,
	}
};

176
static struct platform_device *nas100d_devices[] __initdata = {
M
Michael-Luke Jones 已提交
177
	&nas100d_i2c_gpio,
178
	&nas100d_flash,
179
	&nas100d_leds,
180
	&nas100d_eth[0],
181 182 183 184 185 186
};

static void nas100d_power_off(void)
{
	/* This causes the box to drop the power and go dead. */

187 188
	/* enable the pwr cntl gpio and assert power off */
	gpio_direction_output(NAS100D_PO_GPIO, 1);
189 190
}

191 192 193 194 195 196 197 198 199
/* This is used to make sure the power-button pusher is serious.  The button
 * must be held until the value of this counter reaches zero.
 */
static int power_button_countdown;

/* Must hold the button down for at least this many counts to be processed */
#define PBUTTON_HOLDDOWN_COUNT 4 /* 2 secs */

static void nas100d_power_handler(unsigned long data);
200
static DEFINE_TIMER(nas100d_power_timer, nas100d_power_handler);
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224

static void nas100d_power_handler(unsigned long data)
{
	/* This routine is called twice per second to check the
	 * state of the power button.
	 */

	if (gpio_get_value(NAS100D_PB_GPIO)) {

		/* IO Pin is 1 (button pushed) */
		if (power_button_countdown > 0)
			power_button_countdown--;

	} else {

		/* Done on button release, to allow for auto-power-on mods. */
		if (power_button_countdown == 0) {
			/* Signal init to do the ctrlaltdel action,
			 * this will bypass init if it hasn't started
			 * and do a kernel_restart.
			 */
			ctrl_alt_del();

			/* Change the state of the power LED to "blink" */
225
			gpio_set_value(NAS100D_LED_PWR_GPIO, 0);
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
		} else {
			power_button_countdown = PBUTTON_HOLDDOWN_COUNT;
		}
	}

	mod_timer(&nas100d_power_timer, jiffies + msecs_to_jiffies(500));
}

static irqreturn_t nas100d_reset_handler(int irq, void *dev_id)
{
	/* This is the paper-clip reset, it shuts the machine down directly. */
	machine_power_off();

	return IRQ_HANDLED;
}

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
static int __init nas100d_gpio_init(void)
{
	if (!machine_is_nas100d())
		return 0;

	/*
	 * The power button on the Iomega NAS100d is on GPIO 14, but
	 * it cannot handle interrupts on that GPIO line.  So we'll
	 * have to poll it with a kernel timer.
	 */

	/* Request the power off GPIO */
	gpio_request(NAS100D_PO_GPIO, "power off");

	/* Make sure that the power button GPIO is set up as an input */
	gpio_request(NAS100D_PB_GPIO, "power button");
	gpio_direction_input(NAS100D_PB_GPIO);

	/* Set the initial value for the power button IRQ handler */
	power_button_countdown = PBUTTON_HOLDDOWN_COUNT;

	mod_timer(&nas100d_power_timer, jiffies + msecs_to_jiffies(500));

	return 0;
}
device_initcall(nas100d_gpio_init);

269 270
static void __init nas100d_init(void)
{
271 272 273
	uint8_t __iomem *f;
	int i;

274 275
	ixp4xx_sys_init();

276 277 278
	/* gpio 14 and 15 are _not_ clocks */
	*IXP4XX_GPIO_GPCLKR = 0;

279 280 281 282
	nas100d_flash_resource.start = IXP4XX_EXP_BUS_BASE(0);
	nas100d_flash_resource.end =
		IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1;

283 284 285
	i2c_register_board_info(0, nas100d_i2c_board_info,
				ARRAY_SIZE(nas100d_i2c_board_info));

286 287
	/*
	 * This is only useful on a modified machine, but it is valuable
288 289 290 291 292
	 * to have it first in order to see debug messages, and so that
	 * it does *not* get removed if platform_add_devices fails!
	 */
	(void)platform_device_register(&nas100d_uart);

293
	platform_add_devices(nas100d_devices, ARRAY_SIZE(nas100d_devices));
294

295 296 297
	pm_power_off = nas100d_power_off;

	if (request_irq(gpio_to_irq(NAS100D_RB_GPIO), &nas100d_reset_handler,
298
		IRQF_TRIGGER_LOW, "NAS100D reset button", NULL) < 0) {
299 300 301 302 303

		printk(KERN_DEBUG "Reset Button IRQ %d not available\n",
			gpio_to_irq(NAS100D_RB_GPIO));
	}

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
	/*
	 * Map in a portion of the flash and read the MAC address.
	 * Since it is stored in BE in the flash itself, we need to
	 * byteswap it if we're in LE mode.
	 */
	f = ioremap(IXP4XX_EXP_BUS_BASE(0), 0x1000000);
	if (f) {
		for (i = 0; i < 6; i++)
#ifdef __ARMEB__
			nas100d_plat_eth[0].hwaddr[i] = readb(f + 0xFC0FD8 + i);
#else
			nas100d_plat_eth[0].hwaddr[i] = readb(f + 0xFC0FD8 + (i^3));
#endif
		iounmap(f);
	}
J
Johannes Berg 已提交
319 320
	printk(KERN_INFO "NAS100D: Using MAC address %pM for port 0\n",
	       nas100d_plat_eth[0].hwaddr);
321

322 323 324 325
}

MACHINE_START(NAS100D, "Iomega NAS 100d")
	/* Maintainer: www.nslu2-linux.org */
326
	.atag_offset	= 0x100,
327
	.map_io		= ixp4xx_map_io,
328
	.init_early	= ixp4xx_init_early,
329
	.init_irq	= ixp4xx_init_irq,
S
Stephen Warren 已提交
330
	.init_time	= ixp4xx_timer_init,
331
	.init_machine	= nas100d_init,
332 333 334
#if defined(CONFIG_PCI)
	.dma_zone_size	= SZ_64M,
#endif
335
	.restart	= ixp4xx_restart,
336
MACHINE_END