diff options
Diffstat (limited to 'drivers/ssb')
-rw-r--r-- | drivers/ssb/main.c | 126 | ||||
-rw-r--r-- | drivers/ssb/sprom.c | 10 | ||||
-rw-r--r-- | drivers/ssb/ssb_private.h | 12 |
3 files changed, 78 insertions, 70 deletions
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 579b114be412..5681ebed9c65 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c | |||
@@ -140,6 +140,19 @@ static void ssb_device_put(struct ssb_device *dev) | |||
140 | put_device(dev->dev); | 140 | put_device(dev->dev); |
141 | } | 141 | } |
142 | 142 | ||
143 | static inline struct ssb_driver *ssb_driver_get(struct ssb_driver *drv) | ||
144 | { | ||
145 | if (drv) | ||
146 | get_driver(&drv->drv); | ||
147 | return drv; | ||
148 | } | ||
149 | |||
150 | static inline void ssb_driver_put(struct ssb_driver *drv) | ||
151 | { | ||
152 | if (drv) | ||
153 | put_driver(&drv->drv); | ||
154 | } | ||
155 | |||
143 | static int ssb_device_resume(struct device *dev) | 156 | static int ssb_device_resume(struct device *dev) |
144 | { | 157 | { |
145 | struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); | 158 | struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); |
@@ -210,90 +223,81 @@ int ssb_bus_suspend(struct ssb_bus *bus) | |||
210 | EXPORT_SYMBOL(ssb_bus_suspend); | 223 | EXPORT_SYMBOL(ssb_bus_suspend); |
211 | 224 | ||
212 | #ifdef CONFIG_SSB_SPROM | 225 | #ifdef CONFIG_SSB_SPROM |
213 | int ssb_devices_freeze(struct ssb_bus *bus) | 226 | /** ssb_devices_freeze - Freeze all devices on the bus. |
227 | * | ||
228 | * After freezing no device driver will be handling a device | ||
229 | * on this bus anymore. ssb_devices_thaw() must be called after | ||
230 | * a successful freeze to reactivate the devices. | ||
231 | * | ||
232 | * @bus: The bus. | ||
233 | * @ctx: Context structure. Pass this to ssb_devices_thaw(). | ||
234 | */ | ||
235 | int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx) | ||
214 | { | 236 | { |
215 | struct ssb_device *dev; | 237 | struct ssb_device *sdev; |
216 | struct ssb_driver *drv; | 238 | struct ssb_driver *sdrv; |
217 | int err = 0; | 239 | unsigned int i; |
218 | int i; | 240 | |
219 | pm_message_t state = PMSG_FREEZE; | 241 | memset(ctx, 0, sizeof(*ctx)); |
242 | ctx->bus = bus; | ||
243 | SSB_WARN_ON(bus->nr_devices > ARRAY_SIZE(ctx->device_frozen)); | ||
220 | 244 | ||
221 | /* First check that we are capable to freeze all devices. */ | ||
222 | for (i = 0; i < bus->nr_devices; i++) { | 245 | for (i = 0; i < bus->nr_devices; i++) { |
223 | dev = &(bus->devices[i]); | 246 | sdev = ssb_device_get(&bus->devices[i]); |
224 | if (!dev->dev || | 247 | |
225 | !dev->dev->driver || | 248 | if (!sdev->dev || !sdev->dev->driver || |
226 | !device_is_registered(dev->dev)) | 249 | !device_is_registered(sdev->dev)) { |
227 | continue; | 250 | ssb_device_put(sdev); |
228 | drv = drv_to_ssb_drv(dev->dev->driver); | ||
229 | if (!drv) | ||
230 | continue; | 251 | continue; |
231 | if (!drv->suspend) { | ||
232 | /* Nope, can't suspend this one. */ | ||
233 | return -EOPNOTSUPP; | ||
234 | } | 252 | } |
235 | } | 253 | sdrv = ssb_driver_get(drv_to_ssb_drv(sdev->dev->driver)); |
236 | /* Now suspend all devices */ | 254 | if (!sdrv || SSB_WARN_ON(!sdrv->remove)) { |
237 | for (i = 0; i < bus->nr_devices; i++) { | 255 | ssb_device_put(sdev); |
238 | dev = &(bus->devices[i]); | ||
239 | if (!dev->dev || | ||
240 | !dev->dev->driver || | ||
241 | !device_is_registered(dev->dev)) | ||
242 | continue; | ||
243 | drv = drv_to_ssb_drv(dev->dev->driver); | ||
244 | if (!drv) | ||
245 | continue; | 256 | continue; |
246 | err = drv->suspend(dev, state); | ||
247 | if (err) { | ||
248 | ssb_printk(KERN_ERR PFX "Failed to freeze device %s\n", | ||
249 | dev_name(dev->dev)); | ||
250 | goto err_unwind; | ||
251 | } | 257 | } |
258 | sdrv->remove(sdev); | ||
259 | ctx->device_frozen[i] = 1; | ||
252 | } | 260 | } |
253 | 261 | ||
254 | return 0; | 262 | return 0; |
255 | err_unwind: | ||
256 | for (i--; i >= 0; i--) { | ||
257 | dev = &(bus->devices[i]); | ||
258 | if (!dev->dev || | ||
259 | !dev->dev->driver || | ||
260 | !device_is_registered(dev->dev)) | ||
261 | continue; | ||
262 | drv = drv_to_ssb_drv(dev->dev->driver); | ||
263 | if (!drv) | ||
264 | continue; | ||
265 | if (drv->resume) | ||
266 | drv->resume(dev); | ||
267 | } | ||
268 | return err; | ||
269 | } | 263 | } |
270 | 264 | ||
271 | int ssb_devices_thaw(struct ssb_bus *bus) | 265 | /** ssb_devices_thaw - Unfreeze all devices on the bus. |
266 | * | ||
267 | * This will re-attach the device drivers and re-init the devices. | ||
268 | * | ||
269 | * @ctx: The context structure from ssb_devices_freeze() | ||
270 | */ | ||
271 | int ssb_devices_thaw(struct ssb_freeze_context *ctx) | ||
272 | { | 272 | { |
273 | struct ssb_device *dev; | 273 | struct ssb_bus *bus = ctx->bus; |
274 | struct ssb_driver *drv; | 274 | struct ssb_device *sdev; |
275 | int err; | 275 | struct ssb_driver *sdrv; |
276 | int i; | 276 | unsigned int i; |
277 | int err, result = 0; | ||
277 | 278 | ||
278 | for (i = 0; i < bus->nr_devices; i++) { | 279 | for (i = 0; i < bus->nr_devices; i++) { |
279 | dev = &(bus->devices[i]); | 280 | if (!ctx->device_frozen[i]) |
280 | if (!dev->dev || | ||
281 | !dev->dev->driver || | ||
282 | !device_is_registered(dev->dev)) | ||
283 | continue; | 281 | continue; |
284 | drv = drv_to_ssb_drv(dev->dev->driver); | 282 | sdev = &bus->devices[i]; |
285 | if (!drv) | 283 | |
284 | if (SSB_WARN_ON(!sdev->dev || !sdev->dev->driver)) | ||
286 | continue; | 285 | continue; |
287 | if (SSB_WARN_ON(!drv->resume)) | 286 | sdrv = drv_to_ssb_drv(sdev->dev->driver); |
287 | if (SSB_WARN_ON(!sdrv || !sdrv->probe)) | ||
288 | continue; | 288 | continue; |
289 | err = drv->resume(dev); | 289 | |
290 | err = sdrv->probe(sdev, &sdev->id); | ||
290 | if (err) { | 291 | if (err) { |
291 | ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n", | 292 | ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n", |
292 | dev_name(dev->dev)); | 293 | dev_name(sdev->dev)); |
294 | result = err; | ||
293 | } | 295 | } |
296 | ssb_driver_put(sdrv); | ||
297 | ssb_device_put(sdev); | ||
294 | } | 298 | } |
295 | 299 | ||
296 | return 0; | 300 | return result; |
297 | } | 301 | } |
298 | #endif /* CONFIG_SSB_SPROM */ | 302 | #endif /* CONFIG_SSB_SPROM */ |
299 | 303 | ||
diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c index 8943015a3eef..580f779ecf49 100644 --- a/drivers/ssb/sprom.c +++ b/drivers/ssb/sprom.c | |||
@@ -90,6 +90,7 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, | |||
90 | u16 *sprom; | 90 | u16 *sprom; |
91 | int res = 0, err = -ENOMEM; | 91 | int res = 0, err = -ENOMEM; |
92 | size_t sprom_size_words = bus->sprom_size; | 92 | size_t sprom_size_words = bus->sprom_size; |
93 | struct ssb_freeze_context freeze; | ||
93 | 94 | ||
94 | sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); | 95 | sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); |
95 | if (!sprom) | 96 | if (!sprom) |
@@ -111,18 +112,13 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, | |||
111 | err = -ERESTARTSYS; | 112 | err = -ERESTARTSYS; |
112 | if (mutex_lock_interruptible(&bus->sprom_mutex)) | 113 | if (mutex_lock_interruptible(&bus->sprom_mutex)) |
113 | goto out_kfree; | 114 | goto out_kfree; |
114 | err = ssb_devices_freeze(bus); | 115 | err = ssb_devices_freeze(bus, &freeze); |
115 | if (err == -EOPNOTSUPP) { | ||
116 | ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. " | ||
117 | "No suspend support. Is CONFIG_PM enabled?\n"); | ||
118 | goto out_unlock; | ||
119 | } | ||
120 | if (err) { | 116 | if (err) { |
121 | ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); | 117 | ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); |
122 | goto out_unlock; | 118 | goto out_unlock; |
123 | } | 119 | } |
124 | res = sprom_write(bus, sprom); | 120 | res = sprom_write(bus, sprom); |
125 | err = ssb_devices_thaw(bus); | 121 | err = ssb_devices_thaw(&freeze); |
126 | if (err) | 122 | if (err) |
127 | ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); | 123 | ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); |
128 | out_unlock: | 124 | out_unlock: |
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index 25433565dfda..56054be4d113 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h | |||
@@ -176,13 +176,21 @@ extern const struct ssb_sprom *ssb_get_fallback_sprom(void); | |||
176 | 176 | ||
177 | /* core.c */ | 177 | /* core.c */ |
178 | extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m); | 178 | extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m); |
179 | extern int ssb_devices_freeze(struct ssb_bus *bus); | ||
180 | extern int ssb_devices_thaw(struct ssb_bus *bus); | ||
181 | extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev); | 179 | extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev); |
182 | int ssb_for_each_bus_call(unsigned long data, | 180 | int ssb_for_each_bus_call(unsigned long data, |
183 | int (*func)(struct ssb_bus *bus, unsigned long data)); | 181 | int (*func)(struct ssb_bus *bus, unsigned long data)); |
184 | extern struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev); | 182 | extern struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev); |
185 | 183 | ||
184 | struct ssb_freeze_context { | ||
185 | /* Pointer to the bus */ | ||
186 | struct ssb_bus *bus; | ||
187 | /* Boolean list to indicate whether a device is frozen on this bus. */ | ||
188 | bool device_frozen[SSB_MAX_NR_CORES]; | ||
189 | }; | ||
190 | extern int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx); | ||
191 | extern int ssb_devices_thaw(struct ssb_freeze_context *ctx); | ||
192 | |||
193 | |||
186 | 194 | ||
187 | /* b43_pci_bridge.c */ | 195 | /* b43_pci_bridge.c */ |
188 | #ifdef CONFIG_SSB_B43_PCI_BRIDGE | 196 | #ifdef CONFIG_SSB_B43_PCI_BRIDGE |