Skip to content

Commit

Permalink
ssb: Add SPROM/invariants support for PCMCIA devices
Browse files Browse the repository at this point in the history
This adds support for reading/writing the SPROM invariants
for PCMCIA based devices.

Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Michael Buesch authored and John W. Linville committed Mar 13, 2008
1 parent 068edce commit e7ec2e3
Show file tree
Hide file tree
Showing 8 changed files with 648 additions and 167 deletions.
6 changes: 6 additions & 0 deletions drivers/ssb/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ config SSB

If unsure, say N.

# Common SPROM support routines
config SSB_SPROM
bool

config SSB_PCIHOST_POSSIBLE
bool
depends on SSB && (PCI = y || PCI = SSB)
Expand All @@ -28,6 +32,7 @@ config SSB_PCIHOST_POSSIBLE
config SSB_PCIHOST
bool "Support for SSB on PCI-bus host"
depends on SSB_PCIHOST_POSSIBLE
select SSB_SPROM
default y
help
Support for a Sonics Silicon Backplane on top
Expand All @@ -48,6 +53,7 @@ config SSB_PCMCIAHOST_POSSIBLE
config SSB_PCMCIAHOST
bool "Support for SSB on PCMCIA-bus host (EXPERIMENTAL)"
depends on SSB_PCMCIAHOST_POSSIBLE
select SSB_SPROM
help
Support for a Sonics Silicon Backplane on top
of a PCMCIA device.
Expand Down
1 change: 1 addition & 0 deletions drivers/ssb/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# core
ssb-y += main.o scan.o
ssb-$(CONFIG_SSB_EMBEDDED) += embedded.o
ssb-$(CONFIG_SSB_SPROM) += sprom.o

# host support
ssb-$(CONFIG_SSB_PCIHOST) += pci.o pcihost_wrapper.o
Expand Down
23 changes: 21 additions & 2 deletions drivers/ssb/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,25 @@ struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev)
}
#endif /* CONFIG_SSB_PCIHOST */

#ifdef CONFIG_SSB_PCMCIAHOST
struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev)
{
struct ssb_bus *bus;

ssb_buses_lock();
list_for_each_entry(bus, &buses, list) {
if (bus->bustype == SSB_BUSTYPE_PCMCIA &&
bus->host_pcmcia == pdev)
goto found;
}
bus = NULL;
found:
ssb_buses_unlock();

return bus;
}
#endif /* CONFIG_SSB_PCMCIAHOST */

int ssb_for_each_bus_call(unsigned long data,
int (*func)(struct ssb_bus *bus, unsigned long data))
{
Expand Down Expand Up @@ -398,7 +417,7 @@ void ssb_bus_unregister(struct ssb_bus *bus)
list_del(&bus->list);
ssb_buses_unlock();

/* ssb_pcmcia_exit(bus); */
ssb_pcmcia_exit(bus);
ssb_pci_exit(bus);
ssb_iounmap(bus);
}
Expand Down Expand Up @@ -663,7 +682,7 @@ static int ssb_bus_register(struct ssb_bus *bus,
err_dequeue:
list_del(&bus->list);
err_pcmcia_exit:
/* ssb_pcmcia_exit(bus); */
ssb_pcmcia_exit(bus);
err_pci_exit:
ssb_pci_exit(bus);
err_unmap:
Expand Down
113 changes: 10 additions & 103 deletions drivers/ssb/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ static u8 ssb_sprom_crc(const u16 *sprom, u16 size)
return crc;
}

static int sprom_check_crc(const u16 *sprom, u16 size)
static int sprom_check_crc(const u16 *sprom, size_t size)
{
u8 crc;
u8 expected_crc;
Expand All @@ -242,12 +242,14 @@ static int sprom_check_crc(const u16 *sprom, u16 size)
return 0;
}

static void sprom_do_read(struct ssb_bus *bus, u16 *sprom)
static int sprom_do_read(struct ssb_bus *bus, u16 *sprom)
{
int i;

for (i = 0; i < bus->sprom_size; i++)
sprom[i] = ioread16(bus->mmio + SSB_SPROM_BASE + (i * 2));

return 0;
}

static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
Expand Down Expand Up @@ -660,71 +662,18 @@ const struct ssb_bus_ops ssb_pci_ops = {
.write32 = ssb_pci_write32,
};

static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, u16 size)
{
int i, pos = 0;

for (i = 0; i < size; i++)
pos += snprintf(buf + pos, buf_len - pos - 1,
"%04X", swab16(sprom[i]) & 0xFFFF);
pos += snprintf(buf + pos, buf_len - pos - 1, "\n");

return pos + 1;
}

static int hex2sprom(u16 *sprom, const char *dump, size_t len, u16 size)
{
char tmp[5] = { 0 };
int cnt = 0;
unsigned long parsed;

if (len < size * 2)
return -EINVAL;

while (cnt < size) {
memcpy(tmp, dump, 4);
dump += 4;
parsed = simple_strtoul(tmp, NULL, 16);
sprom[cnt++] = swab16((u16)parsed);
}

return 0;
}

static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev,
struct device_attribute *attr,
char *buf)
{
struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
struct ssb_bus *bus;
u16 *sprom;
int err = -ENODEV;
ssize_t count = 0;

bus = ssb_pci_dev_to_bus(pdev);
if (!bus)
goto out;
err = -ENOMEM;
sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
if (!sprom)
goto out;
return -ENODEV;

/* Use interruptible locking, as the SPROM write might
* be holding the lock for several seconds. So allow userspace
* to cancel operation. */
err = -ERESTARTSYS;
if (mutex_lock_interruptible(&bus->pci_sprom_mutex))
goto out_kfree;
sprom_do_read(bus, sprom);
mutex_unlock(&bus->pci_sprom_mutex);

count = sprom2hex(sprom, buf, PAGE_SIZE, bus->sprom_size);
err = 0;

out_kfree:
kfree(sprom);
out:
return err ? err : count;
return ssb_attr_sprom_show(bus, buf, sprom_do_read);
}

static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev,
Expand All @@ -733,55 +682,13 @@ static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev,
{
struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
struct ssb_bus *bus;
u16 *sprom;
int res = 0, err = -ENODEV;

bus = ssb_pci_dev_to_bus(pdev);
if (!bus)
goto out;
err = -ENOMEM;
sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
if (!sprom)
goto out;
err = hex2sprom(sprom, buf, count, bus->sprom_size);
if (err) {
err = -EINVAL;
goto out_kfree;
}
err = sprom_check_crc(sprom, bus->sprom_size);
if (err) {
err = -EINVAL;
goto out_kfree;
}
return -ENODEV;

/* Use interruptible locking, as the SPROM write might
* be holding the lock for several seconds. So allow userspace
* to cancel operation. */
err = -ERESTARTSYS;
if (mutex_lock_interruptible(&bus->pci_sprom_mutex))
goto out_kfree;
err = ssb_devices_freeze(bus);
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) {
ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n");
goto out_unlock;
}
res = sprom_do_write(bus, sprom);
err = ssb_devices_thaw(bus);
if (err)
ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n");
out_unlock:
mutex_unlock(&bus->pci_sprom_mutex);
out_kfree:
kfree(sprom);
out:
if (res)
return res;
return err ? err : count;
return ssb_attr_sprom_store(bus, buf, count,
sprom_check_crc, sprom_do_write);
}

static DEVICE_ATTR(ssb_sprom, 0600,
Expand All @@ -808,7 +715,7 @@ int ssb_pci_init(struct ssb_bus *bus)
return 0;

pdev = bus->host_pci;
mutex_init(&bus->pci_sprom_mutex);
mutex_init(&bus->sprom_mutex);
err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom);
if (err)
goto out;
Expand Down
Loading

0 comments on commit e7ec2e3

Please sign in to comment.