]>
Commit | Line | Data |
---|---|---|
2f52a517 VK |
1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
2 | // Copyright(c) 2015-17 Intel Corporation. | |
3 | ||
4 | /* | |
5 | * Cadence SoundWire Master module | |
6 | * Used by Master driver | |
7 | */ | |
8 | ||
9 | #include <linux/delay.h> | |
10 | #include <linux/device.h> | |
aa85066e | 11 | #include <linux/debugfs.h> |
2f52a517 | 12 | #include <linux/interrupt.h> |
18de65d9 | 13 | #include <linux/io.h> |
2f52a517 VK |
14 | #include <linux/module.h> |
15 | #include <linux/mod_devicetable.h> | |
16 | #include <linux/soundwire/sdw_registers.h> | |
17 | #include <linux/soundwire/sdw.h> | |
5d6b3c8b VK |
18 | #include <sound/pcm_params.h> |
19 | #include <sound/soc.h> | |
2f52a517 VK |
20 | #include "bus.h" |
21 | #include "cadence_master.h" | |
22 | ||
04592dce PLB |
23 | static int interrupt_mask; |
24 | module_param_named(cnds_mcp_int_mask, interrupt_mask, int, 0444); | |
25 | MODULE_PARM_DESC(cdns_mcp_int_mask, "Cadence MCP IntMask"); | |
26 | ||
2f52a517 VK |
27 | #define CDNS_MCP_CONFIG 0x0 |
28 | ||
29 | #define CDNS_MCP_CONFIG_MCMD_RETRY GENMASK(27, 24) | |
30 | #define CDNS_MCP_CONFIG_MPREQ_DELAY GENMASK(20, 16) | |
31 | #define CDNS_MCP_CONFIG_MMASTER BIT(7) | |
32 | #define CDNS_MCP_CONFIG_BUS_REL BIT(6) | |
33 | #define CDNS_MCP_CONFIG_SNIFFER BIT(5) | |
34 | #define CDNS_MCP_CONFIG_SSPMOD BIT(4) | |
35 | #define CDNS_MCP_CONFIG_CMD BIT(3) | |
36 | #define CDNS_MCP_CONFIG_OP GENMASK(2, 0) | |
37 | #define CDNS_MCP_CONFIG_OP_NORMAL 0 | |
38 | ||
39 | #define CDNS_MCP_CONTROL 0x4 | |
40 | ||
41 | #define CDNS_MCP_CONTROL_RST_DELAY GENMASK(10, 8) | |
42 | #define CDNS_MCP_CONTROL_CMD_RST BIT(7) | |
43 | #define CDNS_MCP_CONTROL_SOFT_RST BIT(6) | |
44 | #define CDNS_MCP_CONTROL_SW_RST BIT(5) | |
45 | #define CDNS_MCP_CONTROL_HW_RST BIT(4) | |
46 | #define CDNS_MCP_CONTROL_CLK_PAUSE BIT(3) | |
47 | #define CDNS_MCP_CONTROL_CLK_STOP_CLR BIT(2) | |
48 | #define CDNS_MCP_CONTROL_CMD_ACCEPT BIT(1) | |
49 | #define CDNS_MCP_CONTROL_BLOCK_WAKEUP BIT(0) | |
50 | ||
2f52a517 VK |
51 | #define CDNS_MCP_CMDCTRL 0x8 |
52 | #define CDNS_MCP_SSPSTAT 0xC | |
53 | #define CDNS_MCP_FRAME_SHAPE 0x10 | |
54 | #define CDNS_MCP_FRAME_SHAPE_INIT 0x14 | |
05be59ac PLB |
55 | #define CDNS_MCP_FRAME_SHAPE_COL_MASK GENMASK(2, 0) |
56 | #define CDNS_MCP_FRAME_SHAPE_ROW_OFFSET 3 | |
2f52a517 VK |
57 | |
58 | #define CDNS_MCP_CONFIG_UPDATE 0x18 | |
59 | #define CDNS_MCP_CONFIG_UPDATE_BIT BIT(0) | |
60 | ||
61 | #define CDNS_MCP_PHYCTRL 0x1C | |
62 | #define CDNS_MCP_SSP_CTRL0 0x20 | |
63 | #define CDNS_MCP_SSP_CTRL1 0x28 | |
64 | #define CDNS_MCP_CLK_CTRL0 0x30 | |
65 | #define CDNS_MCP_CLK_CTRL1 0x38 | |
a50954e2 | 66 | #define CDNS_MCP_CLK_MCLKD_MASK GENMASK(7, 0) |
2f52a517 VK |
67 | |
68 | #define CDNS_MCP_STAT 0x40 | |
69 | ||
70 | #define CDNS_MCP_STAT_ACTIVE_BANK BIT(20) | |
71 | #define CDNS_MCP_STAT_CLK_STOP BIT(16) | |
72 | ||
73 | #define CDNS_MCP_INTSTAT 0x44 | |
74 | #define CDNS_MCP_INTMASK 0x48 | |
75 | ||
76 | #define CDNS_MCP_INT_IRQ BIT(31) | |
a2cff9ee | 77 | #define CDNS_MCP_INT_RESERVED1 GENMASK(30, 17) |
2f52a517 VK |
78 | #define CDNS_MCP_INT_WAKEUP BIT(16) |
79 | #define CDNS_MCP_INT_SLAVE_RSVD BIT(15) | |
80 | #define CDNS_MCP_INT_SLAVE_ALERT BIT(14) | |
81 | #define CDNS_MCP_INT_SLAVE_ATTACH BIT(13) | |
82 | #define CDNS_MCP_INT_SLAVE_NATTACH BIT(12) | |
83 | #define CDNS_MCP_INT_SLAVE_MASK GENMASK(15, 12) | |
84 | #define CDNS_MCP_INT_DPINT BIT(11) | |
85 | #define CDNS_MCP_INT_CTRL_CLASH BIT(10) | |
86 | #define CDNS_MCP_INT_DATA_CLASH BIT(9) | |
9b5884a0 | 87 | #define CDNS_MCP_INT_PARITY BIT(8) |
2f52a517 | 88 | #define CDNS_MCP_INT_CMD_ERR BIT(7) |
a2cff9ee | 89 | #define CDNS_MCP_INT_RESERVED2 GENMASK(6, 4) |
9b5884a0 | 90 | #define CDNS_MCP_INT_RX_NE BIT(3) |
2f52a517 VK |
91 | #define CDNS_MCP_INT_RX_WL BIT(2) |
92 | #define CDNS_MCP_INT_TXE BIT(1) | |
9b5884a0 | 93 | #define CDNS_MCP_INT_TXF BIT(0) |
a2cff9ee | 94 | #define CDNS_MCP_INT_RESERVED (CDNS_MCP_INT_RESERVED1 | CDNS_MCP_INT_RESERVED2) |
2f52a517 VK |
95 | |
96 | #define CDNS_MCP_INTSET 0x4C | |
97 | ||
b07dd9b4 PLB |
98 | #define CDNS_MCP_SLAVE_STAT 0x50 |
99 | #define CDNS_MCP_SLAVE_STAT_MASK GENMASK(1, 0) | |
2f52a517 VK |
100 | |
101 | #define CDNS_MCP_SLAVE_INTSTAT0 0x54 | |
102 | #define CDNS_MCP_SLAVE_INTSTAT1 0x58 | |
103 | #define CDNS_MCP_SLAVE_INTSTAT_NPRESENT BIT(0) | |
104 | #define CDNS_MCP_SLAVE_INTSTAT_ATTACHED BIT(1) | |
105 | #define CDNS_MCP_SLAVE_INTSTAT_ALERT BIT(2) | |
106 | #define CDNS_MCP_SLAVE_INTSTAT_RESERVED BIT(3) | |
107 | #define CDNS_MCP_SLAVE_STATUS_BITS GENMASK(3, 0) | |
108 | #define CDNS_MCP_SLAVE_STATUS_NUM 4 | |
109 | ||
110 | #define CDNS_MCP_SLAVE_INTMASK0 0x5C | |
111 | #define CDNS_MCP_SLAVE_INTMASK1 0x60 | |
112 | ||
664b1658 PLB |
113 | #define CDNS_MCP_SLAVE_INTMASK0_MASK GENMASK(31, 0) |
114 | #define CDNS_MCP_SLAVE_INTMASK1_MASK GENMASK(15, 0) | |
2f52a517 VK |
115 | |
116 | #define CDNS_MCP_PORT_INTSTAT 0x64 | |
117 | #define CDNS_MCP_PDI_STAT 0x6C | |
118 | ||
119 | #define CDNS_MCP_FIFOLEVEL 0x78 | |
120 | #define CDNS_MCP_FIFOSTAT 0x7C | |
121 | #define CDNS_MCP_RX_FIFO_AVAIL GENMASK(5, 0) | |
122 | ||
123 | #define CDNS_MCP_CMD_BASE 0x80 | |
124 | #define CDNS_MCP_RESP_BASE 0x80 | |
125 | #define CDNS_MCP_CMD_LEN 0x20 | |
126 | #define CDNS_MCP_CMD_WORD_LEN 0x4 | |
127 | ||
128 | #define CDNS_MCP_CMD_SSP_TAG BIT(31) | |
129 | #define CDNS_MCP_CMD_COMMAND GENMASK(30, 28) | |
130 | #define CDNS_MCP_CMD_DEV_ADDR GENMASK(27, 24) | |
131 | #define CDNS_MCP_CMD_REG_ADDR_H GENMASK(23, 16) | |
132 | #define CDNS_MCP_CMD_REG_ADDR_L GENMASK(15, 8) | |
133 | #define CDNS_MCP_CMD_REG_DATA GENMASK(7, 0) | |
134 | ||
135 | #define CDNS_MCP_CMD_READ 2 | |
136 | #define CDNS_MCP_CMD_WRITE 3 | |
137 | ||
138 | #define CDNS_MCP_RESP_RDATA GENMASK(15, 8) | |
139 | #define CDNS_MCP_RESP_ACK BIT(0) | |
140 | #define CDNS_MCP_RESP_NACK BIT(1) | |
141 | ||
142 | #define CDNS_DP_SIZE 128 | |
143 | ||
144 | #define CDNS_DPN_B0_CONFIG(n) (0x100 + CDNS_DP_SIZE * (n)) | |
145 | #define CDNS_DPN_B0_CH_EN(n) (0x104 + CDNS_DP_SIZE * (n)) | |
146 | #define CDNS_DPN_B0_SAMPLE_CTRL(n) (0x108 + CDNS_DP_SIZE * (n)) | |
147 | #define CDNS_DPN_B0_OFFSET_CTRL(n) (0x10C + CDNS_DP_SIZE * (n)) | |
148 | #define CDNS_DPN_B0_HCTRL(n) (0x110 + CDNS_DP_SIZE * (n)) | |
149 | #define CDNS_DPN_B0_ASYNC_CTRL(n) (0x114 + CDNS_DP_SIZE * (n)) | |
150 | ||
151 | #define CDNS_DPN_B1_CONFIG(n) (0x118 + CDNS_DP_SIZE * (n)) | |
152 | #define CDNS_DPN_B1_CH_EN(n) (0x11C + CDNS_DP_SIZE * (n)) | |
153 | #define CDNS_DPN_B1_SAMPLE_CTRL(n) (0x120 + CDNS_DP_SIZE * (n)) | |
154 | #define CDNS_DPN_B1_OFFSET_CTRL(n) (0x124 + CDNS_DP_SIZE * (n)) | |
155 | #define CDNS_DPN_B1_HCTRL(n) (0x128 + CDNS_DP_SIZE * (n)) | |
156 | #define CDNS_DPN_B1_ASYNC_CTRL(n) (0x12C + CDNS_DP_SIZE * (n)) | |
157 | ||
158 | #define CDNS_DPN_CONFIG_BPM BIT(18) | |
159 | #define CDNS_DPN_CONFIG_BGC GENMASK(17, 16) | |
160 | #define CDNS_DPN_CONFIG_WL GENMASK(12, 8) | |
161 | #define CDNS_DPN_CONFIG_PORT_DAT GENMASK(3, 2) | |
162 | #define CDNS_DPN_CONFIG_PORT_FLOW GENMASK(1, 0) | |
163 | ||
164 | #define CDNS_DPN_SAMPLE_CTRL_SI GENMASK(15, 0) | |
165 | ||
166 | #define CDNS_DPN_OFFSET_CTRL_1 GENMASK(7, 0) | |
167 | #define CDNS_DPN_OFFSET_CTRL_2 GENMASK(15, 8) | |
168 | ||
169 | #define CDNS_DPN_HCTRL_HSTOP GENMASK(3, 0) | |
170 | #define CDNS_DPN_HCTRL_HSTART GENMASK(7, 4) | |
171 | #define CDNS_DPN_HCTRL_LCTRL GENMASK(10, 8) | |
172 | ||
173 | #define CDNS_PORTCTRL 0x130 | |
174 | #define CDNS_PORTCTRL_DIRN BIT(7) | |
175 | #define CDNS_PORTCTRL_BANK_INVERT BIT(8) | |
176 | ||
177 | #define CDNS_PORT_OFFSET 0x80 | |
178 | ||
179 | #define CDNS_PDI_CONFIG(n) (0x1100 + (n) * 16) | |
180 | ||
181 | #define CDNS_PDI_CONFIG_SOFT_RESET BIT(24) | |
182 | #define CDNS_PDI_CONFIG_CHANNEL GENMASK(15, 8) | |
183 | #define CDNS_PDI_CONFIG_PORT GENMASK(4, 0) | |
184 | ||
185 | /* Driver defaults */ | |
2f52a517 VK |
186 | #define CDNS_DEFAULT_SSP_INTERVAL 0x18 |
187 | #define CDNS_TX_TIMEOUT 2000 | |
188 | ||
2f52a517 VK |
189 | #define CDNS_SCP_RX_FIFOLEVEL 0x2 |
190 | ||
191 | /* | |
192 | * register accessor helpers | |
193 | */ | |
194 | static inline u32 cdns_readl(struct sdw_cdns *cdns, int offset) | |
195 | { | |
196 | return readl(cdns->registers + offset); | |
197 | } | |
198 | ||
199 | static inline void cdns_writel(struct sdw_cdns *cdns, int offset, u32 value) | |
200 | { | |
201 | writel(value, cdns->registers + offset); | |
202 | } | |
203 | ||
204 | static inline void cdns_updatel(struct sdw_cdns *cdns, | |
205 | int offset, u32 mask, u32 val) | |
206 | { | |
207 | u32 tmp; | |
208 | ||
209 | tmp = cdns_readl(cdns, offset); | |
210 | tmp = (tmp & ~mask) | val; | |
211 | cdns_writel(cdns, offset, tmp); | |
212 | } | |
213 | ||
214 | static int cdns_clear_bit(struct sdw_cdns *cdns, int offset, u32 value) | |
215 | { | |
216 | int timeout = 10; | |
217 | u32 reg_read; | |
218 | ||
219 | writel(value, cdns->registers + offset); | |
220 | ||
221 | /* Wait for bit to be self cleared */ | |
222 | do { | |
223 | reg_read = readl(cdns->registers + offset); | |
224 | if ((reg_read & value) == 0) | |
225 | return 0; | |
226 | ||
227 | timeout--; | |
228 | udelay(50); | |
229 | } while (timeout != 0); | |
230 | ||
231 | return -EAGAIN; | |
232 | } | |
233 | ||
49ea07d3 PLB |
234 | /* |
235 | * all changes to the MCP_CONFIG, MCP_CONTROL, MCP_CMDCTRL and MCP_PHYCTRL | |
236 | * need to be confirmed with a write to MCP_CONFIG_UPDATE | |
237 | */ | |
238 | static int cdns_update_config(struct sdw_cdns *cdns) | |
239 | { | |
240 | int ret; | |
241 | ||
242 | ret = cdns_clear_bit(cdns, CDNS_MCP_CONFIG_UPDATE, | |
243 | CDNS_MCP_CONFIG_UPDATE_BIT); | |
244 | if (ret < 0) | |
245 | dev_err(cdns->dev, "Config update timedout\n"); | |
246 | ||
247 | return ret; | |
248 | } | |
249 | ||
aa85066e PLB |
250 | /* |
251 | * debugfs | |
252 | */ | |
253 | #ifdef CONFIG_DEBUG_FS | |
254 | ||
255 | #define RD_BUF (2 * PAGE_SIZE) | |
256 | ||
257 | static ssize_t cdns_sprintf(struct sdw_cdns *cdns, | |
258 | char *buf, size_t pos, unsigned int reg) | |
259 | { | |
260 | return scnprintf(buf + pos, RD_BUF - pos, | |
261 | "%4x\t%8x\n", reg, cdns_readl(cdns, reg)); | |
262 | } | |
263 | ||
264 | static int cdns_reg_show(struct seq_file *s, void *data) | |
265 | { | |
266 | struct sdw_cdns *cdns = s->private; | |
267 | char *buf; | |
268 | ssize_t ret; | |
269 | int num_ports; | |
270 | int i, j; | |
271 | ||
272 | buf = kzalloc(RD_BUF, GFP_KERNEL); | |
273 | if (!buf) | |
274 | return -ENOMEM; | |
275 | ||
276 | ret = scnprintf(buf, RD_BUF, "Register Value\n"); | |
277 | ret += scnprintf(buf + ret, RD_BUF - ret, "\nMCP Registers\n"); | |
278 | /* 8 MCP registers */ | |
279 | for (i = CDNS_MCP_CONFIG; i <= CDNS_MCP_PHYCTRL; i += sizeof(u32)) | |
280 | ret += cdns_sprintf(cdns, buf, ret, i); | |
281 | ||
282 | ret += scnprintf(buf + ret, RD_BUF - ret, | |
283 | "\nStatus & Intr Registers\n"); | |
284 | /* 13 Status & Intr registers (offsets 0x70 and 0x74 not defined) */ | |
285 | for (i = CDNS_MCP_STAT; i <= CDNS_MCP_FIFOSTAT; i += sizeof(u32)) | |
286 | ret += cdns_sprintf(cdns, buf, ret, i); | |
287 | ||
288 | ret += scnprintf(buf + ret, RD_BUF - ret, | |
289 | "\nSSP & Clk ctrl Registers\n"); | |
290 | ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL0); | |
291 | ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_SSP_CTRL1); | |
292 | ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL0); | |
293 | ret += cdns_sprintf(cdns, buf, ret, CDNS_MCP_CLK_CTRL1); | |
294 | ||
295 | ret += scnprintf(buf + ret, RD_BUF - ret, | |
296 | "\nDPn B0 Registers\n"); | |
297 | ||
807c15bc | 298 | num_ports = cdns->num_ports; |
aa85066e PLB |
299 | |
300 | for (i = 0; i < num_ports; i++) { | |
301 | ret += scnprintf(buf + ret, RD_BUF - ret, | |
302 | "\nDP-%d\n", i); | |
303 | for (j = CDNS_DPN_B0_CONFIG(i); | |
304 | j < CDNS_DPN_B0_ASYNC_CTRL(i); j += sizeof(u32)) | |
305 | ret += cdns_sprintf(cdns, buf, ret, j); | |
306 | } | |
307 | ||
308 | ret += scnprintf(buf + ret, RD_BUF - ret, | |
309 | "\nDPn B1 Registers\n"); | |
310 | for (i = 0; i < num_ports; i++) { | |
311 | ret += scnprintf(buf + ret, RD_BUF - ret, | |
312 | "\nDP-%d\n", i); | |
313 | ||
314 | for (j = CDNS_DPN_B1_CONFIG(i); | |
315 | j < CDNS_DPN_B1_ASYNC_CTRL(i); j += sizeof(u32)) | |
316 | ret += cdns_sprintf(cdns, buf, ret, j); | |
317 | } | |
318 | ||
319 | ret += scnprintf(buf + ret, RD_BUF - ret, | |
320 | "\nDPn Control Registers\n"); | |
321 | for (i = 0; i < num_ports; i++) | |
322 | ret += cdns_sprintf(cdns, buf, ret, | |
323 | CDNS_PORTCTRL + i * CDNS_PORT_OFFSET); | |
324 | ||
325 | ret += scnprintf(buf + ret, RD_BUF - ret, | |
326 | "\nPDIn Config Registers\n"); | |
327 | ||
328 | /* number of PDI and ports is interchangeable */ | |
329 | for (i = 0; i < num_ports; i++) | |
330 | ret += cdns_sprintf(cdns, buf, ret, CDNS_PDI_CONFIG(i)); | |
331 | ||
332 | seq_printf(s, "%s", buf); | |
333 | kfree(buf); | |
334 | ||
335 | return 0; | |
336 | } | |
337 | DEFINE_SHOW_ATTRIBUTE(cdns_reg); | |
338 | ||
675d4c9a PLB |
339 | static int cdns_hw_reset(void *data, u64 value) |
340 | { | |
341 | struct sdw_cdns *cdns = data; | |
342 | int ret; | |
343 | ||
344 | if (value != 1) | |
345 | return -EINVAL; | |
346 | ||
347 | /* Userspace changed the hardware state behind the kernel's back */ | |
348 | add_taint(TAINT_USER, LOCKDEP_STILL_OK); | |
349 | ||
350 | ret = sdw_cdns_exit_reset(cdns); | |
351 | ||
352 | dev_dbg(cdns->dev, "link hw_reset done: %d\n", ret); | |
353 | ||
354 | return ret; | |
355 | } | |
356 | ||
357 | DEFINE_DEBUGFS_ATTRIBUTE(cdns_hw_reset_fops, NULL, cdns_hw_reset, "%llu\n"); | |
358 | ||
aa85066e PLB |
359 | /** |
360 | * sdw_cdns_debugfs_init() - Cadence debugfs init | |
361 | * @cdns: Cadence instance | |
362 | * @root: debugfs root | |
363 | */ | |
364 | void sdw_cdns_debugfs_init(struct sdw_cdns *cdns, struct dentry *root) | |
365 | { | |
366 | debugfs_create_file("cdns-registers", 0400, root, cdns, &cdns_reg_fops); | |
675d4c9a PLB |
367 | |
368 | debugfs_create_file("cdns-hw-reset", 0200, root, cdns, | |
369 | &cdns_hw_reset_fops); | |
aa85066e PLB |
370 | } |
371 | EXPORT_SYMBOL_GPL(sdw_cdns_debugfs_init); | |
372 | ||
373 | #endif /* CONFIG_DEBUG_FS */ | |
374 | ||
956baa19 SK |
375 | /* |
376 | * IO Calls | |
377 | */ | |
bbb63817 PLB |
378 | static enum sdw_command_response |
379 | cdns_fill_msg_resp(struct sdw_cdns *cdns, | |
380 | struct sdw_msg *msg, int count, int offset) | |
956baa19 SK |
381 | { |
382 | int nack = 0, no_ack = 0; | |
383 | int i; | |
384 | ||
385 | /* check message response */ | |
386 | for (i = 0; i < count; i++) { | |
387 | if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) { | |
388 | no_ack = 1; | |
eb7df4c8 | 389 | dev_dbg_ratelimited(cdns->dev, "Msg Ack not received\n"); |
956baa19 SK |
390 | if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) { |
391 | nack = 1; | |
eb7df4c8 | 392 | dev_err_ratelimited(cdns->dev, "Msg NACK received\n"); |
956baa19 SK |
393 | } |
394 | } | |
395 | } | |
396 | ||
397 | if (nack) { | |
eb7df4c8 | 398 | dev_err_ratelimited(cdns->dev, "Msg NACKed for Slave %d\n", msg->dev_num); |
956baa19 SK |
399 | return SDW_CMD_FAIL; |
400 | } else if (no_ack) { | |
eb7df4c8 | 401 | dev_dbg_ratelimited(cdns->dev, "Msg ignored for Slave %d\n", msg->dev_num); |
956baa19 SK |
402 | return SDW_CMD_IGNORED; |
403 | } | |
404 | ||
405 | /* fill response */ | |
406 | for (i = 0; i < count; i++) | |
407 | msg->buf[i + offset] = cdns->response_buf[i] >> | |
408 | SDW_REG_SHIFT(CDNS_MCP_RESP_RDATA); | |
409 | ||
410 | return SDW_CMD_OK; | |
411 | } | |
412 | ||
413 | static enum sdw_command_response | |
414 | _cdns_xfer_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int cmd, | |
bbb63817 | 415 | int offset, int count, bool defer) |
956baa19 SK |
416 | { |
417 | unsigned long time; | |
418 | u32 base, i, data; | |
419 | u16 addr; | |
420 | ||
421 | /* Program the watermark level for RX FIFO */ | |
422 | if (cdns->msg_count != count) { | |
423 | cdns_writel(cdns, CDNS_MCP_FIFOLEVEL, count); | |
424 | cdns->msg_count = count; | |
425 | } | |
426 | ||
427 | base = CDNS_MCP_CMD_BASE; | |
428 | addr = msg->addr; | |
429 | ||
430 | for (i = 0; i < count; i++) { | |
431 | data = msg->dev_num << SDW_REG_SHIFT(CDNS_MCP_CMD_DEV_ADDR); | |
432 | data |= cmd << SDW_REG_SHIFT(CDNS_MCP_CMD_COMMAND); | |
433 | data |= addr++ << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L); | |
434 | ||
435 | if (msg->flags == SDW_MSG_FLAG_WRITE) | |
436 | data |= msg->buf[i + offset]; | |
437 | ||
438 | data |= msg->ssp_sync << SDW_REG_SHIFT(CDNS_MCP_CMD_SSP_TAG); | |
439 | cdns_writel(cdns, base, data); | |
440 | base += CDNS_MCP_CMD_WORD_LEN; | |
441 | } | |
442 | ||
443 | if (defer) | |
444 | return SDW_CMD_OK; | |
445 | ||
446 | /* wait for timeout or response */ | |
447 | time = wait_for_completion_timeout(&cdns->tx_complete, | |
bbb63817 | 448 | msecs_to_jiffies(CDNS_TX_TIMEOUT)); |
956baa19 | 449 | if (!time) { |
53ee9572 PLB |
450 | dev_err(cdns->dev, "IO transfer timed out, cmd %d device %d addr %x len %d\n", |
451 | cmd, msg->dev_num, msg->addr, msg->len); | |
956baa19 SK |
452 | msg->len = 0; |
453 | return SDW_CMD_TIMEOUT; | |
454 | } | |
455 | ||
456 | return cdns_fill_msg_resp(cdns, msg, count, offset); | |
457 | } | |
458 | ||
bbb63817 PLB |
459 | static enum sdw_command_response |
460 | cdns_program_scp_addr(struct sdw_cdns *cdns, struct sdw_msg *msg) | |
956baa19 SK |
461 | { |
462 | int nack = 0, no_ack = 0; | |
463 | unsigned long time; | |
464 | u32 data[2], base; | |
465 | int i; | |
466 | ||
467 | /* Program the watermark level for RX FIFO */ | |
468 | if (cdns->msg_count != CDNS_SCP_RX_FIFOLEVEL) { | |
469 | cdns_writel(cdns, CDNS_MCP_FIFOLEVEL, CDNS_SCP_RX_FIFOLEVEL); | |
470 | cdns->msg_count = CDNS_SCP_RX_FIFOLEVEL; | |
471 | } | |
472 | ||
473 | data[0] = msg->dev_num << SDW_REG_SHIFT(CDNS_MCP_CMD_DEV_ADDR); | |
474 | data[0] |= 0x3 << SDW_REG_SHIFT(CDNS_MCP_CMD_COMMAND); | |
475 | data[1] = data[0]; | |
476 | ||
477 | data[0] |= SDW_SCP_ADDRPAGE1 << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L); | |
478 | data[1] |= SDW_SCP_ADDRPAGE2 << SDW_REG_SHIFT(CDNS_MCP_CMD_REG_ADDR_L); | |
479 | ||
480 | data[0] |= msg->addr_page1; | |
481 | data[1] |= msg->addr_page2; | |
482 | ||
483 | base = CDNS_MCP_CMD_BASE; | |
484 | cdns_writel(cdns, base, data[0]); | |
485 | base += CDNS_MCP_CMD_WORD_LEN; | |
486 | cdns_writel(cdns, base, data[1]); | |
487 | ||
488 | time = wait_for_completion_timeout(&cdns->tx_complete, | |
bbb63817 | 489 | msecs_to_jiffies(CDNS_TX_TIMEOUT)); |
956baa19 SK |
490 | if (!time) { |
491 | dev_err(cdns->dev, "SCP Msg trf timed out\n"); | |
492 | msg->len = 0; | |
493 | return SDW_CMD_TIMEOUT; | |
494 | } | |
495 | ||
496 | /* check response the writes */ | |
497 | for (i = 0; i < 2; i++) { | |
498 | if (!(cdns->response_buf[i] & CDNS_MCP_RESP_ACK)) { | |
499 | no_ack = 1; | |
17ed5bef | 500 | dev_err(cdns->dev, "Program SCP Ack not received\n"); |
956baa19 SK |
501 | if (cdns->response_buf[i] & CDNS_MCP_RESP_NACK) { |
502 | nack = 1; | |
17ed5bef | 503 | dev_err(cdns->dev, "Program SCP NACK received\n"); |
956baa19 SK |
504 | } |
505 | } | |
506 | } | |
507 | ||
508 | /* For NACK, NO ack, don't return err if we are in Broadcast mode */ | |
509 | if (nack) { | |
eb7df4c8 PLB |
510 | dev_err_ratelimited(cdns->dev, |
511 | "SCP_addrpage NACKed for Slave %d\n", msg->dev_num); | |
956baa19 SK |
512 | return SDW_CMD_FAIL; |
513 | } else if (no_ack) { | |
eb7df4c8 PLB |
514 | dev_dbg_ratelimited(cdns->dev, |
515 | "SCP_addrpage ignored for Slave %d\n", msg->dev_num); | |
956baa19 SK |
516 | return SDW_CMD_IGNORED; |
517 | } | |
518 | ||
519 | return SDW_CMD_OK; | |
520 | } | |
521 | ||
522 | static int cdns_prep_msg(struct sdw_cdns *cdns, struct sdw_msg *msg, int *cmd) | |
523 | { | |
524 | int ret; | |
525 | ||
526 | if (msg->page) { | |
527 | ret = cdns_program_scp_addr(cdns, msg); | |
528 | if (ret) { | |
529 | msg->len = 0; | |
530 | return ret; | |
531 | } | |
532 | } | |
533 | ||
534 | switch (msg->flags) { | |
535 | case SDW_MSG_FLAG_READ: | |
536 | *cmd = CDNS_MCP_CMD_READ; | |
537 | break; | |
538 | ||
539 | case SDW_MSG_FLAG_WRITE: | |
540 | *cmd = CDNS_MCP_CMD_WRITE; | |
541 | break; | |
542 | ||
543 | default: | |
544 | dev_err(cdns->dev, "Invalid msg cmd: %d\n", msg->flags); | |
545 | return -EINVAL; | |
546 | } | |
547 | ||
548 | return 0; | |
549 | } | |
550 | ||
c91605f4 | 551 | enum sdw_command_response |
956baa19 SK |
552 | cdns_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg) |
553 | { | |
554 | struct sdw_cdns *cdns = bus_to_cdns(bus); | |
555 | int cmd = 0, ret, i; | |
556 | ||
557 | ret = cdns_prep_msg(cdns, msg, &cmd); | |
558 | if (ret) | |
559 | return SDW_CMD_FAIL_OTHER; | |
560 | ||
561 | for (i = 0; i < msg->len / CDNS_MCP_CMD_LEN; i++) { | |
562 | ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN, | |
bbb63817 | 563 | CDNS_MCP_CMD_LEN, false); |
956baa19 SK |
564 | if (ret < 0) |
565 | goto exit; | |
566 | } | |
567 | ||
568 | if (!(msg->len % CDNS_MCP_CMD_LEN)) | |
569 | goto exit; | |
570 | ||
571 | ret = _cdns_xfer_msg(cdns, msg, cmd, i * CDNS_MCP_CMD_LEN, | |
bbb63817 | 572 | msg->len % CDNS_MCP_CMD_LEN, false); |
956baa19 SK |
573 | |
574 | exit: | |
575 | return ret; | |
576 | } | |
c91605f4 | 577 | EXPORT_SYMBOL(cdns_xfer_msg); |
956baa19 | 578 | |
c91605f4 | 579 | enum sdw_command_response |
956baa19 | 580 | cdns_xfer_msg_defer(struct sdw_bus *bus, |
bbb63817 | 581 | struct sdw_msg *msg, struct sdw_defer *defer) |
956baa19 SK |
582 | { |
583 | struct sdw_cdns *cdns = bus_to_cdns(bus); | |
584 | int cmd = 0, ret; | |
585 | ||
586 | /* for defer only 1 message is supported */ | |
587 | if (msg->len > 1) | |
588 | return -ENOTSUPP; | |
589 | ||
590 | ret = cdns_prep_msg(cdns, msg, &cmd); | |
591 | if (ret) | |
592 | return SDW_CMD_FAIL_OTHER; | |
593 | ||
594 | cdns->defer = defer; | |
595 | cdns->defer->length = msg->len; | |
596 | ||
597 | return _cdns_xfer_msg(cdns, msg, cmd, 0, msg->len, true); | |
598 | } | |
c91605f4 | 599 | EXPORT_SYMBOL(cdns_xfer_msg_defer); |
956baa19 | 600 | |
c91605f4 | 601 | enum sdw_command_response |
956baa19 SK |
602 | cdns_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num) |
603 | { | |
604 | struct sdw_cdns *cdns = bus_to_cdns(bus); | |
605 | struct sdw_msg msg; | |
606 | ||
607 | /* Create dummy message with valid device number */ | |
608 | memset(&msg, 0, sizeof(msg)); | |
609 | msg.dev_num = dev_num; | |
610 | ||
611 | return cdns_program_scp_addr(cdns, &msg); | |
612 | } | |
c91605f4 | 613 | EXPORT_SYMBOL(cdns_reset_page_addr); |
956baa19 | 614 | |
2f52a517 VK |
615 | /* |
616 | * IRQ handling | |
617 | */ | |
618 | ||
956baa19 SK |
619 | static void cdns_read_response(struct sdw_cdns *cdns) |
620 | { | |
621 | u32 num_resp, cmd_base; | |
622 | int i; | |
623 | ||
624 | num_resp = cdns_readl(cdns, CDNS_MCP_FIFOSTAT); | |
625 | num_resp &= CDNS_MCP_RX_FIFO_AVAIL; | |
626 | ||
627 | cmd_base = CDNS_MCP_CMD_BASE; | |
628 | ||
629 | for (i = 0; i < num_resp; i++) { | |
630 | cdns->response_buf[i] = cdns_readl(cdns, cmd_base); | |
631 | cmd_base += CDNS_MCP_CMD_WORD_LEN; | |
632 | } | |
633 | } | |
634 | ||
2f52a517 | 635 | static int cdns_update_slave_status(struct sdw_cdns *cdns, |
bbb63817 | 636 | u32 slave0, u32 slave1) |
2f52a517 VK |
637 | { |
638 | enum sdw_slave_status status[SDW_MAX_DEVICES + 1]; | |
639 | bool is_slave = false; | |
a78b32d9 PLB |
640 | u64 slave; |
641 | u32 mask; | |
2f52a517 VK |
642 | int i, set_status; |
643 | ||
644 | /* combine the two status */ | |
645 | slave = ((u64)slave1 << 32) | slave0; | |
646 | memset(status, 0, sizeof(status)); | |
647 | ||
648 | for (i = 0; i <= SDW_MAX_DEVICES; i++) { | |
649 | mask = (slave >> (i * CDNS_MCP_SLAVE_STATUS_NUM)) & | |
650 | CDNS_MCP_SLAVE_STATUS_BITS; | |
651 | if (!mask) | |
652 | continue; | |
653 | ||
654 | is_slave = true; | |
655 | set_status = 0; | |
656 | ||
657 | if (mask & CDNS_MCP_SLAVE_INTSTAT_RESERVED) { | |
658 | status[i] = SDW_SLAVE_RESERVED; | |
659 | set_status++; | |
660 | } | |
661 | ||
662 | if (mask & CDNS_MCP_SLAVE_INTSTAT_ATTACHED) { | |
663 | status[i] = SDW_SLAVE_ATTACHED; | |
664 | set_status++; | |
665 | } | |
666 | ||
667 | if (mask & CDNS_MCP_SLAVE_INTSTAT_ALERT) { | |
668 | status[i] = SDW_SLAVE_ALERT; | |
669 | set_status++; | |
670 | } | |
671 | ||
672 | if (mask & CDNS_MCP_SLAVE_INTSTAT_NPRESENT) { | |
673 | status[i] = SDW_SLAVE_UNATTACHED; | |
674 | set_status++; | |
675 | } | |
676 | ||
677 | /* first check if Slave reported multiple status */ | |
678 | if (set_status > 1) { | |
7181b1d4 PLB |
679 | u32 val; |
680 | ||
eb7df4c8 | 681 | dev_warn_ratelimited(cdns->dev, |
7181b1d4 PLB |
682 | "Slave %d reported multiple Status: %d\n", |
683 | i, mask); | |
684 | ||
685 | /* check latest status extracted from PING commands */ | |
686 | val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT); | |
687 | val >>= (i * 2); | |
688 | ||
689 | switch (val & 0x3) { | |
690 | case 0: | |
691 | status[i] = SDW_SLAVE_UNATTACHED; | |
692 | break; | |
693 | case 1: | |
694 | status[i] = SDW_SLAVE_ATTACHED; | |
695 | break; | |
696 | case 2: | |
697 | status[i] = SDW_SLAVE_ALERT; | |
698 | break; | |
699 | case 3: | |
700 | default: | |
701 | status[i] = SDW_SLAVE_RESERVED; | |
702 | break; | |
703 | } | |
704 | ||
705 | dev_warn_ratelimited(cdns->dev, | |
706 | "Slave %d status updated to %d\n", | |
707 | i, status[i]); | |
708 | ||
2f52a517 VK |
709 | } |
710 | } | |
711 | ||
712 | if (is_slave) | |
713 | return sdw_handle_slave_status(&cdns->bus, status); | |
714 | ||
715 | return 0; | |
716 | } | |
717 | ||
718 | /** | |
719 | * sdw_cdns_irq() - Cadence interrupt handler | |
720 | * @irq: irq number | |
721 | * @dev_id: irq context | |
722 | */ | |
723 | irqreturn_t sdw_cdns_irq(int irq, void *dev_id) | |
724 | { | |
725 | struct sdw_cdns *cdns = dev_id; | |
726 | u32 int_status; | |
727 | int ret = IRQ_HANDLED; | |
728 | ||
729 | /* Check if the link is up */ | |
730 | if (!cdns->link_up) | |
731 | return IRQ_NONE; | |
732 | ||
733 | int_status = cdns_readl(cdns, CDNS_MCP_INTSTAT); | |
734 | ||
a2cff9ee PLB |
735 | /* check for reserved values read as zero */ |
736 | if (int_status & CDNS_MCP_INT_RESERVED) | |
737 | return IRQ_NONE; | |
738 | ||
2f52a517 VK |
739 | if (!(int_status & CDNS_MCP_INT_IRQ)) |
740 | return IRQ_NONE; | |
741 | ||
956baa19 SK |
742 | if (int_status & CDNS_MCP_INT_RX_WL) { |
743 | cdns_read_response(cdns); | |
744 | ||
745 | if (cdns->defer) { | |
746 | cdns_fill_msg_resp(cdns, cdns->defer->msg, | |
bbb63817 | 747 | cdns->defer->length, 0); |
956baa19 SK |
748 | complete(&cdns->defer->complete); |
749 | cdns->defer = NULL; | |
f6e20967 | 750 | } else { |
956baa19 | 751 | complete(&cdns->tx_complete); |
f6e20967 | 752 | } |
956baa19 SK |
753 | } |
754 | ||
9b5884a0 PLB |
755 | if (int_status & CDNS_MCP_INT_PARITY) { |
756 | /* Parity error detected by Master */ | |
757 | dev_err_ratelimited(cdns->dev, "Parity error\n"); | |
758 | } | |
759 | ||
2f52a517 | 760 | if (int_status & CDNS_MCP_INT_CTRL_CLASH) { |
2f52a517 VK |
761 | /* Slave is driving bit slot during control word */ |
762 | dev_err_ratelimited(cdns->dev, "Bus clash for control word\n"); | |
2f52a517 VK |
763 | } |
764 | ||
765 | if (int_status & CDNS_MCP_INT_DATA_CLASH) { | |
766 | /* | |
767 | * Multiple slaves trying to drive bit slot, or issue with | |
768 | * ownership of data bits or Slave gone bonkers | |
769 | */ | |
770 | dev_err_ratelimited(cdns->dev, "Bus clash for data word\n"); | |
2f52a517 VK |
771 | } |
772 | ||
773 | if (int_status & CDNS_MCP_INT_SLAVE_MASK) { | |
774 | /* Mask the Slave interrupt and wake thread */ | |
775 | cdns_updatel(cdns, CDNS_MCP_INTMASK, | |
bbb63817 | 776 | CDNS_MCP_INT_SLAVE_MASK, 0); |
2f52a517 VK |
777 | |
778 | int_status &= ~CDNS_MCP_INT_SLAVE_MASK; | |
779 | ret = IRQ_WAKE_THREAD; | |
780 | } | |
781 | ||
782 | cdns_writel(cdns, CDNS_MCP_INTSTAT, int_status); | |
783 | return ret; | |
784 | } | |
785 | EXPORT_SYMBOL(sdw_cdns_irq); | |
786 | ||
787 | /** | |
788 | * sdw_cdns_thread() - Cadence irq thread handler | |
789 | * @irq: irq number | |
790 | * @dev_id: irq context | |
791 | */ | |
792 | irqreturn_t sdw_cdns_thread(int irq, void *dev_id) | |
793 | { | |
794 | struct sdw_cdns *cdns = dev_id; | |
795 | u32 slave0, slave1; | |
796 | ||
eb7df4c8 | 797 | dev_dbg_ratelimited(cdns->dev, "Slave status change\n"); |
2f52a517 VK |
798 | |
799 | slave0 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0); | |
800 | slave1 = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1); | |
801 | ||
802 | cdns_update_slave_status(cdns, slave0, slave1); | |
803 | cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave0); | |
804 | cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave1); | |
805 | ||
806 | /* clear and unmask Slave interrupt now */ | |
807 | cdns_writel(cdns, CDNS_MCP_INTSTAT, CDNS_MCP_INT_SLAVE_MASK); | |
808 | cdns_updatel(cdns, CDNS_MCP_INTMASK, | |
bbb63817 | 809 | CDNS_MCP_INT_SLAVE_MASK, CDNS_MCP_INT_SLAVE_MASK); |
2f52a517 VK |
810 | |
811 | return IRQ_HANDLED; | |
812 | } | |
813 | EXPORT_SYMBOL(sdw_cdns_thread); | |
814 | ||
815 | /* | |
816 | * init routines | |
817 | */ | |
49ea07d3 PLB |
818 | |
819 | /** | |
820 | * sdw_cdns_exit_reset() - Program reset parameters and start bus operations | |
821 | * @cdns: Cadence instance | |
822 | */ | |
823 | int sdw_cdns_exit_reset(struct sdw_cdns *cdns) | |
824 | { | |
825 | /* program maximum length reset to be safe */ | |
826 | cdns_updatel(cdns, CDNS_MCP_CONTROL, | |
827 | CDNS_MCP_CONTROL_RST_DELAY, | |
828 | CDNS_MCP_CONTROL_RST_DELAY); | |
829 | ||
830 | /* use hardware generated reset */ | |
831 | cdns_updatel(cdns, CDNS_MCP_CONTROL, | |
832 | CDNS_MCP_CONTROL_HW_RST, | |
833 | CDNS_MCP_CONTROL_HW_RST); | |
834 | ||
835 | /* enable bus operations with clock and data */ | |
836 | cdns_updatel(cdns, CDNS_MCP_CONFIG, | |
837 | CDNS_MCP_CONFIG_OP, | |
838 | CDNS_MCP_CONFIG_OP_NORMAL); | |
839 | ||
840 | /* commit changes */ | |
841 | return cdns_update_config(cdns); | |
842 | } | |
843 | EXPORT_SYMBOL(sdw_cdns_exit_reset); | |
844 | ||
845 | /** | |
ae478d6e | 846 | * sdw_cdns_enable_interrupt() - Enable SDW interrupts |
49ea07d3 | 847 | * @cdns: Cadence instance |
550f9052 | 848 | * @state: True if we are trying to enable interrupt. |
49ea07d3 | 849 | */ |
9e3d47fb | 850 | int sdw_cdns_enable_interrupt(struct sdw_cdns *cdns, bool state) |
956baa19 | 851 | { |
9e3d47fb PLB |
852 | u32 slave_intmask0 = 0; |
853 | u32 slave_intmask1 = 0; | |
854 | u32 mask = 0; | |
855 | ||
856 | if (!state) | |
857 | goto update_masks; | |
956baa19 | 858 | |
9e3d47fb PLB |
859 | slave_intmask0 = CDNS_MCP_SLAVE_INTMASK0_MASK; |
860 | slave_intmask1 = CDNS_MCP_SLAVE_INTMASK1_MASK; | |
956baa19 | 861 | |
9b5884a0 PLB |
862 | /* enable detection of all slave state changes */ |
863 | mask = CDNS_MCP_INT_SLAVE_MASK; | |
864 | ||
865 | /* enable detection of bus issues */ | |
866 | mask |= CDNS_MCP_INT_CTRL_CLASH | CDNS_MCP_INT_DATA_CLASH | | |
867 | CDNS_MCP_INT_PARITY; | |
868 | ||
869 | /* no detection of port interrupts for now */ | |
870 | ||
871 | /* enable detection of RX fifo level */ | |
872 | mask |= CDNS_MCP_INT_RX_WL; | |
873 | ||
874 | /* | |
875 | * CDNS_MCP_INT_IRQ needs to be set otherwise all previous | |
876 | * settings are irrelevant | |
877 | */ | |
878 | mask |= CDNS_MCP_INT_IRQ; | |
956baa19 | 879 | |
04592dce PLB |
880 | if (interrupt_mask) /* parameter override */ |
881 | mask = interrupt_mask; | |
956baa19 | 882 | |
9e3d47fb | 883 | update_masks: |
5ebb0945 RW |
884 | /* clear slave interrupt status before enabling interrupt */ |
885 | if (state) { | |
886 | u32 slave_state; | |
887 | ||
888 | slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT0); | |
889 | cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT0, slave_state); | |
890 | slave_state = cdns_readl(cdns, CDNS_MCP_SLAVE_INTSTAT1); | |
891 | cdns_writel(cdns, CDNS_MCP_SLAVE_INTSTAT1, slave_state); | |
892 | } | |
893 | ||
9e3d47fb PLB |
894 | cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK0, slave_intmask0); |
895 | cdns_writel(cdns, CDNS_MCP_SLAVE_INTMASK1, slave_intmask1); | |
956baa19 SK |
896 | cdns_writel(cdns, CDNS_MCP_INTMASK, mask); |
897 | ||
ae478d6e | 898 | return 0; |
956baa19 SK |
899 | } |
900 | EXPORT_SYMBOL(sdw_cdns_enable_interrupt); | |
2f52a517 | 901 | |
07abeff1 | 902 | static int cdns_allocate_pdi(struct sdw_cdns *cdns, |
bbb63817 PLB |
903 | struct sdw_cdns_pdi **stream, |
904 | u32 num, u32 pdi_offset) | |
07abeff1 VK |
905 | { |
906 | struct sdw_cdns_pdi *pdi; | |
907 | int i; | |
908 | ||
909 | if (!num) | |
910 | return 0; | |
911 | ||
912 | pdi = devm_kcalloc(cdns->dev, num, sizeof(*pdi), GFP_KERNEL); | |
913 | if (!pdi) | |
914 | return -ENOMEM; | |
915 | ||
916 | for (i = 0; i < num; i++) { | |
917 | pdi[i].num = i + pdi_offset; | |
07abeff1 VK |
918 | } |
919 | ||
920 | *stream = pdi; | |
921 | return 0; | |
922 | } | |
923 | ||
924 | /** | |
925 | * sdw_cdns_pdi_init() - PDI initialization routine | |
926 | * | |
927 | * @cdns: Cadence instance | |
928 | * @config: Stream configurations | |
929 | */ | |
930 | int sdw_cdns_pdi_init(struct sdw_cdns *cdns, | |
bbb63817 | 931 | struct sdw_cdns_stream_config config) |
07abeff1 VK |
932 | { |
933 | struct sdw_cdns_streams *stream; | |
57a34790 PLB |
934 | int offset; |
935 | int ret; | |
07abeff1 VK |
936 | |
937 | cdns->pcm.num_bd = config.pcm_bd; | |
938 | cdns->pcm.num_in = config.pcm_in; | |
939 | cdns->pcm.num_out = config.pcm_out; | |
940 | cdns->pdm.num_bd = config.pdm_bd; | |
941 | cdns->pdm.num_in = config.pdm_in; | |
942 | cdns->pdm.num_out = config.pdm_out; | |
943 | ||
944 | /* Allocate PDIs for PCMs */ | |
945 | stream = &cdns->pcm; | |
946 | ||
807c15bc PLB |
947 | /* we allocate PDI0 and PDI1 which are used for Bulk */ |
948 | offset = 0; | |
07abeff1 VK |
949 | |
950 | ret = cdns_allocate_pdi(cdns, &stream->bd, | |
951 | stream->num_bd, offset); | |
952 | if (ret) | |
953 | return ret; | |
954 | ||
955 | offset += stream->num_bd; | |
956 | ||
957 | ret = cdns_allocate_pdi(cdns, &stream->in, | |
958 | stream->num_in, offset); | |
959 | if (ret) | |
960 | return ret; | |
961 | ||
962 | offset += stream->num_in; | |
963 | ||
964 | ret = cdns_allocate_pdi(cdns, &stream->out, | |
965 | stream->num_out, offset); | |
966 | if (ret) | |
967 | return ret; | |
968 | ||
969 | /* Update total number of PCM PDIs */ | |
970 | stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; | |
971 | cdns->num_ports = stream->num_pdi; | |
972 | ||
973 | /* Allocate PDIs for PDMs */ | |
974 | stream = &cdns->pdm; | |
07abeff1 VK |
975 | ret = cdns_allocate_pdi(cdns, &stream->bd, |
976 | stream->num_bd, offset); | |
977 | if (ret) | |
978 | return ret; | |
979 | ||
980 | offset += stream->num_bd; | |
981 | ||
982 | ret = cdns_allocate_pdi(cdns, &stream->in, | |
983 | stream->num_in, offset); | |
984 | if (ret) | |
985 | return ret; | |
986 | ||
987 | offset += stream->num_in; | |
988 | ||
989 | ret = cdns_allocate_pdi(cdns, &stream->out, | |
990 | stream->num_out, offset); | |
807c15bc | 991 | |
07abeff1 VK |
992 | if (ret) |
993 | return ret; | |
994 | ||
995 | /* Update total number of PDM PDIs */ | |
996 | stream->num_pdi = stream->num_bd + stream->num_in + stream->num_out; | |
997 | cdns->num_ports += stream->num_pdi; | |
998 | ||
07abeff1 VK |
999 | return 0; |
1000 | } | |
1001 | EXPORT_SYMBOL(sdw_cdns_pdi_init); | |
1002 | ||
05be59ac PLB |
1003 | static u32 cdns_set_initial_frame_shape(int n_rows, int n_cols) |
1004 | { | |
1005 | u32 val; | |
1006 | int c; | |
1007 | int r; | |
1008 | ||
1009 | r = sdw_find_row_index(n_rows); | |
1010 | c = sdw_find_col_index(n_cols) & CDNS_MCP_FRAME_SHAPE_COL_MASK; | |
1011 | ||
1012 | val = (r << CDNS_MCP_FRAME_SHAPE_ROW_OFFSET) | c; | |
1013 | ||
1014 | return val; | |
1015 | } | |
1016 | ||
2f52a517 VK |
1017 | /** |
1018 | * sdw_cdns_init() - Cadence initialization | |
1019 | * @cdns: Cadence instance | |
1020 | */ | |
3ccb8551 | 1021 | int sdw_cdns_init(struct sdw_cdns *cdns, bool clock_stop_exit) |
2f52a517 | 1022 | { |
3859872f PLB |
1023 | struct sdw_bus *bus = &cdns->bus; |
1024 | struct sdw_master_prop *prop = &bus->prop; | |
2f52a517 | 1025 | u32 val; |
3859872f | 1026 | int divider; |
2f52a517 VK |
1027 | int ret; |
1028 | ||
3ccb8551 PLB |
1029 | if (clock_stop_exit) { |
1030 | ret = cdns_clear_bit(cdns, CDNS_MCP_CONTROL, | |
1031 | CDNS_MCP_CONTROL_CLK_STOP_CLR); | |
1032 | if (ret < 0) { | |
1033 | dev_err(cdns->dev, "Couldn't exit from clock stop\n"); | |
1034 | return ret; | |
1035 | } | |
2f52a517 VK |
1036 | } |
1037 | ||
1038 | /* Set clock divider */ | |
3859872f | 1039 | divider = (prop->mclk_freq / prop->max_clk_freq) - 1; |
2f52a517 | 1040 | |
a50954e2 RW |
1041 | cdns_updatel(cdns, CDNS_MCP_CLK_CTRL0, |
1042 | CDNS_MCP_CLK_MCLKD_MASK, divider); | |
1043 | cdns_updatel(cdns, CDNS_MCP_CLK_CTRL1, | |
1044 | CDNS_MCP_CLK_MCLKD_MASK, divider); | |
2f52a517 | 1045 | |
05be59ac PLB |
1046 | /* |
1047 | * Frame shape changes after initialization have to be done | |
1048 | * with the bank switch mechanism | |
1049 | */ | |
1050 | val = cdns_set_initial_frame_shape(prop->default_row, | |
1051 | prop->default_col); | |
1052 | cdns_writel(cdns, CDNS_MCP_FRAME_SHAPE_INIT, val); | |
2f52a517 VK |
1053 | |
1054 | /* Set SSP interval to default value */ | |
1055 | cdns_writel(cdns, CDNS_MCP_SSP_CTRL0, CDNS_DEFAULT_SSP_INTERVAL); | |
1056 | cdns_writel(cdns, CDNS_MCP_SSP_CTRL1, CDNS_DEFAULT_SSP_INTERVAL); | |
1057 | ||
49ea07d3 PLB |
1058 | /* flush command FIFOs */ |
1059 | cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_RST, | |
1060 | CDNS_MCP_CONTROL_CMD_RST); | |
1061 | ||
2f52a517 VK |
1062 | /* Set cmd accept mode */ |
1063 | cdns_updatel(cdns, CDNS_MCP_CONTROL, CDNS_MCP_CONTROL_CMD_ACCEPT, | |
bbb63817 | 1064 | CDNS_MCP_CONTROL_CMD_ACCEPT); |
2f52a517 VK |
1065 | |
1066 | /* Configure mcp config */ | |
1067 | val = cdns_readl(cdns, CDNS_MCP_CONFIG); | |
1068 | ||
1069 | /* Set Max cmd retry to 15 */ | |
1070 | val |= CDNS_MCP_CONFIG_MCMD_RETRY; | |
1071 | ||
1072 | /* Set frame delay between PREQ and ping frame to 15 frames */ | |
1073 | val |= 0xF << SDW_REG_SHIFT(CDNS_MCP_CONFIG_MPREQ_DELAY); | |
1074 | ||
1075 | /* Disable auto bus release */ | |
1076 | val &= ~CDNS_MCP_CONFIG_BUS_REL; | |
1077 | ||
1078 | /* Disable sniffer mode */ | |
1079 | val &= ~CDNS_MCP_CONFIG_SNIFFER; | |
1080 | ||
1081 | /* Set cmd mode for Tx and Rx cmds */ | |
1082 | val &= ~CDNS_MCP_CONFIG_CMD; | |
1083 | ||
2f52a517 VK |
1084 | cdns_writel(cdns, CDNS_MCP_CONFIG, val); |
1085 | ||
49ea07d3 PLB |
1086 | /* commit changes */ |
1087 | return cdns_update_config(cdns); | |
2f52a517 VK |
1088 | } |
1089 | EXPORT_SYMBOL(sdw_cdns_init); | |
1090 | ||
07abeff1 VK |
1091 | int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params) |
1092 | { | |
3859872f | 1093 | struct sdw_master_prop *prop = &bus->prop; |
07abeff1 | 1094 | struct sdw_cdns *cdns = bus_to_cdns(bus); |
a50954e2 | 1095 | int mcp_clkctrl_off; |
07abeff1 VK |
1096 | int divider; |
1097 | ||
1098 | if (!params->curr_dr_freq) { | |
17ed5bef | 1099 | dev_err(cdns->dev, "NULL curr_dr_freq\n"); |
07abeff1 VK |
1100 | return -EINVAL; |
1101 | } | |
1102 | ||
3859872f PLB |
1103 | divider = prop->mclk_freq * SDW_DOUBLE_RATE_FACTOR / |
1104 | params->curr_dr_freq; | |
1105 | divider--; /* divider is 1/(N+1) */ | |
07abeff1 VK |
1106 | |
1107 | if (params->next_bank) | |
1108 | mcp_clkctrl_off = CDNS_MCP_CLK_CTRL1; | |
1109 | else | |
1110 | mcp_clkctrl_off = CDNS_MCP_CLK_CTRL0; | |
1111 | ||
a50954e2 | 1112 | cdns_updatel(cdns, mcp_clkctrl_off, CDNS_MCP_CLK_MCLKD_MASK, divider); |
07abeff1 VK |
1113 | |
1114 | return 0; | |
1115 | } | |
1116 | EXPORT_SYMBOL(cdns_bus_conf); | |
1117 | ||
1118 | static int cdns_port_params(struct sdw_bus *bus, | |
bbb63817 | 1119 | struct sdw_port_params *p_params, unsigned int bank) |
07abeff1 VK |
1120 | { |
1121 | struct sdw_cdns *cdns = bus_to_cdns(bus); | |
1122 | int dpn_config = 0, dpn_config_off; | |
1123 | ||
1124 | if (bank) | |
1125 | dpn_config_off = CDNS_DPN_B1_CONFIG(p_params->num); | |
1126 | else | |
1127 | dpn_config_off = CDNS_DPN_B0_CONFIG(p_params->num); | |
1128 | ||
1129 | dpn_config = cdns_readl(cdns, dpn_config_off); | |
1130 | ||
1131 | dpn_config |= ((p_params->bps - 1) << | |
1132 | SDW_REG_SHIFT(CDNS_DPN_CONFIG_WL)); | |
1133 | dpn_config |= (p_params->flow_mode << | |
1134 | SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_FLOW)); | |
1135 | dpn_config |= (p_params->data_mode << | |
1136 | SDW_REG_SHIFT(CDNS_DPN_CONFIG_PORT_DAT)); | |
1137 | ||
1138 | cdns_writel(cdns, dpn_config_off, dpn_config); | |
1139 | ||
1140 | return 0; | |
1141 | } | |
1142 | ||
1143 | static int cdns_transport_params(struct sdw_bus *bus, | |
bbb63817 PLB |
1144 | struct sdw_transport_params *t_params, |
1145 | enum sdw_reg_bank bank) | |
07abeff1 VK |
1146 | { |
1147 | struct sdw_cdns *cdns = bus_to_cdns(bus); | |
1148 | int dpn_offsetctrl = 0, dpn_offsetctrl_off; | |
1149 | int dpn_config = 0, dpn_config_off; | |
1150 | int dpn_hctrl = 0, dpn_hctrl_off; | |
1151 | int num = t_params->port_num; | |
1152 | int dpn_samplectrl_off; | |
1153 | ||
1154 | /* | |
1155 | * Note: Only full data port is supported on the Master side for | |
1156 | * both PCM and PDM ports. | |
1157 | */ | |
1158 | ||
1159 | if (bank) { | |
1160 | dpn_config_off = CDNS_DPN_B1_CONFIG(num); | |
1161 | dpn_samplectrl_off = CDNS_DPN_B1_SAMPLE_CTRL(num); | |
1162 | dpn_hctrl_off = CDNS_DPN_B1_HCTRL(num); | |
1163 | dpn_offsetctrl_off = CDNS_DPN_B1_OFFSET_CTRL(num); | |
1164 | } else { | |
1165 | dpn_config_off = CDNS_DPN_B0_CONFIG(num); | |
1166 | dpn_samplectrl_off = CDNS_DPN_B0_SAMPLE_CTRL(num); | |
1167 | dpn_hctrl_off = CDNS_DPN_B0_HCTRL(num); | |
1168 | dpn_offsetctrl_off = CDNS_DPN_B0_OFFSET_CTRL(num); | |
1169 | } | |
1170 | ||
1171 | dpn_config = cdns_readl(cdns, dpn_config_off); | |
1172 | ||
1173 | dpn_config |= (t_params->blk_grp_ctrl << | |
1174 | SDW_REG_SHIFT(CDNS_DPN_CONFIG_BGC)); | |
1175 | dpn_config |= (t_params->blk_pkg_mode << | |
1176 | SDW_REG_SHIFT(CDNS_DPN_CONFIG_BPM)); | |
1177 | cdns_writel(cdns, dpn_config_off, dpn_config); | |
1178 | ||
1179 | dpn_offsetctrl |= (t_params->offset1 << | |
1180 | SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_1)); | |
1181 | dpn_offsetctrl |= (t_params->offset2 << | |
1182 | SDW_REG_SHIFT(CDNS_DPN_OFFSET_CTRL_2)); | |
1183 | cdns_writel(cdns, dpn_offsetctrl_off, dpn_offsetctrl); | |
1184 | ||
1185 | dpn_hctrl |= (t_params->hstart << | |
1186 | SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTART)); | |
1187 | dpn_hctrl |= (t_params->hstop << SDW_REG_SHIFT(CDNS_DPN_HCTRL_HSTOP)); | |
1188 | dpn_hctrl |= (t_params->lane_ctrl << | |
1189 | SDW_REG_SHIFT(CDNS_DPN_HCTRL_LCTRL)); | |
1190 | ||
1191 | cdns_writel(cdns, dpn_hctrl_off, dpn_hctrl); | |
1192 | cdns_writel(cdns, dpn_samplectrl_off, (t_params->sample_interval - 1)); | |
1193 | ||
1194 | return 0; | |
1195 | } | |
1196 | ||
1197 | static int cdns_port_enable(struct sdw_bus *bus, | |
bbb63817 | 1198 | struct sdw_enable_ch *enable_ch, unsigned int bank) |
07abeff1 VK |
1199 | { |
1200 | struct sdw_cdns *cdns = bus_to_cdns(bus); | |
1201 | int dpn_chnen_off, ch_mask; | |
1202 | ||
1203 | if (bank) | |
1204 | dpn_chnen_off = CDNS_DPN_B1_CH_EN(enable_ch->port_num); | |
1205 | else | |
1206 | dpn_chnen_off = CDNS_DPN_B0_CH_EN(enable_ch->port_num); | |
1207 | ||
1208 | ch_mask = enable_ch->ch_mask * enable_ch->enable; | |
1209 | cdns_writel(cdns, dpn_chnen_off, ch_mask); | |
1210 | ||
1211 | return 0; | |
1212 | } | |
1213 | ||
1214 | static const struct sdw_master_port_ops cdns_port_ops = { | |
1215 | .dpn_set_port_params = cdns_port_params, | |
1216 | .dpn_set_port_transport_params = cdns_transport_params, | |
1217 | .dpn_port_enable_ch = cdns_port_enable, | |
1218 | }; | |
1219 | ||
956baa19 SK |
1220 | /** |
1221 | * sdw_cdns_probe() - Cadence probe routine | |
1222 | * @cdns: Cadence instance | |
1223 | */ | |
1224 | int sdw_cdns_probe(struct sdw_cdns *cdns) | |
1225 | { | |
1226 | init_completion(&cdns->tx_complete); | |
07abeff1 | 1227 | cdns->bus.port_ops = &cdns_port_ops; |
956baa19 SK |
1228 | |
1229 | return 0; | |
1230 | } | |
1231 | EXPORT_SYMBOL(sdw_cdns_probe); | |
1232 | ||
5d6b3c8b | 1233 | int cdns_set_sdw_stream(struct snd_soc_dai *dai, |
bbb63817 | 1234 | void *stream, bool pcm, int direction) |
5d6b3c8b VK |
1235 | { |
1236 | struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); | |
1237 | struct sdw_cdns_dma_data *dma; | |
1238 | ||
1239 | dma = kzalloc(sizeof(*dma), GFP_KERNEL); | |
1240 | if (!dma) | |
1241 | return -ENOMEM; | |
1242 | ||
1243 | if (pcm) | |
1244 | dma->stream_type = SDW_STREAM_PCM; | |
1245 | else | |
1246 | dma->stream_type = SDW_STREAM_PDM; | |
1247 | ||
1248 | dma->bus = &cdns->bus; | |
1249 | dma->link_id = cdns->instance; | |
1250 | ||
1251 | dma->stream = stream; | |
1252 | ||
1253 | if (direction == SNDRV_PCM_STREAM_PLAYBACK) | |
1254 | dai->playback_dma_data = dma; | |
1255 | else | |
1256 | dai->capture_dma_data = dma; | |
1257 | ||
1258 | return 0; | |
1259 | } | |
1260 | EXPORT_SYMBOL(cdns_set_sdw_stream); | |
1261 | ||
1262 | /** | |
1263 | * cdns_find_pdi() - Find a free PDI | |
1264 | * | |
1265 | * @cdns: Cadence instance | |
39737a31 | 1266 | * @offset: Starting offset |
5d6b3c8b VK |
1267 | * @num: Number of PDIs |
1268 | * @pdi: PDI instances | |
39737a31 | 1269 | * @dai_id: DAI id |
5d6b3c8b | 1270 | * |
1b53385e BL |
1271 | * Find a PDI for a given PDI array. The PDI num and dai_id are |
1272 | * expected to match, return NULL otherwise. | |
5d6b3c8b VK |
1273 | */ |
1274 | static struct sdw_cdns_pdi *cdns_find_pdi(struct sdw_cdns *cdns, | |
807c15bc | 1275 | unsigned int offset, |
bbb63817 | 1276 | unsigned int num, |
1b53385e BL |
1277 | struct sdw_cdns_pdi *pdi, |
1278 | int dai_id) | |
5d6b3c8b VK |
1279 | { |
1280 | int i; | |
1281 | ||
1b53385e BL |
1282 | for (i = offset; i < offset + num; i++) |
1283 | if (pdi[i].num == dai_id) | |
1284 | return &pdi[i]; | |
5d6b3c8b VK |
1285 | |
1286 | return NULL; | |
1287 | } | |
1288 | ||
1289 | /** | |
1290 | * sdw_cdns_config_stream: Configure a stream | |
1291 | * | |
1292 | * @cdns: Cadence instance | |
5d6b3c8b VK |
1293 | * @ch: Channel count |
1294 | * @dir: Data direction | |
1295 | * @pdi: PDI to be used | |
1296 | */ | |
1297 | void sdw_cdns_config_stream(struct sdw_cdns *cdns, | |
bbb63817 | 1298 | u32 ch, u32 dir, struct sdw_cdns_pdi *pdi) |
5d6b3c8b VK |
1299 | { |
1300 | u32 offset, val = 0; | |
1301 | ||
1302 | if (dir == SDW_DATA_DIR_RX) | |
1303 | val = CDNS_PORTCTRL_DIRN; | |
1304 | ||
57a34790 | 1305 | offset = CDNS_PORTCTRL + pdi->num * CDNS_PORT_OFFSET; |
5d6b3c8b VK |
1306 | cdns_updatel(cdns, offset, CDNS_PORTCTRL_DIRN, val); |
1307 | ||
57a34790 | 1308 | val = pdi->num; |
5d6b3c8b VK |
1309 | val |= ((1 << ch) - 1) << SDW_REG_SHIFT(CDNS_PDI_CONFIG_CHANNEL); |
1310 | cdns_writel(cdns, CDNS_PDI_CONFIG(pdi->num), val); | |
1311 | } | |
1312 | EXPORT_SYMBOL(sdw_cdns_config_stream); | |
1313 | ||
1314 | /** | |
57a34790 | 1315 | * sdw_cdns_alloc_pdi() - Allocate a PDI |
5d6b3c8b VK |
1316 | * |
1317 | * @cdns: Cadence instance | |
1318 | * @stream: Stream to be allocated | |
5d6b3c8b VK |
1319 | * @ch: Channel count |
1320 | * @dir: Data direction | |
39737a31 | 1321 | * @dai_id: DAI id |
5d6b3c8b | 1322 | */ |
57a34790 PLB |
1323 | struct sdw_cdns_pdi *sdw_cdns_alloc_pdi(struct sdw_cdns *cdns, |
1324 | struct sdw_cdns_streams *stream, | |
1b53385e | 1325 | u32 ch, u32 dir, int dai_id) |
5d6b3c8b VK |
1326 | { |
1327 | struct sdw_cdns_pdi *pdi = NULL; | |
1328 | ||
1329 | if (dir == SDW_DATA_DIR_RX) | |
1b53385e BL |
1330 | pdi = cdns_find_pdi(cdns, 0, stream->num_in, stream->in, |
1331 | dai_id); | |
5d6b3c8b | 1332 | else |
1b53385e BL |
1333 | pdi = cdns_find_pdi(cdns, 0, stream->num_out, stream->out, |
1334 | dai_id); | |
5d6b3c8b VK |
1335 | |
1336 | /* check if we found a PDI, else find in bi-directional */ | |
1337 | if (!pdi) | |
1b53385e BL |
1338 | pdi = cdns_find_pdi(cdns, 2, stream->num_bd, stream->bd, |
1339 | dai_id); | |
5d6b3c8b | 1340 | |
57a34790 PLB |
1341 | if (pdi) { |
1342 | pdi->l_ch_num = 0; | |
1343 | pdi->h_ch_num = ch - 1; | |
1344 | pdi->dir = dir; | |
1345 | pdi->ch_count = ch; | |
1346 | } | |
5d6b3c8b | 1347 | |
57a34790 | 1348 | return pdi; |
5d6b3c8b | 1349 | } |
57a34790 | 1350 | EXPORT_SYMBOL(sdw_cdns_alloc_pdi); |
5d6b3c8b | 1351 | |
2f52a517 VK |
1352 | MODULE_LICENSE("Dual BSD/GPL"); |
1353 | MODULE_DESCRIPTION("Cadence Soundwire Library"); |