diff options
Diffstat (limited to 'drivers/soundwire/intel.c')
-rw-r--r-- | drivers/soundwire/intel.c | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c new file mode 100644 index 000000000000..86a7bd1fc912 --- /dev/null +++ b/drivers/soundwire/intel.c | |||
@@ -0,0 +1,345 @@ | |||
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 | |||
255 | /* | ||
256 | * probe and init | ||
257 | */ | ||
258 | static int intel_probe(struct platform_device *pdev) | ||
259 | { | ||
260 | struct sdw_intel *sdw; | ||
261 | int ret; | ||
262 | |||
263 | sdw = devm_kzalloc(&pdev->dev, sizeof(*sdw), GFP_KERNEL); | ||
264 | if (!sdw) | ||
265 | return -ENOMEM; | ||
266 | |||
267 | sdw->instance = pdev->id; | ||
268 | sdw->res = dev_get_platdata(&pdev->dev); | ||
269 | sdw->cdns.dev = &pdev->dev; | ||
270 | sdw->cdns.registers = sdw->res->registers; | ||
271 | sdw->cdns.instance = sdw->instance; | ||
272 | sdw->cdns.msg_count = 0; | ||
273 | sdw->cdns.bus.dev = &pdev->dev; | ||
274 | sdw->cdns.bus.link_id = pdev->id; | ||
275 | |||
276 | sdw_cdns_probe(&sdw->cdns); | ||
277 | |||
278 | /* Set property read ops */ | ||
279 | sdw_cdns_master_ops.read_prop = intel_prop_read; | ||
280 | sdw->cdns.bus.ops = &sdw_cdns_master_ops; | ||
281 | |||
282 | platform_set_drvdata(pdev, sdw); | ||
283 | |||
284 | ret = sdw_add_bus_master(&sdw->cdns.bus); | ||
285 | if (ret) { | ||
286 | dev_err(&pdev->dev, "sdw_add_bus_master fail: %d\n", ret); | ||
287 | goto err_master_reg; | ||
288 | } | ||
289 | |||
290 | /* Initialize shim and controller */ | ||
291 | intel_link_power_up(sdw); | ||
292 | intel_shim_init(sdw); | ||
293 | |||
294 | ret = sdw_cdns_init(&sdw->cdns); | ||
295 | if (ret) | ||
296 | goto err_init; | ||
297 | |||
298 | ret = sdw_cdns_enable_interrupt(&sdw->cdns); | ||
299 | if (ret) | ||
300 | goto err_init; | ||
301 | |||
302 | /* Acquire IRQ */ | ||
303 | ret = request_threaded_irq(sdw->res->irq, sdw_cdns_irq, | ||
304 | sdw_cdns_thread, IRQF_SHARED, KBUILD_MODNAME, | ||
305 | &sdw->cdns); | ||
306 | if (ret < 0) { | ||
307 | dev_err(sdw->cdns.dev, "unable to grab IRQ %d, disabling device\n", | ||
308 | sdw->res->irq); | ||
309 | goto err_init; | ||
310 | } | ||
311 | |||
312 | return 0; | ||
313 | |||
314 | err_init: | ||
315 | sdw_delete_bus_master(&sdw->cdns.bus); | ||
316 | err_master_reg: | ||
317 | return ret; | ||
318 | } | ||
319 | |||
320 | static int intel_remove(struct platform_device *pdev) | ||
321 | { | ||
322 | struct sdw_intel *sdw; | ||
323 | |||
324 | sdw = platform_get_drvdata(pdev); | ||
325 | |||
326 | free_irq(sdw->res->irq, sdw); | ||
327 | sdw_delete_bus_master(&sdw->cdns.bus); | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static struct platform_driver sdw_intel_drv = { | ||
333 | .probe = intel_probe, | ||
334 | .remove = intel_remove, | ||
335 | .driver = { | ||
336 | .name = "int-sdw", | ||
337 | |||
338 | }, | ||
339 | }; | ||
340 | |||
341 | module_platform_driver(sdw_intel_drv); | ||
342 | |||
343 | MODULE_LICENSE("Dual BSD/GPL"); | ||
344 | MODULE_ALIAS("platform:int-sdw"); | ||
345 | MODULE_DESCRIPTION("Intel Soundwire Master Driver"); | ||