]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * drivers/sh/superhyway/superhyway.c | |
3 | * | |
4 | * SuperHyway Bus Driver | |
5 | * | |
6 | * Copyright (C) 2004, 2005 Paul Mundt <lethal@linux-sh.org> | |
7 | * | |
8 | * This file is subject to the terms and conditions of the GNU General Public | |
9 | * License. See the file "COPYING" in the main directory of this archive | |
10 | * for more details. | |
11 | */ | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/types.h> | |
17 | #include <linux/list.h> | |
18 | #include <linux/superhyway.h> | |
4e57b681 TS |
19 | #include <linux/string.h> |
20 | #include <linux/slab.h> | |
1da177e4 LT |
21 | |
22 | static int superhyway_devices; | |
23 | ||
24 | static struct device superhyway_bus_device = { | |
93fde774 | 25 | .init_name = "superhyway", |
1da177e4 LT |
26 | }; |
27 | ||
28 | static void superhyway_device_release(struct device *dev) | |
29 | { | |
055a2512 PM |
30 | struct superhyway_device *sdev = to_superhyway_device(dev); |
31 | ||
32 | kfree(sdev->resource); | |
33 | kfree(sdev); | |
1da177e4 LT |
34 | } |
35 | ||
36 | /** | |
37 | * superhyway_add_device - Add a SuperHyway module | |
1da177e4 | 38 | * @base: Physical address where module is mapped. |
055a2512 PM |
39 | * @sdev: SuperHyway device to add, or NULL to allocate a new one. |
40 | * @bus: Bus where SuperHyway module resides. | |
1da177e4 LT |
41 | * |
42 | * This is responsible for adding a new SuperHyway module. This sets up a new | |
055a2512 | 43 | * struct superhyway_device for the module being added if @sdev == NULL. |
1da177e4 LT |
44 | * |
45 | * Devices are initially added in the order that they are scanned (from the | |
46 | * top-down of the memory map), and are assigned an ID based on the order that | |
47 | * they are added. Any manual addition of a module will thus get the ID after | |
48 | * the devices already discovered regardless of where it resides in memory. | |
49 | * | |
50 | * Further work can and should be done in superhyway_scan_bus(), to be sure | |
51 | * that any new modules are properly discovered and subsequently registered. | |
52 | */ | |
055a2512 PM |
53 | int superhyway_add_device(unsigned long base, struct superhyway_device *sdev, |
54 | struct superhyway_bus *bus) | |
1da177e4 | 55 | { |
055a2512 PM |
56 | struct superhyway_device *dev = sdev; |
57 | ||
58 | if (!dev) { | |
dd00cc48 | 59 | dev = kzalloc(sizeof(struct superhyway_device), GFP_KERNEL); |
055a2512 PM |
60 | if (!dev) |
61 | return -ENOMEM; | |
1da177e4 | 62 | |
055a2512 | 63 | } |
1da177e4 | 64 | |
055a2512 PM |
65 | dev->bus = bus; |
66 | superhyway_read_vcr(dev, base, &dev->vcr); | |
1da177e4 | 67 | |
055a2512 PM |
68 | if (!dev->resource) { |
69 | dev->resource = kmalloc(sizeof(struct resource), GFP_KERNEL); | |
70 | if (!dev->resource) { | |
71 | kfree(dev); | |
72 | return -ENOMEM; | |
73 | } | |
74 | ||
75 | dev->resource->name = dev->name; | |
76 | dev->resource->start = base; | |
77 | dev->resource->end = dev->resource->start + 0x01000000; | |
78 | } | |
1da177e4 | 79 | |
1da177e4 LT |
80 | dev->dev.parent = &superhyway_bus_device; |
81 | dev->dev.bus = &superhyway_bus_type; | |
82 | dev->dev.release = superhyway_device_release; | |
055a2512 | 83 | dev->id.id = dev->vcr.mod_id; |
1da177e4 | 84 | |
055a2512 | 85 | sprintf(dev->name, "SuperHyway device %04x", dev->id.id); |
93fde774 | 86 | dev_set_name(&dev->dev, "%02x", superhyway_devices); |
1da177e4 LT |
87 | |
88 | superhyway_devices++; | |
89 | ||
90 | return device_register(&dev->dev); | |
91 | } | |
92 | ||
055a2512 PM |
93 | int superhyway_add_devices(struct superhyway_bus *bus, |
94 | struct superhyway_device **devices, | |
95 | int nr_devices) | |
96 | { | |
97 | int i, ret = 0; | |
98 | ||
99 | for (i = 0; i < nr_devices; i++) { | |
100 | struct superhyway_device *dev = devices[i]; | |
101 | ret |= superhyway_add_device(dev->resource[0].start, dev, bus); | |
102 | } | |
103 | ||
104 | return ret; | |
105 | } | |
106 | ||
1da177e4 LT |
107 | static int __init superhyway_init(void) |
108 | { | |
055a2512 | 109 | struct superhyway_bus *bus; |
07782cec | 110 | int ret; |
055a2512 | 111 | |
07782cec PM |
112 | ret = device_register(&superhyway_bus_device); |
113 | if (unlikely(ret)) | |
114 | return ret; | |
055a2512 PM |
115 | |
116 | for (bus = superhyway_channels; bus->ops; bus++) | |
117 | ret |= superhyway_scan_bus(bus); | |
118 | ||
119 | return ret; | |
1da177e4 | 120 | } |
1da177e4 LT |
121 | postcore_initcall(superhyway_init); |
122 | ||
123 | static const struct superhyway_device_id * | |
124 | superhyway_match_id(const struct superhyway_device_id *ids, | |
125 | struct superhyway_device *dev) | |
126 | { | |
127 | while (ids->id) { | |
128 | if (ids->id == dev->id.id) | |
129 | return ids; | |
130 | ||
131 | ids++; | |
132 | } | |
133 | ||
134 | return NULL; | |
135 | } | |
136 | ||
137 | static int superhyway_device_probe(struct device *dev) | |
138 | { | |
139 | struct superhyway_device *shyway_dev = to_superhyway_device(dev); | |
140 | struct superhyway_driver *shyway_drv = to_superhyway_driver(dev->driver); | |
141 | ||
142 | if (shyway_drv && shyway_drv->probe) { | |
143 | const struct superhyway_device_id *id; | |
144 | ||
145 | id = superhyway_match_id(shyway_drv->id_table, shyway_dev); | |
146 | if (id) | |
147 | return shyway_drv->probe(shyway_dev, id); | |
148 | } | |
149 | ||
150 | return -ENODEV; | |
151 | } | |
152 | ||
153 | static int superhyway_device_remove(struct device *dev) | |
154 | { | |
155 | struct superhyway_device *shyway_dev = to_superhyway_device(dev); | |
156 | struct superhyway_driver *shyway_drv = to_superhyway_driver(dev->driver); | |
157 | ||
158 | if (shyway_drv && shyway_drv->remove) { | |
159 | shyway_drv->remove(shyway_dev); | |
160 | return 0; | |
161 | } | |
162 | ||
163 | return -ENODEV; | |
164 | } | |
165 | ||
166 | /** | |
167 | * superhyway_register_driver - Register a new SuperHyway driver | |
168 | * @drv: SuperHyway driver to register. | |
169 | * | |
170 | * This registers the passed in @drv. Any devices matching the id table will | |
171 | * automatically be populated and handed off to the driver's specified probe | |
172 | * routine. | |
173 | */ | |
174 | int superhyway_register_driver(struct superhyway_driver *drv) | |
175 | { | |
176 | drv->drv.name = drv->name; | |
177 | drv->drv.bus = &superhyway_bus_type; | |
1da177e4 LT |
178 | |
179 | return driver_register(&drv->drv); | |
180 | } | |
181 | ||
182 | /** | |
183 | * superhyway_unregister_driver - Unregister a SuperHyway driver | |
184 | * @drv: SuperHyway driver to unregister. | |
185 | * | |
186 | * This cleans up after superhyway_register_driver(), and should be invoked in | |
187 | * the exit path of any module drivers. | |
188 | */ | |
189 | void superhyway_unregister_driver(struct superhyway_driver *drv) | |
190 | { | |
191 | driver_unregister(&drv->drv); | |
192 | } | |
193 | ||
194 | static int superhyway_bus_match(struct device *dev, struct device_driver *drv) | |
195 | { | |
196 | struct superhyway_device *shyway_dev = to_superhyway_device(dev); | |
197 | struct superhyway_driver *shyway_drv = to_superhyway_driver(drv); | |
198 | const struct superhyway_device_id *ids = shyway_drv->id_table; | |
199 | ||
200 | if (!ids) | |
201 | return -EINVAL; | |
202 | if (superhyway_match_id(ids, shyway_dev)) | |
203 | return 1; | |
204 | ||
205 | return -ENODEV; | |
206 | } | |
207 | ||
208 | struct bus_type superhyway_bus_type = { | |
209 | .name = "superhyway", | |
210 | .match = superhyway_bus_match, | |
211 | #ifdef CONFIG_SYSFS | |
212 | .dev_attrs = superhyway_dev_attrs, | |
213 | #endif | |
ff2dae79 RK |
214 | .probe = superhyway_device_probe, |
215 | .remove = superhyway_device_remove, | |
1da177e4 LT |
216 | }; |
217 | ||
218 | static int __init superhyway_bus_init(void) | |
219 | { | |
220 | return bus_register(&superhyway_bus_type); | |
221 | } | |
222 | ||
223 | static void __exit superhyway_bus_exit(void) | |
224 | { | |
225 | device_unregister(&superhyway_bus_device); | |
226 | bus_unregister(&superhyway_bus_type); | |
227 | } | |
228 | ||
229 | core_initcall(superhyway_bus_init); | |
230 | module_exit(superhyway_bus_exit); | |
231 | ||
232 | EXPORT_SYMBOL(superhyway_bus_type); | |
233 | EXPORT_SYMBOL(superhyway_add_device); | |
055a2512 | 234 | EXPORT_SYMBOL(superhyway_add_devices); |
1da177e4 LT |
235 | EXPORT_SYMBOL(superhyway_register_driver); |
236 | EXPORT_SYMBOL(superhyway_unregister_driver); | |
237 | ||
238 | MODULE_LICENSE("GPL"); |