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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2026 Morse Micro
*/
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/stringify.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#include <linux/gpio.h>
#include "hif.h"
#include "mac.h"
#include "bus.h"
#include "core.h"
#include "fw.h"
#include "yaps.h"
#define MM8108_REG_HOST_MAGIC_VALUE 0xDEADBEEF
#define MM8108_REG_RESET_VALUE 0xDEAD
#define MM8108_REG_SDIO_DEVICE_ADDR 0x0000207C
#define MM8108_REG_SDIO_DEVICE_BURST_OFFSET 9
#define MM8108_REG_TRGR_BASE 0x00003c00
#define MM8108_REG_INT_BASE 0x00003c50
#define MM8108_REG_MSI_ADDRESS 0x00004100
#define MM8108_REG_MSI_VALUE 0x1
#define MM8108_REG_MANIFEST_PTR_ADDRESS 0x00002d40
#define MM8108_REG_APPS_BOOT_ADDR 0x00002084
#define MM8108_REG_RESET 0x000020AC
#define MM8108_REG_AON_COUNT 2
#define MM8108_REG_AON_ADDR 0x00002114
#define MM8108_REG_AON_LATCH_ADDR 0x00405020
#define MM8108_REG_AON_LATCH_MASK 0x1
#define MM8108_REG_AON_RESET_USB_VALUE 0x8
#define MM8108_APPS_MAC_DMEM_ADDR_START 0x00100000
#define MM8108_REG_RC_CLK_POWER_OFF_ADDR 0x00405020
#define MM8108_REG_RC_CLK_POWER_OFF_MASK 0x00000040
#define MM8108_SLOW_RC_POWER_ON_DELAY_MS 2
#define MM8108_RESET_DELAY_TIME_MS 400
#define MM8108_REG_OTPCTRL_PLDO 0x00004014
#define MM8108_REG_OTPCTRL_PENVDD2 0x00004010
#define MM8108_REG_OTPCTRL_PDSTB 0x00004018
#define MM8108_REG_OTPCTRL_PTM 0x0000401c
#define MM8108_REG_OTPCTRL_PCE 0x00004020
#define MM8108_REG_OTPCTRL_PA 0x00004034
#define MM8108_REG_OTPCTRL_PECCRDB 0x00004048
#define MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START 0x0000400c
#define MM8108_REG_OTPCTRL_PDOUT 0x00004040
#define MM81X_OTP_MAC_ADDR_2_BANK_NUM 27
#define MM81X_OTP_MAC_ADDR_1_BANK_NUM 26
#define MM81X_OTP_MAC_ADDR_1_MASK GENMASK(31, 16)
#define MM81X_OTP_BOARD_TYPE_BANK_NUM 26
#define MM81X_OTP_BOARD_TYPE_MASK GENMASK(15, 0)
#define MM810X_BOARD_TYPE_MAX_VALUE (MM81X_OTP_BOARD_TYPE_MASK - 1)
static void mm81x_hw_otp_power_up(struct mm81x *mors)
{
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_PENVDD2, 1);
udelay(2);
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_PLDO, 1);
usleep_range(10, 20);
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_PDSTB, 1);
udelay(3);
}
static void mm81x_hw_otp_power_down(struct mm81x *mors)
{
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_PDSTB, 0);
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_PLDO, 0);
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_PENVDD2, 0);
}
static void mm81x_hw_otp_read_enable(struct mm81x *mors)
{
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_PTM, 0);
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_PCE, 1);
usleep_range(10, 20);
}
static void mm81x_hw_otp_read_disable(struct mm81x *mors)
{
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_PCE, 0);
udelay(1);
}
static int mm81x_hw_otp_read(struct mm81x *mors, u8 bank_num, u32 *buf,
u8 ignore_ecc)
{
u32 auto_rd_start_tmp;
u32 auto_rd_start = 1;
int i;
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_PA, bank_num);
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_PECCRDB, ignore_ecc);
mm81x_reg32_read(mors, MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START,
&auto_rd_start_tmp);
auto_rd_start_tmp &= 0xfffffffe;
mm81x_reg32_write(mors, MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START,
auto_rd_start | auto_rd_start_tmp);
/* Attempt reading up to 5 times. */
for (i = 0; i < 5 && auto_rd_start; i++) {
usleep_range(15, 20);
mm81x_reg32_read(mors, MM8108_REG_OTPCTRL_ACTION_AUTO_RD_START,
&auto_rd_start_tmp);
auto_rd_start = auto_rd_start_tmp & 0x1;
}
if (i == 5)
return -EIO;
mm81x_reg32_read(mors, MM8108_REG_OTPCTRL_PDOUT, buf);
return 0;
}
int mm81x_hw_otp_get_board_type(struct mm81x *mors)
{
int board_type = 0;
u32 otp_word = 0;
int ret;
mm81x_claim_bus(mors);
mm81x_hw_otp_power_up(mors);
mm81x_hw_otp_read_enable(mors);
ret = mm81x_hw_otp_read(mors, MM81X_OTP_BOARD_TYPE_BANK_NUM, &otp_word,
1);
mm81x_hw_otp_read_disable(mors);
mm81x_hw_otp_power_down(mors);
mm81x_release_bus(mors);
if (ret)
return -EINVAL;
board_type = otp_word & MM81X_OTP_BOARD_TYPE_MASK;
return board_type;
}
bool mm81x_hw_otp_valid_board_type(u32 board_type)
{
return board_type > 0 && board_type < MM810X_BOARD_TYPE_MAX_VALUE;
}
int mm81x_hw_otp_get_mac_addr(struct mm81x *mors)
{
u32 mac1 = 0;
u32 mac2 = 0;
int ret = 0;
mm81x_claim_bus(mors);
mm81x_hw_otp_power_up(mors);
mm81x_hw_otp_read_enable(mors);
ret = mm81x_hw_otp_read(mors, MM81X_OTP_MAC_ADDR_1_BANK_NUM, &mac1, 1);
if (ret)
goto exit;
ret = mm81x_hw_otp_read(mors, MM81X_OTP_MAC_ADDR_2_BANK_NUM, &mac2, 1);
if (ret)
goto exit;
put_unaligned_le16((mac1 & MM81X_OTP_MAC_ADDR_1_MASK) >> 16,
&mors->macaddr[0]);
put_unaligned_le32(mac2, &mors->macaddr[2]);
exit:
mm81x_hw_otp_read_disable(mors);
mm81x_hw_otp_power_down(mors);
mm81x_release_bus(mors);
return ret;
}
void mm81x_hw_irq_enable(struct mm81x *mors, u32 irq, bool enable)
{
u32 irq_en, irq_en_addr = irq < 32 ? MM81X_REG_INT1_EN(mors) :
MM81X_REG_INT2_EN(mors);
u32 irq_clr_addr = irq < 32 ? MM81X_REG_INT1_CLR(mors) :
MM81X_REG_INT2_CLR(mors);
u32 mask = irq < 32 ? (1 << irq) : (1 << (irq - 32));
mm81x_claim_bus(mors);
mm81x_reg32_read(mors, irq_en_addr, &irq_en);
if (enable)
irq_en |= (mask);
else
irq_en &= ~(mask);
mm81x_reg32_write(mors, irq_clr_addr, mask);
mm81x_reg32_write(mors, irq_en_addr, irq_en);
mm81x_release_bus(mors);
}
int mm81x_hw_irq_handle(struct mm81x *mors)
{
u32 status1 = 0;
mm81x_reg32_read(mors, MM81X_REG_INT1_STS(mors), &status1);
if (status1 & MM81X_HIF_IRQ_MASK_ALL)
mm81x_hif_handle_irq(mors, status1);
if (status1 & MM81X_INT_BEACON_VIF_MASK_ALL)
mm81x_mac_beacon_irq_handle(mors, status1);
mm81x_reg32_write(mors, MM81X_REG_INT1_CLR(mors), status1);
return status1 ? 1 : 0;
}
EXPORT_SYMBOL_GPL(mm81x_hw_irq_handle);
void mm81x_hw_irq_clear(struct mm81x *mors)
{
mm81x_claim_bus(mors);
mm81x_reg32_write(mors, MM81X_REG_INT1_CLR(mors), 0xFFFFFFFF);
mm81x_reg32_write(mors, MM81X_REG_INT2_CLR(mors), 0xFFFFFFFF);
mm81x_release_bus(mors);
}
void mm81x_hw_toggle_aon_latch(struct mm81x *mors)
{
u32 address = MM81X_REG_AON_LATCH_ADDR(mors);
u32 mask = MM81X_REG_AON_LATCH_MASK(mors);
u32 latch;
mm81x_reg32_read(mors, address, &latch);
mm81x_reg32_write(mors, address, latch & ~(mask));
mdelay(5);
mm81x_reg32_write(mors, address, latch | mask);
mdelay(5);
mm81x_reg32_write(mors, address, latch & ~(mask));
mdelay(5);
}
void mm81x_hw_enable_stop_notifications(struct mm81x *mors, bool enable)
{
mm81x_hw_irq_enable(mors, MM81X_INT_HW_STOP_NOTIFICATION_NUM, enable);
}
void mm81x_hw_enable_burst_mode(struct mm81x *mors, const u8 burst_mode)
{
u32 reg32_value;
mm81x_claim_bus(mors);
if (mm81x_reg32_read(mors, MM8108_REG_SDIO_DEVICE_ADDR, ®32_value))
goto end;
reg32_value &= ~(u32)(SDIO_WORD_BURST_MASK
<< MM8108_REG_SDIO_DEVICE_BURST_OFFSET);
reg32_value |= (u32)(burst_mode << MM8108_REG_SDIO_DEVICE_BURST_OFFSET);
dev_dbg(mors->dev,
"Setting Burst mode to %d Writing 0x%08X to the register",
burst_mode, reg32_value);
if (mm81x_reg32_write(mors, MM8108_REG_SDIO_DEVICE_ADDR, reg32_value))
goto end;
end:
mm81x_release_bus(mors);
}
EXPORT_SYMBOL_GPL(mm81x_hw_enable_burst_mode);
static int mm81x_hw_enable_internal_slow_clock(struct mm81x *mors)
{
u32 rc_clock_reg_value;
int ret = 0;
dev_dbg(mors->dev, "Enabling internal slow clock");
ret = mm81x_reg32_read(mors, MM8108_REG_RC_CLK_POWER_OFF_ADDR,
&rc_clock_reg_value);
if (ret)
goto exit;
rc_clock_reg_value &= ~MM8108_REG_RC_CLK_POWER_OFF_MASK;
ret = mm81x_reg32_write(mors, MM8108_REG_RC_CLK_POWER_OFF_ADDR,
rc_clock_reg_value);
if (ret)
goto exit;
mm81x_hw_toggle_aon_latch(mors);
/* Wait for the clock to turn on and settle */
mdelay(MM8108_SLOW_RC_POWER_ON_DELAY_MS);
exit:
return ret;
}
int mm81x_hw_digital_reset(struct mm81x *mors)
{
int ret = 0;
mm81x_claim_bus(mors);
/* This should be the first step in digital reset, do not reorder */
ret = mm81x_hw_enable_internal_slow_clock(mors);
if (ret)
goto exit;
if (mors->bus_type == MM81X_BUS_TYPE_USB) {
ret = mm81x_bus_digital_reset(mors);
goto usb_done;
}
if (MM81X_REG_RESET(mors) != 0)
ret = mm81x_reg32_write(mors, MM81X_REG_RESET(mors),
MM81X_REG_RESET_VALUE(mors));
usb_done:
msleep(MM8108_RESET_DELAY_TIME_MS);
exit:
mm81x_release_bus(mors);
if (!ret)
mors->chip_was_reset = true;
return ret;
}
void mm81x_hw_pre_firmware_ndr_hook(struct mm81x *mors)
{
/* We need disable bursting for firmware download/init procedure */
mm81x_bus_config_burst_mode(mors, false);
}
void mm81x_hw_post_firmware_ndr_hook(struct mm81x *mors)
{
/* We are safe here to re-enable bursting again, if supported */
mm81x_bus_config_burst_mode(mors, true);
}
const struct mm81x_regs mm8108_regs = {
.chip_id_address = MM8108_REG_CHIP_ID,
.irq_base_address = MM8108_REG_INT_BASE,
.trgr_base_address = MM8108_REG_TRGR_BASE,
.cpu_reset_address = MM8108_REG_RESET,
.cpu_reset_value = MM8108_REG_RESET_VALUE,
.manifest_ptr_address = MM8108_REG_MANIFEST_PTR_ADDRESS,
.msi_address = MM8108_REG_MSI_ADDRESS,
.msi_value = MM8108_REG_MSI_VALUE,
.magic_num_value = MM8108_REG_HOST_MAGIC_VALUE,
.early_clk_ctrl_value = 0,
.pager_base_address = MM8108_APPS_MAC_DMEM_ADDR_START,
.aon_latch = MM8108_REG_AON_LATCH_ADDR,
.aon_latch_mask = MM8108_REG_AON_LATCH_MASK,
.aon_reset_usb_value = MM8108_REG_AON_RESET_USB_VALUE,
.aon = MM8108_REG_AON_ADDR,
.aon_count = MM8108_REG_AON_COUNT,
.boot_address = MM8108_REG_APPS_BOOT_ADDR,
};
MODULE_FIRMWARE(MM81X_FW_DIR "/v" __stringify(MM81X_FW_VER_MAX) "/"
MM8108_FW_BASE MM81X_FW_EXT);
|