aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sn/ioc4.c
diff options
context:
space:
mode:
authorBrent Casavant <bcasavan@sgi.com>2005-06-21 20:15:59 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-21 21:46:32 -0400
commit22329b511a97557b293583194037d1f4c71e1504 (patch)
tree925e6c4566371e7ffb66a54b631049b958c19eca /drivers/sn/ioc4.c
parente400bae98499583767da58fb0a1b9ad3e24fcb86 (diff)
[PATCH] ioc4: Core driver rewrite
This series of patches reworks the configuration and internal structure of the SGI IOC4 I/O controller device drivers. These changes are motivated by several factors: - The IOC4 chip PCI resources are of mixed use between functions (i.e. multiple functions are handled in the same address range, sometimes within the same register), muddling resource ownership and initialization issues. Centralizing this ownership in a core driver is desirable. - The IOC4 chip implements multiple functions (serial, IDE, others not yet implemented in the mainline kernel) but is not a multifunction PCI device. In order to properly handle device addition and removal as well as module insertion and deletion, an intermediary IOC4-specific driver layer is needed to handle these operations cleanly. - All IOC4 drivers are currently enabled by a single CONFIG value. As not all systems need all IOC4 functions, it is desireable to enable these drivers independently. - The current IOC4 core driver will trigger loading of all function-level drivers, as it makes direct calls to them. This situation should be reversed (i.e. function-level drivers cause loading of core driver) in order to maintain a clear and least-surprise driver loading model. - IOC4 hardware design necessitates some driver-level dependency on the PCI bus clock speed. Current code assumes a 66MHz bus, but the speed should be autodetected and appropriate compensation taken. This patch series effects the above changes by a newly and better designed IOC4 core driver with which the function-level drivers can register and deregister themselves upon module insertion/removal. By tracking these modules, device addition/removal is also handled properly. PCI resource management and ownership issues are centralized in this core driver, and IOC4-wide configuration actions such as bus speed detection are also handled in this core driver. This patch: The SGI IOC4 I/O controller chip implements multiple functions, though it is not a multi-function PCI device. Additionally, various PCI resources of the IOC4 are shared by multiple hardware functions, and thus resource ownership by driver is not clearly delineated. Due to the current driver design, all core and subordinate drivers must be loaded, or none, which is undesirable if not all IOC4 hardware features are being used. This patch reorganizes the IOC4 drivers so that the core driver provides a subdriver registration service. Through appropriate callbacks the subdrivers can now handle device addition and removal, as well as module insertion and deletion (though the IOC4 IDE driver requires further work before module deletion will work). The core driver now takes care of allocating PCI resources and data which must be shared between subdrivers, to clearly delineate module ownership of these items. Signed-off-by: Brent Casavant <bcasavan@sgi.com> Acked-by: Pat Gefre <pfg@sgi.com Acked-by: Jeremy Higdon <jeremy@sgi.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/sn/ioc4.c')
-rw-r--r--drivers/sn/ioc4.c290
1 files changed, 262 insertions, 28 deletions
diff --git a/drivers/sn/ioc4.c b/drivers/sn/ioc4.c
index d9e4ee280e5f..70862d72ea9d 100644
--- a/drivers/sn/ioc4.c
+++ b/drivers/sn/ioc4.c
@@ -6,60 +6,294 @@
6 * Copyright (C) 2005 Silicon Graphics, Inc. All Rights Reserved. 6 * Copyright (C) 2005 Silicon Graphics, Inc. All Rights Reserved.
7 */ 7 */
8 8
9/* 9/* This file contains the master driver module for use by SGI IOC4 subdrivers.
10 * This file contains a shim driver for the IOC4 IDE and serial drivers. 10 *
11 * It allocates any resources shared between multiple subdevices, and
12 * provides accessor functions (where needed) and the like for those
13 * resources. It also provides a mechanism for the subdevice modules
14 * to support loading and unloading.
15 *
16 * Non-shared resources (e.g. external interrupt A_INT_OUT register page
17 * alias, serial port and UART registers) are handled by the subdevice
18 * modules themselves.
19 *
20 * This is all necessary because IOC4 is not implemented as a multi-function
21 * PCI device, but an amalgamation of disparate registers for several
22 * types of device (ATA, serial, external interrupts). The normal
23 * resource management in the kernel doesn't have quite the right interfaces
24 * to handle this situation (e.g. multiple modules can't claim the same
25 * PCI ID), thus this IOC4 master module.
11 */ 26 */
12 27
13#include <linux/errno.h> 28#include <linux/errno.h>
14#include <linux/module.h> 29#include <linux/module.h>
15#include <linux/pci.h> 30#include <linux/pci.h>
16#include <linux/ioc4_common.h> 31#include <linux/ioc4.h>
17#include <linux/ide.h> 32#include <linux/rwsem.h>
18 33
34/************************
35 * Submodule management *
36 ************************/
19 37
20static int __devinit 38static LIST_HEAD(ioc4_devices);
21ioc4_probe_one(struct pci_dev *pdev, const struct pci_device_id *pci_id) 39static DECLARE_RWSEM(ioc4_devices_rwsem);
40
41static LIST_HEAD(ioc4_submodules);
42static DECLARE_RWSEM(ioc4_submodules_rwsem);
43
44/* Register an IOC4 submodule */
45int
46ioc4_register_submodule(struct ioc4_submodule *is)
47{
48 struct ioc4_driver_data *idd;
49
50 down_write(&ioc4_submodules_rwsem);
51 list_add(&is->is_list, &ioc4_submodules);
52 up_write(&ioc4_submodules_rwsem);
53
54 /* Initialize submodule for each IOC4 */
55 if (!is->is_probe)
56 return 0;
57
58 down_read(&ioc4_devices_rwsem);
59 list_for_each_entry(idd, &ioc4_devices, idd_list) {
60 if (is->is_probe(idd)) {
61 printk(KERN_WARNING
62 "%s: IOC4 submodule %s probe failed "
63 "for pci_dev %s",
64 __FUNCTION__, module_name(is->is_owner),
65 pci_name(idd->idd_pdev));
66 }
67 }
68 up_read(&ioc4_devices_rwsem);
69
70 return 0;
71}
72
73/* Unregister an IOC4 submodule */
74void
75ioc4_unregister_submodule(struct ioc4_submodule *is)
22{ 76{
77 struct ioc4_driver_data *idd;
78
79 down_write(&ioc4_submodules_rwsem);
80 list_del(&is->is_list);
81 up_write(&ioc4_submodules_rwsem);
82
83 /* Remove submodule for each IOC4 */
84 if (!is->is_remove)
85 return;
86
87 down_read(&ioc4_devices_rwsem);
88 list_for_each_entry(idd, &ioc4_devices, idd_list) {
89 if (is->is_remove(idd)) {
90 printk(KERN_WARNING
91 "%s: IOC4 submodule %s remove failed "
92 "for pci_dev %s.\n",
93 __FUNCTION__, module_name(is->is_owner),
94 pci_name(idd->idd_pdev));
95 }
96 }
97 up_read(&ioc4_devices_rwsem);
98}
99
100/*********************
101 * Device management *
102 *********************/
103
104/* Adds a new instance of an IOC4 card */
105static int
106ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
107{
108 struct ioc4_driver_data *idd;
109 struct ioc4_submodule *is;
110 uint32_t pcmd;
23 int ret; 111 int ret;
24 112
113 /* Enable IOC4 and take ownership of it */
25 if ((ret = pci_enable_device(pdev))) { 114 if ((ret = pci_enable_device(pdev))) {
26 printk(KERN_WARNING 115 printk(KERN_WARNING
27 "%s: Failed to enable device with " 116 "%s: Failed to enable IOC4 device for pci_dev %s.\n",
28 "pci_dev 0x%p... returning\n", 117 __FUNCTION__, pci_name(pdev));
29 __FUNCTION__, (void *)pdev); 118 goto out;
30 return ret;
31 } 119 }
32 pci_set_master(pdev); 120 pci_set_master(pdev);
33 121
34 /* attach each sub-device */ 122 /* Set up per-IOC4 data */
35 ret = ioc4_ide_attach_one(pdev, pci_id); 123 idd = kmalloc(sizeof(struct ioc4_driver_data), GFP_KERNEL);
36 if (ret) 124 if (!idd) {
37 return ret; 125 printk(KERN_WARNING
38 return ioc4_serial_attach_one(pdev, pci_id); 126 "%s: Failed to allocate IOC4 data for pci_dev %s.\n",
127 __FUNCTION__, pci_name(pdev));
128 ret = -ENODEV;
129 goto out_idd;
130 }
131 idd->idd_pdev = pdev;
132 idd->idd_pci_id = pci_id;
133
134 /* Map IOC4 misc registers. These are shared between subdevices
135 * so the main IOC4 module manages them.
136 */
137 idd->idd_bar0 = pci_resource_start(idd->idd_pdev, 0);
138 if (!idd->idd_bar0) {
139 printk(KERN_WARNING
140 "%s: Unable to find IOC4 misc resource "
141 "for pci_dev %s.\n",
142 __FUNCTION__, pci_name(idd->idd_pdev));
143 ret = -ENODEV;
144 goto out_pci;
145 }
146 if (!request_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs),
147 "ioc4_misc")) {
148 printk(KERN_WARNING
149 "%s: Unable to request IOC4 misc region "
150 "for pci_dev %s.\n",
151 __FUNCTION__, pci_name(idd->idd_pdev));
152 ret = -ENODEV;
153 goto out_pci;
154 }
155 idd->idd_misc_regs = ioremap(idd->idd_bar0,
156 sizeof(struct ioc4_misc_regs));
157 if (!idd->idd_misc_regs) {
158 printk(KERN_WARNING
159 "%s: Unable to remap IOC4 misc region "
160 "for pci_dev %s.\n",
161 __FUNCTION__, pci_name(idd->idd_pdev));
162 ret = -ENODEV;
163 goto out_misc_region;
164 }
165
166 /* Failsafe portion of per-IOC4 initialization */
167
168 /* Initialize IOC4 */
169 pci_read_config_dword(idd->idd_pdev, PCI_COMMAND, &pcmd);
170 pci_write_config_dword(idd->idd_pdev, PCI_COMMAND,
171 pcmd | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
172
173 /* Disable/clear all interrupts. Need to do this here lest
174 * one submodule request the shared IOC4 IRQ, but interrupt
175 * is generated by a different subdevice.
176 */
177 /* Disable */
178 writel(~0, &idd->idd_misc_regs->other_iec.raw);
179 writel(~0, &idd->idd_misc_regs->sio_iec);
180 /* Clear (i.e. acknowledge) */
181 writel(~0, &idd->idd_misc_regs->other_ir.raw);
182 writel(~0, &idd->idd_misc_regs->sio_ir);
183
184 /* Track PCI-device specific data */
185 idd->idd_serial_data = NULL;
186 pci_set_drvdata(idd->idd_pdev, idd);
187 down_write(&ioc4_devices_rwsem);
188 list_add(&idd->idd_list, &ioc4_devices);
189 up_write(&ioc4_devices_rwsem);
190
191 /* Add this IOC4 to all submodules */
192 down_read(&ioc4_submodules_rwsem);
193 list_for_each_entry(is, &ioc4_submodules, is_list) {
194 if (is->is_probe && is->is_probe(idd)) {
195 printk(KERN_WARNING
196 "%s: IOC4 submodule 0x%s probe failed "
197 "for pci_dev %s.\n",
198 __FUNCTION__, module_name(is->is_owner),
199 pci_name(idd->idd_pdev));
200 }
201 }
202 up_read(&ioc4_submodules_rwsem);
203
204 return 0;
205
206out_misc_region:
207 release_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs));
208out_pci:
209 kfree(idd);
210out_idd:
211 pci_disable_device(pdev);
212out:
213 return ret;
39} 214}
40 215
41/* pci device struct */ 216/* Removes a particular instance of an IOC4 card. */
42static struct pci_device_id ioc4_s_id_table[] = { 217static void
218ioc4_remove(struct pci_dev *pdev)
219{
220 struct ioc4_submodule *is;
221 struct ioc4_driver_data *idd;
222
223 idd = pci_get_drvdata(pdev);
224
225 /* Remove this IOC4 from all submodules */
226 down_read(&ioc4_submodules_rwsem);
227 list_for_each_entry(is, &ioc4_submodules, is_list) {
228 if (is->is_remove && is->is_remove(idd)) {
229 printk(KERN_WARNING
230 "%s: IOC4 submodule 0x%s remove failed "
231 "for pci_dev %s.\n",
232 __FUNCTION__, module_name(is->is_owner),
233 pci_name(idd->idd_pdev));
234 }
235 }
236 up_read(&ioc4_submodules_rwsem);
237
238 /* Release resources */
239 iounmap(idd->idd_misc_regs);
240 if (!idd->idd_bar0) {
241 printk(KERN_WARNING
242 "%s: Unable to get IOC4 misc mapping for pci_dev %s. "
243 "Device removal may be incomplete.\n",
244 __FUNCTION__, pci_name(idd->idd_pdev));
245 }
246 release_region(idd->idd_bar0, sizeof(struct ioc4_misc_regs));
247
248 /* Disable IOC4 and relinquish */
249 pci_disable_device(pdev);
250
251 /* Remove and free driver data */
252 down_write(&ioc4_devices_rwsem);
253 list_del(&idd->idd_list);
254 up_write(&ioc4_devices_rwsem);
255 kfree(idd);
256}
257
258static struct pci_device_id ioc4_id_table[] = {
43 {PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID, 259 {PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID,
44 PCI_ANY_ID, 0x0b4000, 0xFFFFFF}, 260 PCI_ANY_ID, 0x0b4000, 0xFFFFFF},
45 {0} 261 {0}
46}; 262};
47MODULE_DEVICE_TABLE(pci, ioc4_s_id_table);
48 263
49static struct pci_driver __devinitdata ioc4_s_driver = { 264static struct pci_driver __devinitdata ioc4_driver = {
50 .name = "IOC4", 265 .name = "IOC4",
51 .id_table = ioc4_s_id_table, 266 .id_table = ioc4_id_table,
52 .probe = ioc4_probe_one, 267 .probe = ioc4_probe,
268 .remove = ioc4_remove,
53}; 269};
54 270
55static int __devinit ioc4_detect(void) 271MODULE_DEVICE_TABLE(pci, ioc4_id_table);
272
273/*********************
274 * Module management *
275 *********************/
276
277/* Module load */
278static int __devinit
279ioc4_init(void)
56{ 280{
57 ioc4_serial_init(); 281 return pci_register_driver(&ioc4_driver);
282}
58 283
59 return pci_register_driver(&ioc4_s_driver); 284/* Module unload */
285static void __devexit
286ioc4_exit(void)
287{
288 pci_unregister_driver(&ioc4_driver);
60} 289}
61module_init(ioc4_detect);
62 290
63MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>"); 291module_init(ioc4_init);
64MODULE_DESCRIPTION("PCI driver module for SGI IOC4 Base-IO Card"); 292module_exit(ioc4_exit);
293
294MODULE_AUTHOR("Brent Casavant - Silicon Graphics, Inc. <bcasavan@sgi.com>");
295MODULE_DESCRIPTION("PCI driver master module for SGI IOC4 Base-IO Card");
65MODULE_LICENSE("GPL"); 296MODULE_LICENSE("GPL");
297
298EXPORT_SYMBOL(ioc4_register_submodule);
299EXPORT_SYMBOL(ioc4_unregister_submodule);