Blame view

drivers/uwb/umc-bus.c 5.45 KB
da389eac3   David Vrabel   uwb: add the umc bus
1
2
3
4
5
6
7
8
9
10
  /*
   * Bus for UWB Multi-interface Controller capabilities.
   *
   * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
   *
   * This file is released under the GNU GPL v2.
   */
  #include <linux/kernel.h>
  #include <linux/sysfs.h>
  #include <linux/workqueue.h>
c7394efc2   Paul Gortmaker   uwb: Add module.h...
11
  #include <linux/module.h>
da389eac3   David Vrabel   uwb: add the umc bus
12
13
  #include <linux/uwb/umc.h>
  #include <linux/pci.h>
307ba6dd7   David Vrabel   uwb: don't unbind...
14
  static int umc_bus_pre_reset_helper(struct device *dev, void *data)
da389eac3   David Vrabel   uwb: add the umc bus
15
  {
307ba6dd7   David Vrabel   uwb: don't unbind...
16
  	int ret = 0;
da389eac3   David Vrabel   uwb: add the umc bus
17

307ba6dd7   David Vrabel   uwb: don't unbind...
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  	if (dev->driver) {
  		struct umc_dev *umc = to_umc_dev(dev);
  		struct umc_driver *umc_drv = to_umc_driver(dev->driver);
  
  		if (umc_drv->pre_reset)
  			ret = umc_drv->pre_reset(umc);
  		else
  			device_release_driver(dev);
  	}
  	return ret;
  }
  
  static int umc_bus_post_reset_helper(struct device *dev, void *data)
  {
  	int ret = 0;
  
  	if (dev->driver) {
  		struct umc_dev *umc = to_umc_dev(dev);
  		struct umc_driver *umc_drv = to_umc_driver(dev->driver);
  
  		if (umc_drv->post_reset)
  			ret = umc_drv->post_reset(umc);
  	} else
  		ret = device_attach(dev);
  
  	return ret;
da389eac3   David Vrabel   uwb: add the umc bus
44
45
46
47
48
49
  }
  
  /**
   * umc_controller_reset - reset the whole UMC controller
   * @umc: the UMC device for the radio controller.
   *
307ba6dd7   David Vrabel   uwb: don't unbind...
50
51
52
53
54
55
   * Drivers or all capabilities of the controller will have their
   * pre_reset methods called or be unbound from their device.  Then all
   * post_reset methods will be called or the drivers will be rebound.
   *
   * Radio controllers must provide pre_reset and post_reset methods and
   * reset the hardware in their start method.
da389eac3   David Vrabel   uwb: add the umc bus
56
57
58
59
60
61
62
   *
   * If this is called while a probe() or remove() is in progress it
   * will return -EAGAIN and not perform the reset.
   */
  int umc_controller_reset(struct umc_dev *umc)
  {
  	struct device *parent = umc->dev.parent;
307ba6dd7   David Vrabel   uwb: don't unbind...
63
  	int ret = 0;
da389eac3   David Vrabel   uwb: add the umc bus
64

8e9394ce2   Greg Kroah-Hartman   Driver core: crea...
65
  	if (device_trylock(parent))
da389eac3   David Vrabel   uwb: add the umc bus
66
  		return -EAGAIN;
307ba6dd7   David Vrabel   uwb: don't unbind...
67
68
  	ret = device_for_each_child(parent, parent, umc_bus_pre_reset_helper);
  	if (ret >= 0)
0396c215f   David Vrabel   uwb: avoid radio ...
69
  		ret = device_for_each_child(parent, parent, umc_bus_post_reset_helper);
8e9394ce2   Greg Kroah-Hartman   Driver core: crea...
70
  	device_unlock(parent);
da389eac3   David Vrabel   uwb: add the umc bus
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(umc_controller_reset);
  
  /**
   * umc_match_pci_id - match a UMC driver to a UMC device's parent PCI device.
   * @umc_drv: umc driver with match_data pointing to a zero-terminated
   * table of pci_device_id's.
   * @umc: umc device whose parent is to be matched.
   */
  int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc)
  {
  	const struct pci_device_id *id_table = umc_drv->match_data;
  	struct pci_dev *pci;
  
  	if (umc->dev.parent->bus != &pci_bus_type)
  		return 0;
  
  	pci = to_pci_dev(umc->dev.parent);
  	return pci_match_id(id_table, pci) != NULL;
  }
  EXPORT_SYMBOL_GPL(umc_match_pci_id);
  
  static int umc_bus_rescan_helper(struct device *dev, void *data)
  {
  	int ret = 0;
  
  	if (!dev->driver)
  		ret = device_attach(dev);
307ba6dd7   David Vrabel   uwb: don't unbind...
101
  	return ret;
da389eac3   David Vrabel   uwb: add the umc bus
102
  }
307ba6dd7   David Vrabel   uwb: don't unbind...
103
  static void umc_bus_rescan(struct device *parent)
da389eac3   David Vrabel   uwb: add the umc bus
104
105
106
107
108
109
110
  {
  	int err;
  
  	/*
  	 * We can't use bus_rescan_devices() here as it deadlocks when
  	 * it tries to retake the dev->parent semaphore.
  	 */
307ba6dd7   David Vrabel   uwb: don't unbind...
111
  	err = device_for_each_child(parent, NULL, umc_bus_rescan_helper);
da389eac3   David Vrabel   uwb: add the umc bus
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
  	if (err < 0)
  		printk(KERN_WARNING "%s: rescan of bus failed: %d
  ",
  		       KBUILD_MODNAME, err);
  }
  
  static int umc_bus_match(struct device *dev, struct device_driver *drv)
  {
  	struct umc_dev *umc = to_umc_dev(dev);
  	struct umc_driver *umc_driver = to_umc_driver(drv);
  
  	if (umc->cap_id == umc_driver->cap_id) {
  		if (umc_driver->match)
  			return umc_driver->match(umc_driver, umc);
  		else
  			return 1;
  	}
  	return 0;
  }
  
  static int umc_device_probe(struct device *dev)
  {
  	struct umc_dev *umc;
  	struct umc_driver *umc_driver;
  	int err;
  
  	umc_driver = to_umc_driver(dev->driver);
  	umc = to_umc_dev(dev);
  
  	get_device(dev);
  	err = umc_driver->probe(umc);
  	if (err)
  		put_device(dev);
  	else
307ba6dd7   David Vrabel   uwb: don't unbind...
146
  		umc_bus_rescan(dev->parent);
da389eac3   David Vrabel   uwb: add the umc bus
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
  
  	return err;
  }
  
  static int umc_device_remove(struct device *dev)
  {
  	struct umc_dev *umc;
  	struct umc_driver *umc_driver;
  
  	umc_driver = to_umc_driver(dev->driver);
  	umc = to_umc_dev(dev);
  
  	umc_driver->remove(umc);
  	put_device(dev);
  	return 0;
  }
  
  static int umc_device_suspend(struct device *dev, pm_message_t state)
  {
  	struct umc_dev *umc;
  	struct umc_driver *umc_driver;
  	int err = 0;
  
  	umc = to_umc_dev(dev);
  
  	if (dev->driver) {
  		umc_driver = to_umc_driver(dev->driver);
  		if (umc_driver->suspend)
  			err = umc_driver->suspend(umc, state);
  	}
  	return err;
  }
  
  static int umc_device_resume(struct device *dev)
  {
  	struct umc_dev *umc;
  	struct umc_driver *umc_driver;
  	int err = 0;
  
  	umc = to_umc_dev(dev);
  
  	if (dev->driver) {
  		umc_driver = to_umc_driver(dev->driver);
  		if (umc_driver->resume)
  			err = umc_driver->resume(umc);
  	}
  	return err;
  }
  
  static ssize_t capability_id_show(struct device *dev, struct device_attribute *attr, char *buf)
  {
  	struct umc_dev *umc = to_umc_dev(dev);
  
  	return sprintf(buf, "0x%02x
  ", umc->cap_id);
  }
  
  static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf)
  {
  	struct umc_dev *umc = to_umc_dev(dev);
  
  	return sprintf(buf, "0x%04x
  ", umc->version);
  }
  
  static struct device_attribute umc_dev_attrs[] = {
  	__ATTR_RO(capability_id),
  	__ATTR_RO(version),
  	__ATTR_NULL,
  };
  
  struct bus_type umc_bus_type = {
  	.name		= "umc",
  	.match		= umc_bus_match,
  	.probe		= umc_device_probe,
  	.remove		= umc_device_remove,
  	.suspend        = umc_device_suspend,
  	.resume         = umc_device_resume,
  	.dev_attrs	= umc_dev_attrs,
  };
  EXPORT_SYMBOL_GPL(umc_bus_type);
  
  static int __init umc_bus_init(void)
  {
  	return bus_register(&umc_bus_type);
  }
  module_init(umc_bus_init);
  
  static void __exit umc_bus_exit(void)
  {
  	bus_unregister(&umc_bus_type);
  }
  module_exit(umc_bus_exit);
  
  MODULE_DESCRIPTION("UWB Multi-interface Controller capability bus");
  MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
  MODULE_LICENSE("GPL");