1 /*
2  * SHPCHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
3  *
4  * Copyright (C) 1995,2001 Compaq Computer Corporation
5  * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
6  * Copyright (C) 2001 IBM Corp.
7  * Copyright (C) 2003-2004 Intel Corporation
8  *
9  * All rights reserved.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or (at
14  * your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
19  * NON INFRINGEMENT.  See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  *
26  * Send feedback to <greg@kroah.com>, <dely.l.sy@intel.com>
27  *
28  */
29 
30 #include <linux/config.h>
31 #include <linux/module.h>
32 #include <linux/kernel.h>
33 #include <linux/types.h>
34 #include <linux/pci.h>
35 #include <linux/init.h>
36 #include <asm/uaccess.h>
37 #ifdef CONFIG_IA64
38 #include <asm/iosapic.h>
39 #endif
40 #include "shpchp.h"
41 #include "shpchprm.h"
42 #include "shpchprm_nonacpi.h"
43 
shpchprm_cleanup(void)44 void shpchprm_cleanup(void)
45 {
46 	return;
47 }
48 
shpchprm_print_pirt(void)49 int shpchprm_print_pirt(void)
50 {
51 	return 0;
52 }
53 
shpchprm_get_slot(struct slot * slot)54 void * shpchprm_get_slot(struct slot *slot)
55 {
56 	return NULL;
57 }
58 
shpchprm_get_physical_slot_number(struct controller * ctrl,u32 * sun,u8 busnum,u8 devnum)59 int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
60 {
61 	int	offset = devnum - ctrl->slot_device_offset;
62 
63 	dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset);
64 	*sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc * offset);
65 	return 0;
66 }
67 
print_pci_resource(struct pci_resource * aprh)68 static void print_pci_resource ( struct pci_resource	*aprh)
69 {
70 	struct pci_resource	*res;
71 
72 	for (res = aprh; res; res = res->next)
73 		dbg("        base= 0x%x length= 0x%x\n", res->base, res->length);
74 }
75 
76 
phprm_dump_func_res(struct pci_func * fun)77 static void phprm_dump_func_res( struct pci_func *fun)
78 {
79 	struct pci_func *func = fun;
80 
81 	if (func->bus_head) {
82 		dbg(":    BUS Resources:\n");
83 		print_pci_resource (func->bus_head);
84 	}
85 	if (func->io_head) {
86 		dbg(":    IO Resources:\n");
87 		print_pci_resource (func->io_head);
88 	}
89 	if (func->mem_head) {
90 		dbg(":    MEM Resources:\n");
91 		print_pci_resource (func->mem_head);
92 	}
93 	if (func->p_mem_head) {
94 		dbg(":    PMEM Resources:\n");
95 		print_pci_resource (func->p_mem_head);
96 	}
97 }
98 
phprm_get_used_resources(struct controller * ctrl,struct pci_func * func)99 static int phprm_get_used_resources (
100 	struct controller *ctrl,
101 	struct pci_func *func
102 	)
103 {
104 	return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD);
105 }
106 
phprm_delete_resource(struct pci_resource ** aprh,ulong base,ulong size)107 static int phprm_delete_resource(
108 	struct pci_resource **aprh,
109 	ulong base,
110 	ulong size)
111 {
112 	struct pci_resource *res;
113 	struct pci_resource *prevnode;
114 	struct pci_resource *split_node;
115 	ulong tbase;
116 
117 	shpchp_resource_sort_and_combine(aprh);
118 
119 	for (res = *aprh; res; res = res->next) {
120 		if (res->base > base)
121 			continue;
122 
123 		if ((res->base + res->length) < (base + size))
124 			continue;
125 
126 		if (res->base < base) {
127 			tbase = base;
128 
129 			if ((res->length - (tbase - res->base)) < size)
130 				continue;
131 
132 			split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
133 			if (!split_node)
134 				return -ENOMEM;
135 
136 			split_node->base = res->base;
137 			split_node->length = tbase - res->base;
138 			res->base = tbase;
139 			res->length -= split_node->length;
140 
141 			split_node->next = res->next;
142 			res->next = split_node;
143 		}
144 
145 		if (res->length >= size) {
146 			split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
147 			if (!split_node)
148 				return -ENOMEM;
149 
150 			split_node->base = res->base + size;
151 			split_node->length = res->length - size;
152 			res->length = size;
153 
154 			split_node->next = res->next;
155 			res->next = split_node;
156 		}
157 
158 		if (*aprh == res) {
159 			*aprh = res->next;
160 		} else {
161 			prevnode = *aprh;
162 			while (prevnode->next != res)
163 				prevnode = prevnode->next;
164 
165 			prevnode->next = res->next;
166 		}
167 		res->next = NULL;
168 		kfree(res);
169 		break;
170 	}
171 
172 	return 0;
173 }
174 
175 
phprm_delete_resources(struct pci_resource ** aprh,struct pci_resource * this)176 static int phprm_delete_resources(
177 	struct pci_resource **aprh,
178 	struct pci_resource *this
179 	)
180 {
181 	struct pci_resource *res;
182 
183 	for (res = this; res; res = res->next)
184 		phprm_delete_resource(aprh, res->base, res->length);
185 
186 	return 0;
187 }
188 
189 
configure_existing_function(struct controller * ctrl,struct pci_func * func)190 static int configure_existing_function(
191 	struct controller *ctrl,
192 	struct pci_func *func
193 	)
194 {
195 	int rc;
196 
197 	/* see how much resources the func has used. */
198 	rc = phprm_get_used_resources (ctrl, func);
199 
200 	if (!rc) {
201 		/* subtract the resources used by the func from ctrl resources */
202 		rc  = phprm_delete_resources (&ctrl->bus_head, func->bus_head);
203 		rc |= phprm_delete_resources (&ctrl->io_head, func->io_head);
204 		rc |= phprm_delete_resources (&ctrl->mem_head, func->mem_head);
205 		rc |= phprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head);
206 		if (rc)
207 			warn("aCEF: cannot del used resources\n");
208 	} else
209 		err("aCEF: cannot get used resources\n");
210 
211 	return rc;
212 }
213 
bind_pci_resources_to_slots(struct controller * ctrl)214 static int bind_pci_resources_to_slots ( struct controller *ctrl)
215 {
216 	struct pci_func *func, new_func;
217 	int busn = ctrl->slot_bus;
218 	int devn, funn;
219 	u32	vid;
220 
221 	for (devn = 0; devn < 32; devn++) {
222 		for (funn = 0; funn < 8; funn++) {
223 			/* if (devn == ctrl->device && funn == ctrl->function)
224 				continue;
225 			 */
226 			/* find out if this entry is for an occupied slot */
227 			vid = 0xFFFFFFFF;
228 
229 			pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid);
230 
231 			if (vid != 0xFFFFFFFF) {
232 				func = shpchp_slot_find(busn, devn, funn);
233 				if (!func) {
234 					memset(&new_func, 0, sizeof(struct pci_func));
235 					new_func.bus = busn;
236 					new_func.device = devn;
237 					new_func.function = funn;
238 					new_func.is_a_board = 1;
239 					configure_existing_function(ctrl, &new_func);
240 					phprm_dump_func_res(&new_func);
241 				} else {
242 					configure_existing_function(ctrl, func);
243 					phprm_dump_func_res(func);
244 				}
245 				dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus);
246 			}
247 		}
248 	}
249 
250 	return 0;
251 }
252 
phprm_dump_ctrl_res(struct controller * ctlr)253 static void phprm_dump_ctrl_res( struct controller *ctlr)
254 {
255 	struct controller *ctrl = ctlr;
256 
257 	if (ctrl->bus_head) {
258 		dbg(":    BUS Resources:\n");
259 		print_pci_resource (ctrl->bus_head);
260 	}
261 	if (ctrl->io_head) {
262 		dbg(":    IO Resources:\n");
263 		print_pci_resource (ctrl->io_head);
264 	}
265 	if (ctrl->mem_head) {
266 		dbg(":    MEM Resources:\n");
267 		print_pci_resource (ctrl->mem_head);
268 	}
269 	if (ctrl->p_mem_head) {
270 		dbg(":    PMEM Resources:\n");
271 		print_pci_resource (ctrl->p_mem_head);
272 	}
273 }
274 
275 /*
276  * phprm_find_available_resources
277  *
278  *  Finds available memory, IO, and IRQ resources for programming
279  *  devices which may be added to the system
280  *  this function is for hot plug ADD!
281  *
282  * returns 0 if success
283  */
shpchprm_find_available_resources(struct controller * ctrl)284 int shpchprm_find_available_resources(struct controller *ctrl)
285 {
286 	struct pci_func func;
287 	u32 rc;
288 
289 	memset(&func, 0, sizeof(struct pci_func));
290 
291 	func.bus = ctrl->bus;
292 	func.device = ctrl->device;
293 	func.function = ctrl->function;
294 	func.is_a_board = 1;
295 
296 	/* Get resources for this PCI bridge */
297 	rc = shpchp_save_used_resources (ctrl, &func, !DISABLE_CARD);
298 	dbg("%s: shpchp_save_used_resources rc = %d\n", __FUNCTION__, rc);
299 
300 	if (func.mem_head)
301 		func.mem_head->next = ctrl->mem_head;
302 	ctrl->mem_head = func.mem_head;
303 
304 	if (func.p_mem_head)
305 		func.p_mem_head->next = ctrl->p_mem_head;
306 	ctrl->p_mem_head = func.p_mem_head;
307 
308 	if (func.io_head)
309 		func.io_head->next = ctrl->io_head;
310 	ctrl->io_head = func.io_head;
311 
312 	if(func.bus_head)
313 		func.bus_head->next = ctrl->bus_head;
314 	ctrl->bus_head = func.bus_head;
315 	if (ctrl->bus_head)
316 		phprm_delete_resource(&ctrl->bus_head, ctrl->pci_dev->subordinate->number, 1);
317 
318 	dbg("%s:pre-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
319 	phprm_dump_ctrl_res(ctrl);
320 
321 	bind_pci_resources_to_slots (ctrl);
322 
323 	dbg("%s:post-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
324 	phprm_dump_ctrl_res(ctrl);
325 
326 
327 	/* If all of the following fail, we don't have any resources for hot plug add */
328 	rc = 1;
329 	rc &= shpchp_resource_sort_and_combine(&(ctrl->mem_head));
330 	rc &= shpchp_resource_sort_and_combine(&(ctrl->p_mem_head));
331 	rc &= shpchp_resource_sort_and_combine(&(ctrl->io_head));
332 	rc &= shpchp_resource_sort_and_combine(&(ctrl->bus_head));
333 
334 	return (rc);
335 }
336 
shpchprm_set_hpp(struct controller * ctrl,struct pci_func * func,u8 card_type)337 int shpchprm_set_hpp(
338 	struct controller *ctrl,
339 	struct pci_func *func,
340 	u8	card_type)
341 {
342 	u32 rc;
343 	u8 temp_byte;
344 	struct pci_bus lpci_bus, *pci_bus;
345 	unsigned int	devfn;
346 	memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
347 	pci_bus = &lpci_bus;
348 	pci_bus->number = func->bus;
349 	devfn = PCI_DEVFN(func->device, func->function);
350 
351 	temp_byte = 0x40;	/* hard coded value for LT */
352 	if (card_type == PCI_HEADER_TYPE_BRIDGE) {
353 		/* set subordinate Latency Timer */
354 		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
355 
356 		if (rc) {
357 			dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
358 			return rc;
359 		}
360 	}
361 
362 	/* set base Latency Timer */
363 	rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
364 
365 	if (rc) {
366 		dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
367 		return rc;
368 	}
369 
370 	/* set Cache Line size */
371 	temp_byte = 0x08;	/* hard coded value for CLS */
372 
373 	rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
374 
375 	if (rc) {
376 		dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
377 	}
378 
379 	/* set enable_perr */
380 	/* set enable_serr */
381 
382 	return rc;
383 }
384 
shpchprm_enable_card(struct controller * ctrl,struct pci_func * func,u8 card_type)385 void shpchprm_enable_card(
386 	struct controller *ctrl,
387 	struct pci_func *func,
388 	u8 card_type)
389 {
390 	u16 command, bcommand;
391 	struct pci_bus lpci_bus, *pci_bus;
392 	unsigned int devfn;
393 	int rc;
394 
395 	memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
396 	pci_bus = &lpci_bus;
397 	pci_bus->number = func->bus;
398 	devfn = PCI_DEVFN(func->device, func->function);
399 
400 	rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
401 
402 	command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR
403 		| PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
404 		| PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
405 
406 	rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
407 
408 	if (card_type == PCI_HEADER_TYPE_BRIDGE) {
409 
410 		rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
411 
412 		bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR
413 			| PCI_BRIDGE_CTL_NO_ISA;
414 
415 		rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
416 	}
417 }
418 
legacy_shpchprm_init_pci(void)419 static int legacy_shpchprm_init_pci(void)
420 {
421 	return 0;
422 }
423 
shpchprm_init(enum php_ctlr_type ctrl_type)424 int shpchprm_init(enum php_ctlr_type ctrl_type)
425 {
426 	int retval;
427 
428 	switch (ctrl_type) {
429 	case PCI:
430 		retval = legacy_shpchprm_init_pci();
431 		break;
432 	default:
433 		retval = -ENODEV;
434 		break;
435 	}
436 
437 	return retval;
438 }
439