Commit | Line | Data |
---|---|---|
9fff0425 | 1 | // SPDX-License-Identifier: GPL-2.0 |
3ce72726 | 2 | /* |
907b471c | 3 | * Copyright (c) 2003-2019, Intel Corporation. All rights reserved. |
3ce72726 | 4 | * Intel Management Engine Interface (Intel MEI) Linux driver |
3ce72726 OW |
5 | */ |
6 | ||
7 | #include <linux/pci.h> | |
06ecd645 TW |
8 | |
9 | #include <linux/kthread.h> | |
10 | #include <linux/interrupt.h> | |
77537ad2 | 11 | #include <linux/pm_runtime.h> |
7026a5fd | 12 | #include <linux/sizes.h> |
47a73801 TW |
13 | |
14 | #include "mei_dev.h" | |
06ecd645 TW |
15 | #include "hbm.h" |
16 | ||
6e4cd27a TW |
17 | #include "hw-me.h" |
18 | #include "hw-me-regs.h" | |
06ecd645 | 19 | |
a0a927d0 TW |
20 | #include "mei-trace.h" |
21 | ||
3a65dd4e | 22 | /** |
b68301e9 | 23 | * mei_me_reg_read - Reads 32bit data from the mei device |
3a65dd4e | 24 | * |
a8605ea2 | 25 | * @hw: the me hardware structure |
3a65dd4e TW |
26 | * @offset: offset from which to read the data |
27 | * | |
a8605ea2 | 28 | * Return: register value (u32) |
3a65dd4e | 29 | */ |
b68301e9 | 30 | static inline u32 mei_me_reg_read(const struct mei_me_hw *hw, |
3a65dd4e TW |
31 | unsigned long offset) |
32 | { | |
52c34561 | 33 | return ioread32(hw->mem_addr + offset); |
3a65dd4e TW |
34 | } |
35 | ||
36 | ||
37 | /** | |
b68301e9 | 38 | * mei_me_reg_write - Writes 32bit data to the mei device |
3a65dd4e | 39 | * |
a8605ea2 | 40 | * @hw: the me hardware structure |
3a65dd4e TW |
41 | * @offset: offset from which to write the data |
42 | * @value: register value to write (u32) | |
43 | */ | |
b68301e9 | 44 | static inline void mei_me_reg_write(const struct mei_me_hw *hw, |
3a65dd4e TW |
45 | unsigned long offset, u32 value) |
46 | { | |
52c34561 | 47 | iowrite32(value, hw->mem_addr + offset); |
3a65dd4e | 48 | } |
3ce72726 | 49 | |
3a65dd4e | 50 | /** |
b68301e9 | 51 | * mei_me_mecbrw_read - Reads 32bit data from ME circular buffer |
d025284d | 52 | * read window register |
3a65dd4e TW |
53 | * |
54 | * @dev: the device structure | |
55 | * | |
a8605ea2 | 56 | * Return: ME_CB_RW register value (u32) |
3a65dd4e | 57 | */ |
381a58c7 | 58 | static inline u32 mei_me_mecbrw_read(const struct mei_device *dev) |
3a65dd4e | 59 | { |
b68301e9 | 60 | return mei_me_reg_read(to_me_hw(dev), ME_CB_RW); |
3a65dd4e | 61 | } |
381a58c7 TW |
62 | |
63 | /** | |
64 | * mei_me_hcbww_write - write 32bit data to the host circular buffer | |
65 | * | |
66 | * @dev: the device structure | |
67 | * @data: 32bit data to be written to the host circular buffer | |
68 | */ | |
69 | static inline void mei_me_hcbww_write(struct mei_device *dev, u32 data) | |
70 | { | |
71 | mei_me_reg_write(to_me_hw(dev), H_CB_WW, data); | |
72 | } | |
73 | ||
3a65dd4e | 74 | /** |
b68301e9 | 75 | * mei_me_mecsr_read - Reads 32bit data from the ME CSR |
3a65dd4e | 76 | * |
381a58c7 | 77 | * @dev: the device structure |
3a65dd4e | 78 | * |
a8605ea2 | 79 | * Return: ME_CSR_HA register value (u32) |
3a65dd4e | 80 | */ |
381a58c7 | 81 | static inline u32 mei_me_mecsr_read(const struct mei_device *dev) |
3a65dd4e | 82 | { |
a0a927d0 TW |
83 | u32 reg; |
84 | ||
85 | reg = mei_me_reg_read(to_me_hw(dev), ME_CSR_HA); | |
86 | trace_mei_reg_read(dev->dev, "ME_CSR_HA", ME_CSR_HA, reg); | |
87 | ||
88 | return reg; | |
3a65dd4e | 89 | } |
3ce72726 OW |
90 | |
91 | /** | |
d025284d TW |
92 | * mei_hcsr_read - Reads 32bit data from the host CSR |
93 | * | |
381a58c7 | 94 | * @dev: the device structure |
d025284d | 95 | * |
a8605ea2 | 96 | * Return: H_CSR register value (u32) |
d025284d | 97 | */ |
381a58c7 | 98 | static inline u32 mei_hcsr_read(const struct mei_device *dev) |
d025284d | 99 | { |
a0a927d0 TW |
100 | u32 reg; |
101 | ||
102 | reg = mei_me_reg_read(to_me_hw(dev), H_CSR); | |
103 | trace_mei_reg_read(dev->dev, "H_CSR", H_CSR, reg); | |
104 | ||
105 | return reg; | |
381a58c7 TW |
106 | } |
107 | ||
108 | /** | |
109 | * mei_hcsr_write - writes H_CSR register to the mei device | |
110 | * | |
111 | * @dev: the device structure | |
112 | * @reg: new register value | |
113 | */ | |
114 | static inline void mei_hcsr_write(struct mei_device *dev, u32 reg) | |
115 | { | |
a0a927d0 | 116 | trace_mei_reg_write(dev->dev, "H_CSR", H_CSR, reg); |
381a58c7 | 117 | mei_me_reg_write(to_me_hw(dev), H_CSR, reg); |
d025284d TW |
118 | } |
119 | ||
120 | /** | |
121 | * mei_hcsr_set - writes H_CSR register to the mei device, | |
3ce72726 OW |
122 | * and ignores the H_IS bit for it is write-one-to-zero. |
123 | * | |
381a58c7 TW |
124 | * @dev: the device structure |
125 | * @reg: new register value | |
3ce72726 | 126 | */ |
381a58c7 | 127 | static inline void mei_hcsr_set(struct mei_device *dev, u32 reg) |
3ce72726 | 128 | { |
1fa55b4e | 129 | reg &= ~H_CSR_IS_MASK; |
381a58c7 | 130 | mei_hcsr_write(dev, reg); |
3ce72726 OW |
131 | } |
132 | ||
9c7daa61 AU |
133 | /** |
134 | * mei_hcsr_set_hig - set host interrupt (set H_IG) | |
135 | * | |
136 | * @dev: the device structure | |
137 | */ | |
138 | static inline void mei_hcsr_set_hig(struct mei_device *dev) | |
139 | { | |
140 | u32 hcsr; | |
141 | ||
142 | hcsr = mei_hcsr_read(dev) | H_IG; | |
143 | mei_hcsr_set(dev, hcsr); | |
144 | } | |
145 | ||
859ef2ff AU |
146 | /** |
147 | * mei_me_d0i3c_read - Reads 32bit data from the D0I3C register | |
148 | * | |
149 | * @dev: the device structure | |
150 | * | |
151 | * Return: H_D0I3C register value (u32) | |
152 | */ | |
153 | static inline u32 mei_me_d0i3c_read(const struct mei_device *dev) | |
154 | { | |
155 | u32 reg; | |
156 | ||
157 | reg = mei_me_reg_read(to_me_hw(dev), H_D0I3C); | |
cf094ebe | 158 | trace_mei_reg_read(dev->dev, "H_D0I3C", H_D0I3C, reg); |
859ef2ff AU |
159 | |
160 | return reg; | |
161 | } | |
162 | ||
163 | /** | |
164 | * mei_me_d0i3c_write - writes H_D0I3C register to device | |
165 | * | |
166 | * @dev: the device structure | |
167 | * @reg: new register value | |
168 | */ | |
169 | static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg) | |
170 | { | |
cf094ebe | 171 | trace_mei_reg_write(dev->dev, "H_D0I3C", H_D0I3C, reg); |
859ef2ff AU |
172 | mei_me_reg_write(to_me_hw(dev), H_D0I3C, reg); |
173 | } | |
174 | ||
1bd30b6a TW |
175 | /** |
176 | * mei_me_fw_status - read fw status register from pci config space | |
177 | * | |
178 | * @dev: mei device | |
179 | * @fw_status: fw status register values | |
ce23139c AU |
180 | * |
181 | * Return: 0 on success, error otherwise | |
1bd30b6a TW |
182 | */ |
183 | static int mei_me_fw_status(struct mei_device *dev, | |
184 | struct mei_fw_status *fw_status) | |
185 | { | |
1bd30b6a | 186 | struct pci_dev *pdev = to_pci_dev(dev->dev); |
4ad96db6 TW |
187 | struct mei_me_hw *hw = to_me_hw(dev); |
188 | const struct mei_fw_status *fw_src = &hw->cfg->fw_status; | |
1bd30b6a TW |
189 | int ret; |
190 | int i; | |
191 | ||
192 | if (!fw_status) | |
193 | return -EINVAL; | |
194 | ||
195 | fw_status->count = fw_src->count; | |
196 | for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) { | |
a96c5482 TW |
197 | ret = pci_read_config_dword(pdev, fw_src->status[i], |
198 | &fw_status->status[i]); | |
199 | trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HSF_X", | |
200 | fw_src->status[i], | |
201 | fw_status->status[i]); | |
1bd30b6a TW |
202 | if (ret) |
203 | return ret; | |
204 | } | |
205 | ||
206 | return 0; | |
207 | } | |
e7e0c231 TW |
208 | |
209 | /** | |
393b148f | 210 | * mei_me_hw_config - configure hw dependent settings |
e7e0c231 TW |
211 | * |
212 | * @dev: mei device | |
213 | */ | |
827eef51 | 214 | static void mei_me_hw_config(struct mei_device *dev) |
e7e0c231 | 215 | { |
bb9f4d26 | 216 | struct pci_dev *pdev = to_pci_dev(dev->dev); |
ba9cdd0e | 217 | struct mei_me_hw *hw = to_me_hw(dev); |
bb9f4d26 AU |
218 | u32 hcsr, reg; |
219 | ||
e7e0c231 | 220 | /* Doesn't change in runtime */ |
bb9f4d26 | 221 | hcsr = mei_hcsr_read(dev); |
8c8d964c | 222 | hw->hbuf_depth = (hcsr & H_CBD) >> 24; |
ba9cdd0e | 223 | |
bb9f4d26 AU |
224 | reg = 0; |
225 | pci_read_config_dword(pdev, PCI_CFG_HFS_1, ®); | |
a96c5482 | 226 | trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); |
bb9f4d26 AU |
227 | hw->d0i3_supported = |
228 | ((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK); | |
b9a1fc99 AU |
229 | |
230 | hw->pg_state = MEI_PG_OFF; | |
231 | if (hw->d0i3_supported) { | |
232 | reg = mei_me_d0i3c_read(dev); | |
233 | if (reg & H_D0I3C_I3) | |
234 | hw->pg_state = MEI_PG_ON; | |
235 | } | |
e7e0c231 | 236 | } |
964a2331 TW |
237 | |
238 | /** | |
239 | * mei_me_pg_state - translate internal pg state | |
240 | * to the mei power gating state | |
241 | * | |
ce23139c AU |
242 | * @dev: mei device |
243 | * | |
244 | * Return: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise | |
964a2331 TW |
245 | */ |
246 | static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev) | |
247 | { | |
ba9cdd0e | 248 | struct mei_me_hw *hw = to_me_hw(dev); |
92db1555 | 249 | |
ba9cdd0e | 250 | return hw->pg_state; |
964a2331 TW |
251 | } |
252 | ||
a2eb0fc0 AU |
253 | static inline u32 me_intr_src(u32 hcsr) |
254 | { | |
255 | return hcsr & H_CSR_IS_MASK; | |
256 | } | |
257 | ||
258 | /** | |
259 | * me_intr_disable - disables mei device interrupts | |
260 | * using supplied hcsr register value. | |
261 | * | |
262 | * @dev: the device structure | |
263 | * @hcsr: supplied hcsr register value | |
264 | */ | |
265 | static inline void me_intr_disable(struct mei_device *dev, u32 hcsr) | |
266 | { | |
267 | hcsr &= ~H_CSR_IE_MASK; | |
268 | mei_hcsr_set(dev, hcsr); | |
269 | } | |
270 | ||
271 | /** | |
3e917975 | 272 | * me_intr_clear - clear and stop interrupts |
a2eb0fc0 AU |
273 | * |
274 | * @dev: the device structure | |
275 | * @hcsr: supplied hcsr register value | |
276 | */ | |
277 | static inline void me_intr_clear(struct mei_device *dev, u32 hcsr) | |
278 | { | |
279 | if (me_intr_src(hcsr)) | |
280 | mei_hcsr_write(dev, hcsr); | |
281 | } | |
282 | ||
3ce72726 | 283 | /** |
ce23139c | 284 | * mei_me_intr_clear - clear and stop interrupts |
3a65dd4e TW |
285 | * |
286 | * @dev: the device structure | |
287 | */ | |
827eef51 | 288 | static void mei_me_intr_clear(struct mei_device *dev) |
3a65dd4e | 289 | { |
381a58c7 | 290 | u32 hcsr = mei_hcsr_read(dev); |
92db1555 | 291 | |
a2eb0fc0 | 292 | me_intr_clear(dev, hcsr); |
3a65dd4e | 293 | } |
3a65dd4e | 294 | /** |
827eef51 | 295 | * mei_me_intr_enable - enables mei device interrupts |
3ce72726 OW |
296 | * |
297 | * @dev: the device structure | |
298 | */ | |
827eef51 | 299 | static void mei_me_intr_enable(struct mei_device *dev) |
3ce72726 | 300 | { |
381a58c7 | 301 | u32 hcsr = mei_hcsr_read(dev); |
92db1555 | 302 | |
1fa55b4e | 303 | hcsr |= H_CSR_IE_MASK; |
381a58c7 | 304 | mei_hcsr_set(dev, hcsr); |
3ce72726 OW |
305 | } |
306 | ||
307 | /** | |
ce23139c | 308 | * mei_me_intr_disable - disables mei device interrupts |
3ce72726 OW |
309 | * |
310 | * @dev: the device structure | |
311 | */ | |
827eef51 | 312 | static void mei_me_intr_disable(struct mei_device *dev) |
3ce72726 | 313 | { |
381a58c7 | 314 | u32 hcsr = mei_hcsr_read(dev); |
92db1555 | 315 | |
a2eb0fc0 | 316 | me_intr_disable(dev, hcsr); |
3ce72726 OW |
317 | } |
318 | ||
4a8efd4a TW |
319 | /** |
320 | * mei_me_synchronize_irq - wait for pending IRQ handlers | |
321 | * | |
322 | * @dev: the device structure | |
323 | */ | |
324 | static void mei_me_synchronize_irq(struct mei_device *dev) | |
325 | { | |
261b3e1f | 326 | struct mei_me_hw *hw = to_me_hw(dev); |
4a8efd4a | 327 | |
261b3e1f | 328 | synchronize_irq(hw->irq); |
4a8efd4a TW |
329 | } |
330 | ||
68f8ea18 TW |
331 | /** |
332 | * mei_me_hw_reset_release - release device from the reset | |
333 | * | |
334 | * @dev: the device structure | |
335 | */ | |
336 | static void mei_me_hw_reset_release(struct mei_device *dev) | |
337 | { | |
381a58c7 | 338 | u32 hcsr = mei_hcsr_read(dev); |
68f8ea18 TW |
339 | |
340 | hcsr |= H_IG; | |
341 | hcsr &= ~H_RST; | |
381a58c7 | 342 | mei_hcsr_set(dev, hcsr); |
68f8ea18 | 343 | } |
adfba322 | 344 | |
115ba28c | 345 | /** |
827eef51 | 346 | * mei_me_host_set_ready - enable device |
115ba28c | 347 | * |
ce23139c | 348 | * @dev: mei device |
115ba28c | 349 | */ |
827eef51 | 350 | static void mei_me_host_set_ready(struct mei_device *dev) |
115ba28c | 351 | { |
381a58c7 | 352 | u32 hcsr = mei_hcsr_read(dev); |
92db1555 | 353 | |
1fa55b4e | 354 | hcsr |= H_CSR_IE_MASK | H_IG | H_RDY; |
381a58c7 | 355 | mei_hcsr_set(dev, hcsr); |
115ba28c | 356 | } |
ce23139c | 357 | |
115ba28c | 358 | /** |
827eef51 | 359 | * mei_me_host_is_ready - check whether the host has turned ready |
115ba28c | 360 | * |
a8605ea2 AU |
361 | * @dev: mei device |
362 | * Return: bool | |
115ba28c | 363 | */ |
827eef51 | 364 | static bool mei_me_host_is_ready(struct mei_device *dev) |
115ba28c | 365 | { |
381a58c7 | 366 | u32 hcsr = mei_hcsr_read(dev); |
92db1555 | 367 | |
18caeb70 | 368 | return (hcsr & H_RDY) == H_RDY; |
115ba28c TW |
369 | } |
370 | ||
371 | /** | |
827eef51 | 372 | * mei_me_hw_is_ready - check whether the me(hw) has turned ready |
115ba28c | 373 | * |
a8605ea2 AU |
374 | * @dev: mei device |
375 | * Return: bool | |
115ba28c | 376 | */ |
827eef51 | 377 | static bool mei_me_hw_is_ready(struct mei_device *dev) |
115ba28c | 378 | { |
381a58c7 | 379 | u32 mecsr = mei_me_mecsr_read(dev); |
92db1555 | 380 | |
18caeb70 | 381 | return (mecsr & ME_RDY_HRA) == ME_RDY_HRA; |
115ba28c | 382 | } |
3a65dd4e | 383 | |
47f60a01 AU |
384 | /** |
385 | * mei_me_hw_is_resetting - check whether the me(hw) is in reset | |
386 | * | |
387 | * @dev: mei device | |
388 | * Return: bool | |
389 | */ | |
390 | static bool mei_me_hw_is_resetting(struct mei_device *dev) | |
391 | { | |
392 | u32 mecsr = mei_me_mecsr_read(dev); | |
393 | ||
394 | return (mecsr & ME_RST_HRA) == ME_RST_HRA; | |
395 | } | |
396 | ||
ce23139c AU |
397 | /** |
398 | * mei_me_hw_ready_wait - wait until the me(hw) has turned ready | |
399 | * or timeout is reached | |
400 | * | |
401 | * @dev: mei device | |
402 | * Return: 0 on success, error otherwise | |
403 | */ | |
aafae7ec TW |
404 | static int mei_me_hw_ready_wait(struct mei_device *dev) |
405 | { | |
aafae7ec | 406 | mutex_unlock(&dev->device_lock); |
2c2b93ec | 407 | wait_event_timeout(dev->wait_hw_ready, |
dab9bf41 | 408 | dev->recvd_hw_ready, |
7d93e58d | 409 | mei_secs_to_jiffies(MEI_HW_READY_TIMEOUT)); |
aafae7ec | 410 | mutex_lock(&dev->device_lock); |
2c2b93ec | 411 | if (!dev->recvd_hw_ready) { |
2bf94cab | 412 | dev_err(dev->dev, "wait hw ready failed\n"); |
2c2b93ec | 413 | return -ETIME; |
aafae7ec TW |
414 | } |
415 | ||
663b7ee9 | 416 | mei_me_hw_reset_release(dev); |
aafae7ec TW |
417 | dev->recvd_hw_ready = false; |
418 | return 0; | |
419 | } | |
420 | ||
ce23139c AU |
421 | /** |
422 | * mei_me_hw_start - hw start routine | |
423 | * | |
424 | * @dev: mei device | |
425 | * Return: 0 on success, error otherwise | |
426 | */ | |
aafae7ec TW |
427 | static int mei_me_hw_start(struct mei_device *dev) |
428 | { | |
429 | int ret = mei_me_hw_ready_wait(dev); | |
92db1555 | 430 | |
aafae7ec TW |
431 | if (ret) |
432 | return ret; | |
2bf94cab | 433 | dev_dbg(dev->dev, "hw is ready\n"); |
aafae7ec TW |
434 | |
435 | mei_me_host_set_ready(dev); | |
436 | return ret; | |
437 | } | |
438 | ||
439 | ||
3ce72726 | 440 | /** |
726917f0 | 441 | * mei_hbuf_filled_slots - gets number of device filled buffer slots |
3ce72726 | 442 | * |
7353f85c | 443 | * @dev: the device structure |
3ce72726 | 444 | * |
a8605ea2 | 445 | * Return: number of filled slots |
3ce72726 | 446 | */ |
726917f0 | 447 | static unsigned char mei_hbuf_filled_slots(struct mei_device *dev) |
3ce72726 | 448 | { |
18caeb70 | 449 | u32 hcsr; |
3ce72726 OW |
450 | char read_ptr, write_ptr; |
451 | ||
381a58c7 | 452 | hcsr = mei_hcsr_read(dev); |
726917f0 | 453 | |
18caeb70 TW |
454 | read_ptr = (char) ((hcsr & H_CBRP) >> 8); |
455 | write_ptr = (char) ((hcsr & H_CBWP) >> 16); | |
3ce72726 OW |
456 | |
457 | return (unsigned char) (write_ptr - read_ptr); | |
458 | } | |
459 | ||
460 | /** | |
393b148f | 461 | * mei_me_hbuf_is_empty - checks if host buffer is empty. |
3ce72726 OW |
462 | * |
463 | * @dev: the device structure | |
464 | * | |
a8605ea2 | 465 | * Return: true if empty, false - otherwise. |
3ce72726 | 466 | */ |
827eef51 | 467 | static bool mei_me_hbuf_is_empty(struct mei_device *dev) |
3ce72726 | 468 | { |
726917f0 | 469 | return mei_hbuf_filled_slots(dev) == 0; |
3ce72726 OW |
470 | } |
471 | ||
472 | /** | |
827eef51 | 473 | * mei_me_hbuf_empty_slots - counts write empty slots. |
3ce72726 OW |
474 | * |
475 | * @dev: the device structure | |
476 | * | |
a8605ea2 | 477 | * Return: -EOVERFLOW if overflow, otherwise empty slots count |
3ce72726 | 478 | */ |
827eef51 | 479 | static int mei_me_hbuf_empty_slots(struct mei_device *dev) |
3ce72726 | 480 | { |
8c8d964c | 481 | struct mei_me_hw *hw = to_me_hw(dev); |
24aadc80 | 482 | unsigned char filled_slots, empty_slots; |
3ce72726 | 483 | |
726917f0 | 484 | filled_slots = mei_hbuf_filled_slots(dev); |
8c8d964c | 485 | empty_slots = hw->hbuf_depth - filled_slots; |
3ce72726 OW |
486 | |
487 | /* check for overflow */ | |
8c8d964c | 488 | if (filled_slots > hw->hbuf_depth) |
3ce72726 OW |
489 | return -EOVERFLOW; |
490 | ||
491 | return empty_slots; | |
492 | } | |
493 | ||
ce23139c | 494 | /** |
8c8d964c | 495 | * mei_me_hbuf_depth - returns depth of the hw buffer. |
ce23139c AU |
496 | * |
497 | * @dev: the device structure | |
498 | * | |
8c8d964c | 499 | * Return: size of hw buffer in slots |
ce23139c | 500 | */ |
8c8d964c | 501 | static u32 mei_me_hbuf_depth(const struct mei_device *dev) |
827eef51 | 502 | { |
8c8d964c TW |
503 | struct mei_me_hw *hw = to_me_hw(dev); |
504 | ||
505 | return hw->hbuf_depth; | |
827eef51 TW |
506 | } |
507 | ||
3ce72726 | 508 | /** |
4b9960d0 | 509 | * mei_me_hbuf_write - writes a message to host hw buffer. |
3ce72726 OW |
510 | * |
511 | * @dev: the device structure | |
98e70866 TW |
512 | * @hdr: header of message |
513 | * @hdr_len: header length in bytes: must be multiplication of a slot (4bytes) | |
514 | * @data: payload | |
515 | * @data_len: payload length in bytes | |
3ce72726 | 516 | * |
98e70866 | 517 | * Return: 0 if success, < 0 - otherwise. |
3ce72726 | 518 | */ |
4b9960d0 | 519 | static int mei_me_hbuf_write(struct mei_device *dev, |
98e70866 TW |
520 | const void *hdr, size_t hdr_len, |
521 | const void *data, size_t data_len) | |
3ce72726 | 522 | { |
c8c8d080 | 523 | unsigned long rem; |
44c98df0 | 524 | unsigned long i; |
98e70866 | 525 | const u32 *reg_buf; |
c8c8d080 | 526 | u32 dw_cnt; |
169d1338 | 527 | int empty_slots; |
3ce72726 | 528 | |
98e70866 TW |
529 | if (WARN_ON(!hdr || !data || hdr_len & 0x3)) |
530 | return -EINVAL; | |
531 | ||
532 | dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM((struct mei_msg_hdr *)hdr)); | |
3ce72726 | 533 | |
726917f0 | 534 | empty_slots = mei_hbuf_empty_slots(dev); |
2bf94cab | 535 | dev_dbg(dev->dev, "empty slots = %hu.\n", empty_slots); |
3ce72726 | 536 | |
de877437 TW |
537 | if (empty_slots < 0) |
538 | return -EOVERFLOW; | |
539 | ||
98e70866 | 540 | dw_cnt = mei_data2slots(hdr_len + data_len); |
de877437 | 541 | if (dw_cnt > (u32)empty_slots) |
9d098192 | 542 | return -EMSGSIZE; |
3ce72726 | 543 | |
98e70866 TW |
544 | reg_buf = hdr; |
545 | for (i = 0; i < hdr_len / MEI_SLOT_SIZE; i++) | |
546 | mei_me_hcbww_write(dev, reg_buf[i]); | |
3ce72726 | 547 | |
98e70866 TW |
548 | reg_buf = data; |
549 | for (i = 0; i < data_len / MEI_SLOT_SIZE; i++) | |
381a58c7 | 550 | mei_me_hcbww_write(dev, reg_buf[i]); |
3ce72726 | 551 | |
98e70866 | 552 | rem = data_len & 0x3; |
169d1338 TW |
553 | if (rem > 0) { |
554 | u32 reg = 0; | |
92db1555 | 555 | |
98e70866 | 556 | memcpy(®, (const u8 *)data + data_len - rem, rem); |
381a58c7 | 557 | mei_me_hcbww_write(dev, reg); |
3ce72726 OW |
558 | } |
559 | ||
9c7daa61 | 560 | mei_hcsr_set_hig(dev); |
827eef51 | 561 | if (!mei_me_hw_is_ready(dev)) |
1ccb7b62 | 562 | return -EIO; |
3ce72726 | 563 | |
1ccb7b62 | 564 | return 0; |
3ce72726 OW |
565 | } |
566 | ||
567 | /** | |
827eef51 | 568 | * mei_me_count_full_read_slots - counts read full slots. |
3ce72726 OW |
569 | * |
570 | * @dev: the device structure | |
571 | * | |
a8605ea2 | 572 | * Return: -EOVERFLOW if overflow, otherwise filled slots count |
3ce72726 | 573 | */ |
827eef51 | 574 | static int mei_me_count_full_read_slots(struct mei_device *dev) |
3ce72726 | 575 | { |
18caeb70 | 576 | u32 me_csr; |
3ce72726 OW |
577 | char read_ptr, write_ptr; |
578 | unsigned char buffer_depth, filled_slots; | |
579 | ||
381a58c7 | 580 | me_csr = mei_me_mecsr_read(dev); |
18caeb70 TW |
581 | buffer_depth = (unsigned char)((me_csr & ME_CBD_HRA) >> 24); |
582 | read_ptr = (char) ((me_csr & ME_CBRP_HRA) >> 8); | |
583 | write_ptr = (char) ((me_csr & ME_CBWP_HRA) >> 16); | |
3ce72726 OW |
584 | filled_slots = (unsigned char) (write_ptr - read_ptr); |
585 | ||
586 | /* check for overflow */ | |
587 | if (filled_slots > buffer_depth) | |
588 | return -EOVERFLOW; | |
589 | ||
2bf94cab | 590 | dev_dbg(dev->dev, "filled_slots =%08x\n", filled_slots); |
3ce72726 OW |
591 | return (int)filled_slots; |
592 | } | |
593 | ||
594 | /** | |
827eef51 | 595 | * mei_me_read_slots - reads a message from mei device. |
3ce72726 OW |
596 | * |
597 | * @dev: the device structure | |
598 | * @buffer: message buffer will be written | |
599 | * @buffer_length: message size will be read | |
ce23139c AU |
600 | * |
601 | * Return: always 0 | |
3ce72726 | 602 | */ |
827eef51 | 603 | static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, |
9fc5f0f8 | 604 | unsigned long buffer_length) |
3ce72726 | 605 | { |
edf1eed4 | 606 | u32 *reg_buf = (u32 *)buffer; |
3ce72726 | 607 | |
9fc5f0f8 | 608 | for (; buffer_length >= MEI_SLOT_SIZE; buffer_length -= MEI_SLOT_SIZE) |
827eef51 | 609 | *reg_buf++ = mei_me_mecbrw_read(dev); |
3ce72726 OW |
610 | |
611 | if (buffer_length > 0) { | |
827eef51 | 612 | u32 reg = mei_me_mecbrw_read(dev); |
92db1555 | 613 | |
edf1eed4 | 614 | memcpy(reg_buf, ®, buffer_length); |
3ce72726 OW |
615 | } |
616 | ||
9c7daa61 | 617 | mei_hcsr_set_hig(dev); |
827eef51 | 618 | return 0; |
3ce72726 OW |
619 | } |
620 | ||
b16c3571 | 621 | /** |
2d1995fc | 622 | * mei_me_pg_set - write pg enter register |
b16c3571 TW |
623 | * |
624 | * @dev: the device structure | |
625 | */ | |
2d1995fc | 626 | static void mei_me_pg_set(struct mei_device *dev) |
b16c3571 TW |
627 | { |
628 | struct mei_me_hw *hw = to_me_hw(dev); | |
a0a927d0 TW |
629 | u32 reg; |
630 | ||
631 | reg = mei_me_reg_read(hw, H_HPG_CSR); | |
632 | trace_mei_reg_read(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); | |
92db1555 | 633 | |
b16c3571 | 634 | reg |= H_HPG_CSR_PGI; |
a0a927d0 TW |
635 | |
636 | trace_mei_reg_write(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); | |
b16c3571 TW |
637 | mei_me_reg_write(hw, H_HPG_CSR, reg); |
638 | } | |
639 | ||
640 | /** | |
2d1995fc | 641 | * mei_me_pg_unset - write pg exit register |
b16c3571 TW |
642 | * |
643 | * @dev: the device structure | |
644 | */ | |
2d1995fc | 645 | static void mei_me_pg_unset(struct mei_device *dev) |
b16c3571 TW |
646 | { |
647 | struct mei_me_hw *hw = to_me_hw(dev); | |
a0a927d0 TW |
648 | u32 reg; |
649 | ||
650 | reg = mei_me_reg_read(hw, H_HPG_CSR); | |
651 | trace_mei_reg_read(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); | |
b16c3571 TW |
652 | |
653 | WARN(!(reg & H_HPG_CSR_PGI), "PGI is not set\n"); | |
654 | ||
655 | reg |= H_HPG_CSR_PGIHEXR; | |
a0a927d0 TW |
656 | |
657 | trace_mei_reg_write(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); | |
b16c3571 TW |
658 | mei_me_reg_write(hw, H_HPG_CSR, reg); |
659 | } | |
660 | ||
ba9cdd0e | 661 | /** |
859ef2ff | 662 | * mei_me_pg_legacy_enter_sync - perform legacy pg entry procedure |
ba9cdd0e TW |
663 | * |
664 | * @dev: the device structure | |
665 | * | |
a8605ea2 | 666 | * Return: 0 on success an error code otherwise |
ba9cdd0e | 667 | */ |
859ef2ff | 668 | static int mei_me_pg_legacy_enter_sync(struct mei_device *dev) |
ba9cdd0e TW |
669 | { |
670 | struct mei_me_hw *hw = to_me_hw(dev); | |
671 | unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); | |
672 | int ret; | |
673 | ||
674 | dev->pg_event = MEI_PG_EVENT_WAIT; | |
675 | ||
676 | ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD); | |
677 | if (ret) | |
678 | return ret; | |
679 | ||
680 | mutex_unlock(&dev->device_lock); | |
681 | wait_event_timeout(dev->wait_pg, | |
682 | dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout); | |
683 | mutex_lock(&dev->device_lock); | |
684 | ||
685 | if (dev->pg_event == MEI_PG_EVENT_RECEIVED) { | |
2d1995fc | 686 | mei_me_pg_set(dev); |
ba9cdd0e TW |
687 | ret = 0; |
688 | } else { | |
689 | ret = -ETIME; | |
690 | } | |
691 | ||
692 | dev->pg_event = MEI_PG_EVENT_IDLE; | |
693 | hw->pg_state = MEI_PG_ON; | |
694 | ||
695 | return ret; | |
696 | } | |
697 | ||
698 | /** | |
859ef2ff | 699 | * mei_me_pg_legacy_exit_sync - perform legacy pg exit procedure |
ba9cdd0e TW |
700 | * |
701 | * @dev: the device structure | |
702 | * | |
a8605ea2 | 703 | * Return: 0 on success an error code otherwise |
ba9cdd0e | 704 | */ |
859ef2ff | 705 | static int mei_me_pg_legacy_exit_sync(struct mei_device *dev) |
ba9cdd0e TW |
706 | { |
707 | struct mei_me_hw *hw = to_me_hw(dev); | |
708 | unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); | |
709 | int ret; | |
710 | ||
711 | if (dev->pg_event == MEI_PG_EVENT_RECEIVED) | |
712 | goto reply; | |
713 | ||
714 | dev->pg_event = MEI_PG_EVENT_WAIT; | |
715 | ||
2d1995fc | 716 | mei_me_pg_unset(dev); |
ba9cdd0e TW |
717 | |
718 | mutex_unlock(&dev->device_lock); | |
719 | wait_event_timeout(dev->wait_pg, | |
720 | dev->pg_event == MEI_PG_EVENT_RECEIVED, timeout); | |
721 | mutex_lock(&dev->device_lock); | |
722 | ||
723 | reply: | |
3dc196ea AU |
724 | if (dev->pg_event != MEI_PG_EVENT_RECEIVED) { |
725 | ret = -ETIME; | |
726 | goto out; | |
727 | } | |
728 | ||
729 | dev->pg_event = MEI_PG_EVENT_INTR_WAIT; | |
730 | ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD); | |
731 | if (ret) | |
732 | return ret; | |
733 | ||
734 | mutex_unlock(&dev->device_lock); | |
735 | wait_event_timeout(dev->wait_pg, | |
736 | dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED, timeout); | |
737 | mutex_lock(&dev->device_lock); | |
738 | ||
739 | if (dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED) | |
740 | ret = 0; | |
ba9cdd0e TW |
741 | else |
742 | ret = -ETIME; | |
743 | ||
3dc196ea | 744 | out: |
ba9cdd0e TW |
745 | dev->pg_event = MEI_PG_EVENT_IDLE; |
746 | hw->pg_state = MEI_PG_OFF; | |
747 | ||
748 | return ret; | |
749 | } | |
750 | ||
3dc196ea AU |
751 | /** |
752 | * mei_me_pg_in_transition - is device now in pg transition | |
753 | * | |
754 | * @dev: the device structure | |
755 | * | |
756 | * Return: true if in pg transition, false otherwise | |
757 | */ | |
758 | static bool mei_me_pg_in_transition(struct mei_device *dev) | |
759 | { | |
760 | return dev->pg_event >= MEI_PG_EVENT_WAIT && | |
761 | dev->pg_event <= MEI_PG_EVENT_INTR_WAIT; | |
762 | } | |
763 | ||
ee7e5afd TW |
764 | /** |
765 | * mei_me_pg_is_enabled - detect if PG is supported by HW | |
766 | * | |
767 | * @dev: the device structure | |
768 | * | |
a8605ea2 | 769 | * Return: true is pg supported, false otherwise |
ee7e5afd TW |
770 | */ |
771 | static bool mei_me_pg_is_enabled(struct mei_device *dev) | |
772 | { | |
859ef2ff | 773 | struct mei_me_hw *hw = to_me_hw(dev); |
381a58c7 | 774 | u32 reg = mei_me_mecsr_read(dev); |
ee7e5afd | 775 | |
859ef2ff AU |
776 | if (hw->d0i3_supported) |
777 | return true; | |
778 | ||
ee7e5afd TW |
779 | if ((reg & ME_PGIC_HRA) == 0) |
780 | goto notsupported; | |
781 | ||
bae1cc7d | 782 | if (!dev->hbm_f_pg_supported) |
ee7e5afd TW |
783 | goto notsupported; |
784 | ||
785 | return true; | |
786 | ||
787 | notsupported: | |
859ef2ff AU |
788 | dev_dbg(dev->dev, "pg: not supported: d0i3 = %d HGP = %d hbm version %d.%d ?= %d.%d\n", |
789 | hw->d0i3_supported, | |
ee7e5afd TW |
790 | !!(reg & ME_PGIC_HRA), |
791 | dev->version.major_version, | |
792 | dev->version.minor_version, | |
793 | HBM_MAJOR_VERSION_PGI, | |
794 | HBM_MINOR_VERSION_PGI); | |
795 | ||
796 | return false; | |
797 | } | |
798 | ||
3dc196ea | 799 | /** |
859ef2ff | 800 | * mei_me_d0i3_set - write d0i3 register bit on mei device. |
3dc196ea AU |
801 | * |
802 | * @dev: the device structure | |
859ef2ff AU |
803 | * @intr: ask for interrupt |
804 | * | |
805 | * Return: D0I3C register value | |
3dc196ea | 806 | */ |
859ef2ff AU |
807 | static u32 mei_me_d0i3_set(struct mei_device *dev, bool intr) |
808 | { | |
809 | u32 reg = mei_me_d0i3c_read(dev); | |
810 | ||
811 | reg |= H_D0I3C_I3; | |
812 | if (intr) | |
813 | reg |= H_D0I3C_IR; | |
814 | else | |
815 | reg &= ~H_D0I3C_IR; | |
816 | mei_me_d0i3c_write(dev, reg); | |
817 | /* read it to ensure HW consistency */ | |
818 | reg = mei_me_d0i3c_read(dev); | |
819 | return reg; | |
820 | } | |
821 | ||
822 | /** | |
823 | * mei_me_d0i3_unset - clean d0i3 register bit on mei device. | |
824 | * | |
825 | * @dev: the device structure | |
826 | * | |
827 | * Return: D0I3C register value | |
828 | */ | |
829 | static u32 mei_me_d0i3_unset(struct mei_device *dev) | |
830 | { | |
831 | u32 reg = mei_me_d0i3c_read(dev); | |
832 | ||
833 | reg &= ~H_D0I3C_I3; | |
834 | reg |= H_D0I3C_IR; | |
835 | mei_me_d0i3c_write(dev, reg); | |
836 | /* read it to ensure HW consistency */ | |
837 | reg = mei_me_d0i3c_read(dev); | |
838 | return reg; | |
839 | } | |
840 | ||
841 | /** | |
842 | * mei_me_d0i3_enter_sync - perform d0i3 entry procedure | |
843 | * | |
844 | * @dev: the device structure | |
845 | * | |
846 | * Return: 0 on success an error code otherwise | |
847 | */ | |
848 | static int mei_me_d0i3_enter_sync(struct mei_device *dev) | |
849 | { | |
850 | struct mei_me_hw *hw = to_me_hw(dev); | |
851 | unsigned long d0i3_timeout = mei_secs_to_jiffies(MEI_D0I3_TIMEOUT); | |
852 | unsigned long pgi_timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); | |
853 | int ret; | |
854 | u32 reg; | |
855 | ||
856 | reg = mei_me_d0i3c_read(dev); | |
857 | if (reg & H_D0I3C_I3) { | |
858 | /* we are in d0i3, nothing to do */ | |
859 | dev_dbg(dev->dev, "d0i3 set not needed\n"); | |
860 | ret = 0; | |
861 | goto on; | |
862 | } | |
863 | ||
864 | /* PGI entry procedure */ | |
865 | dev->pg_event = MEI_PG_EVENT_WAIT; | |
866 | ||
867 | ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD); | |
868 | if (ret) | |
869 | /* FIXME: should we reset here? */ | |
870 | goto out; | |
871 | ||
872 | mutex_unlock(&dev->device_lock); | |
873 | wait_event_timeout(dev->wait_pg, | |
874 | dev->pg_event == MEI_PG_EVENT_RECEIVED, pgi_timeout); | |
875 | mutex_lock(&dev->device_lock); | |
876 | ||
877 | if (dev->pg_event != MEI_PG_EVENT_RECEIVED) { | |
878 | ret = -ETIME; | |
879 | goto out; | |
880 | } | |
881 | /* end PGI entry procedure */ | |
882 | ||
883 | dev->pg_event = MEI_PG_EVENT_INTR_WAIT; | |
884 | ||
885 | reg = mei_me_d0i3_set(dev, true); | |
886 | if (!(reg & H_D0I3C_CIP)) { | |
887 | dev_dbg(dev->dev, "d0i3 enter wait not needed\n"); | |
888 | ret = 0; | |
889 | goto on; | |
890 | } | |
891 | ||
892 | mutex_unlock(&dev->device_lock); | |
893 | wait_event_timeout(dev->wait_pg, | |
894 | dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED, d0i3_timeout); | |
895 | mutex_lock(&dev->device_lock); | |
896 | ||
897 | if (dev->pg_event != MEI_PG_EVENT_INTR_RECEIVED) { | |
898 | reg = mei_me_d0i3c_read(dev); | |
899 | if (!(reg & H_D0I3C_I3)) { | |
900 | ret = -ETIME; | |
901 | goto out; | |
902 | } | |
903 | } | |
904 | ||
905 | ret = 0; | |
906 | on: | |
907 | hw->pg_state = MEI_PG_ON; | |
908 | out: | |
909 | dev->pg_event = MEI_PG_EVENT_IDLE; | |
910 | dev_dbg(dev->dev, "d0i3 enter ret = %d\n", ret); | |
911 | return ret; | |
912 | } | |
913 | ||
914 | /** | |
915 | * mei_me_d0i3_enter - perform d0i3 entry procedure | |
916 | * no hbm PG handshake | |
917 | * no waiting for confirmation; runs with interrupts | |
918 | * disabled | |
919 | * | |
920 | * @dev: the device structure | |
921 | * | |
922 | * Return: 0 on success an error code otherwise | |
923 | */ | |
924 | static int mei_me_d0i3_enter(struct mei_device *dev) | |
925 | { | |
926 | struct mei_me_hw *hw = to_me_hw(dev); | |
927 | u32 reg; | |
928 | ||
929 | reg = mei_me_d0i3c_read(dev); | |
930 | if (reg & H_D0I3C_I3) { | |
931 | /* we are in d0i3, nothing to do */ | |
932 | dev_dbg(dev->dev, "already d0i3 : set not needed\n"); | |
933 | goto on; | |
934 | } | |
935 | ||
936 | mei_me_d0i3_set(dev, false); | |
937 | on: | |
938 | hw->pg_state = MEI_PG_ON; | |
939 | dev->pg_event = MEI_PG_EVENT_IDLE; | |
940 | dev_dbg(dev->dev, "d0i3 enter\n"); | |
941 | return 0; | |
942 | } | |
943 | ||
944 | /** | |
945 | * mei_me_d0i3_exit_sync - perform d0i3 exit procedure | |
946 | * | |
947 | * @dev: the device structure | |
948 | * | |
949 | * Return: 0 on success an error code otherwise | |
950 | */ | |
951 | static int mei_me_d0i3_exit_sync(struct mei_device *dev) | |
952 | { | |
953 | struct mei_me_hw *hw = to_me_hw(dev); | |
954 | unsigned long timeout = mei_secs_to_jiffies(MEI_D0I3_TIMEOUT); | |
955 | int ret; | |
956 | u32 reg; | |
957 | ||
958 | dev->pg_event = MEI_PG_EVENT_INTR_WAIT; | |
959 | ||
960 | reg = mei_me_d0i3c_read(dev); | |
961 | if (!(reg & H_D0I3C_I3)) { | |
962 | /* we are not in d0i3, nothing to do */ | |
963 | dev_dbg(dev->dev, "d0i3 exit not needed\n"); | |
964 | ret = 0; | |
965 | goto off; | |
966 | } | |
967 | ||
968 | reg = mei_me_d0i3_unset(dev); | |
969 | if (!(reg & H_D0I3C_CIP)) { | |
970 | dev_dbg(dev->dev, "d0i3 exit wait not needed\n"); | |
971 | ret = 0; | |
972 | goto off; | |
973 | } | |
974 | ||
975 | mutex_unlock(&dev->device_lock); | |
976 | wait_event_timeout(dev->wait_pg, | |
977 | dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED, timeout); | |
978 | mutex_lock(&dev->device_lock); | |
979 | ||
980 | if (dev->pg_event != MEI_PG_EVENT_INTR_RECEIVED) { | |
981 | reg = mei_me_d0i3c_read(dev); | |
982 | if (reg & H_D0I3C_I3) { | |
983 | ret = -ETIME; | |
984 | goto out; | |
985 | } | |
986 | } | |
987 | ||
988 | ret = 0; | |
989 | off: | |
990 | hw->pg_state = MEI_PG_OFF; | |
991 | out: | |
992 | dev->pg_event = MEI_PG_EVENT_IDLE; | |
993 | ||
994 | dev_dbg(dev->dev, "d0i3 exit ret = %d\n", ret); | |
995 | return ret; | |
996 | } | |
997 | ||
998 | /** | |
999 | * mei_me_pg_legacy_intr - perform legacy pg processing | |
1000 | * in interrupt thread handler | |
1001 | * | |
1002 | * @dev: the device structure | |
1003 | */ | |
1004 | static void mei_me_pg_legacy_intr(struct mei_device *dev) | |
3dc196ea AU |
1005 | { |
1006 | struct mei_me_hw *hw = to_me_hw(dev); | |
1007 | ||
1008 | if (dev->pg_event != MEI_PG_EVENT_INTR_WAIT) | |
1009 | return; | |
1010 | ||
1011 | dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; | |
1012 | hw->pg_state = MEI_PG_OFF; | |
1013 | if (waitqueue_active(&dev->wait_pg)) | |
1014 | wake_up(&dev->wait_pg); | |
1015 | } | |
1016 | ||
859ef2ff AU |
1017 | /** |
1018 | * mei_me_d0i3_intr - perform d0i3 processing in interrupt thread handler | |
1019 | * | |
1020 | * @dev: the device structure | |
a2eb0fc0 | 1021 | * @intr_source: interrupt source |
859ef2ff | 1022 | */ |
a2eb0fc0 | 1023 | static void mei_me_d0i3_intr(struct mei_device *dev, u32 intr_source) |
859ef2ff AU |
1024 | { |
1025 | struct mei_me_hw *hw = to_me_hw(dev); | |
1026 | ||
1027 | if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT && | |
a2eb0fc0 | 1028 | (intr_source & H_D0I3C_IS)) { |
859ef2ff AU |
1029 | dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; |
1030 | if (hw->pg_state == MEI_PG_ON) { | |
1031 | hw->pg_state = MEI_PG_OFF; | |
1032 | if (dev->hbm_state != MEI_HBM_IDLE) { | |
1033 | /* | |
1034 | * force H_RDY because it could be | |
1035 | * wiped off during PG | |
1036 | */ | |
1037 | dev_dbg(dev->dev, "d0i3 set host ready\n"); | |
1038 | mei_me_host_set_ready(dev); | |
1039 | } | |
1040 | } else { | |
1041 | hw->pg_state = MEI_PG_ON; | |
1042 | } | |
1043 | ||
1044 | wake_up(&dev->wait_pg); | |
1045 | } | |
1046 | ||
a2eb0fc0 | 1047 | if (hw->pg_state == MEI_PG_ON && (intr_source & H_IS)) { |
859ef2ff AU |
1048 | /* |
1049 | * HW sent some data and we are in D0i3, so | |
1050 | * we got here because of HW initiated exit from D0i3. | |
1051 | * Start runtime pm resume sequence to exit low power state. | |
1052 | */ | |
1053 | dev_dbg(dev->dev, "d0i3 want resume\n"); | |
1054 | mei_hbm_pg_resume(dev); | |
1055 | } | |
1056 | } | |
1057 | ||
1058 | /** | |
1059 | * mei_me_pg_intr - perform pg processing in interrupt thread handler | |
1060 | * | |
1061 | * @dev: the device structure | |
a2eb0fc0 | 1062 | * @intr_source: interrupt source |
859ef2ff | 1063 | */ |
a2eb0fc0 | 1064 | static void mei_me_pg_intr(struct mei_device *dev, u32 intr_source) |
859ef2ff AU |
1065 | { |
1066 | struct mei_me_hw *hw = to_me_hw(dev); | |
1067 | ||
1068 | if (hw->d0i3_supported) | |
a2eb0fc0 | 1069 | mei_me_d0i3_intr(dev, intr_source); |
859ef2ff AU |
1070 | else |
1071 | mei_me_pg_legacy_intr(dev); | |
1072 | } | |
1073 | ||
1074 | /** | |
1075 | * mei_me_pg_enter_sync - perform runtime pm entry procedure | |
1076 | * | |
1077 | * @dev: the device structure | |
1078 | * | |
1079 | * Return: 0 on success an error code otherwise | |
1080 | */ | |
1081 | int mei_me_pg_enter_sync(struct mei_device *dev) | |
1082 | { | |
1083 | struct mei_me_hw *hw = to_me_hw(dev); | |
1084 | ||
1085 | if (hw->d0i3_supported) | |
1086 | return mei_me_d0i3_enter_sync(dev); | |
1087 | else | |
1088 | return mei_me_pg_legacy_enter_sync(dev); | |
1089 | } | |
1090 | ||
1091 | /** | |
1092 | * mei_me_pg_exit_sync - perform runtime pm exit procedure | |
1093 | * | |
1094 | * @dev: the device structure | |
1095 | * | |
1096 | * Return: 0 on success an error code otherwise | |
1097 | */ | |
1098 | int mei_me_pg_exit_sync(struct mei_device *dev) | |
1099 | { | |
1100 | struct mei_me_hw *hw = to_me_hw(dev); | |
1101 | ||
1102 | if (hw->d0i3_supported) | |
1103 | return mei_me_d0i3_exit_sync(dev); | |
1104 | else | |
1105 | return mei_me_pg_legacy_exit_sync(dev); | |
1106 | } | |
1107 | ||
ebad6b94 AU |
1108 | /** |
1109 | * mei_me_hw_reset - resets fw via mei csr register. | |
1110 | * | |
1111 | * @dev: the device structure | |
1112 | * @intr_enable: if interrupt should be enabled after reset. | |
1113 | * | |
b9a1fc99 | 1114 | * Return: 0 on success an error code otherwise |
ebad6b94 AU |
1115 | */ |
1116 | static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) | |
1117 | { | |
b9a1fc99 AU |
1118 | struct mei_me_hw *hw = to_me_hw(dev); |
1119 | int ret; | |
1120 | u32 hcsr; | |
1121 | ||
1122 | if (intr_enable) { | |
1123 | mei_me_intr_enable(dev); | |
1124 | if (hw->d0i3_supported) { | |
1125 | ret = mei_me_d0i3_exit_sync(dev); | |
1126 | if (ret) | |
1127 | return ret; | |
1128 | } | |
1129 | } | |
ebad6b94 | 1130 | |
77537ad2 AU |
1131 | pm_runtime_set_active(dev->dev); |
1132 | ||
b9a1fc99 | 1133 | hcsr = mei_hcsr_read(dev); |
ebad6b94 AU |
1134 | /* H_RST may be found lit before reset is started, |
1135 | * for example if preceding reset flow hasn't completed. | |
1136 | * In that case asserting H_RST will be ignored, therefore | |
1137 | * we need to clean H_RST bit to start a successful reset sequence. | |
1138 | */ | |
1139 | if ((hcsr & H_RST) == H_RST) { | |
1140 | dev_warn(dev->dev, "H_RST is set = 0x%08X", hcsr); | |
1141 | hcsr &= ~H_RST; | |
1142 | mei_hcsr_set(dev, hcsr); | |
1143 | hcsr = mei_hcsr_read(dev); | |
1144 | } | |
1145 | ||
1146 | hcsr |= H_RST | H_IG | H_CSR_IS_MASK; | |
1147 | ||
b9a1fc99 | 1148 | if (!intr_enable) |
ebad6b94 AU |
1149 | hcsr &= ~H_CSR_IE_MASK; |
1150 | ||
1151 | dev->recvd_hw_ready = false; | |
1152 | mei_hcsr_write(dev, hcsr); | |
1153 | ||
1154 | /* | |
1155 | * Host reads the H_CSR once to ensure that the | |
1156 | * posted write to H_CSR completes. | |
1157 | */ | |
1158 | hcsr = mei_hcsr_read(dev); | |
1159 | ||
1160 | if ((hcsr & H_RST) == 0) | |
1161 | dev_warn(dev->dev, "H_RST is not set = 0x%08X", hcsr); | |
1162 | ||
1163 | if ((hcsr & H_RDY) == H_RDY) | |
1164 | dev_warn(dev->dev, "H_RDY is not cleared 0x%08X", hcsr); | |
1165 | ||
b9a1fc99 | 1166 | if (!intr_enable) { |
ebad6b94 | 1167 | mei_me_hw_reset_release(dev); |
b9a1fc99 AU |
1168 | if (hw->d0i3_supported) { |
1169 | ret = mei_me_d0i3_enter(dev); | |
1170 | if (ret) | |
1171 | return ret; | |
1172 | } | |
1173 | } | |
ebad6b94 AU |
1174 | return 0; |
1175 | } | |
1176 | ||
06ecd645 TW |
1177 | /** |
1178 | * mei_me_irq_quick_handler - The ISR of the MEI device | |
1179 | * | |
1180 | * @irq: The irq number | |
1181 | * @dev_id: pointer to the device structure | |
1182 | * | |
a8605ea2 | 1183 | * Return: irqreturn_t |
06ecd645 | 1184 | */ |
06ecd645 TW |
1185 | irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) |
1186 | { | |
1fa55b4e | 1187 | struct mei_device *dev = (struct mei_device *)dev_id; |
1fa55b4e | 1188 | u32 hcsr; |
06ecd645 | 1189 | |
1fa55b4e | 1190 | hcsr = mei_hcsr_read(dev); |
a2eb0fc0 | 1191 | if (!me_intr_src(hcsr)) |
06ecd645 TW |
1192 | return IRQ_NONE; |
1193 | ||
a2eb0fc0 | 1194 | dev_dbg(dev->dev, "interrupt source 0x%08X\n", me_intr_src(hcsr)); |
06ecd645 | 1195 | |
a2eb0fc0 AU |
1196 | /* disable interrupts on device */ |
1197 | me_intr_disable(dev, hcsr); | |
06ecd645 TW |
1198 | return IRQ_WAKE_THREAD; |
1199 | } | |
1200 | ||
1201 | /** | |
1202 | * mei_me_irq_thread_handler - function called after ISR to handle the interrupt | |
1203 | * processing. | |
1204 | * | |
1205 | * @irq: The irq number | |
1206 | * @dev_id: pointer to the device structure | |
1207 | * | |
a8605ea2 | 1208 | * Return: irqreturn_t |
06ecd645 TW |
1209 | * |
1210 | */ | |
1211 | irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) | |
1212 | { | |
1213 | struct mei_device *dev = (struct mei_device *) dev_id; | |
962ff7bc | 1214 | struct list_head cmpl_list; |
06ecd645 | 1215 | s32 slots; |
a2eb0fc0 | 1216 | u32 hcsr; |
544f9460 | 1217 | int rets = 0; |
06ecd645 | 1218 | |
2bf94cab | 1219 | dev_dbg(dev->dev, "function called after ISR to handle the interrupt processing.\n"); |
06ecd645 TW |
1220 | /* initialize our complete list */ |
1221 | mutex_lock(&dev->device_lock); | |
a2eb0fc0 AU |
1222 | |
1223 | hcsr = mei_hcsr_read(dev); | |
1224 | me_intr_clear(dev, hcsr); | |
1225 | ||
962ff7bc | 1226 | INIT_LIST_HEAD(&cmpl_list); |
06ecd645 | 1227 | |
06ecd645 | 1228 | /* check if ME wants a reset */ |
33ec0826 | 1229 | if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { |
2bf94cab | 1230 | dev_warn(dev->dev, "FW not ready: resetting.\n"); |
544f9460 TW |
1231 | schedule_work(&dev->reset_work); |
1232 | goto end; | |
06ecd645 TW |
1233 | } |
1234 | ||
47f60a01 AU |
1235 | if (mei_me_hw_is_resetting(dev)) |
1236 | mei_hcsr_set_hig(dev); | |
1237 | ||
a2eb0fc0 | 1238 | mei_me_pg_intr(dev, me_intr_src(hcsr)); |
3dc196ea | 1239 | |
06ecd645 TW |
1240 | /* check if we need to start the dev */ |
1241 | if (!mei_host_is_ready(dev)) { | |
1242 | if (mei_hw_is_ready(dev)) { | |
2bf94cab | 1243 | dev_dbg(dev->dev, "we need to start the dev.\n"); |
aafae7ec | 1244 | dev->recvd_hw_ready = true; |
2c2b93ec | 1245 | wake_up(&dev->wait_hw_ready); |
06ecd645 | 1246 | } else { |
2bf94cab | 1247 | dev_dbg(dev->dev, "Spurious Interrupt\n"); |
06ecd645 | 1248 | } |
544f9460 | 1249 | goto end; |
06ecd645 TW |
1250 | } |
1251 | /* check slots available for reading */ | |
1252 | slots = mei_count_full_read_slots(dev); | |
1253 | while (slots > 0) { | |
2bf94cab | 1254 | dev_dbg(dev->dev, "slots to read = %08x\n", slots); |
962ff7bc | 1255 | rets = mei_irq_read_handler(dev, &cmpl_list, &slots); |
b1b94b5d TW |
1256 | /* There is a race between ME write and interrupt delivery: |
1257 | * Not all data is always available immediately after the | |
1258 | * interrupt, so try to read again on the next interrupt. | |
1259 | */ | |
1260 | if (rets == -ENODATA) | |
1261 | break; | |
1262 | ||
8d52af67 | 1263 | if (rets && |
912ed8af | 1264 | (dev->dev_state != MEI_DEV_RESETTING && |
8d52af67 | 1265 | dev->dev_state != MEI_DEV_POWER_DOWN)) { |
2bf94cab | 1266 | dev_err(dev->dev, "mei_irq_read_handler ret = %d.\n", |
b1b94b5d | 1267 | rets); |
544f9460 | 1268 | schedule_work(&dev->reset_work); |
06ecd645 | 1269 | goto end; |
544f9460 | 1270 | } |
06ecd645 | 1271 | } |
544f9460 | 1272 | |
6aae48ff TW |
1273 | dev->hbuf_is_ready = mei_hbuf_is_ready(dev); |
1274 | ||
ba9cdd0e TW |
1275 | /* |
1276 | * During PG handshake only allowed write is the replay to the | |
1277 | * PG exit message, so block calling write function | |
3dc196ea | 1278 | * if the pg event is in PG handshake |
ba9cdd0e | 1279 | */ |
3dc196ea AU |
1280 | if (dev->pg_event != MEI_PG_EVENT_WAIT && |
1281 | dev->pg_event != MEI_PG_EVENT_RECEIVED) { | |
962ff7bc | 1282 | rets = mei_irq_write_handler(dev, &cmpl_list); |
ba9cdd0e TW |
1283 | dev->hbuf_is_ready = mei_hbuf_is_ready(dev); |
1284 | } | |
06ecd645 | 1285 | |
962ff7bc | 1286 | mei_irq_compl_handler(dev, &cmpl_list); |
06ecd645 | 1287 | |
544f9460 | 1288 | end: |
2bf94cab | 1289 | dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets); |
a2eb0fc0 | 1290 | mei_me_intr_enable(dev); |
544f9460 | 1291 | mutex_unlock(&dev->device_lock); |
06ecd645 TW |
1292 | return IRQ_HANDLED; |
1293 | } | |
04dd3661 | 1294 | |
827eef51 TW |
1295 | static const struct mei_hw_ops mei_me_hw_ops = { |
1296 | ||
1bd30b6a | 1297 | .fw_status = mei_me_fw_status, |
964a2331 TW |
1298 | .pg_state = mei_me_pg_state, |
1299 | ||
827eef51 TW |
1300 | .host_is_ready = mei_me_host_is_ready, |
1301 | ||
1302 | .hw_is_ready = mei_me_hw_is_ready, | |
1303 | .hw_reset = mei_me_hw_reset, | |
aafae7ec TW |
1304 | .hw_config = mei_me_hw_config, |
1305 | .hw_start = mei_me_hw_start, | |
827eef51 | 1306 | |
3dc196ea | 1307 | .pg_in_transition = mei_me_pg_in_transition, |
ee7e5afd TW |
1308 | .pg_is_enabled = mei_me_pg_is_enabled, |
1309 | ||
827eef51 TW |
1310 | .intr_clear = mei_me_intr_clear, |
1311 | .intr_enable = mei_me_intr_enable, | |
1312 | .intr_disable = mei_me_intr_disable, | |
4a8efd4a | 1313 | .synchronize_irq = mei_me_synchronize_irq, |
827eef51 TW |
1314 | |
1315 | .hbuf_free_slots = mei_me_hbuf_empty_slots, | |
1316 | .hbuf_is_ready = mei_me_hbuf_is_empty, | |
8c8d964c | 1317 | .hbuf_depth = mei_me_hbuf_depth, |
827eef51 | 1318 | |
4b9960d0 | 1319 | .write = mei_me_hbuf_write, |
827eef51 TW |
1320 | |
1321 | .rdbuf_full_slots = mei_me_count_full_read_slots, | |
1322 | .read_hdr = mei_me_mecbrw_read, | |
1323 | .read = mei_me_read_slots | |
1324 | }; | |
1325 | ||
c919951d TW |
1326 | static bool mei_me_fw_type_nm(struct pci_dev *pdev) |
1327 | { | |
1328 | u32 reg; | |
92db1555 | 1329 | |
c919951d | 1330 | pci_read_config_dword(pdev, PCI_CFG_HFS_2, ®); |
a96c5482 | 1331 | trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg); |
c919951d TW |
1332 | /* make sure that bit 9 (NM) is up and bit 10 (DM) is down */ |
1333 | return (reg & 0x600) == 0x200; | |
1334 | } | |
1335 | ||
1336 | #define MEI_CFG_FW_NM \ | |
1337 | .quirk_probe = mei_me_fw_type_nm | |
1338 | ||
1339 | static bool mei_me_fw_type_sps(struct pci_dev *pdev) | |
1340 | { | |
1341 | u32 reg; | |
8c57cac1 TW |
1342 | unsigned int devfn; |
1343 | ||
1344 | /* | |
1345 | * Read ME FW Status register to check for SPS Firmware | |
1346 | * The SPS FW is only signaled in pci function 0 | |
1347 | */ | |
1348 | devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); | |
1349 | pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_1, ®); | |
a96c5482 | 1350 | trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg); |
c919951d TW |
1351 | /* if bits [19:16] = 15, running SPS Firmware */ |
1352 | return (reg & 0xf0000) == 0xf0000; | |
1353 | } | |
1354 | ||
1355 | #define MEI_CFG_FW_SPS \ | |
1356 | .quirk_probe = mei_me_fw_type_sps | |
1357 | ||
f8204f0d AU |
1358 | #define MEI_CFG_FW_VER_SUPP \ |
1359 | .fw_ver_supported = 1 | |
c919951d | 1360 | |
f5ac3c49 | 1361 | #define MEI_CFG_ICH_HFS \ |
8d929d48 AU |
1362 | .fw_status.count = 0 |
1363 | ||
f5ac3c49 | 1364 | #define MEI_CFG_ICH10_HFS \ |
8d929d48 AU |
1365 | .fw_status.count = 1, \ |
1366 | .fw_status.status[0] = PCI_CFG_HFS_1 | |
1367 | ||
1368 | #define MEI_CFG_PCH_HFS \ | |
1369 | .fw_status.count = 2, \ | |
1370 | .fw_status.status[0] = PCI_CFG_HFS_1, \ | |
1371 | .fw_status.status[1] = PCI_CFG_HFS_2 | |
1372 | ||
edca5ea3 AU |
1373 | #define MEI_CFG_PCH8_HFS \ |
1374 | .fw_status.count = 6, \ | |
1375 | .fw_status.status[0] = PCI_CFG_HFS_1, \ | |
1376 | .fw_status.status[1] = PCI_CFG_HFS_2, \ | |
1377 | .fw_status.status[2] = PCI_CFG_HFS_3, \ | |
1378 | .fw_status.status[3] = PCI_CFG_HFS_4, \ | |
1379 | .fw_status.status[4] = PCI_CFG_HFS_5, \ | |
1380 | .fw_status.status[5] = PCI_CFG_HFS_6 | |
8d929d48 | 1381 | |
7026a5fd AU |
1382 | #define MEI_CFG_DMA_128 \ |
1383 | .dma_size[DMA_DSCR_HOST] = SZ_128K, \ | |
1384 | .dma_size[DMA_DSCR_DEVICE] = SZ_128K, \ | |
1385 | .dma_size[DMA_DSCR_CTRL] = PAGE_SIZE | |
1386 | ||
8d929d48 | 1387 | /* ICH Legacy devices */ |
f5ac3c49 TW |
1388 | static const struct mei_cfg mei_me_ich_cfg = { |
1389 | MEI_CFG_ICH_HFS, | |
8d929d48 AU |
1390 | }; |
1391 | ||
1392 | /* ICH devices */ | |
f5ac3c49 TW |
1393 | static const struct mei_cfg mei_me_ich10_cfg = { |
1394 | MEI_CFG_ICH10_HFS, | |
8d929d48 AU |
1395 | }; |
1396 | ||
f8204f0d AU |
1397 | /* PCH6 devices */ |
1398 | static const struct mei_cfg mei_me_pch6_cfg = { | |
8d929d48 AU |
1399 | MEI_CFG_PCH_HFS, |
1400 | }; | |
1401 | ||
f8204f0d AU |
1402 | /* PCH7 devices */ |
1403 | static const struct mei_cfg mei_me_pch7_cfg = { | |
1404 | MEI_CFG_PCH_HFS, | |
1405 | MEI_CFG_FW_VER_SUPP, | |
1406 | }; | |
1407 | ||
c919951d | 1408 | /* PCH Cougar Point and Patsburg with quirk for Node Manager exclusion */ |
f5ac3c49 | 1409 | static const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { |
c919951d | 1410 | MEI_CFG_PCH_HFS, |
f8204f0d | 1411 | MEI_CFG_FW_VER_SUPP, |
c919951d TW |
1412 | MEI_CFG_FW_NM, |
1413 | }; | |
1414 | ||
edca5ea3 | 1415 | /* PCH8 Lynx Point and newer devices */ |
f5ac3c49 | 1416 | static const struct mei_cfg mei_me_pch8_cfg = { |
edca5ea3 | 1417 | MEI_CFG_PCH8_HFS, |
f8204f0d | 1418 | MEI_CFG_FW_VER_SUPP, |
edca5ea3 AU |
1419 | }; |
1420 | ||
1421 | /* PCH8 Lynx Point with quirk for SPS Firmware exclusion */ | |
f5ac3c49 | 1422 | static const struct mei_cfg mei_me_pch8_sps_cfg = { |
edca5ea3 | 1423 | MEI_CFG_PCH8_HFS, |
f8204f0d | 1424 | MEI_CFG_FW_VER_SUPP, |
c919951d TW |
1425 | MEI_CFG_FW_SPS, |
1426 | }; | |
1427 | ||
7026a5fd AU |
1428 | /* Cannon Lake and newer devices */ |
1429 | static const struct mei_cfg mei_me_pch12_cfg = { | |
1430 | MEI_CFG_PCH8_HFS, | |
f8204f0d | 1431 | MEI_CFG_FW_VER_SUPP, |
7026a5fd AU |
1432 | MEI_CFG_DMA_128, |
1433 | }; | |
1434 | ||
f5ac3c49 TW |
1435 | /* |
1436 | * mei_cfg_list - A list of platform platform specific configurations. | |
1437 | * Note: has to be synchronized with enum mei_cfg_idx. | |
1438 | */ | |
1439 | static const struct mei_cfg *const mei_cfg_list[] = { | |
1440 | [MEI_ME_UNDEF_CFG] = NULL, | |
1441 | [MEI_ME_ICH_CFG] = &mei_me_ich_cfg, | |
1442 | [MEI_ME_ICH10_CFG] = &mei_me_ich10_cfg, | |
f8204f0d AU |
1443 | [MEI_ME_PCH6_CFG] = &mei_me_pch6_cfg, |
1444 | [MEI_ME_PCH7_CFG] = &mei_me_pch7_cfg, | |
f5ac3c49 TW |
1445 | [MEI_ME_PCH_CPT_PBG_CFG] = &mei_me_pch_cpt_pbg_cfg, |
1446 | [MEI_ME_PCH8_CFG] = &mei_me_pch8_cfg, | |
1447 | [MEI_ME_PCH8_SPS_CFG] = &mei_me_pch8_sps_cfg, | |
7026a5fd | 1448 | [MEI_ME_PCH12_CFG] = &mei_me_pch12_cfg, |
f5ac3c49 TW |
1449 | }; |
1450 | ||
1451 | const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx) | |
1452 | { | |
1453 | BUILD_BUG_ON(ARRAY_SIZE(mei_cfg_list) != MEI_ME_NUM_CFG); | |
1454 | ||
1455 | if (idx >= MEI_ME_NUM_CFG) | |
1456 | return NULL; | |
1457 | ||
1458 | return mei_cfg_list[idx]; | |
1459 | }; | |
1460 | ||
52c34561 | 1461 | /** |
393b148f | 1462 | * mei_me_dev_init - allocates and initializes the mei device structure |
52c34561 | 1463 | * |
907b471c | 1464 | * @parent: device associated with physical device (pci/platform) |
8d929d48 | 1465 | * @cfg: per device generation config |
52c34561 | 1466 | * |
f8a09605 | 1467 | * Return: The mei_device pointer on success, NULL on failure. |
52c34561 | 1468 | */ |
907b471c | 1469 | struct mei_device *mei_me_dev_init(struct device *parent, |
8d929d48 | 1470 | const struct mei_cfg *cfg) |
52c34561 TW |
1471 | { |
1472 | struct mei_device *dev; | |
4ad96db6 | 1473 | struct mei_me_hw *hw; |
ce0925e8 | 1474 | int i; |
52c34561 | 1475 | |
907b471c | 1476 | dev = devm_kzalloc(parent, sizeof(struct mei_device) + |
f8a09605 | 1477 | sizeof(struct mei_me_hw), GFP_KERNEL); |
52c34561 TW |
1478 | if (!dev) |
1479 | return NULL; | |
ce0925e8 | 1480 | |
4ad96db6 | 1481 | hw = to_me_hw(dev); |
52c34561 | 1482 | |
ce0925e8 TW |
1483 | for (i = 0; i < DMA_DSCR_NUM; i++) |
1484 | dev->dr_dscr[i].size = cfg->dma_size[i]; | |
1485 | ||
907b471c | 1486 | mei_device_init(dev, parent, &mei_me_hw_ops); |
4ad96db6 | 1487 | hw->cfg = cfg; |
ce0925e8 | 1488 | |
f8204f0d AU |
1489 | dev->fw_f_fw_ver_supported = cfg->fw_ver_supported; |
1490 | ||
52c34561 TW |
1491 | return dev; |
1492 | } | |
06ecd645 | 1493 |