1/* 2 * DSM-G600 board-setup 3 * 4 * Copyright (C) 2008 Rod Whitby <rod@whitby.id.au> 5 * Copyright (C) 2006 Tower Technologies 6 * 7 * based on ixdp425-setup.c: 8 * Copyright (C) 2003-2004 MontaVista Software, Inc. 9 * based on nslu2-power.c: 10 * Copyright (C) 2005 Tower Technologies 11 * based on nslu2-io.c: 12 * Copyright (C) 2004 Karen Spearel 13 * 14 * Author: Alessandro Zummo <a.zummo@towertech.it> 15 * Author: Michael Westerhof <mwester@dls.net> 16 * Author: Rod Whitby <rod@whitby.id.au> 17 * Maintainers: http://www.nslu2-linux.org/ 18 */ 19 20#include <linux/irq.h> 21#include <linux/jiffies.h> 22#include <linux/timer.h> 23#include <linux/serial.h> 24#include <linux/serial_8250.h> 25#include <linux/leds.h> 26#include <linux/reboot.h> 27#include <linux/i2c.h> 28#include <linux/i2c-gpio.h> 29 30#include <asm/mach-types.h> 31#include <asm/mach/arch.h> 32#include <asm/mach/flash.h> 33#include <asm/mach/time.h> 34#include <asm/gpio.h> 35 36#define DSMG600_SDA_PIN 5 37#define DSMG600_SCL_PIN 4 38 39/* DSM-G600 Timer Setting */ 40#define DSMG600_FREQ 66000000 41 42/* Buttons */ 43#define DSMG600_PB_GPIO 15 /* power button */ 44#define DSMG600_RB_GPIO 3 /* reset button */ 45 46/* Power control */ 47#define DSMG600_PO_GPIO 2 /* power off */ 48 49/* LEDs */ 50#define DSMG600_LED_PWR_GPIO 0 51#define DSMG600_LED_WLAN_GPIO 14 52 53static struct flash_platform_data dsmg600_flash_data = { 54 .map_name = "cfi_probe", 55 .width = 2, 56}; 57 58static struct resource dsmg600_flash_resource = { 59 .flags = IORESOURCE_MEM, 60}; 61 62static struct platform_device dsmg600_flash = { 63 .name = "IXP4XX-Flash", 64 .id = 0, 65 .dev.platform_data = &dsmg600_flash_data, 66 .num_resources = 1, 67 .resource = &dsmg600_flash_resource, 68}; 69 70static struct i2c_gpio_platform_data dsmg600_i2c_gpio_data = { 71 .sda_pin = DSMG600_SDA_PIN, 72 .scl_pin = DSMG600_SCL_PIN, 73}; 74 75static struct platform_device dsmg600_i2c_gpio = { 76 .name = "i2c-gpio", 77 .id = 0, 78 .dev = { 79 .platform_data = &dsmg600_i2c_gpio_data, 80 }, 81}; 82 83static struct i2c_board_info __initdata dsmg600_i2c_board_info [] = { 84 { 85 I2C_BOARD_INFO("pcf8563", 0x51), 86 }, 87}; 88 89static struct gpio_led dsmg600_led_pins[] = { 90 { 91 .name = "dsmg600:green:power", 92 .gpio = DSMG600_LED_PWR_GPIO, 93 }, 94 { 95 .name = "dsmg600:green:wlan", 96 .gpio = DSMG600_LED_WLAN_GPIO, 97 .active_low = true, 98 }, 99}; 100 101static struct gpio_led_platform_data dsmg600_led_data = { 102 .num_leds = ARRAY_SIZE(dsmg600_led_pins), 103 .leds = dsmg600_led_pins, 104}; 105 106static struct platform_device dsmg600_leds = { 107 .name = "leds-gpio", 108 .id = -1, 109 .dev.platform_data = &dsmg600_led_data, 110}; 111 112static struct resource dsmg600_uart_resources[] = { 113 { 114 .start = IXP4XX_UART1_BASE_PHYS, 115 .end = IXP4XX_UART1_BASE_PHYS + 0x0fff, 116 .flags = IORESOURCE_MEM, 117 }, 118 { 119 .start = IXP4XX_UART2_BASE_PHYS, 120 .end = IXP4XX_UART2_BASE_PHYS + 0x0fff, 121 .flags = IORESOURCE_MEM, 122 } 123}; 124 125static struct plat_serial8250_port dsmg600_uart_data[] = { 126 { 127 .mapbase = IXP4XX_UART1_BASE_PHYS, 128 .membase = (char *)IXP4XX_UART1_BASE_VIRT + REG_OFFSET, 129 .irq = IRQ_IXP4XX_UART1, 130 .flags = UPF_BOOT_AUTOCONF, 131 .iotype = UPIO_MEM, 132 .regshift = 2, 133 .uartclk = IXP4XX_UART_XTAL, 134 }, 135 { 136 .mapbase = IXP4XX_UART2_BASE_PHYS, 137 .membase = (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET, 138 .irq = IRQ_IXP4XX_UART2, 139 .flags = UPF_BOOT_AUTOCONF, 140 .iotype = UPIO_MEM, 141 .regshift = 2, 142 .uartclk = IXP4XX_UART_XTAL, 143 }, 144 { } 145}; 146 147static struct platform_device dsmg600_uart = { 148 .name = "serial8250", 149 .id = PLAT8250_DEV_PLATFORM, 150 .dev.platform_data = dsmg600_uart_data, 151 .num_resources = ARRAY_SIZE(dsmg600_uart_resources), 152 .resource = dsmg600_uart_resources, 153}; 154 155static struct platform_device *dsmg600_devices[] __initdata = { 156 &dsmg600_i2c_gpio, 157 &dsmg600_flash, 158 &dsmg600_leds, 159}; 160 161static void dsmg600_power_off(void) 162{ 163 /* enable the pwr cntl gpio */ 164 gpio_line_config(DSMG600_PO_GPIO, IXP4XX_GPIO_OUT); 165 166 /* poweroff */ 167 gpio_line_set(DSMG600_PO_GPIO, IXP4XX_GPIO_HIGH); 168} 169 170/* This is used to make sure the power-button pusher is serious. The button 171 * must be held until the value of this counter reaches zero. 172 */ 173static int power_button_countdown; 174 175/* Must hold the button down for at least this many counts to be processed */ 176#define PBUTTON_HOLDDOWN_COUNT 4 /* 2 secs */ 177 178static void dsmg600_power_handler(unsigned long data); 179static DEFINE_TIMER(dsmg600_power_timer, dsmg600_power_handler, 0, 0); 180 181static void dsmg600_power_handler(unsigned long data) 182{ 183 /* This routine is called twice per second to check the 184 * state of the power button. 185 */ 186 187 if (gpio_get_value(DSMG600_PB_GPIO)) { 188 189 /* IO Pin is 1 (button pushed) */ 190 if (power_button_countdown > 0) 191 power_button_countdown--; 192 193 } else { 194 195 /* Done on button release, to allow for auto-power-on mods. */ 196 if (power_button_countdown == 0) { 197 /* Signal init to do the ctrlaltdel action, 198 * this will bypass init if it hasn't started 199 * and do a kernel_restart. 200 */ 201 ctrl_alt_del(); 202 203 /* Change the state of the power LED to "blink" */ 204 gpio_line_set(DSMG600_LED_PWR_GPIO, IXP4XX_GPIO_LOW); 205 } else { 206 power_button_countdown = PBUTTON_HOLDDOWN_COUNT; 207 } 208 } 209 210 mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500)); 211} 212 213static irqreturn_t dsmg600_reset_handler(int irq, void *dev_id) 214{ 215 /* This is the paper-clip reset, it shuts the machine down directly. */ 216 machine_power_off(); 217 218 return IRQ_HANDLED; 219} 220 221static void __init dsmg600_timer_init(void) 222{ 223 /* The xtal on this machine is non-standard. */ 224 ixp4xx_timer_freq = DSMG600_FREQ; 225 226 /* Call standard timer_init function. */ 227 ixp4xx_timer_init(); 228} 229 230static struct sys_timer dsmg600_timer = { 231 .init = dsmg600_timer_init, 232}; 233 234static void __init dsmg600_init(void) 235{ 236 ixp4xx_sys_init(); 237 238 /* Make sure that GPIO14 and GPIO15 are not used as clocks */ 239 *IXP4XX_GPIO_GPCLKR = 0; 240 241 dsmg600_flash_resource.start = IXP4XX_EXP_BUS_BASE(0); 242 dsmg600_flash_resource.end = 243 IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; 244 245 i2c_register_board_info(0, dsmg600_i2c_board_info, 246 ARRAY_SIZE(dsmg600_i2c_board_info)); 247 248 /* The UART is required on the DSM-G600 (Redboot cannot use the 249 * NIC) -- do it here so that it does *not* get removed if 250 * platform_add_devices fails! 251 */ 252 (void)platform_device_register(&dsmg600_uart); 253 254 platform_add_devices(dsmg600_devices, ARRAY_SIZE(dsmg600_devices)); 255 256 pm_power_off = dsmg600_power_off; 257 258 if (request_irq(gpio_to_irq(DSMG600_RB_GPIO), &dsmg600_reset_handler, 259 IRQF_DISABLED | IRQF_TRIGGER_LOW, 260 "DSM-G600 reset button", NULL) < 0) { 261 262 printk(KERN_DEBUG "Reset Button IRQ %d not available\n", 263 gpio_to_irq(DSMG600_RB_GPIO)); 264 } 265 266 /* The power button on the D-Link DSM-G600 is on GPIO 15, but 267 * it cannot handle interrupts on that GPIO line. So we'll 268 * have to poll it with a kernel timer. 269 */ 270 271 /* Make sure that the power button GPIO is set up as an input */ 272 gpio_line_config(DSMG600_PB_GPIO, IXP4XX_GPIO_IN); 273 274 /* Set the initial value for the power button IRQ handler */ 275 power_button_countdown = PBUTTON_HOLDDOWN_COUNT; 276 277 mod_timer(&dsmg600_power_timer, jiffies + msecs_to_jiffies(500)); 278} 279 280MACHINE_START(DSMG600, "D-Link DSM-G600 RevA") 281 /* Maintainer: www.nslu2-linux.org */ 282 .phys_io = IXP4XX_PERIPHERAL_BASE_PHYS, 283 .io_pg_offst = ((IXP4XX_PERIPHERAL_BASE_VIRT) >> 18) & 0xFFFC, 284 .boot_params = 0x00000100, 285 .map_io = ixp4xx_map_io, 286 .init_irq = ixp4xx_init_irq, 287 .timer = &dsmg600_timer, 288 .init_machine = dsmg600_init, 289MACHINE_END 290