diff options
author | Michael Buesch <mb@bu3sch.de> | 2008-03-10 12:26:32 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-03-13 19:32:32 -0400 |
commit | e7ec2e3230633a858af1b0b359f6c4670dbeb997 (patch) | |
tree | c43dbd7f6cab0ac066c039697528312d802617ef /drivers/ssb/pci.c | |
parent | 068edceb7e73c05f77e204442ea8f86e238575da (diff) |
ssb: Add SPROM/invariants support for PCMCIA devices
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>
Diffstat (limited to 'drivers/ssb/pci.c')
-rw-r--r-- | drivers/ssb/pci.c | 113 |
1 files changed, 10 insertions, 103 deletions
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 1facc7620fc8..f1514b33cfae 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c | |||
@@ -227,7 +227,7 @@ static u8 ssb_sprom_crc(const u16 *sprom, u16 size) | |||
227 | return crc; | 227 | return crc; |
228 | } | 228 | } |
229 | 229 | ||
230 | static int sprom_check_crc(const u16 *sprom, u16 size) | 230 | static int sprom_check_crc(const u16 *sprom, size_t size) |
231 | { | 231 | { |
232 | u8 crc; | 232 | u8 crc; |
233 | u8 expected_crc; | 233 | u8 expected_crc; |
@@ -242,12 +242,14 @@ static int sprom_check_crc(const u16 *sprom, u16 size) | |||
242 | return 0; | 242 | return 0; |
243 | } | 243 | } |
244 | 244 | ||
245 | static void sprom_do_read(struct ssb_bus *bus, u16 *sprom) | 245 | static int sprom_do_read(struct ssb_bus *bus, u16 *sprom) |
246 | { | 246 | { |
247 | int i; | 247 | int i; |
248 | 248 | ||
249 | for (i = 0; i < bus->sprom_size; i++) | 249 | for (i = 0; i < bus->sprom_size; i++) |
250 | sprom[i] = ioread16(bus->mmio + SSB_SPROM_BASE + (i * 2)); | 250 | sprom[i] = ioread16(bus->mmio + SSB_SPROM_BASE + (i * 2)); |
251 | |||
252 | return 0; | ||
251 | } | 253 | } |
252 | 254 | ||
253 | static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) | 255 | static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) |
@@ -660,71 +662,18 @@ const struct ssb_bus_ops ssb_pci_ops = { | |||
660 | .write32 = ssb_pci_write32, | 662 | .write32 = ssb_pci_write32, |
661 | }; | 663 | }; |
662 | 664 | ||
663 | static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, u16 size) | ||
664 | { | ||
665 | int i, pos = 0; | ||
666 | |||
667 | for (i = 0; i < size; i++) | ||
668 | pos += snprintf(buf + pos, buf_len - pos - 1, | ||
669 | "%04X", swab16(sprom[i]) & 0xFFFF); | ||
670 | pos += snprintf(buf + pos, buf_len - pos - 1, "\n"); | ||
671 | |||
672 | return pos + 1; | ||
673 | } | ||
674 | |||
675 | static int hex2sprom(u16 *sprom, const char *dump, size_t len, u16 size) | ||
676 | { | ||
677 | char tmp[5] = { 0 }; | ||
678 | int cnt = 0; | ||
679 | unsigned long parsed; | ||
680 | |||
681 | if (len < size * 2) | ||
682 | return -EINVAL; | ||
683 | |||
684 | while (cnt < size) { | ||
685 | memcpy(tmp, dump, 4); | ||
686 | dump += 4; | ||
687 | parsed = simple_strtoul(tmp, NULL, 16); | ||
688 | sprom[cnt++] = swab16((u16)parsed); | ||
689 | } | ||
690 | |||
691 | return 0; | ||
692 | } | ||
693 | |||
694 | static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev, | 665 | static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev, |
695 | struct device_attribute *attr, | 666 | struct device_attribute *attr, |
696 | char *buf) | 667 | char *buf) |
697 | { | 668 | { |
698 | struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); | 669 | struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); |
699 | struct ssb_bus *bus; | 670 | struct ssb_bus *bus; |
700 | u16 *sprom; | ||
701 | int err = -ENODEV; | ||
702 | ssize_t count = 0; | ||
703 | 671 | ||
704 | bus = ssb_pci_dev_to_bus(pdev); | 672 | bus = ssb_pci_dev_to_bus(pdev); |
705 | if (!bus) | 673 | if (!bus) |
706 | goto out; | 674 | return -ENODEV; |
707 | err = -ENOMEM; | ||
708 | sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); | ||
709 | if (!sprom) | ||
710 | goto out; | ||
711 | 675 | ||
712 | /* Use interruptible locking, as the SPROM write might | 676 | return ssb_attr_sprom_show(bus, buf, sprom_do_read); |
713 | * be holding the lock for several seconds. So allow userspace | ||
714 | * to cancel operation. */ | ||
715 | err = -ERESTARTSYS; | ||
716 | if (mutex_lock_interruptible(&bus->pci_sprom_mutex)) | ||
717 | goto out_kfree; | ||
718 | sprom_do_read(bus, sprom); | ||
719 | mutex_unlock(&bus->pci_sprom_mutex); | ||
720 | |||
721 | count = sprom2hex(sprom, buf, PAGE_SIZE, bus->sprom_size); | ||
722 | err = 0; | ||
723 | |||
724 | out_kfree: | ||
725 | kfree(sprom); | ||
726 | out: | ||
727 | return err ? err : count; | ||
728 | } | 677 | } |
729 | 678 | ||
730 | static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, | 679 | static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, |
@@ -733,55 +682,13 @@ static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, | |||
733 | { | 682 | { |
734 | struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); | 683 | struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); |
735 | struct ssb_bus *bus; | 684 | struct ssb_bus *bus; |
736 | u16 *sprom; | ||
737 | int res = 0, err = -ENODEV; | ||
738 | 685 | ||
739 | bus = ssb_pci_dev_to_bus(pdev); | 686 | bus = ssb_pci_dev_to_bus(pdev); |
740 | if (!bus) | 687 | if (!bus) |
741 | goto out; | 688 | return -ENODEV; |
742 | err = -ENOMEM; | ||
743 | sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); | ||
744 | if (!sprom) | ||
745 | goto out; | ||
746 | err = hex2sprom(sprom, buf, count, bus->sprom_size); | ||
747 | if (err) { | ||
748 | err = -EINVAL; | ||
749 | goto out_kfree; | ||
750 | } | ||
751 | err = sprom_check_crc(sprom, bus->sprom_size); | ||
752 | if (err) { | ||
753 | err = -EINVAL; | ||
754 | goto out_kfree; | ||
755 | } | ||
756 | 689 | ||
757 | /* Use interruptible locking, as the SPROM write might | 690 | return ssb_attr_sprom_store(bus, buf, count, |
758 | * be holding the lock for several seconds. So allow userspace | 691 | sprom_check_crc, sprom_do_write); |
759 | * to cancel operation. */ | ||
760 | err = -ERESTARTSYS; | ||
761 | if (mutex_lock_interruptible(&bus->pci_sprom_mutex)) | ||
762 | goto out_kfree; | ||
763 | err = ssb_devices_freeze(bus); | ||
764 | if (err == -EOPNOTSUPP) { | ||
765 | ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. " | ||
766 | "No suspend support. Is CONFIG_PM enabled?\n"); | ||
767 | goto out_unlock; | ||
768 | } | ||
769 | if (err) { | ||
770 | ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); | ||
771 | goto out_unlock; | ||
772 | } | ||
773 | res = sprom_do_write(bus, sprom); | ||
774 | err = ssb_devices_thaw(bus); | ||
775 | if (err) | ||
776 | ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); | ||
777 | out_unlock: | ||
778 | mutex_unlock(&bus->pci_sprom_mutex); | ||
779 | out_kfree: | ||
780 | kfree(sprom); | ||
781 | out: | ||
782 | if (res) | ||
783 | return res; | ||
784 | return err ? err : count; | ||
785 | } | 692 | } |
786 | 693 | ||
787 | static DEVICE_ATTR(ssb_sprom, 0600, | 694 | static DEVICE_ATTR(ssb_sprom, 0600, |
@@ -808,7 +715,7 @@ int ssb_pci_init(struct ssb_bus *bus) | |||
808 | return 0; | 715 | return 0; |
809 | 716 | ||
810 | pdev = bus->host_pci; | 717 | pdev = bus->host_pci; |
811 | mutex_init(&bus->pci_sprom_mutex); | 718 | mutex_init(&bus->sprom_mutex); |
812 | err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom); | 719 | err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom); |
813 | if (err) | 720 | if (err) |
814 | goto out; | 721 | goto out; |