aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ar71xx/patches-4.4/523-MIPS-ath79-OTP-support.patch
blob: d11d418f172f8a351a1b3569ba1a9c5cf70ca604 (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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
--- a/arch/mips/ath79/dev-wmac.c
+++ b/arch/mips/ath79/dev-wmac.c
@@ -166,6 +166,149 @@ static void qca955x_wmac_setup(void)
 		ath79_wmac_data.is_clk_25mhz = true;
 }
 
+#define AR93XX_WMAC_SIZE \
+	(soc_is_ar934x() ? AR934X_WMAC_SIZE : AR933X_WMAC_SIZE)
+#define AR93XX_WMAC_BASE \
+	(soc_is_ar934x() ? AR934X_WMAC_BASE : AR933X_WMAC_BASE)
+
+#define AR93XX_OTP_BASE \
+	(soc_is_ar934x() ? AR934X_OTP_BASE : AR9300_OTP_BASE)
+#define AR93XX_OTP_STATUS \
+	(soc_is_ar934x() ? AR934X_OTP_STATUS : AR9300_OTP_STATUS)
+#define AR93XX_OTP_READ_DATA \
+	(soc_is_ar934x() ? AR934X_OTP_READ_DATA : AR9300_OTP_READ_DATA)
+
+static bool __init
+ar93xx_wmac_otp_read_word(void __iomem *base, int addr, u32 *data)
+{
+	int timeout = 1000;
+	u32 val;
+
+	__raw_readl(base + AR93XX_OTP_BASE + (4 * addr));
+	while (timeout--) {
+		val = __raw_readl(base + AR93XX_OTP_STATUS);
+		if ((val & AR9300_OTP_STATUS_TYPE) == AR9300_OTP_STATUS_VALID)
+			break;
+
+		udelay(10);
+	}
+
+	if (!timeout)
+		return false;
+
+	*data = __raw_readl(base + AR93XX_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(AR93XX_WMAC_BASE, AR93XX_WMAC_SIZE);
+	while (addr > sizeof(hdr_u32)) {
+		if (!ar93xx_wmac_otp_read(base, addr, hdr, sizeof(hdr_u32)))
+			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
@@ -14,5 +14,6 @@
 
 void ath79_register_wmac(u8 *cal_data, u8 *mac_addr);
 void ath79_register_wmac_simple(void);
+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
@@ -112,6 +112,14 @@
 #define QCA955X_EHCI1_BASE	0x1b400000
 #define QCA955X_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
  */
@@ -149,6 +157,13 @@
 #define AR934X_DDR_REG_FLUSH_PCIE	0xa8
 #define AR934X_DDR_REG_FLUSH_WMAC	0xac
 
+#define AR934X_OTP_BASE					0x30000
+#define AR934X_OTP_STATUS				0x31018
+#define AR934X_OTP_READ_DATA				0x3101c
+#define AR934X_OTP_INTF2_ADDRESS			0x31008
+#define AR934X_OTP_INTF3_ADDRESS			0x3100c
+#define AR934X_OTP_PGENB_SETUP_HOLD_TIME_ADDRESS	0x31034
+
 /*
  * PLL block
  */