summaryrefslogtreecommitdiffstats
path: root/arch/mips/bcm47xx/wgt634u.c
blob: c63a4c287b5c21f632952a7d3b1925d77b888d40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2007 Aurelien Jarno <aurelien@aurel32.net>
 */

#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/leds.h>
#include <linux/mtd/physmap.h>
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_embedded.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <linux/gpio.h>
#include <asm/mach-bcm47xx/bcm47xx.h>

/* GPIO definitions for the WGT634U */
#define WGT634U_GPIO_LED	3
#define WGT634U_GPIO_RESET	2
#define WGT634U_GPIO_TP1	7
#define WGT634U_GPIO_TP2	6
#define WGT634U_GPIO_TP3	5
#define WGT634U_GPIO_TP4	4
#define WGT634U_GPIO_TP5	1

static struct gpio_led wgt634u_leds[] = {
	{
		.name = "power",
		.gpio = WGT634U_GPIO_LED,
		.active_low = 1,
		.default_trigger = "heartbeat",
	},
};

static struct gpio_led_platform_data wgt634u_led_data = {
	.num_leds =	ARRAY_SIZE(wgt634u_leds),
	.leds =		wgt634u_leds,
};

static struct platform_device wgt634u_gpio_leds = {
	.name =		"leds-gpio",
	.id =		-1,
	.dev = {
		.platform_data = &wgt634u_led_data,
	}
};


/* 8MiB flash. The struct mtd_partition matches original Netgear WGT634U
   firmware. */
static struct mtd_partition wgt634u_partitions[] = {
	{
		.name	    = "cfe",
		.offset	    = 0,
		.size	    = 0x60000,		/* 384k */
		.mask_flags = MTD_WRITEABLE	/* force read-only */
	},
	{
		.name	= "config",
		.offset = 0x60000,
		.size	= 0x20000		/* 128k */
	},
	{
		.name	= "linux",
		.offset = 0x80000,
		.size	= 0x140000		/* 1280k */
	},
	{
		.name	= "jffs",
		.offset = 0x1c0000,
		.size	= 0x620000		/* 6272k */
	},
	{
		.name	= "nvram",
		.offset = 0x7e0000,
		.size	= 0x20000		/* 128k */
	},
};

static struct physmap_flash_data wgt634u_flash_data = {
	.parts	  = wgt634u_partitions,
	.nr_parts = ARRAY_SIZE(wgt634u_partitions)
};

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

static struct platform_device wgt634u_flash = {
	.name	       = "physmap-flash",
	.id	       = 0,
	.dev	       = { .platform_data = &wgt634u_flash_data, },
	.resource      = &wgt634u_flash_resource,
	.num_resources = 1,
};

/* Platform devices */
static struct platform_device *wgt634u_devices[] __initdata = {
	&wgt634u_flash,
	&wgt634u_gpio_leds,
};

static irqreturn_t gpio_interrupt(int irq, void *ignored)
{
	int state;

	/* Interrupts are shared, check if the current one is
	   a GPIO interrupt. */
	if (!ssb_chipco_irq_status(&bcm47xx_bus.ssb.chipco,
				   SSB_CHIPCO_IRQ_GPIO))
		return IRQ_NONE;

	state = gpio_get_value(WGT634U_GPIO_RESET);

	/* Interrupt are level triggered, revert the interrupt polarity
	   to clear the interrupt. */
	ssb_gpio_polarity(&bcm47xx_bus.ssb, 1 << WGT634U_GPIO_RESET,
			  state ? 1 << WGT634U_GPIO_RESET : 0);

	if (!state) {
		printk(KERN_INFO "Reset button pressed");
		ctrl_alt_del();
	}

	return IRQ_HANDLED;
}

static int __init wgt634u_init(void)
{
	/* There is no easy way to detect that we are running on a WGT634U
	 * machine. Use the MAC address as an heuristic. Netgear Inc. has
	 * been allocated ranges 00:09:5b:xx:xx:xx and 00:0f:b5:xx:xx:xx.
	 */
	u8 *et0mac;

	if (bcm47xx_bus_type != BCM47XX_BUS_TYPE_SSB)
		return -ENODEV;

	et0mac = bcm47xx_bus.ssb.sprom.et0mac;

	if (et0mac[0] == 0x00 &&
	    ((et0mac[1] == 0x09 && et0mac[2] == 0x5b) ||
	     (et0mac[1] == 0x0f && et0mac[2] == 0xb5))) {
		struct ssb_mipscore *mcore = &bcm47xx_bus.ssb.mipscore;

		printk(KERN_INFO "WGT634U machine detected.\n");

		if (!request_irq(gpio_to_irq(WGT634U_GPIO_RESET),
				 gpio_interrupt, IRQF_SHARED,
				 "WGT634U GPIO", &bcm47xx_bus.ssb.chipco)) {
			gpio_direction_input(WGT634U_GPIO_RESET);
			ssb_gpio_intmask(&bcm47xx_bus.ssb,
					 1 << WGT634U_GPIO_RESET,
					 1 << WGT634U_GPIO_RESET);
			ssb_chipco_irq_mask(&bcm47xx_bus.ssb.chipco,
					    SSB_CHIPCO_IRQ_GPIO,
					    SSB_CHIPCO_IRQ_GPIO);
		}

		wgt634u_flash_data.width = mcore->pflash.buswidth;
		wgt634u_flash_resource.start = mcore->pflash.window;
		wgt634u_flash_resource.end = mcore->pflash.window
					   + mcore->pflash.window_size
					   - 1;
		return platform_add_devices(wgt634u_devices,
					    ARRAY_SIZE(wgt634u_devices));
	} else
		return -ENODEV;
}

module_init(wgt634u_init);