ssb: Fix SPROM writing

The SPROM writing routines were broken since we rewrote the suspend
handling on wireless devices, because SPROM writing depended on suspend.

This patch changes it and freezes devices with the driver remove(), probe()
callbacks instead. This also simplifies the whole logics a lot.

Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Michael Buesch 2009-11-23 20:12:13 +01:00 committed by John W. Linville
parent 77593ae28c
commit 3ba6018aa3
3 changed files with 78 additions and 70 deletions

View File

@ -140,6 +140,19 @@ static void ssb_device_put(struct ssb_device *dev)
put_device(dev->dev); put_device(dev->dev);
} }
static inline struct ssb_driver *ssb_driver_get(struct ssb_driver *drv)
{
if (drv)
get_driver(&drv->drv);
return drv;
}
static inline void ssb_driver_put(struct ssb_driver *drv)
{
if (drv)
put_driver(&drv->drv);
}
static int ssb_device_resume(struct device *dev) static int ssb_device_resume(struct device *dev)
{ {
struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
@ -210,90 +223,81 @@ int ssb_bus_suspend(struct ssb_bus *bus)
EXPORT_SYMBOL(ssb_bus_suspend); EXPORT_SYMBOL(ssb_bus_suspend);
#ifdef CONFIG_SSB_SPROM #ifdef CONFIG_SSB_SPROM
int ssb_devices_freeze(struct ssb_bus *bus) /** ssb_devices_freeze - Freeze all devices on the bus.
*
* After freezing no device driver will be handling a device
* on this bus anymore. ssb_devices_thaw() must be called after
* a successful freeze to reactivate the devices.
*
* @bus: The bus.
* @ctx: Context structure. Pass this to ssb_devices_thaw().
*/
int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx)
{ {
struct ssb_device *dev; struct ssb_device *sdev;
struct ssb_driver *drv; struct ssb_driver *sdrv;
int err = 0; unsigned int i;
int i;
pm_message_t state = PMSG_FREEZE; memset(ctx, 0, sizeof(*ctx));
ctx->bus = bus;
SSB_WARN_ON(bus->nr_devices > ARRAY_SIZE(ctx->device_frozen));
/* First check that we are capable to freeze all devices. */
for (i = 0; i < bus->nr_devices; i++) { for (i = 0; i < bus->nr_devices; i++) {
dev = &(bus->devices[i]); sdev = ssb_device_get(&bus->devices[i]);
if (!dev->dev ||
!dev->dev->driver || if (!sdev->dev || !sdev->dev->driver ||
!device_is_registered(dev->dev)) !device_is_registered(sdev->dev)) {
ssb_device_put(sdev);
continue; continue;
drv = drv_to_ssb_drv(dev->dev->driver);
if (!drv)
continue;
if (!drv->suspend) {
/* Nope, can't suspend this one. */
return -EOPNOTSUPP;
} }
} sdrv = ssb_driver_get(drv_to_ssb_drv(sdev->dev->driver));
/* Now suspend all devices */ if (!sdrv || SSB_WARN_ON(!sdrv->remove)) {
for (i = 0; i < bus->nr_devices; i++) { ssb_device_put(sdev);
dev = &(bus->devices[i]);
if (!dev->dev ||
!dev->dev->driver ||
!device_is_registered(dev->dev))
continue; continue;
drv = drv_to_ssb_drv(dev->dev->driver);
if (!drv)
continue;
err = drv->suspend(dev, state);
if (err) {
ssb_printk(KERN_ERR PFX "Failed to freeze device %s\n",
dev_name(dev->dev));
goto err_unwind;
} }
sdrv->remove(sdev);
ctx->device_frozen[i] = 1;
} }
return 0; return 0;
err_unwind:
for (i--; i >= 0; i--) {
dev = &(bus->devices[i]);
if (!dev->dev ||
!dev->dev->driver ||
!device_is_registered(dev->dev))
continue;
drv = drv_to_ssb_drv(dev->dev->driver);
if (!drv)
continue;
if (drv->resume)
drv->resume(dev);
}
return err;
} }
int ssb_devices_thaw(struct ssb_bus *bus) /** ssb_devices_thaw - Unfreeze all devices on the bus.
*
* This will re-attach the device drivers and re-init the devices.
*
* @ctx: The context structure from ssb_devices_freeze()
*/
int ssb_devices_thaw(struct ssb_freeze_context *ctx)
{ {
struct ssb_device *dev; struct ssb_bus *bus = ctx->bus;
struct ssb_driver *drv; struct ssb_device *sdev;
int err; struct ssb_driver *sdrv;
int i; unsigned int i;
int err, result = 0;
for (i = 0; i < bus->nr_devices; i++) { for (i = 0; i < bus->nr_devices; i++) {
dev = &(bus->devices[i]); if (!ctx->device_frozen[i])
if (!dev->dev ||
!dev->dev->driver ||
!device_is_registered(dev->dev))
continue; continue;
drv = drv_to_ssb_drv(dev->dev->driver); sdev = &bus->devices[i];
if (!drv)
if (SSB_WARN_ON(!sdev->dev || !sdev->dev->driver))
continue; continue;
if (SSB_WARN_ON(!drv->resume)) sdrv = drv_to_ssb_drv(sdev->dev->driver);
if (SSB_WARN_ON(!sdrv || !sdrv->probe))
continue; continue;
err = drv->resume(dev);
err = sdrv->probe(sdev, &sdev->id);
if (err) { if (err) {
ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n", ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n",
dev_name(dev->dev)); dev_name(sdev->dev));
result = err;
} }
ssb_driver_put(sdrv);
ssb_device_put(sdev);
} }
return 0; return result;
} }
#endif /* CONFIG_SSB_SPROM */ #endif /* CONFIG_SSB_SPROM */

View File

@ -90,6 +90,7 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
u16 *sprom; u16 *sprom;
int res = 0, err = -ENOMEM; int res = 0, err = -ENOMEM;
size_t sprom_size_words = bus->sprom_size; size_t sprom_size_words = bus->sprom_size;
struct ssb_freeze_context freeze;
sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
if (!sprom) if (!sprom)
@ -111,18 +112,13 @@ ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
err = -ERESTARTSYS; err = -ERESTARTSYS;
if (mutex_lock_interruptible(&bus->sprom_mutex)) if (mutex_lock_interruptible(&bus->sprom_mutex))
goto out_kfree; goto out_kfree;
err = ssb_devices_freeze(bus); err = ssb_devices_freeze(bus, &freeze);
if (err == -EOPNOTSUPP) {
ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. "
"No suspend support. Is CONFIG_PM enabled?\n");
goto out_unlock;
}
if (err) { if (err) {
ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n");
goto out_unlock; goto out_unlock;
} }
res = sprom_write(bus, sprom); res = sprom_write(bus, sprom);
err = ssb_devices_thaw(bus); err = ssb_devices_thaw(&freeze);
if (err) if (err)
ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n");
out_unlock: out_unlock:

View File

@ -176,13 +176,21 @@ extern const struct ssb_sprom *ssb_get_fallback_sprom(void);
/* core.c */ /* core.c */
extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m); extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m);
extern int ssb_devices_freeze(struct ssb_bus *bus);
extern int ssb_devices_thaw(struct ssb_bus *bus);
extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev); extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev);
int ssb_for_each_bus_call(unsigned long data, int ssb_for_each_bus_call(unsigned long data,
int (*func)(struct ssb_bus *bus, unsigned long data)); int (*func)(struct ssb_bus *bus, unsigned long data));
extern struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev); extern struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev);
struct ssb_freeze_context {
/* Pointer to the bus */
struct ssb_bus *bus;
/* Boolean list to indicate whether a device is frozen on this bus. */
bool device_frozen[SSB_MAX_NR_CORES];
};
extern int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx);
extern int ssb_devices_thaw(struct ssb_freeze_context *ctx);
/* b43_pci_bridge.c */ /* b43_pci_bridge.c */
#ifdef CONFIG_SSB_B43_PCI_BRIDGE #ifdef CONFIG_SSB_B43_PCI_BRIDGE