Commit | Line | Data |
---|---|---|
71bb8a1b VK |
1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
2 | // Copyright(c) 2015-17 Intel Corporation. | |
3 | ||
4 | /* | |
5 | * Soundwire Intel Master Driver | |
6 | */ | |
7 | ||
8 | #include <linux/acpi.h> | |
9 | #include <linux/delay.h> | |
10 | #include <linux/interrupt.h> | |
11 | #include <linux/platform_device.h> | |
12 | #include <linux/soundwire/sdw_registers.h> | |
13 | #include <linux/soundwire/sdw.h> | |
14 | #include <linux/soundwire/sdw_intel.h> | |
15 | #include "cadence_master.h" | |
16 | #include "intel.h" | |
17 | ||
18 | /* Intel SHIM Registers Definition */ | |
19 | #define SDW_SHIM_LCAP 0x0 | |
20 | #define SDW_SHIM_LCTL 0x4 | |
21 | #define SDW_SHIM_IPPTR 0x8 | |
22 | #define SDW_SHIM_SYNC 0xC | |
23 | ||
24 | #define SDW_SHIM_CTLSCAP(x) (0x010 + 0x60 * x) | |
25 | #define SDW_SHIM_CTLS0CM(x) (0x012 + 0x60 * x) | |
26 | #define SDW_SHIM_CTLS1CM(x) (0x014 + 0x60 * x) | |
27 | #define SDW_SHIM_CTLS2CM(x) (0x016 + 0x60 * x) | |
28 | #define SDW_SHIM_CTLS3CM(x) (0x018 + 0x60 * x) | |
29 | #define SDW_SHIM_PCMSCAP(x) (0x020 + 0x60 * x) | |
30 | ||
31 | #define SDW_SHIM_PCMSYCHM(x, y) (0x022 + (0x60 * x) + (0x2 * y)) | |
32 | #define SDW_SHIM_PCMSYCHC(x, y) (0x042 + (0x60 * x) + (0x2 * y)) | |
33 | #define SDW_SHIM_PDMSCAP(x) (0x062 + 0x60 * x) | |
34 | #define SDW_SHIM_IOCTL(x) (0x06C + 0x60 * x) | |
35 | #define SDW_SHIM_CTMCTL(x) (0x06E + 0x60 * x) | |
36 | ||
37 | #define SDW_SHIM_WAKEEN 0x190 | |
38 | #define SDW_SHIM_WAKESTS 0x192 | |
39 | ||
40 | #define SDW_SHIM_LCTL_SPA BIT(0) | |
41 | #define SDW_SHIM_LCTL_CPA BIT(8) | |
42 | ||
43 | #define SDW_SHIM_SYNC_SYNCPRD_VAL 0x176F | |
44 | #define SDW_SHIM_SYNC_SYNCPRD GENMASK(14, 0) | |
45 | #define SDW_SHIM_SYNC_SYNCCPU BIT(15) | |
46 | #define SDW_SHIM_SYNC_CMDSYNC_MASK GENMASK(19, 16) | |
47 | #define SDW_SHIM_SYNC_CMDSYNC BIT(16) | |
48 | #define SDW_SHIM_SYNC_SYNCGO BIT(24) | |
49 | ||
50 | #define SDW_SHIM_PCMSCAP_ISS GENMASK(3, 0) | |
51 | #define SDW_SHIM_PCMSCAP_OSS GENMASK(7, 4) | |
52 | #define SDW_SHIM_PCMSCAP_BSS GENMASK(12, 8) | |
53 | ||
54 | #define SDW_SHIM_PCMSYCM_LCHN GENMASK(3, 0) | |
55 | #define SDW_SHIM_PCMSYCM_HCHN GENMASK(7, 4) | |
56 | #define SDW_SHIM_PCMSYCM_STREAM GENMASK(13, 8) | |
57 | #define SDW_SHIM_PCMSYCM_DIR BIT(15) | |
58 | ||
59 | #define SDW_SHIM_PDMSCAP_ISS GENMASK(3, 0) | |
60 | #define SDW_SHIM_PDMSCAP_OSS GENMASK(7, 4) | |
61 | #define SDW_SHIM_PDMSCAP_BSS GENMASK(12, 8) | |
62 | #define SDW_SHIM_PDMSCAP_CPSS GENMASK(15, 13) | |
63 | ||
64 | #define SDW_SHIM_IOCTL_MIF BIT(0) | |
65 | #define SDW_SHIM_IOCTL_CO BIT(1) | |
66 | #define SDW_SHIM_IOCTL_COE BIT(2) | |
67 | #define SDW_SHIM_IOCTL_DO BIT(3) | |
68 | #define SDW_SHIM_IOCTL_DOE BIT(4) | |
69 | #define SDW_SHIM_IOCTL_BKE BIT(5) | |
70 | #define SDW_SHIM_IOCTL_WPDD BIT(6) | |
71 | #define SDW_SHIM_IOCTL_CIBD BIT(8) | |
72 | #define SDW_SHIM_IOCTL_DIBD BIT(9) | |
73 | ||
74 | #define SDW_SHIM_CTMCTL_DACTQE BIT(0) | |
75 | #define SDW_SHIM_CTMCTL_DODS BIT(1) | |
76 | #define SDW_SHIM_CTMCTL_DOAIS GENMASK(4, 3) | |
77 | ||
78 | #define SDW_SHIM_WAKEEN_ENABLE BIT(0) | |
79 | #define SDW_SHIM_WAKESTS_STATUS BIT(0) | |
80 | ||
81 | /* Intel ALH Register definitions */ | |
82 | #define SDW_ALH_STRMZCFG(x) (0x000 + (0x4 * x)) | |
83 | ||
84 | #define SDW_ALH_STRMZCFG_DMAT_VAL 0x3 | |
85 | #define SDW_ALH_STRMZCFG_DMAT GENMASK(7, 0) | |
86 | #define SDW_ALH_STRMZCFG_CHN GENMASK(19, 16) | |
87 | ||
88 | struct sdw_intel { | |
89 | struct sdw_cdns cdns; | |
90 | int instance; | |
91 | struct sdw_intel_link_res *res; | |
92 | }; | |
93 | ||
94 | #define cdns_to_intel(_cdns) container_of(_cdns, struct sdw_intel, cdns) | |
95 | ||
96 | /* | |
97 | * Read, write helpers for HW registers | |
98 | */ | |
99 | static inline int intel_readl(void __iomem *base, int offset) | |
100 | { | |
101 | return readl(base + offset); | |
102 | } | |
103 | ||
104 | static inline void intel_writel(void __iomem *base, int offset, int value) | |
105 | { | |
106 | writel(value, base + offset); | |
107 | } | |
108 | ||
109 | static inline u16 intel_readw(void __iomem *base, int offset) | |
110 | { | |
111 | return readw(base + offset); | |
112 | } | |
113 | ||
114 | static inline void intel_writew(void __iomem *base, int offset, u16 value) | |
115 | { | |
116 | writew(value, base + offset); | |
117 | } | |
118 | ||
119 | static int intel_clear_bit(void __iomem *base, int offset, u32 value, u32 mask) | |
120 | { | |
121 | int timeout = 10; | |
122 | u32 reg_read; | |
123 | ||
124 | writel(value, base + offset); | |
125 | do { | |
126 | reg_read = readl(base + offset); | |
127 | if (!(reg_read & mask)) | |
128 | return 0; | |
129 | ||
130 | timeout--; | |
131 | udelay(50); | |
132 | } while (timeout != 0); | |
133 | ||
134 | return -EAGAIN; | |
135 | } | |
136 | ||
137 | static int intel_set_bit(void __iomem *base, int offset, u32 value, u32 mask) | |
138 | { | |
139 | int timeout = 10; | |
140 | u32 reg_read; | |
141 | ||
142 | writel(value, base + offset); | |
143 | do { | |
144 | reg_read = readl(base + offset); | |
145 | if (reg_read & mask) | |
146 | return 0; | |
147 | ||
148 | timeout--; | |
149 | udelay(50); | |
150 | } while (timeout != 0); | |
151 | ||
152 | return -EAGAIN; | |
153 | } | |
154 | ||
155 | /* | |
156 | * shim ops | |
157 | */ | |
158 | ||
159 | static int intel_link_power_up(struct sdw_intel *sdw) | |
160 | { | |
161 | unsigned int link_id = sdw->instance; | |
162 | void __iomem *shim = sdw->res->shim; | |
163 | int spa_mask, cpa_mask; | |
164 | int link_control, ret; | |
165 | ||
166 | /* Link power up sequence */ | |
167 | link_control = intel_readl(shim, SDW_SHIM_LCTL); | |
168 | spa_mask = (SDW_SHIM_LCTL_SPA << link_id); | |
169 | cpa_mask = (SDW_SHIM_LCTL_CPA << link_id); | |
170 | link_control |= spa_mask; | |
171 | ||
172 | ret = intel_set_bit(shim, SDW_SHIM_LCTL, link_control, cpa_mask); | |
173 | if (ret < 0) | |
174 | return ret; | |
175 | ||
176 | sdw->cdns.link_up = true; | |
177 | return 0; | |
178 | } | |
179 | ||
180 | static int intel_shim_init(struct sdw_intel *sdw) | |
181 | { | |
182 | void __iomem *shim = sdw->res->shim; | |
183 | unsigned int link_id = sdw->instance; | |
184 | int sync_reg, ret; | |
185 | u16 ioctl = 0, act = 0; | |
186 | ||
187 | /* Initialize Shim */ | |
188 | ioctl |= SDW_SHIM_IOCTL_BKE; | |
189 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); | |
190 | ||
191 | ioctl |= SDW_SHIM_IOCTL_WPDD; | |
192 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); | |
193 | ||
194 | ioctl |= SDW_SHIM_IOCTL_DO; | |
195 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); | |
196 | ||
197 | ioctl |= SDW_SHIM_IOCTL_DOE; | |
198 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); | |
199 | ||
200 | /* Switch to MIP from Glue logic */ | |
201 | ioctl = intel_readw(shim, SDW_SHIM_IOCTL(link_id)); | |
202 | ||
203 | ioctl &= ~(SDW_SHIM_IOCTL_DOE); | |
204 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); | |
205 | ||
206 | ioctl &= ~(SDW_SHIM_IOCTL_DO); | |
207 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); | |
208 | ||
209 | ioctl |= (SDW_SHIM_IOCTL_MIF); | |
210 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); | |
211 | ||
212 | ioctl &= ~(SDW_SHIM_IOCTL_BKE); | |
213 | ioctl &= ~(SDW_SHIM_IOCTL_COE); | |
214 | ||
215 | intel_writew(shim, SDW_SHIM_IOCTL(link_id), ioctl); | |
216 | ||
217 | act |= 0x1 << SDW_REG_SHIFT(SDW_SHIM_CTMCTL_DOAIS); | |
218 | act |= SDW_SHIM_CTMCTL_DACTQE; | |
219 | act |= SDW_SHIM_CTMCTL_DODS; | |
220 | intel_writew(shim, SDW_SHIM_CTMCTL(link_id), act); | |
221 | ||
222 | /* Now set SyncPRD period */ | |
223 | sync_reg = intel_readl(shim, SDW_SHIM_SYNC); | |
224 | sync_reg |= (SDW_SHIM_SYNC_SYNCPRD_VAL << | |
225 | SDW_REG_SHIFT(SDW_SHIM_SYNC_SYNCPRD)); | |
226 | ||
227 | /* Set SyncCPU bit */ | |
228 | sync_reg |= SDW_SHIM_SYNC_SYNCCPU; | |
229 | ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, | |
230 | SDW_SHIM_SYNC_SYNCCPU); | |
231 | if (ret < 0) | |
232 | dev_err(sdw->cdns.dev, "Failed to set sync period: %d", ret); | |
233 | ||
234 | return ret; | |
235 | } | |
236 | ||
237 | static int intel_prop_read(struct sdw_bus *bus) | |
238 | { | |
239 | /* Initialize with default handler to read all DisCo properties */ | |
240 | sdw_master_read_prop(bus); | |
241 | ||
242 | /* BIOS is not giving some values correctly. So, lets override them */ | |
243 | bus->prop.num_freq = 1; | |
244 | bus->prop.freq = devm_kcalloc(bus->dev, sizeof(*bus->prop.freq), | |
245 | bus->prop.num_freq, GFP_KERNEL); | |
246 | if (!bus->prop.freq) | |
247 | return -ENOMEM; | |
248 | ||
249 | bus->prop.freq[0] = bus->prop.max_freq; | |
250 | bus->prop.err_threshold = 5; | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
c91605f4 SN |
255 | static struct sdw_master_ops sdw_intel_ops = { |
256 | .read_prop = sdw_master_read_prop, | |
257 | .xfer_msg = cdns_xfer_msg, | |
258 | .xfer_msg_defer = cdns_xfer_msg_defer, | |
259 | .reset_page_addr = cdns_reset_page_addr, | |
07abeff1 | 260 | .set_bus_conf = cdns_bus_conf, |
c91605f4 SN |
261 | }; |
262 | ||
71bb8a1b VK |
263 | /* |
264 | * probe and init | |
265 | */ | |
266 | static int intel_probe(struct platform_device *pdev) | |
267 | { | |
268 | struct sdw_intel *sdw; | |
269 | int ret; | |
270 | ||
271 | sdw = devm_kzalloc(&pdev->dev, sizeof(*sdw), GFP_KERNEL); | |
272 | if (!sdw) | |
273 | return -ENOMEM; | |
274 | ||
275 | sdw->instance = pdev->id; | |
276 | sdw->res = dev_get_platdata(&pdev->dev); | |
277 | sdw->cdns.dev = &pdev->dev; | |
278 | sdw->cdns.registers = sdw->res->registers; | |
279 | sdw->cdns.instance = sdw->instance; | |
280 | sdw->cdns.msg_count = 0; | |
281 | sdw->cdns.bus.dev = &pdev->dev; | |
282 | sdw->cdns.bus.link_id = pdev->id; | |
283 | ||
284 | sdw_cdns_probe(&sdw->cdns); | |
285 | ||
286 | /* Set property read ops */ | |
c91605f4 SN |
287 | sdw_intel_ops.read_prop = intel_prop_read; |
288 | sdw->cdns.bus.ops = &sdw_intel_ops; | |
71bb8a1b VK |
289 | |
290 | platform_set_drvdata(pdev, sdw); | |
291 | ||
292 | ret = sdw_add_bus_master(&sdw->cdns.bus); | |
293 | if (ret) { | |
294 | dev_err(&pdev->dev, "sdw_add_bus_master fail: %d\n", ret); | |
295 | goto err_master_reg; | |
296 | } | |
297 | ||
298 | /* Initialize shim and controller */ | |
299 | intel_link_power_up(sdw); | |
300 | intel_shim_init(sdw); | |
301 | ||
302 | ret = sdw_cdns_init(&sdw->cdns); | |
303 | if (ret) | |
304 | goto err_init; | |
305 | ||
7094dc2b | 306 | ret = sdw_cdns_enable_interrupt(&sdw->cdns); |
71bb8a1b VK |
307 | if (ret) |
308 | goto err_init; | |
309 | ||
310 | /* Acquire IRQ */ | |
311 | ret = request_threaded_irq(sdw->res->irq, sdw_cdns_irq, | |
312 | sdw_cdns_thread, IRQF_SHARED, KBUILD_MODNAME, | |
313 | &sdw->cdns); | |
314 | if (ret < 0) { | |
315 | dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", | |
316 | sdw->res->irq); | |
317 | goto err_init; | |
318 | } | |
319 | ||
320 | return 0; | |
321 | ||
322 | err_init: | |
323 | sdw_delete_bus_master(&sdw->cdns.bus); | |
324 | err_master_reg: | |
325 | return ret; | |
326 | } | |
327 | ||
328 | static int intel_remove(struct platform_device *pdev) | |
329 | { | |
330 | struct sdw_intel *sdw; | |
331 | ||
332 | sdw = platform_get_drvdata(pdev); | |
333 | ||
334 | free_irq(sdw->res->irq, sdw); | |
335 | sdw_delete_bus_master(&sdw->cdns.bus); | |
336 | ||
337 | return 0; | |
338 | } | |
339 | ||
340 | static struct platform_driver sdw_intel_drv = { | |
341 | .probe = intel_probe, | |
342 | .remove = intel_remove, | |
343 | .driver = { | |
344 | .name = "int-sdw", | |
345 | ||
346 | }, | |
347 | }; | |
348 | ||
349 | module_platform_driver(sdw_intel_drv); | |
350 | ||
351 | MODULE_LICENSE("Dual BSD/GPL"); | |
352 | MODULE_ALIAS("platform:int-sdw"); | |
353 | MODULE_DESCRIPTION("Intel Soundwire Master Driver"); |