1/*
2 *  D-Link DIR-825 rev. B1 board support
3 *
4 *  Copyright (C) 2009-2011 Lukas Kuna, Evkanet, s.r.o.
5 *
6 *  based on mach-wndr3700.c
7 *
8 *  This program is free software; you can redistribute it and/or modify it
9 *  under the terms of the GNU General Public License version 2 as published
10 *  by the Free Software Foundation.
11 */
12
13#include <linux/platform_device.h>
14#include <linux/delay.h>
15#include <linux/rtl8366.h>
16
17#include <asm/mach-ath79/ath79.h>
18
19#include "dev-eth.h"
20#include "dev-ap9x-pci.h"
21#include "dev-gpio-buttons.h"
22#include "dev-leds-gpio.h"
23#include "dev-m25p80.h"
24#include "dev-usb.h"
25#include "machtypes.h"
26
27#define DIR825B1_GPIO_LED_BLUE_USB		0
28#define DIR825B1_GPIO_LED_ORANGE_POWER		1
29#define DIR825B1_GPIO_LED_BLUE_POWER		2
30#define DIR825B1_GPIO_LED_BLUE_WPS		4
31#define DIR825B1_GPIO_LED_ORANGE_PLANET		6
32#define DIR825B1_GPIO_LED_BLUE_PLANET		11
33
34#define DIR825B1_GPIO_BTN_RESET			3
35#define DIR825B1_GPIO_BTN_WPS			8
36
37#define DIR825B1_GPIO_RTL8366_SDA		5
38#define DIR825B1_GPIO_RTL8366_SCK		7
39
40#define DIR825B1_KEYS_POLL_INTERVAL		20	/* msecs */
41#define DIR825B1_KEYS_DEBOUNCE_INTERVAL		(3 * DIR825B1_KEYS_POLL_INTERVAL)
42
43#define DIR825B1_CAL0_OFFSET			0x1000
44#define DIR825B1_CAL1_OFFSET			0x5000
45#define DIR825B1_MAC0_OFFSET			0xffa0
46#define DIR825B1_MAC1_OFFSET			0xffb4
47
48#define DIR825B1_CAL_LOCATION_0			0x1f660000
49#define DIR825B1_CAL_LOCATION_1			0x1f7f0000
50
51static struct gpio_led dir825b1_leds_gpio[] __initdata = {
52	{
53		.name		= "d-link:blue:usb",
54		.gpio		= DIR825B1_GPIO_LED_BLUE_USB,
55		.active_low	= 1,
56	}, {
57		.name		= "d-link:orange:power",
58		.gpio		= DIR825B1_GPIO_LED_ORANGE_POWER,
59		.active_low	= 1,
60	}, {
61		.name		= "d-link:blue:power",
62		.gpio		= DIR825B1_GPIO_LED_BLUE_POWER,
63		.active_low	= 1,
64	}, {
65		.name		= "d-link:blue:wps",
66		.gpio		= DIR825B1_GPIO_LED_BLUE_WPS,
67		.active_low	= 1,
68	}, {
69		.name		= "d-link:orange:planet",
70		.gpio		= DIR825B1_GPIO_LED_ORANGE_PLANET,
71		.active_low	= 1,
72	}, {
73		.name		= "d-link:blue:planet",
74		.gpio		= DIR825B1_GPIO_LED_BLUE_PLANET,
75		.active_low	= 1,
76	}
77};
78
79static struct gpio_keys_button dir825b1_gpio_keys[] __initdata = {
80	{
81		.desc		= "reset",
82		.type		= EV_KEY,
83		.code		= KEY_RESTART,
84		.debounce_interval = DIR825B1_KEYS_DEBOUNCE_INTERVAL,
85		.gpio		= DIR825B1_GPIO_BTN_RESET,
86		.active_low	= 1,
87	}, {
88		.desc		= "wps",
89		.type		= EV_KEY,
90		.code		= KEY_WPS_BUTTON,
91		.debounce_interval = DIR825B1_KEYS_DEBOUNCE_INTERVAL,
92		.gpio		= DIR825B1_GPIO_BTN_WPS,
93		.active_low	= 1,
94	}
95};
96
97static struct rtl8366_initval dir825b1_rtl8366s_initvals[] = {
98	{ .reg = 0x06, .val = 0x0108 },
99};
100
101static struct rtl8366_platform_data dir825b1_rtl8366s_data = {
102	.gpio_sda	= DIR825B1_GPIO_RTL8366_SDA,
103	.gpio_sck	= DIR825B1_GPIO_RTL8366_SCK,
104	.num_initvals	= ARRAY_SIZE(dir825b1_rtl8366s_initvals),
105	.initvals	= dir825b1_rtl8366s_initvals,
106};
107
108static struct platform_device dir825b1_rtl8366s_device = {
109	.name		= RTL8366S_DRIVER_NAME,
110	.id		= -1,
111	.dev = {
112		.platform_data	= &dir825b1_rtl8366s_data,
113	}
114};
115
116static void dir825b1_read_ascii_mac(u8 *dest, u8 *src)
117{
118	int ret;
119
120	ret = sscanf(src, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
121		     &dest[0], &dest[1], &dest[2],
122		     &dest[3], &dest[4], &dest[5]);
123
124	if (ret != ETH_ALEN)
125		memset(dest, 0, ETH_ALEN);
126}
127
128static bool __init dir825b1_is_caldata_valid(u8 *p)
129{
130	u16 *magic0, *magic1;
131
132	magic0 = (u16 *)(p + DIR825B1_CAL0_OFFSET);
133	magic1 = (u16 *)(p + DIR825B1_CAL1_OFFSET);
134
135	return (*magic0 == 0xa55a && *magic1 == 0xa55a);
136}
137
138static void __init dir825b1_wlan_init(void)
139{
140	u8 *caldata;
141	u8 mac0[ETH_ALEN], mac1[ETH_ALEN];
142	u8 wmac0[ETH_ALEN], wmac1[ETH_ALEN];
143
144	caldata = (u8 *) KSEG1ADDR(DIR825B1_CAL_LOCATION_0);
145	if (!dir825b1_is_caldata_valid(caldata)) {
146		caldata = (u8 *)KSEG1ADDR(DIR825B1_CAL_LOCATION_1);
147		if (!dir825b1_is_caldata_valid(caldata)) {
148			pr_err("no calibration data found\n");
149			return;
150		}
151	}
152
153	dir825b1_read_ascii_mac(mac0, caldata + DIR825B1_MAC0_OFFSET);
154	dir825b1_read_ascii_mac(mac1, caldata + DIR825B1_MAC1_OFFSET);
155
156	ath79_init_mac(ath79_eth0_data.mac_addr, mac0, 0);
157	ath79_init_mac(ath79_eth1_data.mac_addr, mac1, 0);
158	ath79_init_mac(wmac0, mac0, 0);
159	ath79_init_mac(wmac1, mac1, 1);
160
161	ap9x_pci_setup_wmac_led_pin(0, 5);
162	ap9x_pci_setup_wmac_led_pin(1, 5);
163
164	ap94_pci_init(caldata + DIR825B1_CAL0_OFFSET, wmac0,
165		      caldata + DIR825B1_CAL1_OFFSET, wmac1);
166}
167
168static void __init dir825b1_setup(void)
169{
170	dir825b1_wlan_init();
171
172	ath79_register_mdio(0, 0x0);
173
174	ath79_eth0_data.mii_bus_dev = &dir825b1_rtl8366s_device.dev;
175	ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII;
176	ath79_eth0_data.speed = SPEED_1000;
177	ath79_eth0_data.duplex = DUPLEX_FULL;
178	ath79_eth0_pll_data.pll_1000 = 0x11110000;
179
180	ath79_eth1_data.mii_bus_dev = &dir825b1_rtl8366s_device.dev;
181	ath79_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII;
182	ath79_eth1_data.phy_mask = 0x10;
183	ath79_eth1_pll_data.pll_1000 = 0x11110000;
184
185	ath79_register_eth(0);
186	ath79_register_eth(1);
187
188	ath79_register_m25p80(NULL);
189
190	ath79_register_leds_gpio(-1, ARRAY_SIZE(dir825b1_leds_gpio),
191				 dir825b1_leds_gpio);
192
193	ath79_register_gpio_keys_polled(-1, DIR825B1_KEYS_POLL_INTERVAL,
194					ARRAY_SIZE(dir825b1_gpio_keys),
195					dir825b1_gpio_keys);
196
197	ath79_register_usb();
198
199	platform_device_register(&dir825b1_rtl8366s_device);
200}
201
202MIPS_MACHINE(ATH79_MACH_DIR_825_B1, "DIR-825-B1", "D-Link DIR-825 rev. B1",
203	     dir825b1_setup);
204