diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-18 13:23:37 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-18 13:23:37 -0400 |
commit | 31bdc5dc7666aa2fe04c626cea30fe3c20cf481c (patch) | |
tree | a1a78a39379e081e9982c3273a71b4e93e8c1fd0 /arch | |
parent | 5cc97bf2d8eaa6cab60727c3eba3e85e29062669 (diff) | |
parent | a5f8967e171a6fa27da8e6d06d3ef85f7fed43c1 (diff) |
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6
* 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6:
[SPARC64]: Set vio->desc_buf to NULL after freeing.
[SPARC]: Mark sparc and sparc64 as not having virt_to_bus
[SPARC64]: Fix reset handling in VNET driver.
[SPARC64]: Handle reset events in vio_link_state_change().
[SPARC64]: Handle LDC resets properly in domain-services driver.
[SPARC64]: Massively simplify VIO device layer and support hot add/remove.
[SPARC64]: Simplify VNET probing.
[SPARC64]: Simplify VDC device probing.
[SPARC64]: Add basic infrastructure for MD add/remove notification.
Diffstat (limited to 'arch')
-rw-r--r-- | arch/sparc/Kconfig | 3 | ||||
-rw-r--r-- | arch/sparc64/Kconfig | 3 | ||||
-rw-r--r-- | arch/sparc64/kernel/ds.c | 19 | ||||
-rw-r--r-- | arch/sparc64/kernel/mdesc.c | 78 | ||||
-rw-r--r-- | arch/sparc64/kernel/vio.c | 94 | ||||
-rw-r--r-- | arch/sparc64/kernel/viohs.c | 30 |
6 files changed, 191 insertions, 36 deletions
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 73df7115325b..603d83ad65c8 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig | |||
@@ -21,6 +21,9 @@ config GENERIC_ISA_DMA | |||
21 | bool | 21 | bool |
22 | default y | 22 | default y |
23 | 23 | ||
24 | config ARCH_NO_VIRT_TO_BUS | ||
25 | def_bool y | ||
26 | |||
24 | source "init/Kconfig" | 27 | source "init/Kconfig" |
25 | 28 | ||
26 | menu "General machine setup" | 29 | menu "General machine setup" |
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index b84b6af1241e..df6ee71894d1 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig | |||
@@ -62,6 +62,9 @@ config AUDIT_ARCH | |||
62 | bool | 62 | bool |
63 | default y | 63 | default y |
64 | 64 | ||
65 | config ARCH_NO_VIRT_TO_BUS | ||
66 | def_bool y | ||
67 | |||
65 | choice | 68 | choice |
66 | prompt "Kernel page size" | 69 | prompt "Kernel page size" |
67 | default SPARC64_PAGE_SIZE_8KB | 70 | default SPARC64_PAGE_SIZE_8KB |
diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c index ba01533f4e03..fa1f04d756a2 100644 --- a/arch/sparc64/kernel/ds.c +++ b/arch/sparc64/kernel/ds.c | |||
@@ -1013,6 +1013,19 @@ static void ds_up(struct ds_info *dp) | |||
1013 | dp->hs_state = DS_HS_START; | 1013 | dp->hs_state = DS_HS_START; |
1014 | } | 1014 | } |
1015 | 1015 | ||
1016 | static void ds_reset(struct ds_info *dp) | ||
1017 | { | ||
1018 | int i; | ||
1019 | |||
1020 | dp->hs_state = 0; | ||
1021 | |||
1022 | for (i = 0; i < ARRAY_SIZE(ds_states); i++) { | ||
1023 | struct ds_cap_state *cp = &ds_states[i]; | ||
1024 | |||
1025 | cp->state = CAP_STATE_UNKNOWN; | ||
1026 | } | ||
1027 | } | ||
1028 | |||
1016 | static void ds_event(void *arg, int event) | 1029 | static void ds_event(void *arg, int event) |
1017 | { | 1030 | { |
1018 | struct ds_info *dp = arg; | 1031 | struct ds_info *dp = arg; |
@@ -1028,6 +1041,12 @@ static void ds_event(void *arg, int event) | |||
1028 | return; | 1041 | return; |
1029 | } | 1042 | } |
1030 | 1043 | ||
1044 | if (event == LDC_EVENT_RESET) { | ||
1045 | ds_reset(dp); | ||
1046 | spin_unlock_irqrestore(&ds_lock, flags); | ||
1047 | return; | ||
1048 | } | ||
1049 | |||
1031 | if (event != LDC_EVENT_DATA_READY) { | 1050 | if (event != LDC_EVENT_DATA_READY) { |
1032 | printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event); | 1051 | printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event); |
1033 | spin_unlock_irqrestore(&ds_lock, flags); | 1052 | spin_unlock_irqrestore(&ds_lock, flags); |
diff --git a/arch/sparc64/kernel/mdesc.c b/arch/sparc64/kernel/mdesc.c index de5310ffdb48..302ba5e5a0bb 100644 --- a/arch/sparc64/kernel/mdesc.c +++ b/arch/sparc64/kernel/mdesc.c | |||
@@ -137,7 +137,7 @@ static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size) | |||
137 | sizeof(struct mdesc_hdr) + | 137 | sizeof(struct mdesc_hdr) + |
138 | mdesc_size); | 138 | mdesc_size); |
139 | 139 | ||
140 | base = kmalloc(handle_size + 15, GFP_KERNEL); | 140 | base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL); |
141 | if (base) { | 141 | if (base) { |
142 | struct mdesc_handle *hp; | 142 | struct mdesc_handle *hp; |
143 | unsigned long addr; | 143 | unsigned long addr; |
@@ -214,18 +214,83 @@ void mdesc_release(struct mdesc_handle *hp) | |||
214 | } | 214 | } |
215 | EXPORT_SYMBOL(mdesc_release); | 215 | EXPORT_SYMBOL(mdesc_release); |
216 | 216 | ||
217 | static DEFINE_MUTEX(mdesc_mutex); | ||
218 | static struct mdesc_notifier_client *client_list; | ||
219 | |||
220 | void mdesc_register_notifier(struct mdesc_notifier_client *client) | ||
221 | { | ||
222 | u64 node; | ||
223 | |||
224 | mutex_lock(&mdesc_mutex); | ||
225 | client->next = client_list; | ||
226 | client_list = client; | ||
227 | |||
228 | mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name) | ||
229 | client->add(cur_mdesc, node); | ||
230 | |||
231 | mutex_unlock(&mdesc_mutex); | ||
232 | } | ||
233 | |||
234 | /* Run 'func' on nodes which are in A but not in B. */ | ||
235 | static void invoke_on_missing(const char *name, | ||
236 | struct mdesc_handle *a, | ||
237 | struct mdesc_handle *b, | ||
238 | void (*func)(struct mdesc_handle *, u64)) | ||
239 | { | ||
240 | u64 node; | ||
241 | |||
242 | mdesc_for_each_node_by_name(a, node, name) { | ||
243 | const u64 *id = mdesc_get_property(a, node, "id", NULL); | ||
244 | int found = 0; | ||
245 | u64 fnode; | ||
246 | |||
247 | mdesc_for_each_node_by_name(b, fnode, name) { | ||
248 | const u64 *fid = mdesc_get_property(b, fnode, | ||
249 | "id", NULL); | ||
250 | |||
251 | if (*id == *fid) { | ||
252 | found = 1; | ||
253 | break; | ||
254 | } | ||
255 | } | ||
256 | if (!found) | ||
257 | func(a, node); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | static void notify_one(struct mdesc_notifier_client *p, | ||
262 | struct mdesc_handle *old_hp, | ||
263 | struct mdesc_handle *new_hp) | ||
264 | { | ||
265 | invoke_on_missing(p->node_name, old_hp, new_hp, p->remove); | ||
266 | invoke_on_missing(p->node_name, new_hp, old_hp, p->add); | ||
267 | } | ||
268 | |||
269 | static void mdesc_notify_clients(struct mdesc_handle *old_hp, | ||
270 | struct mdesc_handle *new_hp) | ||
271 | { | ||
272 | struct mdesc_notifier_client *p = client_list; | ||
273 | |||
274 | while (p) { | ||
275 | notify_one(p, old_hp, new_hp); | ||
276 | p = p->next; | ||
277 | } | ||
278 | } | ||
279 | |||
217 | void mdesc_update(void) | 280 | void mdesc_update(void) |
218 | { | 281 | { |
219 | unsigned long len, real_len, status; | 282 | unsigned long len, real_len, status; |
220 | struct mdesc_handle *hp, *orig_hp; | 283 | struct mdesc_handle *hp, *orig_hp; |
221 | unsigned long flags; | 284 | unsigned long flags; |
222 | 285 | ||
286 | mutex_lock(&mdesc_mutex); | ||
287 | |||
223 | (void) sun4v_mach_desc(0UL, 0UL, &len); | 288 | (void) sun4v_mach_desc(0UL, 0UL, &len); |
224 | 289 | ||
225 | hp = mdesc_alloc(len, &kmalloc_mdesc_memops); | 290 | hp = mdesc_alloc(len, &kmalloc_mdesc_memops); |
226 | if (!hp) { | 291 | if (!hp) { |
227 | printk(KERN_ERR "MD: mdesc alloc fails\n"); | 292 | printk(KERN_ERR "MD: mdesc alloc fails\n"); |
228 | return; | 293 | goto out; |
229 | } | 294 | } |
230 | 295 | ||
231 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); | 296 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); |
@@ -234,18 +299,25 @@ void mdesc_update(void) | |||
234 | status); | 299 | status); |
235 | atomic_dec(&hp->refcnt); | 300 | atomic_dec(&hp->refcnt); |
236 | mdesc_free(hp); | 301 | mdesc_free(hp); |
237 | return; | 302 | goto out; |
238 | } | 303 | } |
239 | 304 | ||
240 | spin_lock_irqsave(&mdesc_lock, flags); | 305 | spin_lock_irqsave(&mdesc_lock, flags); |
241 | orig_hp = cur_mdesc; | 306 | orig_hp = cur_mdesc; |
242 | cur_mdesc = hp; | 307 | cur_mdesc = hp; |
308 | spin_unlock_irqrestore(&mdesc_lock, flags); | ||
243 | 309 | ||
310 | mdesc_notify_clients(orig_hp, hp); | ||
311 | |||
312 | spin_lock_irqsave(&mdesc_lock, flags); | ||
244 | if (atomic_dec_and_test(&orig_hp->refcnt)) | 313 | if (atomic_dec_and_test(&orig_hp->refcnt)) |
245 | mdesc_free(orig_hp); | 314 | mdesc_free(orig_hp); |
246 | else | 315 | else |
247 | list_add(&orig_hp->list, &mdesc_zombie_list); | 316 | list_add(&orig_hp->list, &mdesc_zombie_list); |
248 | spin_unlock_irqrestore(&mdesc_lock, flags); | 317 | spin_unlock_irqrestore(&mdesc_lock, flags); |
318 | |||
319 | out: | ||
320 | mutex_unlock(&mdesc_mutex); | ||
249 | } | 321 | } |
250 | 322 | ||
251 | static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) | 323 | static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) |
diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c index 49569b44ea1f..8d3cc4fdb557 100644 --- a/arch/sparc64/kernel/vio.c +++ b/arch/sparc64/kernel/vio.c | |||
@@ -201,10 +201,11 @@ static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp, | |||
201 | static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | 201 | static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, |
202 | struct device *parent) | 202 | struct device *parent) |
203 | { | 203 | { |
204 | const char *type, *compat; | 204 | const char *type, *compat, *bus_id_name; |
205 | struct device_node *dp; | 205 | struct device_node *dp; |
206 | struct vio_dev *vdev; | 206 | struct vio_dev *vdev; |
207 | int err, tlen, clen; | 207 | int err, tlen, clen; |
208 | const u64 *id; | ||
208 | 209 | ||
209 | type = mdesc_get_property(hp, mp, "device-type", &tlen); | 210 | type = mdesc_get_property(hp, mp, "device-type", &tlen); |
210 | if (!type) { | 211 | if (!type) { |
@@ -220,6 +221,16 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | |||
220 | return NULL; | 221 | return NULL; |
221 | } | 222 | } |
222 | 223 | ||
224 | bus_id_name = type; | ||
225 | if (!strcmp(type, "domain-services-port")) | ||
226 | bus_id_name = "ds"; | ||
227 | |||
228 | if (strlen(bus_id_name) >= KOBJ_NAME_LEN - 4) { | ||
229 | printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n", | ||
230 | bus_id_name); | ||
231 | return NULL; | ||
232 | } | ||
233 | |||
223 | compat = mdesc_get_property(hp, mp, "device-type", &clen); | 234 | compat = mdesc_get_property(hp, mp, "device-type", &clen); |
224 | if (!compat) { | 235 | if (!compat) { |
225 | clen = 0; | 236 | clen = 0; |
@@ -249,7 +260,14 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | |||
249 | 260 | ||
250 | vio_fill_channel_info(hp, mp, vdev); | 261 | vio_fill_channel_info(hp, mp, vdev); |
251 | 262 | ||
252 | snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%lx", mp); | 263 | id = mdesc_get_property(hp, mp, "id", NULL); |
264 | if (!id) | ||
265 | snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s", | ||
266 | bus_id_name); | ||
267 | else | ||
268 | snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s-%lu", | ||
269 | bus_id_name, *id); | ||
270 | |||
253 | vdev->dev.parent = parent; | 271 | vdev->dev.parent = parent; |
254 | vdev->dev.bus = &vio_bus_type; | 272 | vdev->dev.bus = &vio_bus_type; |
255 | vdev->dev.release = vio_dev_release; | 273 | vdev->dev.release = vio_dev_release; |
@@ -269,6 +287,8 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | |||
269 | } | 287 | } |
270 | vdev->dp = dp; | 288 | vdev->dp = dp; |
271 | 289 | ||
290 | printk(KERN_ERR "VIO: Adding device %s\n", vdev->dev.bus_id); | ||
291 | |||
272 | err = device_register(&vdev->dev); | 292 | err = device_register(&vdev->dev); |
273 | if (err) { | 293 | if (err) { |
274 | printk(KERN_ERR "VIO: Could not register device %s, err=%d\n", | 294 | printk(KERN_ERR "VIO: Could not register device %s, err=%d\n", |
@@ -283,46 +303,46 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, | |||
283 | return vdev; | 303 | return vdev; |
284 | } | 304 | } |
285 | 305 | ||
286 | static void walk_tree(struct mdesc_handle *hp, u64 n, struct vio_dev *parent) | 306 | static void vio_add(struct mdesc_handle *hp, u64 node) |
287 | { | 307 | { |
288 | u64 a; | 308 | (void) vio_create_one(hp, node, &root_vdev->dev); |
289 | |||
290 | mdesc_for_each_arc(a, hp, n, MDESC_ARC_TYPE_FWD) { | ||
291 | struct vio_dev *vdev; | ||
292 | u64 target; | ||
293 | |||
294 | target = mdesc_arc_target(hp, a); | ||
295 | vdev = vio_create_one(hp, target, &parent->dev); | ||
296 | if (vdev) | ||
297 | walk_tree(hp, target, vdev); | ||
298 | } | ||
299 | } | 309 | } |
300 | 310 | ||
301 | static void create_devices(struct mdesc_handle *hp, u64 root) | 311 | static int vio_md_node_match(struct device *dev, void *arg) |
302 | { | 312 | { |
303 | u64 mp; | 313 | struct vio_dev *vdev = to_vio_dev(dev); |
304 | 314 | ||
305 | root_vdev = vio_create_one(hp, root, NULL); | 315 | if (vdev->mp == (u64) arg) |
306 | if (!root_vdev) { | 316 | return 1; |
307 | printk(KERN_ERR "VIO: Coult not create root device.\n"); | ||
308 | return; | ||
309 | } | ||
310 | 317 | ||
311 | walk_tree(hp, root, root_vdev); | 318 | return 0; |
319 | } | ||
320 | |||
321 | static void vio_remove(struct mdesc_handle *hp, u64 node) | ||
322 | { | ||
323 | struct device *dev; | ||
312 | 324 | ||
313 | /* Domain services is odd as it doesn't sit underneath the | 325 | dev = device_find_child(&root_vdev->dev, (void *) node, |
314 | * channel-devices node, so we plug it in manually. | 326 | vio_md_node_match); |
315 | */ | 327 | if (dev) { |
316 | mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "domain-services"); | 328 | printk(KERN_INFO "VIO: Removing device %s\n", dev->bus_id); |
317 | if (mp != MDESC_NODE_NULL) { | ||
318 | struct vio_dev *parent = vio_create_one(hp, mp, | ||
319 | &root_vdev->dev); | ||
320 | 329 | ||
321 | if (parent) | 330 | device_unregister(dev); |
322 | walk_tree(hp, mp, parent); | ||
323 | } | 331 | } |
324 | } | 332 | } |
325 | 333 | ||
334 | static struct mdesc_notifier_client vio_device_notifier = { | ||
335 | .add = vio_add, | ||
336 | .remove = vio_remove, | ||
337 | .node_name = "virtual-device-port", | ||
338 | }; | ||
339 | |||
340 | static struct mdesc_notifier_client vio_ds_notifier = { | ||
341 | .add = vio_add, | ||
342 | .remove = vio_remove, | ||
343 | .node_name = "domain-services-port", | ||
344 | }; | ||
345 | |||
326 | const char *channel_devices_node = "channel-devices"; | 346 | const char *channel_devices_node = "channel-devices"; |
327 | const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; | 347 | const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; |
328 | const char *cfg_handle_prop = "cfg-handle"; | 348 | const char *cfg_handle_prop = "cfg-handle"; |
@@ -381,11 +401,19 @@ static int __init vio_init(void) | |||
381 | 401 | ||
382 | cdev_cfg_handle = *cfg_handle; | 402 | cdev_cfg_handle = *cfg_handle; |
383 | 403 | ||
384 | create_devices(hp, root); | 404 | root_vdev = vio_create_one(hp, root, NULL); |
405 | err = -ENODEV; | ||
406 | if (!root_vdev) { | ||
407 | printk(KERN_ERR "VIO: Coult not create root device.\n"); | ||
408 | goto out_release; | ||
409 | } | ||
410 | |||
411 | mdesc_register_notifier(&vio_device_notifier); | ||
412 | mdesc_register_notifier(&vio_ds_notifier); | ||
385 | 413 | ||
386 | mdesc_release(hp); | 414 | mdesc_release(hp); |
387 | 415 | ||
388 | return 0; | 416 | return err; |
389 | 417 | ||
390 | out_release: | 418 | out_release: |
391 | mdesc_release(hp); | 419 | mdesc_release(hp); |
diff --git a/arch/sparc64/kernel/viohs.c b/arch/sparc64/kernel/viohs.c index 15613add45d1..09126fc338ba 100644 --- a/arch/sparc64/kernel/viohs.c +++ b/arch/sparc64/kernel/viohs.c | |||
@@ -78,6 +78,24 @@ static int start_handshake(struct vio_driver_state *vio) | |||
78 | return 0; | 78 | return 0; |
79 | } | 79 | } |
80 | 80 | ||
81 | static void flush_rx_dring(struct vio_driver_state *vio) | ||
82 | { | ||
83 | struct vio_dring_state *dr; | ||
84 | u64 ident; | ||
85 | |||
86 | BUG_ON(!(vio->dr_state & VIO_DR_STATE_RXREG)); | ||
87 | |||
88 | dr = &vio->drings[VIO_DRIVER_RX_RING]; | ||
89 | ident = dr->ident; | ||
90 | |||
91 | BUG_ON(!vio->desc_buf); | ||
92 | kfree(vio->desc_buf); | ||
93 | vio->desc_buf = NULL; | ||
94 | |||
95 | memset(dr, 0, sizeof(*dr)); | ||
96 | dr->ident = ident; | ||
97 | } | ||
98 | |||
81 | void vio_link_state_change(struct vio_driver_state *vio, int event) | 99 | void vio_link_state_change(struct vio_driver_state *vio, int event) |
82 | { | 100 | { |
83 | if (event == LDC_EVENT_UP) { | 101 | if (event == LDC_EVENT_UP) { |
@@ -98,6 +116,16 @@ void vio_link_state_change(struct vio_driver_state *vio, int event) | |||
98 | break; | 116 | break; |
99 | } | 117 | } |
100 | start_handshake(vio); | 118 | start_handshake(vio); |
119 | } else if (event == LDC_EVENT_RESET) { | ||
120 | vio->hs_state = VIO_HS_INVALID; | ||
121 | |||
122 | if (vio->dr_state & VIO_DR_STATE_RXREG) | ||
123 | flush_rx_dring(vio); | ||
124 | |||
125 | vio->dr_state = 0x00; | ||
126 | memset(&vio->ver, 0, sizeof(vio->ver)); | ||
127 | |||
128 | ldc_disconnect(vio->lp); | ||
101 | } | 129 | } |
102 | } | 130 | } |
103 | EXPORT_SYMBOL(vio_link_state_change); | 131 | EXPORT_SYMBOL(vio_link_state_change); |
@@ -396,6 +424,8 @@ static int process_dreg_info(struct vio_driver_state *vio, | |||
396 | if (vio->dr_state & VIO_DR_STATE_RXREG) | 424 | if (vio->dr_state & VIO_DR_STATE_RXREG) |
397 | goto send_nack; | 425 | goto send_nack; |
398 | 426 | ||
427 | BUG_ON(vio->desc_buf); | ||
428 | |||
399 | vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC); | 429 | vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC); |
400 | if (!vio->desc_buf) | 430 | if (!vio->desc_buf) |
401 | goto send_nack; | 431 | goto send_nack; |