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
|
--- a/arch/mips/ath79/dev-wmac.c
+++ b/arch/mips/ath79/dev-wmac.c
@@ -121,6 +121,137 @@ static void ar934x_wmac_setup(void)
ath79_wmac_data.is_clk_25mhz = true;
}
+static bool __init
+ar93xx_wmac_otp_read_word(void __iomem *base, int addr, u32 *data)
+{
+ int timeout = 1000;
+ u32 val;
+
+ __raw_readl(base + AR9300_OTP_BASE + (4 * addr));
+ while (timeout--) {
+ val = __raw_readl(base + AR9300_OTP_STATUS);
+ if ((val & AR9300_OTP_STATUS_TYPE) == AR9300_OTP_STATUS_VALID)
+ break;
+
+ udelay(10);
+ }
+
+ if (!timeout)
+ return false;
+
+ *data = __raw_readl(base + AR9300_OTP_READ_DATA);
+ return true;
+}
+
+static bool __init
+ar93xx_wmac_otp_read(void __iomem *base, int addr, u8 *dest, int len)
+{
+ u32 data;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ int offset = 8 * ((addr - i) % 4);
+
+ if (!ar93xx_wmac_otp_read_word(base, (addr - i) / 4, &data))
+ return false;
+
+ dest[i] = (data >> offset) & 0xff;
+ }
+
+ return true;
+}
+
+static bool __init
+ar93xx_wmac_otp_uncompress(void __iomem *base, int addr, int len, u8 *dest,
+ int dest_start, int dest_len)
+{
+ int dest_bytes = 0;
+ int offset = 0;
+ int end = addr - len;
+ u8 hdr[2];
+
+ while (addr > end) {
+ if (!ar93xx_wmac_otp_read(base, addr, hdr, 2))
+ return false;
+
+ addr -= 2;
+ offset += hdr[0];
+
+ if (offset <= dest_start + dest_len &&
+ offset + len >= dest_start) {
+ int data_offset = 0;
+ int dest_offset = 0;
+ int copy_len;
+
+ if (offset < dest_start)
+ data_offset = dest_start - offset;
+ else
+ dest_offset = offset - dest_start;
+
+ copy_len = len - data_offset;
+ if (copy_len > dest_len - dest_offset)
+ copy_len = dest_len - dest_offset;
+
+ ar93xx_wmac_otp_read(base, addr - data_offset,
+ dest + dest_offset,
+ copy_len);
+
+ dest_bytes += copy_len;
+ }
+ addr -= hdr[1];
+ }
+ return !!dest_bytes;
+}
+
+bool __init ar93xx_wmac_read_mac_address(u8 *dest)
+{
+ void __iomem *base;
+ bool ret = false;
+ int addr = 0x1ff;
+ unsigned int len;
+ u32 hdr_u32;
+ u8 *hdr = (u8 *) &hdr_u32;
+ u8 mac[6] = { 0x00, 0x02, 0x03, 0x04, 0x05, 0x06 };
+ int mac_start = 2, mac_end = 8;
+
+ BUG_ON(!soc_is_ar933x() && !soc_is_ar934x());
+ base = ioremap_nocache(AR933X_WMAC_BASE, AR933X_WMAC_SIZE);
+ while (addr > sizeof(hdr)) {
+ if (!ar93xx_wmac_otp_read(base, addr, hdr, sizeof(hdr)))
+ break;
+
+ if (hdr_u32 == 0 || hdr_u32 == ~0)
+ break;
+
+ len = (hdr[1] << 4) | (hdr[2] >> 4);
+ addr -= 4;
+
+ switch (hdr[0] >> 5) {
+ case 0:
+ if (len < mac_end)
+ break;
+
+ ar93xx_wmac_otp_read(base, addr - mac_start, mac, 6);
+ ret = true;
+ break;
+ case 3:
+ ret |= ar93xx_wmac_otp_uncompress(base, addr, len, mac,
+ mac_start, 6);
+ break;
+ default:
+ break;
+ }
+
+ addr -= len + 2;
+ }
+
+ iounmap(base);
+ if (ret)
+ memcpy(dest, mac, 6);
+
+ return ret;
+}
+
void __init ath79_register_wmac(u8 *cal_data, u8 *mac_addr)
{
if (soc_is_ar913x())
--- a/arch/mips/ath79/dev-wmac.h
+++ b/arch/mips/ath79/dev-wmac.h
@@ -13,5 +13,6 @@
#define _ATH79_DEV_WMAC_H
void ath79_register_wmac(u8 *cal_data, u8 *mac_addr);
+bool ar93xx_wmac_read_mac_address(u8 *dest);
#endif /* _ATH79_DEV_WMAC_H */
--- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h
+++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h
@@ -78,6 +78,14 @@
#define AR934X_EHCI_BASE 0x1b000000
#define AR934X_EHCI_SIZE 0x1000
+#define AR9300_OTP_BASE 0x14000
+#define AR9300_OTP_STATUS 0x15f18
+#define AR9300_OTP_STATUS_TYPE 0x7
+#define AR9300_OTP_STATUS_VALID 0x4
+#define AR9300_OTP_STATUS_ACCESS_BUSY 0x2
+#define AR9300_OTP_STATUS_SM_BUSY 0x1
+#define AR9300_OTP_READ_DATA 0x15f1c
+
/*
* DDR_CTRL block
*/
|