1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Compaq Hot Plug Controller Driver |
4 | * |
5 | * Copyright (C) 1995,2001 Compaq Computer Corporation |
6 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) |
7 | * Copyright (C) 2001 IBM Corp. |
8 | * |
9 | * All rights reserved. |
10 | * |
11 | * Send feedback to <greg@kroah.com> |
12 | * |
13 | */ |
14 | |
15 | #include <linux/module.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/types.h> |
18 | #include <linux/proc_fs.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/workqueue.h> |
21 | #include <linux/pci.h> |
22 | #include <linux/pci_hotplug.h> |
23 | #include <linux/uaccess.h> |
24 | #include "cpqphp.h" |
25 | #include "cpqphp_nvram.h" |
26 | |
27 | |
28 | #define ROM_INT15_PHY_ADDR 0x0FF859 |
29 | #define READ_EV 0xD8A4 |
30 | #define WRITE_EV 0xD8A5 |
31 | |
32 | struct register_foo { |
33 | union { |
34 | unsigned long lword; /* eax */ |
35 | unsigned short word; /* ax */ |
36 | |
37 | struct { |
38 | unsigned char low; /* al */ |
39 | unsigned char high; /* ah */ |
40 | } byte; |
41 | } data; |
42 | |
43 | unsigned char opcode; /* see below */ |
44 | unsigned long length; /* if the reg. is a pointer, how much data */ |
45 | } __attribute__ ((packed)); |
46 | |
47 | struct all_reg { |
48 | struct register_foo eax_reg; |
49 | struct register_foo ebx_reg; |
50 | struct register_foo ecx_reg; |
51 | struct register_foo edx_reg; |
52 | struct register_foo edi_reg; |
53 | struct register_foo esi_reg; |
54 | struct register_foo eflags_reg; |
55 | } __attribute__ ((packed)); |
56 | |
57 | |
58 | struct { |
59 | u8 ; |
60 | u8 ; |
61 | u8 ; |
62 | }; |
63 | |
64 | struct ev_hrt_ctrl { |
65 | u8 bus; |
66 | u8 device; |
67 | u8 function; |
68 | u8 mem_avail; |
69 | u8 p_mem_avail; |
70 | u8 io_avail; |
71 | u8 bus_avail; |
72 | u8 next; |
73 | }; |
74 | |
75 | |
76 | static u8 evbuffer_init; |
77 | static u8 evbuffer_length; |
78 | static u8 evbuffer[1024]; |
79 | |
80 | static void __iomem *compaq_int15_entry_point; |
81 | |
82 | /* lock for ordering int15_bios_call() */ |
83 | static DEFINE_SPINLOCK(int15_lock); |
84 | |
85 | |
86 | /* This is a series of function that deals with |
87 | * setting & getting the hotplug resource table in some environment variable. |
88 | */ |
89 | |
90 | /* |
91 | * We really shouldn't be doing this unless there is a _very_ good reason to!!! |
92 | * greg k-h |
93 | */ |
94 | |
95 | |
96 | static u32 add_byte(u32 **p_buffer, u8 value, u32 *used, u32 *avail) |
97 | { |
98 | u8 **tByte; |
99 | |
100 | if ((*used + 1) > *avail) |
101 | return(1); |
102 | |
103 | *((u8 *)*p_buffer) = value; |
104 | tByte = (u8 **)p_buffer; |
105 | (*tByte)++; |
106 | *used += 1; |
107 | return(0); |
108 | } |
109 | |
110 | |
111 | static u32 add_dword(u32 **p_buffer, u32 value, u32 *used, u32 *avail) |
112 | { |
113 | if ((*used + 4) > *avail) |
114 | return(1); |
115 | |
116 | **p_buffer = value; |
117 | (*p_buffer)++; |
118 | *used += 4; |
119 | return(0); |
120 | } |
121 | |
122 | |
123 | /* |
124 | * check_for_compaq_ROM |
125 | * |
126 | * this routine verifies that the ROM OEM string is 'COMPAQ' |
127 | * |
128 | * returns 0 for non-Compaq ROM, 1 for Compaq ROM |
129 | */ |
130 | static int check_for_compaq_ROM(void __iomem *rom_start) |
131 | { |
132 | u8 temp1, temp2, temp3, temp4, temp5, temp6; |
133 | int result = 0; |
134 | |
135 | temp1 = readb(addr: rom_start + 0xffea + 0); |
136 | temp2 = readb(addr: rom_start + 0xffea + 1); |
137 | temp3 = readb(addr: rom_start + 0xffea + 2); |
138 | temp4 = readb(addr: rom_start + 0xffea + 3); |
139 | temp5 = readb(addr: rom_start + 0xffea + 4); |
140 | temp6 = readb(addr: rom_start + 0xffea + 5); |
141 | if ((temp1 == 'C') && |
142 | (temp2 == 'O') && |
143 | (temp3 == 'M') && |
144 | (temp4 == 'P') && |
145 | (temp5 == 'A') && |
146 | (temp6 == 'Q')) { |
147 | result = 1; |
148 | } |
149 | dbg("%s - returned %d\n" , __func__, result); |
150 | return result; |
151 | } |
152 | |
153 | |
154 | static u32 access_EV(u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size) |
155 | { |
156 | unsigned long flags; |
157 | int op = operation; |
158 | int ret_val; |
159 | |
160 | if (!compaq_int15_entry_point) |
161 | return -ENODEV; |
162 | |
163 | spin_lock_irqsave(&int15_lock, flags); |
164 | __asm__ ( |
165 | "xorl %%ebx,%%ebx\n" \ |
166 | "xorl %%edx,%%edx\n" \ |
167 | "pushf\n" \ |
168 | "push %%cs\n" \ |
169 | "cli\n" \ |
170 | "call *%6\n" |
171 | : "=c" (*buf_size), "=a" (ret_val) |
172 | : "a" (op), "c" (*buf_size), "S" (ev_name), |
173 | "D" (buffer), "m" (compaq_int15_entry_point) |
174 | : "%ebx" , "%edx" ); |
175 | spin_unlock_irqrestore(lock: &int15_lock, flags); |
176 | |
177 | return((ret_val & 0xFF00) >> 8); |
178 | } |
179 | |
180 | |
181 | /* |
182 | * load_HRT |
183 | * |
184 | * Read the hot plug Resource Table from NVRAM |
185 | */ |
186 | static int load_HRT(void __iomem *rom_start) |
187 | { |
188 | u32 available; |
189 | u32 temp_dword; |
190 | u8 temp_byte = 0xFF; |
191 | u32 rc; |
192 | |
193 | if (!check_for_compaq_ROM(rom_start)) |
194 | return -ENODEV; |
195 | |
196 | available = 1024; |
197 | |
198 | /* Now load the EV */ |
199 | temp_dword = available; |
200 | |
201 | rc = access_EV(READ_EV, ev_name: "CQTHPS" , buffer: evbuffer, buf_size: &temp_dword); |
202 | |
203 | evbuffer_length = temp_dword; |
204 | |
205 | /* We're maintaining the resource lists so write FF to invalidate old |
206 | * info |
207 | */ |
208 | temp_dword = 1; |
209 | |
210 | rc = access_EV(WRITE_EV, ev_name: "CQTHPS" , buffer: &temp_byte, buf_size: &temp_dword); |
211 | |
212 | return rc; |
213 | } |
214 | |
215 | |
216 | /* |
217 | * store_HRT |
218 | * |
219 | * Save the hot plug Resource Table in NVRAM |
220 | */ |
221 | static u32 store_HRT(void __iomem *rom_start) |
222 | { |
223 | u32 *buffer; |
224 | u32 *pFill; |
225 | u32 usedbytes; |
226 | u32 available; |
227 | u32 temp_dword; |
228 | u32 rc; |
229 | u8 loop; |
230 | u8 numCtrl = 0; |
231 | struct controller *ctrl; |
232 | struct pci_resource *resNode; |
233 | struct ev_hrt_header *; |
234 | struct ev_hrt_ctrl *p_ev_ctrl; |
235 | |
236 | available = 1024; |
237 | |
238 | if (!check_for_compaq_ROM(rom_start)) |
239 | return(1); |
240 | |
241 | buffer = (u32 *) evbuffer; |
242 | |
243 | if (!buffer) |
244 | return(1); |
245 | |
246 | pFill = buffer; |
247 | usedbytes = 0; |
248 | |
249 | p_EV_header = (struct ev_hrt_header *) pFill; |
250 | |
251 | ctrl = cpqhp_ctrl_list; |
252 | |
253 | /* The revision of this structure */ |
254 | rc = add_byte(p_buffer: &pFill, value: 1 + ctrl->push_flag, used: &usedbytes, avail: &available); |
255 | if (rc) |
256 | return(rc); |
257 | |
258 | /* The number of controllers */ |
259 | rc = add_byte(p_buffer: &pFill, value: 1, used: &usedbytes, avail: &available); |
260 | if (rc) |
261 | return(rc); |
262 | |
263 | while (ctrl) { |
264 | p_ev_ctrl = (struct ev_hrt_ctrl *) pFill; |
265 | |
266 | numCtrl++; |
267 | |
268 | /* The bus number */ |
269 | rc = add_byte(p_buffer: &pFill, value: ctrl->bus, used: &usedbytes, avail: &available); |
270 | if (rc) |
271 | return(rc); |
272 | |
273 | /* The device Number */ |
274 | rc = add_byte(p_buffer: &pFill, PCI_SLOT(ctrl->pci_dev->devfn), used: &usedbytes, avail: &available); |
275 | if (rc) |
276 | return(rc); |
277 | |
278 | /* The function Number */ |
279 | rc = add_byte(p_buffer: &pFill, PCI_FUNC(ctrl->pci_dev->devfn), used: &usedbytes, avail: &available); |
280 | if (rc) |
281 | return(rc); |
282 | |
283 | /* Skip the number of available entries */ |
284 | rc = add_dword(p_buffer: &pFill, value: 0, used: &usedbytes, avail: &available); |
285 | if (rc) |
286 | return(rc); |
287 | |
288 | /* Figure out memory Available */ |
289 | |
290 | resNode = ctrl->mem_head; |
291 | |
292 | loop = 0; |
293 | |
294 | while (resNode) { |
295 | loop++; |
296 | |
297 | /* base */ |
298 | rc = add_dword(p_buffer: &pFill, value: resNode->base, used: &usedbytes, avail: &available); |
299 | if (rc) |
300 | return(rc); |
301 | |
302 | /* length */ |
303 | rc = add_dword(p_buffer: &pFill, value: resNode->length, used: &usedbytes, avail: &available); |
304 | if (rc) |
305 | return(rc); |
306 | |
307 | resNode = resNode->next; |
308 | } |
309 | |
310 | /* Fill in the number of entries */ |
311 | p_ev_ctrl->mem_avail = loop; |
312 | |
313 | /* Figure out prefetchable memory Available */ |
314 | |
315 | resNode = ctrl->p_mem_head; |
316 | |
317 | loop = 0; |
318 | |
319 | while (resNode) { |
320 | loop++; |
321 | |
322 | /* base */ |
323 | rc = add_dword(p_buffer: &pFill, value: resNode->base, used: &usedbytes, avail: &available); |
324 | if (rc) |
325 | return(rc); |
326 | |
327 | /* length */ |
328 | rc = add_dword(p_buffer: &pFill, value: resNode->length, used: &usedbytes, avail: &available); |
329 | if (rc) |
330 | return(rc); |
331 | |
332 | resNode = resNode->next; |
333 | } |
334 | |
335 | /* Fill in the number of entries */ |
336 | p_ev_ctrl->p_mem_avail = loop; |
337 | |
338 | /* Figure out IO Available */ |
339 | |
340 | resNode = ctrl->io_head; |
341 | |
342 | loop = 0; |
343 | |
344 | while (resNode) { |
345 | loop++; |
346 | |
347 | /* base */ |
348 | rc = add_dword(p_buffer: &pFill, value: resNode->base, used: &usedbytes, avail: &available); |
349 | if (rc) |
350 | return(rc); |
351 | |
352 | /* length */ |
353 | rc = add_dword(p_buffer: &pFill, value: resNode->length, used: &usedbytes, avail: &available); |
354 | if (rc) |
355 | return(rc); |
356 | |
357 | resNode = resNode->next; |
358 | } |
359 | |
360 | /* Fill in the number of entries */ |
361 | p_ev_ctrl->io_avail = loop; |
362 | |
363 | /* Figure out bus Available */ |
364 | |
365 | resNode = ctrl->bus_head; |
366 | |
367 | loop = 0; |
368 | |
369 | while (resNode) { |
370 | loop++; |
371 | |
372 | /* base */ |
373 | rc = add_dword(p_buffer: &pFill, value: resNode->base, used: &usedbytes, avail: &available); |
374 | if (rc) |
375 | return(rc); |
376 | |
377 | /* length */ |
378 | rc = add_dword(p_buffer: &pFill, value: resNode->length, used: &usedbytes, avail: &available); |
379 | if (rc) |
380 | return(rc); |
381 | |
382 | resNode = resNode->next; |
383 | } |
384 | |
385 | /* Fill in the number of entries */ |
386 | p_ev_ctrl->bus_avail = loop; |
387 | |
388 | ctrl = ctrl->next; |
389 | } |
390 | |
391 | p_EV_header->num_of_ctrl = numCtrl; |
392 | |
393 | /* Now store the EV */ |
394 | |
395 | temp_dword = usedbytes; |
396 | |
397 | rc = access_EV(WRITE_EV, ev_name: "CQTHPS" , buffer: (u8 *) buffer, buf_size: &temp_dword); |
398 | |
399 | dbg("usedbytes = 0x%x, length = 0x%x\n" , usedbytes, temp_dword); |
400 | |
401 | evbuffer_length = temp_dword; |
402 | |
403 | if (rc) { |
404 | err(msg_unable_to_save); |
405 | return(1); |
406 | } |
407 | |
408 | return(0); |
409 | } |
410 | |
411 | |
412 | void compaq_nvram_init(void __iomem *rom_start) |
413 | { |
414 | if (rom_start) |
415 | compaq_int15_entry_point = (rom_start + ROM_INT15_PHY_ADDR - ROM_PHY_ADDR); |
416 | |
417 | dbg("int15 entry = %p\n" , compaq_int15_entry_point); |
418 | } |
419 | |
420 | |
421 | int compaq_nvram_load(void __iomem *rom_start, struct controller *ctrl) |
422 | { |
423 | u8 bus, device, function; |
424 | u8 nummem, numpmem, numio, numbus; |
425 | u32 rc; |
426 | u8 *p_byte; |
427 | struct pci_resource *mem_node; |
428 | struct pci_resource *p_mem_node; |
429 | struct pci_resource *io_node; |
430 | struct pci_resource *bus_node; |
431 | struct ev_hrt_ctrl *p_ev_ctrl; |
432 | struct ev_hrt_header *; |
433 | |
434 | if (!evbuffer_init) { |
435 | /* Read the resource list information in from NVRAM */ |
436 | if (load_HRT(rom_start)) |
437 | memset(evbuffer, 0, 1024); |
438 | |
439 | evbuffer_init = 1; |
440 | } |
441 | |
442 | /* If we saved information in NVRAM, use it now */ |
443 | p_EV_header = (struct ev_hrt_header *) evbuffer; |
444 | |
445 | /* The following code is for systems where version 1.0 of this |
446 | * driver has been loaded, but doesn't support the hardware. |
447 | * In that case, the driver would incorrectly store something |
448 | * in NVRAM. |
449 | */ |
450 | if ((p_EV_header->Version == 2) || |
451 | ((p_EV_header->Version == 1) && !ctrl->push_flag)) { |
452 | p_byte = &(p_EV_header->next); |
453 | |
454 | p_ev_ctrl = (struct ev_hrt_ctrl *) &(p_EV_header->next); |
455 | |
456 | p_byte += 3; |
457 | |
458 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) |
459 | return 2; |
460 | |
461 | bus = p_ev_ctrl->bus; |
462 | device = p_ev_ctrl->device; |
463 | function = p_ev_ctrl->function; |
464 | |
465 | while ((bus != ctrl->bus) || |
466 | (device != PCI_SLOT(ctrl->pci_dev->devfn)) || |
467 | (function != PCI_FUNC(ctrl->pci_dev->devfn))) { |
468 | nummem = p_ev_ctrl->mem_avail; |
469 | numpmem = p_ev_ctrl->p_mem_avail; |
470 | numio = p_ev_ctrl->io_avail; |
471 | numbus = p_ev_ctrl->bus_avail; |
472 | |
473 | p_byte += 4; |
474 | |
475 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) |
476 | return 2; |
477 | |
478 | /* Skip forward to the next entry */ |
479 | p_byte += (nummem + numpmem + numio + numbus) * 8; |
480 | |
481 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) |
482 | return 2; |
483 | |
484 | p_ev_ctrl = (struct ev_hrt_ctrl *) p_byte; |
485 | |
486 | p_byte += 3; |
487 | |
488 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) |
489 | return 2; |
490 | |
491 | bus = p_ev_ctrl->bus; |
492 | device = p_ev_ctrl->device; |
493 | function = p_ev_ctrl->function; |
494 | } |
495 | |
496 | nummem = p_ev_ctrl->mem_avail; |
497 | numpmem = p_ev_ctrl->p_mem_avail; |
498 | numio = p_ev_ctrl->io_avail; |
499 | numbus = p_ev_ctrl->bus_avail; |
500 | |
501 | p_byte += 4; |
502 | |
503 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) |
504 | return 2; |
505 | |
506 | while (nummem--) { |
507 | mem_node = kmalloc(size: sizeof(struct pci_resource), GFP_KERNEL); |
508 | |
509 | if (!mem_node) |
510 | break; |
511 | |
512 | mem_node->base = *(u32 *)p_byte; |
513 | dbg("mem base = %8.8x\n" , mem_node->base); |
514 | p_byte += 4; |
515 | |
516 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) { |
517 | kfree(objp: mem_node); |
518 | return 2; |
519 | } |
520 | |
521 | mem_node->length = *(u32 *)p_byte; |
522 | dbg("mem length = %8.8x\n" , mem_node->length); |
523 | p_byte += 4; |
524 | |
525 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) { |
526 | kfree(objp: mem_node); |
527 | return 2; |
528 | } |
529 | |
530 | mem_node->next = ctrl->mem_head; |
531 | ctrl->mem_head = mem_node; |
532 | } |
533 | |
534 | while (numpmem--) { |
535 | p_mem_node = kmalloc(size: sizeof(struct pci_resource), GFP_KERNEL); |
536 | |
537 | if (!p_mem_node) |
538 | break; |
539 | |
540 | p_mem_node->base = *(u32 *)p_byte; |
541 | dbg("pre-mem base = %8.8x\n" , p_mem_node->base); |
542 | p_byte += 4; |
543 | |
544 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) { |
545 | kfree(objp: p_mem_node); |
546 | return 2; |
547 | } |
548 | |
549 | p_mem_node->length = *(u32 *)p_byte; |
550 | dbg("pre-mem length = %8.8x\n" , p_mem_node->length); |
551 | p_byte += 4; |
552 | |
553 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) { |
554 | kfree(objp: p_mem_node); |
555 | return 2; |
556 | } |
557 | |
558 | p_mem_node->next = ctrl->p_mem_head; |
559 | ctrl->p_mem_head = p_mem_node; |
560 | } |
561 | |
562 | while (numio--) { |
563 | io_node = kmalloc(size: sizeof(struct pci_resource), GFP_KERNEL); |
564 | |
565 | if (!io_node) |
566 | break; |
567 | |
568 | io_node->base = *(u32 *)p_byte; |
569 | dbg("io base = %8.8x\n" , io_node->base); |
570 | p_byte += 4; |
571 | |
572 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) { |
573 | kfree(objp: io_node); |
574 | return 2; |
575 | } |
576 | |
577 | io_node->length = *(u32 *)p_byte; |
578 | dbg("io length = %8.8x\n" , io_node->length); |
579 | p_byte += 4; |
580 | |
581 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) { |
582 | kfree(objp: io_node); |
583 | return 2; |
584 | } |
585 | |
586 | io_node->next = ctrl->io_head; |
587 | ctrl->io_head = io_node; |
588 | } |
589 | |
590 | while (numbus--) { |
591 | bus_node = kmalloc(size: sizeof(struct pci_resource), GFP_KERNEL); |
592 | |
593 | if (!bus_node) |
594 | break; |
595 | |
596 | bus_node->base = *(u32 *)p_byte; |
597 | p_byte += 4; |
598 | |
599 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) { |
600 | kfree(objp: bus_node); |
601 | return 2; |
602 | } |
603 | |
604 | bus_node->length = *(u32 *)p_byte; |
605 | p_byte += 4; |
606 | |
607 | if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) { |
608 | kfree(objp: bus_node); |
609 | return 2; |
610 | } |
611 | |
612 | bus_node->next = ctrl->bus_head; |
613 | ctrl->bus_head = bus_node; |
614 | } |
615 | |
616 | /* If all of the following fail, we don't have any resources for |
617 | * hot plug add |
618 | */ |
619 | rc = 1; |
620 | rc &= cpqhp_resource_sort_and_combine(head: &(ctrl->mem_head)); |
621 | rc &= cpqhp_resource_sort_and_combine(head: &(ctrl->p_mem_head)); |
622 | rc &= cpqhp_resource_sort_and_combine(head: &(ctrl->io_head)); |
623 | rc &= cpqhp_resource_sort_and_combine(head: &(ctrl->bus_head)); |
624 | |
625 | if (rc) |
626 | return(rc); |
627 | } else { |
628 | if ((evbuffer[0] != 0) && (!ctrl->push_flag)) |
629 | return 1; |
630 | } |
631 | |
632 | return 0; |
633 | } |
634 | |
635 | |
636 | int compaq_nvram_store(void __iomem *rom_start) |
637 | { |
638 | int rc = 1; |
639 | |
640 | if (rom_start == NULL) |
641 | return -ENODEV; |
642 | |
643 | if (evbuffer_init) { |
644 | rc = store_HRT(rom_start); |
645 | if (rc) |
646 | err(msg_unable_to_save); |
647 | } |
648 | return rc; |
649 | } |
650 | |
651 | |