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 | |
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>
-rw-r--r-- | drivers/ssb/Kconfig | 6 | ||||
-rw-r--r-- | drivers/ssb/Makefile | 1 | ||||
-rw-r--r-- | drivers/ssb/main.c | 23 | ||||
-rw-r--r-- | drivers/ssb/pci.c | 113 | ||||
-rw-r--r-- | drivers/ssb/pcmcia.c | 518 | ||||
-rw-r--r-- | drivers/ssb/sprom.c | 133 | ||||
-rw-r--r-- | drivers/ssb/ssb_private.h | 17 | ||||
-rw-r--r-- | include/linux/ssb/ssb.h | 4 |
8 files changed, 648 insertions, 167 deletions
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index f69ef0ba2613..0f7cce2560d1 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig | |||
@@ -20,6 +20,10 @@ config SSB | |||
20 | 20 | ||
21 | If unsure, say N. | 21 | If unsure, say N. |
22 | 22 | ||
23 | # Common SPROM support routines | ||
24 | config SSB_SPROM | ||
25 | bool | ||
26 | |||
23 | config SSB_PCIHOST_POSSIBLE | 27 | config SSB_PCIHOST_POSSIBLE |
24 | bool | 28 | bool |
25 | depends on SSB && (PCI = y || PCI = SSB) | 29 | depends on SSB && (PCI = y || PCI = SSB) |
@@ -28,6 +32,7 @@ config SSB_PCIHOST_POSSIBLE | |||
28 | config SSB_PCIHOST | 32 | config SSB_PCIHOST |
29 | bool "Support for SSB on PCI-bus host" | 33 | bool "Support for SSB on PCI-bus host" |
30 | depends on SSB_PCIHOST_POSSIBLE | 34 | depends on SSB_PCIHOST_POSSIBLE |
35 | select SSB_SPROM | ||
31 | default y | 36 | default y |
32 | help | 37 | help |
33 | Support for a Sonics Silicon Backplane on top | 38 | Support for a Sonics Silicon Backplane on top |
@@ -48,6 +53,7 @@ config SSB_PCMCIAHOST_POSSIBLE | |||
48 | config SSB_PCMCIAHOST | 53 | config SSB_PCMCIAHOST |
49 | bool "Support for SSB on PCMCIA-bus host (EXPERIMENTAL)" | 54 | bool "Support for SSB on PCMCIA-bus host (EXPERIMENTAL)" |
50 | depends on SSB_PCMCIAHOST_POSSIBLE | 55 | depends on SSB_PCMCIAHOST_POSSIBLE |
56 | select SSB_SPROM | ||
51 | help | 57 | help |
52 | Support for a Sonics Silicon Backplane on top | 58 | Support for a Sonics Silicon Backplane on top |
53 | of a PCMCIA device. | 59 | of a PCMCIA device. |
diff --git a/drivers/ssb/Makefile b/drivers/ssb/Makefile index 910f35e32fc9..6f255e9c5af9 100644 --- a/drivers/ssb/Makefile +++ b/drivers/ssb/Makefile | |||
@@ -1,6 +1,7 @@ | |||
1 | # core | 1 | # core |
2 | ssb-y += main.o scan.o | 2 | ssb-y += main.o scan.o |
3 | ssb-$(CONFIG_SSB_EMBEDDED) += embedded.o | 3 | ssb-$(CONFIG_SSB_EMBEDDED) += embedded.o |
4 | ssb-$(CONFIG_SSB_SPROM) += sprom.o | ||
4 | 5 | ||
5 | # host support | 6 | # host support |
6 | ssb-$(CONFIG_SSB_PCIHOST) += pci.o pcihost_wrapper.o | 7 | ssb-$(CONFIG_SSB_PCIHOST) += pci.o pcihost_wrapper.o |
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 49d7bbb9bea7..e12371916444 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c | |||
@@ -69,6 +69,25 @@ found: | |||
69 | } | 69 | } |
70 | #endif /* CONFIG_SSB_PCIHOST */ | 70 | #endif /* CONFIG_SSB_PCIHOST */ |
71 | 71 | ||
72 | #ifdef CONFIG_SSB_PCMCIAHOST | ||
73 | struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev) | ||
74 | { | ||
75 | struct ssb_bus *bus; | ||
76 | |||
77 | ssb_buses_lock(); | ||
78 | list_for_each_entry(bus, &buses, list) { | ||
79 | if (bus->bustype == SSB_BUSTYPE_PCMCIA && | ||
80 | bus->host_pcmcia == pdev) | ||
81 | goto found; | ||
82 | } | ||
83 | bus = NULL; | ||
84 | found: | ||
85 | ssb_buses_unlock(); | ||
86 | |||
87 | return bus; | ||
88 | } | ||
89 | #endif /* CONFIG_SSB_PCMCIAHOST */ | ||
90 | |||
72 | int ssb_for_each_bus_call(unsigned long data, | 91 | int ssb_for_each_bus_call(unsigned long data, |
73 | int (*func)(struct ssb_bus *bus, unsigned long data)) | 92 | int (*func)(struct ssb_bus *bus, unsigned long data)) |
74 | { | 93 | { |
@@ -398,7 +417,7 @@ void ssb_bus_unregister(struct ssb_bus *bus) | |||
398 | list_del(&bus->list); | 417 | list_del(&bus->list); |
399 | ssb_buses_unlock(); | 418 | ssb_buses_unlock(); |
400 | 419 | ||
401 | /* ssb_pcmcia_exit(bus); */ | 420 | ssb_pcmcia_exit(bus); |
402 | ssb_pci_exit(bus); | 421 | ssb_pci_exit(bus); |
403 | ssb_iounmap(bus); | 422 | ssb_iounmap(bus); |
404 | } | 423 | } |
@@ -663,7 +682,7 @@ out: | |||
663 | err_dequeue: | 682 | err_dequeue: |
664 | list_del(&bus->list); | 683 | list_del(&bus->list); |
665 | err_pcmcia_exit: | 684 | err_pcmcia_exit: |
666 | /* ssb_pcmcia_exit(bus); */ | 685 | ssb_pcmcia_exit(bus); |
667 | err_pci_exit: | 686 | err_pci_exit: |
668 | ssb_pci_exit(bus); | 687 | ssb_pci_exit(bus); |
669 | err_unmap: | 688 | err_unmap: |
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; |
diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c index 84b3a845a8a8..cd49f7c65531 100644 --- a/drivers/ssb/pcmcia.c +++ b/drivers/ssb/pcmcia.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * PCMCIA-Hostbus related functions | 3 | * PCMCIA-Hostbus related functions |
4 | * | 4 | * |
5 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | 5 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright 2007 Michael Buesch <mb@bu3sch.de> | 6 | * Copyright 2007-2008 Michael Buesch <mb@bu3sch.de> |
7 | * | 7 | * |
8 | * Licensed under the GNU/GPL. See COPYING for details. | 8 | * Licensed under the GNU/GPL. See COPYING for details. |
9 | */ | 9 | */ |
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/ssb/ssb.h> | 11 | #include <linux/ssb/ssb.h> |
12 | #include <linux/delay.h> | 12 | #include <linux/delay.h> |
13 | #include <linux/io.h> | 13 | #include <linux/io.h> |
14 | #include <linux/etherdevice.h> | ||
14 | 15 | ||
15 | #include <pcmcia/cs_types.h> | 16 | #include <pcmcia/cs_types.h> |
16 | #include <pcmcia/cs.h> | 17 | #include <pcmcia/cs.h> |
@@ -26,59 +27,132 @@ | |||
26 | #define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 0 | 27 | #define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 0 |
27 | 28 | ||
28 | 29 | ||
30 | /* PCMCIA configuration registers */ | ||
31 | #define SSB_PCMCIA_CORECTL 0x00 | ||
32 | #define SSB_PCMCIA_CORECTL_RESET 0x80 /* Core reset */ | ||
33 | #define SSB_PCMCIA_CORECTL_IRQEN 0x04 /* IRQ enable */ | ||
34 | #define SSB_PCMCIA_CORECTL_FUNCEN 0x01 /* Function enable */ | ||
35 | #define SSB_PCMCIA_CORECTL2 0x80 | ||
36 | #define SSB_PCMCIA_ADDRESS0 0x2E | ||
37 | #define SSB_PCMCIA_ADDRESS1 0x30 | ||
38 | #define SSB_PCMCIA_ADDRESS2 0x32 | ||
39 | #define SSB_PCMCIA_MEMSEG 0x34 | ||
40 | #define SSB_PCMCIA_SPROMCTL 0x36 | ||
41 | #define SSB_PCMCIA_SPROMCTL_IDLE 0 | ||
42 | #define SSB_PCMCIA_SPROMCTL_WRITE 1 | ||
43 | #define SSB_PCMCIA_SPROMCTL_READ 2 | ||
44 | #define SSB_PCMCIA_SPROMCTL_WRITEEN 4 | ||
45 | #define SSB_PCMCIA_SPROMCTL_WRITEDIS 7 | ||
46 | #define SSB_PCMCIA_SPROMCTL_DONE 8 | ||
47 | #define SSB_PCMCIA_SPROM_DATALO 0x38 | ||
48 | #define SSB_PCMCIA_SPROM_DATAHI 0x3A | ||
49 | #define SSB_PCMCIA_SPROM_ADDRLO 0x3C | ||
50 | #define SSB_PCMCIA_SPROM_ADDRHI 0x3E | ||
51 | |||
52 | /* Hardware invariants CIS tuples */ | ||
53 | #define SSB_PCMCIA_CIS 0x80 | ||
54 | #define SSB_PCMCIA_CIS_ID 0x01 | ||
55 | #define SSB_PCMCIA_CIS_BOARDREV 0x02 | ||
56 | #define SSB_PCMCIA_CIS_PA 0x03 | ||
57 | #define SSB_PCMCIA_CIS_PA_PA0B0_LO 0 | ||
58 | #define SSB_PCMCIA_CIS_PA_PA0B0_HI 1 | ||
59 | #define SSB_PCMCIA_CIS_PA_PA0B1_LO 2 | ||
60 | #define SSB_PCMCIA_CIS_PA_PA0B1_HI 3 | ||
61 | #define SSB_PCMCIA_CIS_PA_PA0B2_LO 4 | ||
62 | #define SSB_PCMCIA_CIS_PA_PA0B2_HI 5 | ||
63 | #define SSB_PCMCIA_CIS_PA_ITSSI 6 | ||
64 | #define SSB_PCMCIA_CIS_PA_MAXPOW 7 | ||
65 | #define SSB_PCMCIA_CIS_OEMNAME 0x04 | ||
66 | #define SSB_PCMCIA_CIS_CCODE 0x05 | ||
67 | #define SSB_PCMCIA_CIS_ANTENNA 0x06 | ||
68 | #define SSB_PCMCIA_CIS_ANTGAIN 0x07 | ||
69 | #define SSB_PCMCIA_CIS_BFLAGS 0x08 | ||
70 | #define SSB_PCMCIA_CIS_LEDS 0x09 | ||
71 | |||
72 | /* PCMCIA SPROM size. */ | ||
73 | #define SSB_PCMCIA_SPROM_SIZE 256 | ||
74 | #define SSB_PCMCIA_SPROM_SIZE_BYTES (SSB_PCMCIA_SPROM_SIZE * sizeof(u16)) | ||
75 | |||
76 | |||
77 | /* Write to a PCMCIA configuration register. */ | ||
78 | static int ssb_pcmcia_cfg_write(struct ssb_bus *bus, u8 offset, u8 value) | ||
79 | { | ||
80 | conf_reg_t reg; | ||
81 | int res; | ||
82 | |||
83 | memset(®, 0, sizeof(reg)); | ||
84 | reg.Offset = offset; | ||
85 | reg.Action = CS_WRITE; | ||
86 | reg.Value = value; | ||
87 | res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | ||
88 | if (unlikely(res != CS_SUCCESS)) | ||
89 | return -EBUSY; | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | /* Read from a PCMCIA configuration register. */ | ||
95 | static int ssb_pcmcia_cfg_read(struct ssb_bus *bus, u8 offset, u8 *value) | ||
96 | { | ||
97 | conf_reg_t reg; | ||
98 | int res; | ||
99 | |||
100 | memset(®, 0, sizeof(reg)); | ||
101 | reg.Offset = offset; | ||
102 | reg.Action = CS_READ; | ||
103 | res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | ||
104 | if (unlikely(res != CS_SUCCESS)) | ||
105 | return -EBUSY; | ||
106 | *value = reg.Value; | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
29 | int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, | 111 | int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, |
30 | u8 coreidx) | 112 | u8 coreidx) |
31 | { | 113 | { |
32 | struct pcmcia_device *pdev = bus->host_pcmcia; | ||
33 | int err; | 114 | int err; |
34 | int attempts = 0; | 115 | int attempts = 0; |
35 | u32 cur_core; | 116 | u32 cur_core; |
36 | conf_reg_t reg; | ||
37 | u32 addr; | 117 | u32 addr; |
38 | u32 read_addr; | 118 | u32 read_addr; |
119 | u8 val; | ||
39 | 120 | ||
40 | addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; | 121 | addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; |
41 | while (1) { | 122 | while (1) { |
42 | reg.Action = CS_WRITE; | 123 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS0, |
43 | reg.Offset = 0x2E; | 124 | (addr & 0x0000F000) >> 12); |
44 | reg.Value = (addr & 0x0000F000) >> 12; | 125 | if (err) |
45 | err = pcmcia_access_configuration_register(pdev, ®); | ||
46 | if (err != CS_SUCCESS) | ||
47 | goto error; | 126 | goto error; |
48 | reg.Offset = 0x30; | 127 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS1, |
49 | reg.Value = (addr & 0x00FF0000) >> 16; | 128 | (addr & 0x00FF0000) >> 16); |
50 | err = pcmcia_access_configuration_register(pdev, ®); | 129 | if (err) |
51 | if (err != CS_SUCCESS) | ||
52 | goto error; | 130 | goto error; |
53 | reg.Offset = 0x32; | 131 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS2, |
54 | reg.Value = (addr & 0xFF000000) >> 24; | 132 | (addr & 0xFF000000) >> 24); |
55 | err = pcmcia_access_configuration_register(pdev, ®); | 133 | if (err) |
56 | if (err != CS_SUCCESS) | ||
57 | goto error; | 134 | goto error; |
58 | 135 | ||
59 | read_addr = 0; | 136 | read_addr = 0; |
60 | 137 | ||
61 | reg.Action = CS_READ; | 138 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS0, &val); |
62 | reg.Offset = 0x2E; | 139 | if (err) |
63 | err = pcmcia_access_configuration_register(pdev, ®); | ||
64 | if (err != CS_SUCCESS) | ||
65 | goto error; | 140 | goto error; |
66 | read_addr |= ((u32)(reg.Value & 0x0F)) << 12; | 141 | read_addr |= ((u32)(val & 0x0F)) << 12; |
67 | reg.Offset = 0x30; | 142 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS1, &val); |
68 | err = pcmcia_access_configuration_register(pdev, ®); | 143 | if (err) |
69 | if (err != CS_SUCCESS) | ||
70 | goto error; | 144 | goto error; |
71 | read_addr |= ((u32)reg.Value) << 16; | 145 | read_addr |= ((u32)val) << 16; |
72 | reg.Offset = 0x32; | 146 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS2, &val); |
73 | err = pcmcia_access_configuration_register(pdev, ®); | 147 | if (err) |
74 | if (err != CS_SUCCESS) | ||
75 | goto error; | 148 | goto error; |
76 | read_addr |= ((u32)reg.Value) << 24; | 149 | read_addr |= ((u32)val) << 24; |
77 | 150 | ||
78 | cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE; | 151 | cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE; |
79 | if (cur_core == coreidx) | 152 | if (cur_core == coreidx) |
80 | break; | 153 | break; |
81 | 154 | ||
155 | err = -ETIMEDOUT; | ||
82 | if (attempts++ > SSB_BAR0_MAX_RETRIES) | 156 | if (attempts++ > SSB_BAR0_MAX_RETRIES) |
83 | goto error; | 157 | goto error; |
84 | udelay(10); | 158 | udelay(10); |
@@ -87,7 +161,7 @@ int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, | |||
87 | return 0; | 161 | return 0; |
88 | error: | 162 | error: |
89 | ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); | 163 | ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); |
90 | return -ENODEV; | 164 | return err; |
91 | } | 165 | } |
92 | 166 | ||
93 | int ssb_pcmcia_switch_core(struct ssb_bus *bus, | 167 | int ssb_pcmcia_switch_core(struct ssb_bus *bus, |
@@ -112,27 +186,21 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus, | |||
112 | int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) | 186 | int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) |
113 | { | 187 | { |
114 | int attempts = 0; | 188 | int attempts = 0; |
115 | conf_reg_t reg; | 189 | int err; |
116 | int res; | 190 | u8 val; |
117 | 191 | ||
118 | SSB_WARN_ON((seg != 0) && (seg != 1)); | 192 | SSB_WARN_ON((seg != 0) && (seg != 1)); |
119 | reg.Offset = 0x34; | ||
120 | reg.Function = 0; | ||
121 | while (1) { | 193 | while (1) { |
122 | reg.Action = CS_WRITE; | 194 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_MEMSEG, seg); |
123 | reg.Value = seg; | 195 | if (err) |
124 | res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | ||
125 | if (unlikely(res != CS_SUCCESS)) | ||
126 | goto error; | 196 | goto error; |
127 | reg.Value = 0xFF; | 197 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_MEMSEG, &val); |
128 | reg.Action = CS_READ; | 198 | if (err) |
129 | res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | ||
130 | if (unlikely(res != CS_SUCCESS)) | ||
131 | goto error; | 199 | goto error; |
132 | 200 | if (val == seg) | |
133 | if (reg.Value == seg) | ||
134 | break; | 201 | break; |
135 | 202 | ||
203 | err = -ETIMEDOUT; | ||
136 | if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES)) | 204 | if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES)) |
137 | goto error; | 205 | goto error; |
138 | udelay(10); | 206 | udelay(10); |
@@ -142,7 +210,7 @@ int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) | |||
142 | return 0; | 210 | return 0; |
143 | error: | 211 | error: |
144 | ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); | 212 | ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); |
145 | return -ENODEV; | 213 | return err; |
146 | } | 214 | } |
147 | 215 | ||
148 | static int select_core_and_segment(struct ssb_device *dev, | 216 | static int select_core_and_segment(struct ssb_device *dev, |
@@ -276,18 +344,344 @@ const struct ssb_bus_ops ssb_pcmcia_ops = { | |||
276 | .write32 = ssb_pcmcia_write32, | 344 | .write32 = ssb_pcmcia_write32, |
277 | }; | 345 | }; |
278 | 346 | ||
279 | #include <linux/etherdevice.h> | 347 | static int ssb_pcmcia_sprom_command(struct ssb_bus *bus, u8 command) |
348 | { | ||
349 | unsigned int i; | ||
350 | int err; | ||
351 | u8 value; | ||
352 | |||
353 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROMCTL, command); | ||
354 | if (err) | ||
355 | return err; | ||
356 | for (i = 0; i < 1000; i++) { | ||
357 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROMCTL, &value); | ||
358 | if (err) | ||
359 | return err; | ||
360 | if (value & SSB_PCMCIA_SPROMCTL_DONE) | ||
361 | return 0; | ||
362 | udelay(10); | ||
363 | } | ||
364 | |||
365 | return -ETIMEDOUT; | ||
366 | } | ||
367 | |||
368 | /* offset is the 16bit word offset */ | ||
369 | static int ssb_pcmcia_sprom_read(struct ssb_bus *bus, u16 offset, u16 *value) | ||
370 | { | ||
371 | int err; | ||
372 | u8 lo, hi; | ||
373 | |||
374 | offset *= 2; /* Make byte offset */ | ||
375 | |||
376 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO, | ||
377 | (offset & 0x00FF)); | ||
378 | if (err) | ||
379 | return err; | ||
380 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI, | ||
381 | (offset & 0xFF00) >> 8); | ||
382 | if (err) | ||
383 | return err; | ||
384 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_READ); | ||
385 | if (err) | ||
386 | return err; | ||
387 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATALO, &lo); | ||
388 | if (err) | ||
389 | return err; | ||
390 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATAHI, &hi); | ||
391 | if (err) | ||
392 | return err; | ||
393 | *value = (lo | (((u16)hi) << 8)); | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | /* offset is the 16bit word offset */ | ||
399 | static int ssb_pcmcia_sprom_write(struct ssb_bus *bus, u16 offset, u16 value) | ||
400 | { | ||
401 | int err; | ||
402 | |||
403 | offset *= 2; /* Make byte offset */ | ||
404 | |||
405 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO, | ||
406 | (offset & 0x00FF)); | ||
407 | if (err) | ||
408 | return err; | ||
409 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI, | ||
410 | (offset & 0xFF00) >> 8); | ||
411 | if (err) | ||
412 | return err; | ||
413 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATALO, | ||
414 | (value & 0x00FF)); | ||
415 | if (err) | ||
416 | return err; | ||
417 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATAHI, | ||
418 | (value & 0xFF00) >> 8); | ||
419 | if (err) | ||
420 | return err; | ||
421 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITE); | ||
422 | if (err) | ||
423 | return err; | ||
424 | msleep(20); | ||
425 | |||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | /* Read the SPROM image. bufsize is in 16bit words. */ | ||
430 | static int ssb_pcmcia_sprom_read_all(struct ssb_bus *bus, u16 *sprom) | ||
431 | { | ||
432 | int err, i; | ||
433 | |||
434 | for (i = 0; i < SSB_PCMCIA_SPROM_SIZE; i++) { | ||
435 | err = ssb_pcmcia_sprom_read(bus, i, &sprom[i]); | ||
436 | if (err) | ||
437 | return err; | ||
438 | } | ||
439 | |||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | /* Write the SPROM image. size is in 16bit words. */ | ||
444 | static int ssb_pcmcia_sprom_write_all(struct ssb_bus *bus, const u16 *sprom) | ||
445 | { | ||
446 | int i, err; | ||
447 | bool failed = 0; | ||
448 | size_t size = SSB_PCMCIA_SPROM_SIZE; | ||
449 | |||
450 | ssb_printk(KERN_NOTICE PFX | ||
451 | "Writing SPROM. Do NOT turn off the power! " | ||
452 | "Please stand by...\n"); | ||
453 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEEN); | ||
454 | if (err) { | ||
455 | ssb_printk(KERN_NOTICE PFX | ||
456 | "Could not enable SPROM write access.\n"); | ||
457 | return -EBUSY; | ||
458 | } | ||
459 | ssb_printk(KERN_NOTICE PFX "[ 0%%"); | ||
460 | msleep(500); | ||
461 | for (i = 0; i < size; i++) { | ||
462 | if (i == size / 4) | ||
463 | ssb_printk("25%%"); | ||
464 | else if (i == size / 2) | ||
465 | ssb_printk("50%%"); | ||
466 | else if (i == (size * 3) / 4) | ||
467 | ssb_printk("75%%"); | ||
468 | else if (i % 2) | ||
469 | ssb_printk("."); | ||
470 | err = ssb_pcmcia_sprom_write(bus, i, sprom[i]); | ||
471 | if (err) { | ||
472 | ssb_printk("\n" KERN_NOTICE PFX | ||
473 | "Failed to write to SPROM.\n"); | ||
474 | failed = 1; | ||
475 | break; | ||
476 | } | ||
477 | } | ||
478 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEDIS); | ||
479 | if (err) { | ||
480 | ssb_printk("\n" KERN_NOTICE PFX | ||
481 | "Could not disable SPROM write access.\n"); | ||
482 | failed = 1; | ||
483 | } | ||
484 | msleep(500); | ||
485 | if (!failed) { | ||
486 | ssb_printk("100%% ]\n"); | ||
487 | ssb_printk(KERN_NOTICE PFX "SPROM written.\n"); | ||
488 | } | ||
489 | |||
490 | return failed ? -EBUSY : 0; | ||
491 | } | ||
492 | |||
493 | static int ssb_pcmcia_sprom_check_crc(const u16 *sprom, size_t size) | ||
494 | { | ||
495 | //TODO | ||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | #define GOTO_ERROR_ON(condition, description) do { \ | ||
500 | if (unlikely(condition)) { \ | ||
501 | error_description = description; \ | ||
502 | goto error; \ | ||
503 | } \ | ||
504 | } while (0) | ||
505 | |||
280 | int ssb_pcmcia_get_invariants(struct ssb_bus *bus, | 506 | int ssb_pcmcia_get_invariants(struct ssb_bus *bus, |
281 | struct ssb_init_invariants *iv) | 507 | struct ssb_init_invariants *iv) |
282 | { | 508 | { |
283 | //TODO | 509 | tuple_t tuple; |
284 | random_ether_addr(iv->sprom.il0mac); | 510 | int res; |
511 | unsigned char buf[32]; | ||
512 | struct ssb_sprom *sprom = &iv->sprom; | ||
513 | struct ssb_boardinfo *bi = &iv->boardinfo; | ||
514 | const char *error_description; | ||
515 | |||
516 | memset(sprom, 0xFF, sizeof(*sprom)); | ||
517 | sprom->revision = 1; | ||
518 | sprom->boardflags_lo = 0; | ||
519 | sprom->boardflags_hi = 0; | ||
520 | |||
521 | /* First fetch the MAC address. */ | ||
522 | memset(&tuple, 0, sizeof(tuple)); | ||
523 | tuple.DesiredTuple = CISTPL_FUNCE; | ||
524 | tuple.TupleData = buf; | ||
525 | tuple.TupleDataMax = sizeof(buf); | ||
526 | res = pcmcia_get_first_tuple(bus->host_pcmcia, &tuple); | ||
527 | GOTO_ERROR_ON(res != CS_SUCCESS, "MAC first tpl"); | ||
528 | res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple); | ||
529 | GOTO_ERROR_ON(res != CS_SUCCESS, "MAC first tpl data"); | ||
530 | while (1) { | ||
531 | GOTO_ERROR_ON(tuple.TupleDataLen < 1, "MAC tpl < 1"); | ||
532 | if (tuple.TupleData[0] == CISTPL_FUNCE_LAN_NODE_ID) | ||
533 | break; | ||
534 | res = pcmcia_get_next_tuple(bus->host_pcmcia, &tuple); | ||
535 | GOTO_ERROR_ON(res != CS_SUCCESS, "MAC next tpl"); | ||
536 | res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple); | ||
537 | GOTO_ERROR_ON(res != CS_SUCCESS, "MAC next tpl data"); | ||
538 | } | ||
539 | GOTO_ERROR_ON(tuple.TupleDataLen != ETH_ALEN + 2, "MAC tpl size"); | ||
540 | memcpy(sprom->il0mac, &tuple.TupleData[2], ETH_ALEN); | ||
541 | |||
542 | /* Fetch the vendor specific tuples. */ | ||
543 | memset(&tuple, 0, sizeof(tuple)); | ||
544 | tuple.DesiredTuple = SSB_PCMCIA_CIS; | ||
545 | tuple.TupleData = buf; | ||
546 | tuple.TupleDataMax = sizeof(buf); | ||
547 | res = pcmcia_get_first_tuple(bus->host_pcmcia, &tuple); | ||
548 | GOTO_ERROR_ON(res != CS_SUCCESS, "VEN first tpl"); | ||
549 | res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple); | ||
550 | GOTO_ERROR_ON(res != CS_SUCCESS, "VEN first tpl data"); | ||
551 | while (1) { | ||
552 | GOTO_ERROR_ON(tuple.TupleDataLen < 1, "VEN tpl < 1"); | ||
553 | switch (tuple.TupleData[0]) { | ||
554 | case SSB_PCMCIA_CIS_ID: | ||
555 | GOTO_ERROR_ON((tuple.TupleDataLen != 5) && | ||
556 | (tuple.TupleDataLen != 7), | ||
557 | "id tpl size"); | ||
558 | bi->vendor = tuple.TupleData[1] | | ||
559 | ((u16)tuple.TupleData[2] << 8); | ||
560 | break; | ||
561 | case SSB_PCMCIA_CIS_BOARDREV: | ||
562 | GOTO_ERROR_ON(tuple.TupleDataLen != 2, | ||
563 | "boardrev tpl size"); | ||
564 | sprom->board_rev = tuple.TupleData[1]; | ||
565 | break; | ||
566 | case SSB_PCMCIA_CIS_PA: | ||
567 | GOTO_ERROR_ON(tuple.TupleDataLen != 9, | ||
568 | "pa tpl size"); | ||
569 | sprom->pa0b0 = tuple.TupleData[1] | | ||
570 | ((u16)tuple.TupleData[2] << 8); | ||
571 | sprom->pa0b1 = tuple.TupleData[3] | | ||
572 | ((u16)tuple.TupleData[4] << 8); | ||
573 | sprom->pa0b2 = tuple.TupleData[5] | | ||
574 | ((u16)tuple.TupleData[6] << 8); | ||
575 | sprom->itssi_a = tuple.TupleData[7]; | ||
576 | sprom->itssi_bg = tuple.TupleData[7]; | ||
577 | sprom->maxpwr_a = tuple.TupleData[8]; | ||
578 | sprom->maxpwr_bg = tuple.TupleData[8]; | ||
579 | break; | ||
580 | case SSB_PCMCIA_CIS_OEMNAME: | ||
581 | /* We ignore this. */ | ||
582 | break; | ||
583 | case SSB_PCMCIA_CIS_CCODE: | ||
584 | GOTO_ERROR_ON(tuple.TupleDataLen != 2, | ||
585 | "ccode tpl size"); | ||
586 | sprom->country_code = tuple.TupleData[1]; | ||
587 | break; | ||
588 | case SSB_PCMCIA_CIS_ANTENNA: | ||
589 | GOTO_ERROR_ON(tuple.TupleDataLen != 2, | ||
590 | "ant tpl size"); | ||
591 | sprom->ant_available_a = tuple.TupleData[1]; | ||
592 | sprom->ant_available_bg = tuple.TupleData[1]; | ||
593 | break; | ||
594 | case SSB_PCMCIA_CIS_ANTGAIN: | ||
595 | GOTO_ERROR_ON(tuple.TupleDataLen != 2, | ||
596 | "antg tpl size"); | ||
597 | sprom->antenna_gain.ghz24.a0 = tuple.TupleData[1]; | ||
598 | sprom->antenna_gain.ghz24.a1 = tuple.TupleData[1]; | ||
599 | sprom->antenna_gain.ghz24.a2 = tuple.TupleData[1]; | ||
600 | sprom->antenna_gain.ghz24.a3 = tuple.TupleData[1]; | ||
601 | sprom->antenna_gain.ghz5.a0 = tuple.TupleData[1]; | ||
602 | sprom->antenna_gain.ghz5.a1 = tuple.TupleData[1]; | ||
603 | sprom->antenna_gain.ghz5.a2 = tuple.TupleData[1]; | ||
604 | sprom->antenna_gain.ghz5.a3 = tuple.TupleData[1]; | ||
605 | break; | ||
606 | case SSB_PCMCIA_CIS_BFLAGS: | ||
607 | GOTO_ERROR_ON(tuple.TupleDataLen != 3, | ||
608 | "bfl tpl size"); | ||
609 | sprom->boardflags_lo = tuple.TupleData[1] | | ||
610 | ((u16)tuple.TupleData[2] << 8); | ||
611 | break; | ||
612 | case SSB_PCMCIA_CIS_LEDS: | ||
613 | GOTO_ERROR_ON(tuple.TupleDataLen != 5, | ||
614 | "leds tpl size"); | ||
615 | sprom->gpio0 = tuple.TupleData[1]; | ||
616 | sprom->gpio1 = tuple.TupleData[2]; | ||
617 | sprom->gpio2 = tuple.TupleData[3]; | ||
618 | sprom->gpio3 = tuple.TupleData[4]; | ||
619 | break; | ||
620 | } | ||
621 | res = pcmcia_get_next_tuple(bus->host_pcmcia, &tuple); | ||
622 | if (res == CS_NO_MORE_ITEMS) | ||
623 | break; | ||
624 | GOTO_ERROR_ON(res != CS_SUCCESS, "VEN next tpl"); | ||
625 | res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple); | ||
626 | GOTO_ERROR_ON(res != CS_SUCCESS, "VEN next tpl data"); | ||
627 | } | ||
628 | |||
285 | return 0; | 629 | return 0; |
630 | error: | ||
631 | ssb_printk(KERN_ERR PFX | ||
632 | "PCMCIA: Failed to fetch device invariants: %s\n", | ||
633 | error_description); | ||
634 | return -ENODEV; | ||
635 | } | ||
636 | |||
637 | static ssize_t ssb_pcmcia_attr_sprom_show(struct device *pcmciadev, | ||
638 | struct device_attribute *attr, | ||
639 | char *buf) | ||
640 | { | ||
641 | struct pcmcia_device *pdev = | ||
642 | container_of(pcmciadev, struct pcmcia_device, dev); | ||
643 | struct ssb_bus *bus; | ||
644 | |||
645 | bus = ssb_pcmcia_dev_to_bus(pdev); | ||
646 | if (!bus) | ||
647 | return -ENODEV; | ||
648 | |||
649 | return ssb_attr_sprom_show(bus, buf, | ||
650 | ssb_pcmcia_sprom_read_all); | ||
651 | } | ||
652 | |||
653 | static ssize_t ssb_pcmcia_attr_sprom_store(struct device *pcmciadev, | ||
654 | struct device_attribute *attr, | ||
655 | const char *buf, size_t count) | ||
656 | { | ||
657 | struct pcmcia_device *pdev = | ||
658 | container_of(pcmciadev, struct pcmcia_device, dev); | ||
659 | struct ssb_bus *bus; | ||
660 | |||
661 | bus = ssb_pcmcia_dev_to_bus(pdev); | ||
662 | if (!bus) | ||
663 | return -ENODEV; | ||
664 | |||
665 | return ssb_attr_sprom_store(bus, buf, count, | ||
666 | ssb_pcmcia_sprom_check_crc, | ||
667 | ssb_pcmcia_sprom_write_all); | ||
668 | } | ||
669 | |||
670 | static DEVICE_ATTR(ssb_sprom, 0600, | ||
671 | ssb_pcmcia_attr_sprom_show, | ||
672 | ssb_pcmcia_attr_sprom_store); | ||
673 | |||
674 | void ssb_pcmcia_exit(struct ssb_bus *bus) | ||
675 | { | ||
676 | if (bus->bustype != SSB_BUSTYPE_PCMCIA) | ||
677 | return; | ||
678 | |||
679 | device_remove_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom); | ||
286 | } | 680 | } |
287 | 681 | ||
288 | int ssb_pcmcia_init(struct ssb_bus *bus) | 682 | int ssb_pcmcia_init(struct ssb_bus *bus) |
289 | { | 683 | { |
290 | conf_reg_t reg; | 684 | u8 val, offset; |
291 | int err; | 685 | int err; |
292 | 686 | ||
293 | if (bus->bustype != SSB_BUSTYPE_PCMCIA) | 687 | if (bus->bustype != SSB_BUSTYPE_PCMCIA) |
@@ -298,22 +692,26 @@ int ssb_pcmcia_init(struct ssb_bus *bus) | |||
298 | ssb_pcmcia_switch_segment(bus, 0); | 692 | ssb_pcmcia_switch_segment(bus, 0); |
299 | 693 | ||
300 | /* Init IRQ routing */ | 694 | /* Init IRQ routing */ |
301 | reg.Action = CS_READ; | ||
302 | reg.Function = 0; | ||
303 | if (bus->chip_id == 0x4306) | 695 | if (bus->chip_id == 0x4306) |
304 | reg.Offset = 0x00; | 696 | offset = SSB_PCMCIA_CORECTL; |
305 | else | 697 | else |
306 | reg.Offset = 0x80; | 698 | offset = SSB_PCMCIA_CORECTL2; |
307 | err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | 699 | err = ssb_pcmcia_cfg_read(bus, offset, &val); |
308 | if (err != CS_SUCCESS) | 700 | if (err) |
309 | goto error; | 701 | goto error; |
310 | reg.Action = CS_WRITE; | 702 | val |= SSB_PCMCIA_CORECTL_IRQEN | SSB_PCMCIA_CORECTL_FUNCEN; |
311 | reg.Value |= 0x04 | 0x01; | 703 | err = ssb_pcmcia_cfg_write(bus, offset, val); |
312 | err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | 704 | if (err) |
313 | if (err != CS_SUCCESS) | 705 | goto error; |
706 | |||
707 | bus->sprom_size = SSB_PCMCIA_SPROM_SIZE; | ||
708 | mutex_init(&bus->sprom_mutex); | ||
709 | err = device_create_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom); | ||
710 | if (err) | ||
314 | goto error; | 711 | goto error; |
315 | 712 | ||
316 | return 0; | 713 | return 0; |
317 | error: | 714 | error: |
318 | return -ENODEV; | 715 | ssb_printk(KERN_ERR PFX "Failed to initialize PCMCIA host device\n"); |
716 | return err; | ||
319 | } | 717 | } |
diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c new file mode 100644 index 000000000000..3668edb39315 --- /dev/null +++ b/drivers/ssb/sprom.c | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * Sonics Silicon Backplane | ||
3 | * Common SPROM support routines | ||
4 | * | ||
5 | * Copyright (C) 2005-2008 Michael Buesch <mb@bu3sch.de> | ||
6 | * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de> | ||
7 | * Copyright (C) 2005 Stefano Brivio <st3@riseup.net> | ||
8 | * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org> | ||
9 | * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch> | ||
10 | * | ||
11 | * Licensed under the GNU/GPL. See COPYING for details. | ||
12 | */ | ||
13 | |||
14 | #include "ssb_private.h" | ||
15 | |||
16 | |||
17 | static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, | ||
18 | size_t sprom_size_words) | ||
19 | { | ||
20 | int i, pos = 0; | ||
21 | |||
22 | for (i = 0; i < sprom_size_words; i++) | ||
23 | pos += snprintf(buf + pos, buf_len - pos - 1, | ||
24 | "%04X", swab16(sprom[i]) & 0xFFFF); | ||
25 | pos += snprintf(buf + pos, buf_len - pos - 1, "\n"); | ||
26 | |||
27 | return pos + 1; | ||
28 | } | ||
29 | |||
30 | static int hex2sprom(u16 *sprom, const char *dump, size_t len, | ||
31 | size_t sprom_size_words) | ||
32 | { | ||
33 | char tmp[5] = { 0 }; | ||
34 | int cnt = 0; | ||
35 | unsigned long parsed; | ||
36 | |||
37 | if (len < sprom_size_words * 2) | ||
38 | return -EINVAL; | ||
39 | |||
40 | while (cnt < sprom_size_words) { | ||
41 | memcpy(tmp, dump, 4); | ||
42 | dump += 4; | ||
43 | parsed = simple_strtoul(tmp, NULL, 16); | ||
44 | sprom[cnt++] = swab16((u16)parsed); | ||
45 | } | ||
46 | |||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | /* Common sprom device-attribute show-handler */ | ||
51 | ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf, | ||
52 | int (*sprom_read)(struct ssb_bus *bus, u16 *sprom)) | ||
53 | { | ||
54 | u16 *sprom; | ||
55 | int err = -ENOMEM; | ||
56 | ssize_t count = 0; | ||
57 | size_t sprom_size_words = bus->sprom_size; | ||
58 | |||
59 | sprom = kcalloc(sprom_size_words, sizeof(u16), GFP_KERNEL); | ||
60 | if (!sprom) | ||
61 | goto out; | ||
62 | |||
63 | /* Use interruptible locking, as the SPROM write might | ||
64 | * be holding the lock for several seconds. So allow userspace | ||
65 | * to cancel operation. */ | ||
66 | err = -ERESTARTSYS; | ||
67 | if (mutex_lock_interruptible(&bus->sprom_mutex)) | ||
68 | goto out_kfree; | ||
69 | err = sprom_read(bus, sprom); | ||
70 | mutex_unlock(&bus->sprom_mutex); | ||
71 | |||
72 | if (!err) | ||
73 | count = sprom2hex(sprom, buf, PAGE_SIZE, sprom_size_words); | ||
74 | |||
75 | out_kfree: | ||
76 | kfree(sprom); | ||
77 | out: | ||
78 | return err ? err : count; | ||
79 | } | ||
80 | |||
81 | /* Common sprom device-attribute store-handler */ | ||
82 | ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, | ||
83 | const char *buf, size_t count, | ||
84 | int (*sprom_check_crc)(const u16 *sprom, size_t size), | ||
85 | int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom)) | ||
86 | { | ||
87 | u16 *sprom; | ||
88 | int res = 0, err = -ENOMEM; | ||
89 | size_t sprom_size_words = bus->sprom_size; | ||
90 | |||
91 | sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); | ||
92 | if (!sprom) | ||
93 | goto out; | ||
94 | err = hex2sprom(sprom, buf, count, sprom_size_words); | ||
95 | if (err) { | ||
96 | err = -EINVAL; | ||
97 | goto out_kfree; | ||
98 | } | ||
99 | err = sprom_check_crc(sprom, sprom_size_words); | ||
100 | if (err) { | ||
101 | err = -EINVAL; | ||
102 | goto out_kfree; | ||
103 | } | ||
104 | |||
105 | /* Use interruptible locking, as the SPROM write might | ||
106 | * be holding the lock for several seconds. So allow userspace | ||
107 | * to cancel operation. */ | ||
108 | err = -ERESTARTSYS; | ||
109 | if (mutex_lock_interruptible(&bus->sprom_mutex)) | ||
110 | goto out_kfree; | ||
111 | err = ssb_devices_freeze(bus); | ||
112 | if (err == -EOPNOTSUPP) { | ||
113 | ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. " | ||
114 | "No suspend support. Is CONFIG_PM enabled?\n"); | ||
115 | goto out_unlock; | ||
116 | } | ||
117 | if (err) { | ||
118 | ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); | ||
119 | goto out_unlock; | ||
120 | } | ||
121 | res = sprom_write(bus, sprom); | ||
122 | err = ssb_devices_thaw(bus); | ||
123 | if (err) | ||
124 | ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); | ||
125 | out_unlock: | ||
126 | mutex_unlock(&bus->sprom_mutex); | ||
127 | out_kfree: | ||
128 | kfree(sprom); | ||
129 | out: | ||
130 | if (res) | ||
131 | return res; | ||
132 | return err ? err : count; | ||
133 | } | ||
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index d03b20983b1e..a83bf7a4d80b 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h | |||
@@ -81,6 +81,7 @@ extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus, | |||
81 | u8 seg); | 81 | u8 seg); |
82 | extern int ssb_pcmcia_get_invariants(struct ssb_bus *bus, | 82 | extern int ssb_pcmcia_get_invariants(struct ssb_bus *bus, |
83 | struct ssb_init_invariants *iv); | 83 | struct ssb_init_invariants *iv); |
84 | extern void ssb_pcmcia_exit(struct ssb_bus *bus); | ||
84 | extern int ssb_pcmcia_init(struct ssb_bus *bus); | 85 | extern int ssb_pcmcia_init(struct ssb_bus *bus); |
85 | extern const struct ssb_bus_ops ssb_pcmcia_ops; | 86 | extern const struct ssb_bus_ops ssb_pcmcia_ops; |
86 | #else /* CONFIG_SSB_PCMCIAHOST */ | 87 | #else /* CONFIG_SSB_PCMCIAHOST */ |
@@ -99,6 +100,9 @@ static inline int ssb_pcmcia_switch_segment(struct ssb_bus *bus, | |||
99 | { | 100 | { |
100 | return 0; | 101 | return 0; |
101 | } | 102 | } |
103 | static inline void ssb_pcmcia_exit(struct ssb_bus *bus) | ||
104 | { | ||
105 | } | ||
102 | static inline int ssb_pcmcia_init(struct ssb_bus *bus) | 106 | static inline int ssb_pcmcia_init(struct ssb_bus *bus) |
103 | { | 107 | { |
104 | return 0; | 108 | return 0; |
@@ -113,6 +117,17 @@ extern int ssb_bus_scan(struct ssb_bus *bus, | |||
113 | extern void ssb_iounmap(struct ssb_bus *ssb); | 117 | extern void ssb_iounmap(struct ssb_bus *ssb); |
114 | 118 | ||
115 | 119 | ||
120 | /* sprom.c */ | ||
121 | extern | ||
122 | ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf, | ||
123 | int (*sprom_read)(struct ssb_bus *bus, u16 *sprom)); | ||
124 | extern | ||
125 | ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, | ||
126 | const char *buf, size_t count, | ||
127 | int (*sprom_check_crc)(const u16 *sprom, size_t size), | ||
128 | int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom)); | ||
129 | |||
130 | |||
116 | /* core.c */ | 131 | /* core.c */ |
117 | extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m); | 132 | extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m); |
118 | extern int ssb_devices_freeze(struct ssb_bus *bus); | 133 | extern int ssb_devices_freeze(struct ssb_bus *bus); |
@@ -120,6 +135,8 @@ extern int ssb_devices_thaw(struct ssb_bus *bus); | |||
120 | extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev); | 135 | extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev); |
121 | int ssb_for_each_bus_call(unsigned long data, | 136 | int ssb_for_each_bus_call(unsigned long data, |
122 | int (*func)(struct ssb_bus *bus, unsigned long data)); | 137 | int (*func)(struct ssb_bus *bus, unsigned long data)); |
138 | extern struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev); | ||
139 | |||
123 | 140 | ||
124 | /* b43_pci_bridge.c */ | 141 | /* b43_pci_bridge.c */ |
125 | #ifdef CONFIG_SSB_B43_PCI_BRIDGE | 142 | #ifdef CONFIG_SSB_B43_PCI_BRIDGE |
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index b7c388972fcf..8644e03cf588 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h | |||
@@ -245,9 +245,9 @@ struct ssb_bus { | |||
245 | /* Pointer to the PCMCIA device (only if bustype == SSB_BUSTYPE_PCMCIA). */ | 245 | /* Pointer to the PCMCIA device (only if bustype == SSB_BUSTYPE_PCMCIA). */ |
246 | struct pcmcia_device *host_pcmcia; | 246 | struct pcmcia_device *host_pcmcia; |
247 | 247 | ||
248 | #ifdef CONFIG_SSB_PCIHOST | 248 | #ifdef CONFIG_SSB_SPROM |
249 | /* Mutex to protect the SPROM writing. */ | 249 | /* Mutex to protect the SPROM writing. */ |
250 | struct mutex pci_sprom_mutex; | 250 | struct mutex sprom_mutex; |
251 | #endif | 251 | #endif |
252 | 252 | ||
253 | /* ID information about the Chip. */ | 253 | /* ID information about the Chip. */ |