xref: /openbmc/linux/drivers/pci/hotplug/cpqphp_nvram.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
1736759efSBjorn Helgaas // SPDX-License-Identifier: GPL-2.0+
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Compaq Hot Plug Controller Driver
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 1995,2001 Compaq Computer Corporation
61da177e4SLinus Torvalds  * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
71da177e4SLinus Torvalds  * Copyright (C) 2001 IBM Corp.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * All rights reserved.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * Send feedback to <greg@kroah.com>
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  */
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds #include <linux/module.h>
161da177e4SLinus Torvalds #include <linux/kernel.h>
171da177e4SLinus Torvalds #include <linux/types.h>
181da177e4SLinus Torvalds #include <linux/proc_fs.h>
191da177e4SLinus Torvalds #include <linux/slab.h>
201da177e4SLinus Torvalds #include <linux/workqueue.h>
211da177e4SLinus Torvalds #include <linux/pci.h>
227a54f25cSGreg Kroah-Hartman #include <linux/pci_hotplug.h>
237c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
241da177e4SLinus Torvalds #include "cpqphp.h"
251da177e4SLinus Torvalds #include "cpqphp_nvram.h"
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #define ROM_INT15_PHY_ADDR		0x0FF859
291da177e4SLinus Torvalds #define READ_EV				0xD8A4
301da177e4SLinus Torvalds #define WRITE_EV			0xD8A5
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds struct register_foo {
331da177e4SLinus Torvalds 	union {
341da177e4SLinus Torvalds 		unsigned long lword;		/* eax */
351da177e4SLinus Torvalds 		unsigned short word;		/* ax */
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds 		struct {
381da177e4SLinus Torvalds 			unsigned char low;	/* al */
391da177e4SLinus Torvalds 			unsigned char high;	/* ah */
401da177e4SLinus Torvalds 		} byte;
411da177e4SLinus Torvalds 	} data;
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds 	unsigned char opcode;	/* see below */
441da177e4SLinus Torvalds 	unsigned long length;	/* if the reg. is a pointer, how much data */
451da177e4SLinus Torvalds } __attribute__ ((packed));
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds struct all_reg {
481da177e4SLinus Torvalds 	struct register_foo eax_reg;
491da177e4SLinus Torvalds 	struct register_foo ebx_reg;
501da177e4SLinus Torvalds 	struct register_foo ecx_reg;
511da177e4SLinus Torvalds 	struct register_foo edx_reg;
521da177e4SLinus Torvalds 	struct register_foo edi_reg;
531da177e4SLinus Torvalds 	struct register_foo esi_reg;
541da177e4SLinus Torvalds 	struct register_foo eflags_reg;
551da177e4SLinus Torvalds } __attribute__ ((packed));
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds struct ev_hrt_header {
591da177e4SLinus Torvalds 	u8 Version;
601da177e4SLinus Torvalds 	u8 num_of_ctrl;
611da177e4SLinus Torvalds 	u8 next;
621da177e4SLinus Torvalds };
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds struct ev_hrt_ctrl {
651da177e4SLinus Torvalds 	u8 bus;
661da177e4SLinus Torvalds 	u8 device;
671da177e4SLinus Torvalds 	u8 function;
681da177e4SLinus Torvalds 	u8 mem_avail;
691da177e4SLinus Torvalds 	u8 p_mem_avail;
701da177e4SLinus Torvalds 	u8 io_avail;
711da177e4SLinus Torvalds 	u8 bus_avail;
721da177e4SLinus Torvalds 	u8 next;
731da177e4SLinus Torvalds };
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds static u8 evbuffer_init;
771da177e4SLinus Torvalds static u8 evbuffer_length;
781da177e4SLinus Torvalds static u8 evbuffer[1024];
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds static void __iomem *compaq_int15_entry_point;
811da177e4SLinus Torvalds 
82427438c6SAlex Chiang /* lock for ordering int15_bios_call() */
83*3a306a5bSGuobin Huang static DEFINE_SPINLOCK(int15_lock);
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds /* This is a series of function that deals with
87427438c6SAlex Chiang  * setting & getting the hotplug resource table in some environment variable.
881da177e4SLinus Torvalds  */
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds /*
911da177e4SLinus Torvalds  * We really shouldn't be doing this unless there is a _very_ good reason to!!!
921da177e4SLinus Torvalds  * greg k-h
931da177e4SLinus Torvalds  */
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 
add_byte(u32 ** p_buffer,u8 value,u32 * used,u32 * avail)961da177e4SLinus Torvalds static u32 add_byte(u32 **p_buffer, u8 value, u32 *used, u32 *avail)
971da177e4SLinus Torvalds {
981da177e4SLinus Torvalds 	u8 **tByte;
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds 	if ((*used + 1) > *avail)
1011da177e4SLinus Torvalds 		return(1);
1021da177e4SLinus Torvalds 
1031da177e4SLinus Torvalds 	*((u8 *)*p_buffer) = value;
1041da177e4SLinus Torvalds 	tByte = (u8 **)p_buffer;
1051da177e4SLinus Torvalds 	(*tByte)++;
1061da177e4SLinus Torvalds 	*used += 1;
1071da177e4SLinus Torvalds 	return(0);
1081da177e4SLinus Torvalds }
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds 
add_dword(u32 ** p_buffer,u32 value,u32 * used,u32 * avail)1111da177e4SLinus Torvalds static u32 add_dword(u32 **p_buffer, u32 value, u32 *used, u32 *avail)
1121da177e4SLinus Torvalds {
1131da177e4SLinus Torvalds 	if ((*used + 4) > *avail)
1141da177e4SLinus Torvalds 		return(1);
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	**p_buffer = value;
1171da177e4SLinus Torvalds 	(*p_buffer)++;
1181da177e4SLinus Torvalds 	*used += 4;
1191da177e4SLinus Torvalds 	return(0);
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds /*
1241da177e4SLinus Torvalds  * check_for_compaq_ROM
1251da177e4SLinus Torvalds  *
1261da177e4SLinus Torvalds  * this routine verifies that the ROM OEM string is 'COMPAQ'
1271da177e4SLinus Torvalds  *
1281da177e4SLinus Torvalds  * returns 0 for non-Compaq ROM, 1 for Compaq ROM
1291da177e4SLinus Torvalds  */
check_for_compaq_ROM(void __iomem * rom_start)1301da177e4SLinus Torvalds static int check_for_compaq_ROM(void __iomem *rom_start)
1311da177e4SLinus Torvalds {
1321da177e4SLinus Torvalds 	u8 temp1, temp2, temp3, temp4, temp5, temp6;
1331da177e4SLinus Torvalds 	int result = 0;
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	temp1 = readb(rom_start + 0xffea + 0);
1361da177e4SLinus Torvalds 	temp2 = readb(rom_start + 0xffea + 1);
1371da177e4SLinus Torvalds 	temp3 = readb(rom_start + 0xffea + 2);
1381da177e4SLinus Torvalds 	temp4 = readb(rom_start + 0xffea + 3);
1391da177e4SLinus Torvalds 	temp5 = readb(rom_start + 0xffea + 4);
1401da177e4SLinus Torvalds 	temp6 = readb(rom_start + 0xffea + 5);
1411da177e4SLinus Torvalds 	if ((temp1 == 'C') &&
1421da177e4SLinus Torvalds 	    (temp2 == 'O') &&
1431da177e4SLinus Torvalds 	    (temp3 == 'M') &&
1441da177e4SLinus Torvalds 	    (temp4 == 'P') &&
1451da177e4SLinus Torvalds 	    (temp5 == 'A') &&
1461da177e4SLinus Torvalds 	    (temp6 == 'Q')) {
1471da177e4SLinus Torvalds 		result = 1;
1481da177e4SLinus Torvalds 	}
14966bef8c0SHarvey Harrison 	dbg("%s - returned %d\n", __func__, result);
1501da177e4SLinus Torvalds 	return result;
1511da177e4SLinus Torvalds }
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 
access_EV(u16 operation,u8 * ev_name,u8 * buffer,u32 * buf_size)1541da177e4SLinus Torvalds static u32 access_EV(u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size)
1551da177e4SLinus Torvalds {
1561da177e4SLinus Torvalds 	unsigned long flags;
1571da177e4SLinus Torvalds 	int op = operation;
1581da177e4SLinus Torvalds 	int ret_val;
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds 	if (!compaq_int15_entry_point)
1611da177e4SLinus Torvalds 		return -ENODEV;
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 	spin_lock_irqsave(&int15_lock, flags);
1641da177e4SLinus Torvalds 	__asm__ (
1651da177e4SLinus Torvalds 		"xorl   %%ebx,%%ebx\n" \
1661da177e4SLinus Torvalds 		"xorl    %%edx,%%edx\n" \
1671da177e4SLinus Torvalds 		"pushf\n" \
1681da177e4SLinus Torvalds 		"push %%cs\n" \
1691da177e4SLinus Torvalds 		"cli\n" \
1701da177e4SLinus Torvalds 		"call *%6\n"
1711da177e4SLinus Torvalds 		: "=c" (*buf_size), "=a" (ret_val)
1721da177e4SLinus Torvalds 		: "a" (op), "c" (*buf_size), "S" (ev_name),
1731da177e4SLinus Torvalds 		"D" (buffer), "m" (compaq_int15_entry_point)
1741da177e4SLinus Torvalds 		: "%ebx", "%edx");
1751da177e4SLinus Torvalds 	spin_unlock_irqrestore(&int15_lock, flags);
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	return((ret_val & 0xFF00) >> 8);
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds /*
1821da177e4SLinus Torvalds  * load_HRT
1831da177e4SLinus Torvalds  *
1841da177e4SLinus Torvalds  * Read the hot plug Resource Table from NVRAM
1851da177e4SLinus Torvalds  */
load_HRT(void __iomem * rom_start)1861da177e4SLinus Torvalds static int load_HRT(void __iomem *rom_start)
1871da177e4SLinus Torvalds {
1881da177e4SLinus Torvalds 	u32 available;
1891da177e4SLinus Torvalds 	u32 temp_dword;
1901da177e4SLinus Torvalds 	u8 temp_byte = 0xFF;
1911da177e4SLinus Torvalds 	u32 rc;
1921da177e4SLinus Torvalds 
193656f978fSQuentin Lambert 	if (!check_for_compaq_ROM(rom_start))
1941da177e4SLinus Torvalds 		return -ENODEV;
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds 	available = 1024;
1971da177e4SLinus Torvalds 
198427438c6SAlex Chiang 	/* Now load the EV */
1991da177e4SLinus Torvalds 	temp_dword = available;
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds 	rc = access_EV(READ_EV, "CQTHPS", evbuffer, &temp_dword);
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	evbuffer_length = temp_dword;
2041da177e4SLinus Torvalds 
205427438c6SAlex Chiang 	/* We're maintaining the resource lists so write FF to invalidate old
206427438c6SAlex Chiang 	 * info
207427438c6SAlex Chiang 	 */
2081da177e4SLinus Torvalds 	temp_dword = 1;
2091da177e4SLinus Torvalds 
2101da177e4SLinus Torvalds 	rc = access_EV(WRITE_EV, "CQTHPS", &temp_byte, &temp_dword);
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	return rc;
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds /*
2171da177e4SLinus Torvalds  * store_HRT
2181da177e4SLinus Torvalds  *
2191da177e4SLinus Torvalds  * Save the hot plug Resource Table in NVRAM
2201da177e4SLinus Torvalds  */
store_HRT(void __iomem * rom_start)2211da177e4SLinus Torvalds static u32 store_HRT(void __iomem *rom_start)
2221da177e4SLinus Torvalds {
2231da177e4SLinus Torvalds 	u32 *buffer;
2241da177e4SLinus Torvalds 	u32 *pFill;
2251da177e4SLinus Torvalds 	u32 usedbytes;
2261da177e4SLinus Torvalds 	u32 available;
2271da177e4SLinus Torvalds 	u32 temp_dword;
2281da177e4SLinus Torvalds 	u32 rc;
2291da177e4SLinus Torvalds 	u8 loop;
2301da177e4SLinus Torvalds 	u8 numCtrl = 0;
2311da177e4SLinus Torvalds 	struct controller *ctrl;
2321da177e4SLinus Torvalds 	struct pci_resource *resNode;
2331da177e4SLinus Torvalds 	struct ev_hrt_header *p_EV_header;
2341da177e4SLinus Torvalds 	struct ev_hrt_ctrl *p_ev_ctrl;
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 	available = 1024;
2371da177e4SLinus Torvalds 
238656f978fSQuentin Lambert 	if (!check_for_compaq_ROM(rom_start))
2391da177e4SLinus Torvalds 		return(1);
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	buffer = (u32 *) evbuffer;
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds 	if (!buffer)
2441da177e4SLinus Torvalds 		return(1);
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds 	pFill = buffer;
2471da177e4SLinus Torvalds 	usedbytes = 0;
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds 	p_EV_header = (struct ev_hrt_header *) pFill;
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds 	ctrl = cpqhp_ctrl_list;
2521da177e4SLinus Torvalds 
253427438c6SAlex Chiang 	/* The revision of this structure */
2541da177e4SLinus Torvalds 	rc = add_byte(&pFill, 1 + ctrl->push_flag, &usedbytes, &available);
2551da177e4SLinus Torvalds 	if (rc)
2561da177e4SLinus Torvalds 		return(rc);
2571da177e4SLinus Torvalds 
258427438c6SAlex Chiang 	/* The number of controllers */
2591da177e4SLinus Torvalds 	rc = add_byte(&pFill, 1, &usedbytes, &available);
2601da177e4SLinus Torvalds 	if (rc)
2611da177e4SLinus Torvalds 		return(rc);
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 	while (ctrl) {
2641da177e4SLinus Torvalds 		p_ev_ctrl = (struct ev_hrt_ctrl *) pFill;
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds 		numCtrl++;
2671da177e4SLinus Torvalds 
268427438c6SAlex Chiang 		/* The bus number */
2691da177e4SLinus Torvalds 		rc = add_byte(&pFill, ctrl->bus, &usedbytes, &available);
2701da177e4SLinus Torvalds 		if (rc)
2711da177e4SLinus Torvalds 			return(rc);
2721da177e4SLinus Torvalds 
273427438c6SAlex Chiang 		/* The device Number */
2741da177e4SLinus Torvalds 		rc = add_byte(&pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available);
2751da177e4SLinus Torvalds 		if (rc)
2761da177e4SLinus Torvalds 			return(rc);
2771da177e4SLinus Torvalds 
278427438c6SAlex Chiang 		/* The function Number */
2791da177e4SLinus Torvalds 		rc = add_byte(&pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available);
2801da177e4SLinus Torvalds 		if (rc)
2811da177e4SLinus Torvalds 			return(rc);
2821da177e4SLinus Torvalds 
283427438c6SAlex Chiang 		/* Skip the number of available entries */
2841da177e4SLinus Torvalds 		rc = add_dword(&pFill, 0, &usedbytes, &available);
2851da177e4SLinus Torvalds 		if (rc)
2861da177e4SLinus Torvalds 			return(rc);
2871da177e4SLinus Torvalds 
288427438c6SAlex Chiang 		/* Figure out memory Available */
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds 		resNode = ctrl->mem_head;
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds 		loop = 0;
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds 		while (resNode) {
2951da177e4SLinus Torvalds 			loop++;
2961da177e4SLinus Torvalds 
297427438c6SAlex Chiang 			/* base */
2981da177e4SLinus Torvalds 			rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
2991da177e4SLinus Torvalds 			if (rc)
3001da177e4SLinus Torvalds 				return(rc);
3011da177e4SLinus Torvalds 
302427438c6SAlex Chiang 			/* length */
3031da177e4SLinus Torvalds 			rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
3041da177e4SLinus Torvalds 			if (rc)
3051da177e4SLinus Torvalds 				return(rc);
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 			resNode = resNode->next;
3081da177e4SLinus Torvalds 		}
3091da177e4SLinus Torvalds 
310427438c6SAlex Chiang 		/* Fill in the number of entries */
3111da177e4SLinus Torvalds 		p_ev_ctrl->mem_avail = loop;
3121da177e4SLinus Torvalds 
313427438c6SAlex Chiang 		/* Figure out prefetchable memory Available */
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 		resNode = ctrl->p_mem_head;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 		loop = 0;
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 		while (resNode) {
3201da177e4SLinus Torvalds 			loop++;
3211da177e4SLinus Torvalds 
322427438c6SAlex Chiang 			/* base */
3231da177e4SLinus Torvalds 			rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
3241da177e4SLinus Torvalds 			if (rc)
3251da177e4SLinus Torvalds 				return(rc);
3261da177e4SLinus Torvalds 
327427438c6SAlex Chiang 			/* length */
3281da177e4SLinus Torvalds 			rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
3291da177e4SLinus Torvalds 			if (rc)
3301da177e4SLinus Torvalds 				return(rc);
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 			resNode = resNode->next;
3331da177e4SLinus Torvalds 		}
3341da177e4SLinus Torvalds 
335427438c6SAlex Chiang 		/* Fill in the number of entries */
3361da177e4SLinus Torvalds 		p_ev_ctrl->p_mem_avail = loop;
3371da177e4SLinus Torvalds 
338427438c6SAlex Chiang 		/* Figure out IO Available */
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds 		resNode = ctrl->io_head;
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 		loop = 0;
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds 		while (resNode) {
3451da177e4SLinus Torvalds 			loop++;
3461da177e4SLinus Torvalds 
347427438c6SAlex Chiang 			/* base */
3481da177e4SLinus Torvalds 			rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
3491da177e4SLinus Torvalds 			if (rc)
3501da177e4SLinus Torvalds 				return(rc);
3511da177e4SLinus Torvalds 
352427438c6SAlex Chiang 			/* length */
3531da177e4SLinus Torvalds 			rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
3541da177e4SLinus Torvalds 			if (rc)
3551da177e4SLinus Torvalds 				return(rc);
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 			resNode = resNode->next;
3581da177e4SLinus Torvalds 		}
3591da177e4SLinus Torvalds 
360427438c6SAlex Chiang 		/* Fill in the number of entries */
3611da177e4SLinus Torvalds 		p_ev_ctrl->io_avail = loop;
3621da177e4SLinus Torvalds 
363427438c6SAlex Chiang 		/* Figure out bus Available */
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds 		resNode = ctrl->bus_head;
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 		loop = 0;
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds 		while (resNode) {
3701da177e4SLinus Torvalds 			loop++;
3711da177e4SLinus Torvalds 
372427438c6SAlex Chiang 			/* base */
3731da177e4SLinus Torvalds 			rc = add_dword(&pFill, resNode->base, &usedbytes, &available);
3741da177e4SLinus Torvalds 			if (rc)
3751da177e4SLinus Torvalds 				return(rc);
3761da177e4SLinus Torvalds 
377427438c6SAlex Chiang 			/* length */
3781da177e4SLinus Torvalds 			rc = add_dword(&pFill, resNode->length, &usedbytes, &available);
3791da177e4SLinus Torvalds 			if (rc)
3801da177e4SLinus Torvalds 				return(rc);
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds 			resNode = resNode->next;
3831da177e4SLinus Torvalds 		}
3841da177e4SLinus Torvalds 
385427438c6SAlex Chiang 		/* Fill in the number of entries */
3861da177e4SLinus Torvalds 		p_ev_ctrl->bus_avail = loop;
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 		ctrl = ctrl->next;
3891da177e4SLinus Torvalds 	}
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 	p_EV_header->num_of_ctrl = numCtrl;
3921da177e4SLinus Torvalds 
393427438c6SAlex Chiang 	/* Now store the EV */
3941da177e4SLinus Torvalds 
3951da177e4SLinus Torvalds 	temp_dword = usedbytes;
3961da177e4SLinus Torvalds 
3971da177e4SLinus Torvalds 	rc = access_EV(WRITE_EV, "CQTHPS", (u8 *) buffer, &temp_dword);
3981da177e4SLinus Torvalds 
3991da177e4SLinus Torvalds 	dbg("usedbytes = 0x%x, length = 0x%x\n", usedbytes, temp_dword);
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 	evbuffer_length = temp_dword;
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds 	if (rc) {
4041da177e4SLinus Torvalds 		err(msg_unable_to_save);
4051da177e4SLinus Torvalds 		return(1);
4061da177e4SLinus Torvalds 	}
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds 	return(0);
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 
compaq_nvram_init(void __iomem * rom_start)4121da177e4SLinus Torvalds void compaq_nvram_init(void __iomem *rom_start)
4131da177e4SLinus Torvalds {
414656f978fSQuentin Lambert 	if (rom_start)
4151da177e4SLinus Torvalds 		compaq_int15_entry_point = (rom_start + ROM_INT15_PHY_ADDR - ROM_PHY_ADDR);
416656f978fSQuentin Lambert 
4171da177e4SLinus Torvalds 	dbg("int15 entry  = %p\n", compaq_int15_entry_point);
4181da177e4SLinus Torvalds }
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds 
compaq_nvram_load(void __iomem * rom_start,struct controller * ctrl)4211da177e4SLinus Torvalds int compaq_nvram_load(void __iomem *rom_start, struct controller *ctrl)
4221da177e4SLinus Torvalds {
4231da177e4SLinus Torvalds 	u8 bus, device, function;
4241da177e4SLinus Torvalds 	u8 nummem, numpmem, numio, numbus;
4251da177e4SLinus Torvalds 	u32 rc;
4261da177e4SLinus Torvalds 	u8 *p_byte;
4271da177e4SLinus Torvalds 	struct pci_resource *mem_node;
4281da177e4SLinus Torvalds 	struct pci_resource *p_mem_node;
4291da177e4SLinus Torvalds 	struct pci_resource *io_node;
4301da177e4SLinus Torvalds 	struct pci_resource *bus_node;
4311da177e4SLinus Torvalds 	struct ev_hrt_ctrl *p_ev_ctrl;
4321da177e4SLinus Torvalds 	struct ev_hrt_header *p_EV_header;
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 	if (!evbuffer_init) {
435427438c6SAlex Chiang 		/* Read the resource list information in from NVRAM */
4361da177e4SLinus Torvalds 		if (load_HRT(rom_start))
4371da177e4SLinus Torvalds 			memset(evbuffer, 0, 1024);
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 		evbuffer_init = 1;
4401da177e4SLinus Torvalds 	}
4411da177e4SLinus Torvalds 
442427438c6SAlex Chiang 	/* If we saved information in NVRAM, use it now */
4431da177e4SLinus Torvalds 	p_EV_header = (struct ev_hrt_header *) evbuffer;
4441da177e4SLinus Torvalds 
445427438c6SAlex Chiang 	/* The following code is for systems where version 1.0 of this
446427438c6SAlex Chiang 	 * driver has been loaded, but doesn't support the hardware.
447427438c6SAlex Chiang 	 * In that case, the driver would incorrectly store something
448427438c6SAlex Chiang 	 * in NVRAM.
449427438c6SAlex Chiang 	 */
4501da177e4SLinus Torvalds 	if ((p_EV_header->Version == 2) ||
4511da177e4SLinus Torvalds 	    ((p_EV_header->Version == 1) && !ctrl->push_flag)) {
4521da177e4SLinus Torvalds 		p_byte = &(p_EV_header->next);
4531da177e4SLinus Torvalds 
4541da177e4SLinus Torvalds 		p_ev_ctrl = (struct ev_hrt_ctrl *) &(p_EV_header->next);
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds 		p_byte += 3;
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 		if (p_byte > ((u8 *)p_EV_header + evbuffer_length))
4591da177e4SLinus Torvalds 			return 2;
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds 		bus = p_ev_ctrl->bus;
4621da177e4SLinus Torvalds 		device = p_ev_ctrl->device;
4631da177e4SLinus Torvalds 		function = p_ev_ctrl->function;
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 		while ((bus != ctrl->bus) ||
4661da177e4SLinus Torvalds 		       (device != PCI_SLOT(ctrl->pci_dev->devfn)) ||
4671da177e4SLinus Torvalds 		       (function != PCI_FUNC(ctrl->pci_dev->devfn))) {
4681da177e4SLinus Torvalds 			nummem = p_ev_ctrl->mem_avail;
4691da177e4SLinus Torvalds 			numpmem = p_ev_ctrl->p_mem_avail;
4701da177e4SLinus Torvalds 			numio = p_ev_ctrl->io_avail;
4711da177e4SLinus Torvalds 			numbus = p_ev_ctrl->bus_avail;
4721da177e4SLinus Torvalds 
4731da177e4SLinus Torvalds 			p_byte += 4;
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 			if (p_byte > ((u8 *)p_EV_header + evbuffer_length))
4761da177e4SLinus Torvalds 				return 2;
4771da177e4SLinus Torvalds 
478427438c6SAlex Chiang 			/* Skip forward to the next entry */
4791da177e4SLinus Torvalds 			p_byte += (nummem + numpmem + numio + numbus) * 8;
4801da177e4SLinus Torvalds 
4811da177e4SLinus Torvalds 			if (p_byte > ((u8 *)p_EV_header + evbuffer_length))
4821da177e4SLinus Torvalds 				return 2;
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds 			p_ev_ctrl = (struct ev_hrt_ctrl *) p_byte;
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds 			p_byte += 3;
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds 			if (p_byte > ((u8 *)p_EV_header + evbuffer_length))
4891da177e4SLinus Torvalds 				return 2;
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds 			bus = p_ev_ctrl->bus;
4921da177e4SLinus Torvalds 			device = p_ev_ctrl->device;
4931da177e4SLinus Torvalds 			function = p_ev_ctrl->function;
4941da177e4SLinus Torvalds 		}
4951da177e4SLinus Torvalds 
4961da177e4SLinus Torvalds 		nummem = p_ev_ctrl->mem_avail;
4971da177e4SLinus Torvalds 		numpmem = p_ev_ctrl->p_mem_avail;
4981da177e4SLinus Torvalds 		numio = p_ev_ctrl->io_avail;
4991da177e4SLinus Torvalds 		numbus = p_ev_ctrl->bus_avail;
5001da177e4SLinus Torvalds 
5011da177e4SLinus Torvalds 		p_byte += 4;
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds 		if (p_byte > ((u8 *)p_EV_header + evbuffer_length))
5041da177e4SLinus Torvalds 			return 2;
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds 		while (nummem--) {
5075cbded58SRobert P. J. Day 			mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds 			if (!mem_node)
5101da177e4SLinus Torvalds 				break;
5111da177e4SLinus Torvalds 
5121da177e4SLinus Torvalds 			mem_node->base = *(u32 *)p_byte;
5131da177e4SLinus Torvalds 			dbg("mem base = %8.8x\n", mem_node->base);
5141da177e4SLinus Torvalds 			p_byte += 4;
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds 			if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
5171da177e4SLinus Torvalds 				kfree(mem_node);
5181da177e4SLinus Torvalds 				return 2;
5191da177e4SLinus Torvalds 			}
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 			mem_node->length = *(u32 *)p_byte;
5221da177e4SLinus Torvalds 			dbg("mem length = %8.8x\n", mem_node->length);
5231da177e4SLinus Torvalds 			p_byte += 4;
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 			if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
5261da177e4SLinus Torvalds 				kfree(mem_node);
5271da177e4SLinus Torvalds 				return 2;
5281da177e4SLinus Torvalds 			}
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds 			mem_node->next = ctrl->mem_head;
5311da177e4SLinus Torvalds 			ctrl->mem_head = mem_node;
5321da177e4SLinus Torvalds 		}
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 		while (numpmem--) {
5355cbded58SRobert P. J. Day 			p_mem_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 			if (!p_mem_node)
5381da177e4SLinus Torvalds 				break;
5391da177e4SLinus Torvalds 
5401da177e4SLinus Torvalds 			p_mem_node->base = *(u32 *)p_byte;
5411da177e4SLinus Torvalds 			dbg("pre-mem base = %8.8x\n", p_mem_node->base);
5421da177e4SLinus Torvalds 			p_byte += 4;
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 			if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
5451da177e4SLinus Torvalds 				kfree(p_mem_node);
5461da177e4SLinus Torvalds 				return 2;
5471da177e4SLinus Torvalds 			}
5481da177e4SLinus Torvalds 
5491da177e4SLinus Torvalds 			p_mem_node->length = *(u32 *)p_byte;
5501da177e4SLinus Torvalds 			dbg("pre-mem length = %8.8x\n", p_mem_node->length);
5511da177e4SLinus Torvalds 			p_byte += 4;
5521da177e4SLinus Torvalds 
5531da177e4SLinus Torvalds 			if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
5541da177e4SLinus Torvalds 				kfree(p_mem_node);
5551da177e4SLinus Torvalds 				return 2;
5561da177e4SLinus Torvalds 			}
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 			p_mem_node->next = ctrl->p_mem_head;
5591da177e4SLinus Torvalds 			ctrl->p_mem_head = p_mem_node;
5601da177e4SLinus Torvalds 		}
5611da177e4SLinus Torvalds 
5621da177e4SLinus Torvalds 		while (numio--) {
5635cbded58SRobert P. J. Day 			io_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 			if (!io_node)
5661da177e4SLinus Torvalds 				break;
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 			io_node->base = *(u32 *)p_byte;
5691da177e4SLinus Torvalds 			dbg("io base = %8.8x\n", io_node->base);
5701da177e4SLinus Torvalds 			p_byte += 4;
5711da177e4SLinus Torvalds 
5721da177e4SLinus Torvalds 			if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
5731da177e4SLinus Torvalds 				kfree(io_node);
5741da177e4SLinus Torvalds 				return 2;
5751da177e4SLinus Torvalds 			}
5761da177e4SLinus Torvalds 
5771da177e4SLinus Torvalds 			io_node->length = *(u32 *)p_byte;
5781da177e4SLinus Torvalds 			dbg("io length = %8.8x\n", io_node->length);
5791da177e4SLinus Torvalds 			p_byte += 4;
5801da177e4SLinus Torvalds 
5811da177e4SLinus Torvalds 			if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
5821da177e4SLinus Torvalds 				kfree(io_node);
5831da177e4SLinus Torvalds 				return 2;
5841da177e4SLinus Torvalds 			}
5851da177e4SLinus Torvalds 
5861da177e4SLinus Torvalds 			io_node->next = ctrl->io_head;
5871da177e4SLinus Torvalds 			ctrl->io_head = io_node;
5881da177e4SLinus Torvalds 		}
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds 		while (numbus--) {
5915cbded58SRobert P. J. Day 			bus_node = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds 			if (!bus_node)
5941da177e4SLinus Torvalds 				break;
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds 			bus_node->base = *(u32 *)p_byte;
5971da177e4SLinus Torvalds 			p_byte += 4;
5981da177e4SLinus Torvalds 
5991da177e4SLinus Torvalds 			if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
6001da177e4SLinus Torvalds 				kfree(bus_node);
6011da177e4SLinus Torvalds 				return 2;
6021da177e4SLinus Torvalds 			}
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds 			bus_node->length = *(u32 *)p_byte;
6051da177e4SLinus Torvalds 			p_byte += 4;
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds 			if (p_byte > ((u8 *)p_EV_header + evbuffer_length)) {
6081da177e4SLinus Torvalds 				kfree(bus_node);
6091da177e4SLinus Torvalds 				return 2;
6101da177e4SLinus Torvalds 			}
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 			bus_node->next = ctrl->bus_head;
6131da177e4SLinus Torvalds 			ctrl->bus_head = bus_node;
6141da177e4SLinus Torvalds 		}
6151da177e4SLinus Torvalds 
616427438c6SAlex Chiang 		/* If all of the following fail, we don't have any resources for
617427438c6SAlex Chiang 		 * hot plug add
618427438c6SAlex Chiang 		 */
6191da177e4SLinus Torvalds 		rc = 1;
6201da177e4SLinus Torvalds 		rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
6211da177e4SLinus Torvalds 		rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
6221da177e4SLinus Torvalds 		rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head));
6231da177e4SLinus Torvalds 		rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
6241da177e4SLinus Torvalds 
6251da177e4SLinus Torvalds 		if (rc)
6261da177e4SLinus Torvalds 			return(rc);
6271da177e4SLinus Torvalds 	} else {
6281da177e4SLinus Torvalds 		if ((evbuffer[0] != 0) && (!ctrl->push_flag))
6291da177e4SLinus Torvalds 			return 1;
6301da177e4SLinus Torvalds 	}
6311da177e4SLinus Torvalds 
6321da177e4SLinus Torvalds 	return 0;
6331da177e4SLinus Torvalds }
6341da177e4SLinus Torvalds 
6351da177e4SLinus Torvalds 
compaq_nvram_store(void __iomem * rom_start)6361da177e4SLinus Torvalds int compaq_nvram_store(void __iomem *rom_start)
6371da177e4SLinus Torvalds {
6381da177e4SLinus Torvalds 	int rc = 1;
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds 	if (rom_start == NULL)
6411da177e4SLinus Torvalds 		return -ENODEV;
6421da177e4SLinus Torvalds 
6431da177e4SLinus Torvalds 	if (evbuffer_init) {
6441da177e4SLinus Torvalds 		rc = store_HRT(rom_start);
645656f978fSQuentin Lambert 		if (rc)
6461da177e4SLinus Torvalds 			err(msg_unable_to_save);
6471da177e4SLinus Torvalds 	}
6481da177e4SLinus Torvalds 	return rc;
6491da177e4SLinus Torvalds }
6501da177e4SLinus Torvalds 
651