diff options
Diffstat (limited to 'drivers/ssb')
-rw-r--r-- | drivers/ssb/Kconfig | 20 | ||||
-rw-r--r-- | drivers/ssb/Makefile | 2 | ||||
-rw-r--r-- | drivers/ssb/driver_chipcommon.c | 12 | ||||
-rw-r--r-- | drivers/ssb/driver_gige.c | 294 | ||||
-rw-r--r-- | drivers/ssb/driver_mipscore.c | 1 | ||||
-rw-r--r-- | drivers/ssb/driver_pcicore.c | 172 | ||||
-rw-r--r-- | drivers/ssb/embedded.c | 90 | ||||
-rw-r--r-- | drivers/ssb/main.c | 260 | ||||
-rw-r--r-- | drivers/ssb/pci.c | 211 | ||||
-rw-r--r-- | drivers/ssb/pcihost_wrapper.c | 10 | ||||
-rw-r--r-- | drivers/ssb/pcmcia.c | 699 | ||||
-rw-r--r-- | drivers/ssb/sprom.c | 133 | ||||
-rw-r--r-- | drivers/ssb/ssb_private.h | 24 |
13 files changed, 1627 insertions, 301 deletions
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index adea792fb675..cd845b8acd17 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig | |||
@@ -20,6 +20,15 @@ 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 | |||
27 | # Support for Block-I/O. SELECT this from the driver that needs it. | ||
28 | config SSB_BLOCKIO | ||
29 | bool | ||
30 | depends on SSB | ||
31 | |||
23 | config SSB_PCIHOST_POSSIBLE | 32 | config SSB_PCIHOST_POSSIBLE |
24 | bool | 33 | bool |
25 | depends on SSB && (PCI = y || PCI = SSB) | 34 | depends on SSB && (PCI = y || PCI = SSB) |
@@ -28,6 +37,7 @@ config SSB_PCIHOST_POSSIBLE | |||
28 | config SSB_PCIHOST | 37 | config SSB_PCIHOST |
29 | bool "Support for SSB on PCI-bus host" | 38 | bool "Support for SSB on PCI-bus host" |
30 | depends on SSB_PCIHOST_POSSIBLE | 39 | depends on SSB_PCIHOST_POSSIBLE |
40 | select SSB_SPROM | ||
31 | default y | 41 | default y |
32 | help | 42 | help |
33 | Support for a Sonics Silicon Backplane on top | 43 | Support for a Sonics Silicon Backplane on top |
@@ -48,6 +58,7 @@ config SSB_PCMCIAHOST_POSSIBLE | |||
48 | config SSB_PCMCIAHOST | 58 | config SSB_PCMCIAHOST |
49 | bool "Support for SSB on PCMCIA-bus host (EXPERIMENTAL)" | 59 | bool "Support for SSB on PCMCIA-bus host (EXPERIMENTAL)" |
50 | depends on SSB_PCMCIAHOST_POSSIBLE | 60 | depends on SSB_PCMCIAHOST_POSSIBLE |
61 | select SSB_SPROM | ||
51 | help | 62 | help |
52 | Support for a Sonics Silicon Backplane on top | 63 | Support for a Sonics Silicon Backplane on top |
53 | of a PCMCIA device. | 64 | of a PCMCIA device. |
@@ -125,4 +136,13 @@ config SSB_DRIVER_EXTIF | |||
125 | 136 | ||
126 | If unsure, say N | 137 | If unsure, say N |
127 | 138 | ||
139 | config SSB_DRIVER_GIGE | ||
140 | bool "SSB Broadcom Gigabit Ethernet driver" | ||
141 | depends on SSB_PCIHOST_POSSIBLE && SSB_EMBEDDED && MIPS | ||
142 | help | ||
143 | Driver for the Sonics Silicon Backplane attached | ||
144 | Broadcom Gigabit Ethernet. | ||
145 | |||
146 | If unsure, say N | ||
147 | |||
128 | endmenu | 148 | endmenu |
diff --git a/drivers/ssb/Makefile b/drivers/ssb/Makefile index de94c2eb7a37..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 |
@@ -11,6 +12,7 @@ ssb-y += driver_chipcommon.o | |||
11 | ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o | 12 | ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o |
12 | ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o | 13 | ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o |
13 | ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o | 14 | ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o |
15 | ssb-$(CONFIG_SSB_DRIVER_GIGE) += driver_gige.o | ||
14 | 16 | ||
15 | # b43 pci-ssb-bridge driver | 17 | # b43 pci-ssb-bridge driver |
16 | # Not strictly a part of SSB, but kept here for convenience | 18 | # Not strictly a part of SSB, but kept here for convenience |
diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c index e586321a473a..571f4fd55236 100644 --- a/drivers/ssb/driver_chipcommon.c +++ b/drivers/ssb/driver_chipcommon.c | |||
@@ -251,7 +251,7 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc) | |||
251 | calc_fast_powerup_delay(cc); | 251 | calc_fast_powerup_delay(cc); |
252 | } | 252 | } |
253 | 253 | ||
254 | void ssb_chipco_suspend(struct ssb_chipcommon *cc, pm_message_t state) | 254 | void ssb_chipco_suspend(struct ssb_chipcommon *cc) |
255 | { | 255 | { |
256 | if (!cc->dev) | 256 | if (!cc->dev) |
257 | return; | 257 | return; |
@@ -353,6 +353,16 @@ void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks) | |||
353 | chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks); | 353 | chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks); |
354 | } | 354 | } |
355 | 355 | ||
356 | void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value) | ||
357 | { | ||
358 | chipco_write32_masked(cc, SSB_CHIPCO_IRQMASK, mask, value); | ||
359 | } | ||
360 | |||
361 | u32 ssb_chipco_irq_status(struct ssb_chipcommon *cc, u32 mask) | ||
362 | { | ||
363 | return chipco_read32(cc, SSB_CHIPCO_IRQSTAT) & mask; | ||
364 | } | ||
365 | |||
356 | u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask) | 366 | u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask) |
357 | { | 367 | { |
358 | return chipco_read32(cc, SSB_CHIPCO_GPIOIN) & mask; | 368 | return chipco_read32(cc, SSB_CHIPCO_GPIOIN) & mask; |
diff --git a/drivers/ssb/driver_gige.c b/drivers/ssb/driver_gige.c new file mode 100644 index 000000000000..172f90407b93 --- /dev/null +++ b/drivers/ssb/driver_gige.c | |||
@@ -0,0 +1,294 @@ | |||
1 | /* | ||
2 | * Sonics Silicon Backplane | ||
3 | * Broadcom Gigabit Ethernet core driver | ||
4 | * | ||
5 | * Copyright 2008, Broadcom Corporation | ||
6 | * Copyright 2008, Michael Buesch <mb@bu3sch.de> | ||
7 | * | ||
8 | * Licensed under the GNU/GPL. See COPYING for details. | ||
9 | */ | ||
10 | |||
11 | #include <linux/ssb/ssb.h> | ||
12 | #include <linux/ssb/ssb_driver_gige.h> | ||
13 | #include <linux/pci.h> | ||
14 | #include <linux/pci_regs.h> | ||
15 | |||
16 | |||
17 | /* | ||
18 | MODULE_DESCRIPTION("SSB Broadcom Gigabit Ethernet driver"); | ||
19 | MODULE_AUTHOR("Michael Buesch"); | ||
20 | MODULE_LICENSE("GPL"); | ||
21 | */ | ||
22 | |||
23 | static const struct ssb_device_id ssb_gige_tbl[] = { | ||
24 | SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET_GBIT, SSB_ANY_REV), | ||
25 | SSB_DEVTABLE_END | ||
26 | }; | ||
27 | /* MODULE_DEVICE_TABLE(ssb, ssb_gige_tbl); */ | ||
28 | |||
29 | |||
30 | static inline u8 gige_read8(struct ssb_gige *dev, u16 offset) | ||
31 | { | ||
32 | return ssb_read8(dev->dev, offset); | ||
33 | } | ||
34 | |||
35 | static inline u16 gige_read16(struct ssb_gige *dev, u16 offset) | ||
36 | { | ||
37 | return ssb_read16(dev->dev, offset); | ||
38 | } | ||
39 | |||
40 | static inline u32 gige_read32(struct ssb_gige *dev, u16 offset) | ||
41 | { | ||
42 | return ssb_read32(dev->dev, offset); | ||
43 | } | ||
44 | |||
45 | static inline void gige_write8(struct ssb_gige *dev, | ||
46 | u16 offset, u8 value) | ||
47 | { | ||
48 | ssb_write8(dev->dev, offset, value); | ||
49 | } | ||
50 | |||
51 | static inline void gige_write16(struct ssb_gige *dev, | ||
52 | u16 offset, u16 value) | ||
53 | { | ||
54 | ssb_write16(dev->dev, offset, value); | ||
55 | } | ||
56 | |||
57 | static inline void gige_write32(struct ssb_gige *dev, | ||
58 | u16 offset, u32 value) | ||
59 | { | ||
60 | ssb_write32(dev->dev, offset, value); | ||
61 | } | ||
62 | |||
63 | static inline | ||
64 | u8 gige_pcicfg_read8(struct ssb_gige *dev, unsigned int offset) | ||
65 | { | ||
66 | BUG_ON(offset >= 256); | ||
67 | return gige_read8(dev, SSB_GIGE_PCICFG + offset); | ||
68 | } | ||
69 | |||
70 | static inline | ||
71 | u16 gige_pcicfg_read16(struct ssb_gige *dev, unsigned int offset) | ||
72 | { | ||
73 | BUG_ON(offset >= 256); | ||
74 | return gige_read16(dev, SSB_GIGE_PCICFG + offset); | ||
75 | } | ||
76 | |||
77 | static inline | ||
78 | u32 gige_pcicfg_read32(struct ssb_gige *dev, unsigned int offset) | ||
79 | { | ||
80 | BUG_ON(offset >= 256); | ||
81 | return gige_read32(dev, SSB_GIGE_PCICFG + offset); | ||
82 | } | ||
83 | |||
84 | static inline | ||
85 | void gige_pcicfg_write8(struct ssb_gige *dev, | ||
86 | unsigned int offset, u8 value) | ||
87 | { | ||
88 | BUG_ON(offset >= 256); | ||
89 | gige_write8(dev, SSB_GIGE_PCICFG + offset, value); | ||
90 | } | ||
91 | |||
92 | static inline | ||
93 | void gige_pcicfg_write16(struct ssb_gige *dev, | ||
94 | unsigned int offset, u16 value) | ||
95 | { | ||
96 | BUG_ON(offset >= 256); | ||
97 | gige_write16(dev, SSB_GIGE_PCICFG + offset, value); | ||
98 | } | ||
99 | |||
100 | static inline | ||
101 | void gige_pcicfg_write32(struct ssb_gige *dev, | ||
102 | unsigned int offset, u32 value) | ||
103 | { | ||
104 | BUG_ON(offset >= 256); | ||
105 | gige_write32(dev, SSB_GIGE_PCICFG + offset, value); | ||
106 | } | ||
107 | |||
108 | static int ssb_gige_pci_read_config(struct pci_bus *bus, unsigned int devfn, | ||
109 | int reg, int size, u32 *val) | ||
110 | { | ||
111 | struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops); | ||
112 | unsigned long flags; | ||
113 | |||
114 | if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0)) | ||
115 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
116 | if (reg >= 256) | ||
117 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
118 | |||
119 | spin_lock_irqsave(&dev->lock, flags); | ||
120 | switch (size) { | ||
121 | case 1: | ||
122 | *val = gige_pcicfg_read8(dev, reg); | ||
123 | break; | ||
124 | case 2: | ||
125 | *val = gige_pcicfg_read16(dev, reg); | ||
126 | break; | ||
127 | case 4: | ||
128 | *val = gige_pcicfg_read32(dev, reg); | ||
129 | break; | ||
130 | default: | ||
131 | WARN_ON(1); | ||
132 | } | ||
133 | spin_unlock_irqrestore(&dev->lock, flags); | ||
134 | |||
135 | return PCIBIOS_SUCCESSFUL; | ||
136 | } | ||
137 | |||
138 | static int ssb_gige_pci_write_config(struct pci_bus *bus, unsigned int devfn, | ||
139 | int reg, int size, u32 val) | ||
140 | { | ||
141 | struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops); | ||
142 | unsigned long flags; | ||
143 | |||
144 | if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0)) | ||
145 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
146 | if (reg >= 256) | ||
147 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
148 | |||
149 | spin_lock_irqsave(&dev->lock, flags); | ||
150 | switch (size) { | ||
151 | case 1: | ||
152 | gige_pcicfg_write8(dev, reg, val); | ||
153 | break; | ||
154 | case 2: | ||
155 | gige_pcicfg_write16(dev, reg, val); | ||
156 | break; | ||
157 | case 4: | ||
158 | gige_pcicfg_write32(dev, reg, val); | ||
159 | break; | ||
160 | default: | ||
161 | WARN_ON(1); | ||
162 | } | ||
163 | spin_unlock_irqrestore(&dev->lock, flags); | ||
164 | |||
165 | return PCIBIOS_SUCCESSFUL; | ||
166 | } | ||
167 | |||
168 | static int ssb_gige_probe(struct ssb_device *sdev, const struct ssb_device_id *id) | ||
169 | { | ||
170 | struct ssb_gige *dev; | ||
171 | u32 base, tmslow, tmshigh; | ||
172 | |||
173 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
174 | if (!dev) | ||
175 | return -ENOMEM; | ||
176 | dev->dev = sdev; | ||
177 | |||
178 | spin_lock_init(&dev->lock); | ||
179 | dev->pci_controller.pci_ops = &dev->pci_ops; | ||
180 | dev->pci_controller.io_resource = &dev->io_resource; | ||
181 | dev->pci_controller.mem_resource = &dev->mem_resource; | ||
182 | dev->pci_controller.io_map_base = 0x800; | ||
183 | dev->pci_ops.read = ssb_gige_pci_read_config; | ||
184 | dev->pci_ops.write = ssb_gige_pci_write_config; | ||
185 | |||
186 | dev->io_resource.name = SSB_GIGE_IO_RES_NAME; | ||
187 | dev->io_resource.start = 0x800; | ||
188 | dev->io_resource.end = 0x8FF; | ||
189 | dev->io_resource.flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED; | ||
190 | |||
191 | if (!ssb_device_is_enabled(sdev)) | ||
192 | ssb_device_enable(sdev, 0); | ||
193 | |||
194 | /* Setup BAR0. This is a 64k MMIO region. */ | ||
195 | base = ssb_admatch_base(ssb_read32(sdev, SSB_ADMATCH1)); | ||
196 | gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_0, base); | ||
197 | gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_1, 0); | ||
198 | |||
199 | dev->mem_resource.name = SSB_GIGE_MEM_RES_NAME; | ||
200 | dev->mem_resource.start = base; | ||
201 | dev->mem_resource.end = base + 0x10000 - 1; | ||
202 | dev->mem_resource.flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED; | ||
203 | |||
204 | /* Enable the memory region. */ | ||
205 | gige_pcicfg_write16(dev, PCI_COMMAND, | ||
206 | gige_pcicfg_read16(dev, PCI_COMMAND) | ||
207 | | PCI_COMMAND_MEMORY); | ||
208 | |||
209 | /* Write flushing is controlled by the Flush Status Control register. | ||
210 | * We want to flush every register write with a timeout and we want | ||
211 | * to disable the IRQ mask while flushing to avoid concurrency. | ||
212 | * Note that automatic write flushing does _not_ work from | ||
213 | * an IRQ handler. The driver must flush manually by reading a register. | ||
214 | */ | ||
215 | gige_write32(dev, SSB_GIGE_SHIM_FLUSHSTAT, 0x00000068); | ||
216 | |||
217 | /* Check if we have an RGMII or GMII PHY-bus. | ||
218 | * On RGMII do not bypass the DLLs */ | ||
219 | tmslow = ssb_read32(sdev, SSB_TMSLOW); | ||
220 | tmshigh = ssb_read32(sdev, SSB_TMSHIGH); | ||
221 | if (tmshigh & SSB_GIGE_TMSHIGH_RGMII) { | ||
222 | tmslow &= ~SSB_GIGE_TMSLOW_TXBYPASS; | ||
223 | tmslow &= ~SSB_GIGE_TMSLOW_RXBYPASS; | ||
224 | dev->has_rgmii = 1; | ||
225 | } else { | ||
226 | tmslow |= SSB_GIGE_TMSLOW_TXBYPASS; | ||
227 | tmslow |= SSB_GIGE_TMSLOW_RXBYPASS; | ||
228 | dev->has_rgmii = 0; | ||
229 | } | ||
230 | tmslow |= SSB_GIGE_TMSLOW_DLLEN; | ||
231 | ssb_write32(sdev, SSB_TMSLOW, tmslow); | ||
232 | |||
233 | ssb_set_drvdata(sdev, dev); | ||
234 | register_pci_controller(&dev->pci_controller); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | bool pdev_is_ssb_gige_core(struct pci_dev *pdev) | ||
240 | { | ||
241 | if (!pdev->resource[0].name) | ||
242 | return 0; | ||
243 | return (strcmp(pdev->resource[0].name, SSB_GIGE_MEM_RES_NAME) == 0); | ||
244 | } | ||
245 | EXPORT_SYMBOL(pdev_is_ssb_gige_core); | ||
246 | |||
247 | int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev, | ||
248 | struct pci_dev *pdev) | ||
249 | { | ||
250 | struct ssb_gige *dev = ssb_get_drvdata(sdev); | ||
251 | struct resource *res; | ||
252 | |||
253 | if (pdev->bus->ops != &dev->pci_ops) { | ||
254 | /* The PCI device is not on this SSB GigE bridge device. */ | ||
255 | return -ENODEV; | ||
256 | } | ||
257 | |||
258 | /* Fixup the PCI resources. */ | ||
259 | res = &(pdev->resource[0]); | ||
260 | res->flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED; | ||
261 | res->name = dev->mem_resource.name; | ||
262 | res->start = dev->mem_resource.start; | ||
263 | res->end = dev->mem_resource.end; | ||
264 | |||
265 | /* Fixup interrupt lines. */ | ||
266 | pdev->irq = ssb_mips_irq(sdev) + 2; | ||
267 | pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, pdev->irq); | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | int ssb_gige_map_irq(struct ssb_device *sdev, | ||
273 | const struct pci_dev *pdev) | ||
274 | { | ||
275 | struct ssb_gige *dev = ssb_get_drvdata(sdev); | ||
276 | |||
277 | if (pdev->bus->ops != &dev->pci_ops) { | ||
278 | /* The PCI device is not on this SSB GigE bridge device. */ | ||
279 | return -ENODEV; | ||
280 | } | ||
281 | |||
282 | return ssb_mips_irq(sdev) + 2; | ||
283 | } | ||
284 | |||
285 | static struct ssb_driver ssb_gige_driver = { | ||
286 | .name = "BCM-GigE", | ||
287 | .id_table = ssb_gige_tbl, | ||
288 | .probe = ssb_gige_probe, | ||
289 | }; | ||
290 | |||
291 | int ssb_gige_init(void) | ||
292 | { | ||
293 | return ssb_driver_register(&ssb_gige_driver); | ||
294 | } | ||
diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c index a9e7eb45b2e7..3fd3e3b412b6 100644 --- a/drivers/ssb/driver_mipscore.c +++ b/drivers/ssb/driver_mipscore.c | |||
@@ -210,6 +210,7 @@ void ssb_mipscore_init(struct ssb_mipscore *mcore) | |||
210 | /* fallthrough */ | 210 | /* fallthrough */ |
211 | case SSB_DEV_PCI: | 211 | case SSB_DEV_PCI: |
212 | case SSB_DEV_ETHERNET: | 212 | case SSB_DEV_ETHERNET: |
213 | case SSB_DEV_ETHERNET_GBIT: | ||
213 | case SSB_DEV_80211: | 214 | case SSB_DEV_80211: |
214 | case SSB_DEV_USB20_HOST: | 215 | case SSB_DEV_USB20_HOST: |
215 | /* These devices get their own IRQ line if available, the rest goes on IRQ0 */ | 216 | /* These devices get their own IRQ line if available, the rest goes on IRQ0 */ |
diff --git a/drivers/ssb/driver_pcicore.c b/drivers/ssb/driver_pcicore.c index 5d777f211699..75def13e797d 100644 --- a/drivers/ssb/driver_pcicore.c +++ b/drivers/ssb/driver_pcicore.c | |||
@@ -60,77 +60,6 @@ static DEFINE_SPINLOCK(cfgspace_lock); | |||
60 | /* Core to access the external PCI config space. Can only have one. */ | 60 | /* Core to access the external PCI config space. Can only have one. */ |
61 | static struct ssb_pcicore *extpci_core; | 61 | static struct ssb_pcicore *extpci_core; |
62 | 62 | ||
63 | static u32 ssb_pcicore_pcibus_iobase = 0x100; | ||
64 | static u32 ssb_pcicore_pcibus_membase = SSB_PCI_DMA; | ||
65 | |||
66 | int pcibios_plat_dev_init(struct pci_dev *d) | ||
67 | { | ||
68 | struct resource *res; | ||
69 | int pos, size; | ||
70 | u32 *base; | ||
71 | |||
72 | ssb_printk(KERN_INFO "PCI: Fixing up device %s\n", | ||
73 | pci_name(d)); | ||
74 | |||
75 | /* Fix up resource bases */ | ||
76 | for (pos = 0; pos < 6; pos++) { | ||
77 | res = &d->resource[pos]; | ||
78 | if (res->flags & IORESOURCE_IO) | ||
79 | base = &ssb_pcicore_pcibus_iobase; | ||
80 | else | ||
81 | base = &ssb_pcicore_pcibus_membase; | ||
82 | res->flags |= IORESOURCE_PCI_FIXED; | ||
83 | if (res->end) { | ||
84 | size = res->end - res->start + 1; | ||
85 | if (*base & (size - 1)) | ||
86 | *base = (*base + size) & ~(size - 1); | ||
87 | res->start = *base; | ||
88 | res->end = res->start + size - 1; | ||
89 | *base += size; | ||
90 | pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start); | ||
91 | } | ||
92 | /* Fix up PCI bridge BAR0 only */ | ||
93 | if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0) | ||
94 | break; | ||
95 | } | ||
96 | /* Fix up interrupt lines */ | ||
97 | d->irq = ssb_mips_irq(extpci_core->dev) + 2; | ||
98 | pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq); | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static void __init ssb_fixup_pcibridge(struct pci_dev *dev) | ||
104 | { | ||
105 | u8 lat; | ||
106 | |||
107 | if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0) | ||
108 | return; | ||
109 | |||
110 | ssb_printk(KERN_INFO "PCI: Fixing up bridge %s\n", pci_name(dev)); | ||
111 | |||
112 | /* Enable PCI bridge bus mastering and memory space */ | ||
113 | pci_set_master(dev); | ||
114 | if (pcibios_enable_device(dev, ~0) < 0) { | ||
115 | ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n"); | ||
116 | return; | ||
117 | } | ||
118 | |||
119 | /* Enable PCI bridge BAR1 prefetch and burst */ | ||
120 | pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3); | ||
121 | |||
122 | /* Make sure our latency is high enough to handle the devices behind us */ | ||
123 | lat = 168; | ||
124 | ssb_printk(KERN_INFO "PCI: Fixing latency timer of device %s to %u\n", | ||
125 | pci_name(dev), lat); | ||
126 | pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); | ||
127 | } | ||
128 | DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_fixup_pcibridge); | ||
129 | |||
130 | int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) | ||
131 | { | ||
132 | return ssb_mips_irq(extpci_core->dev) + 2; | ||
133 | } | ||
134 | 63 | ||
135 | static u32 get_cfgspace_addr(struct ssb_pcicore *pc, | 64 | static u32 get_cfgspace_addr(struct ssb_pcicore *pc, |
136 | unsigned int bus, unsigned int dev, | 65 | unsigned int bus, unsigned int dev, |
@@ -320,6 +249,95 @@ static struct pci_controller ssb_pcicore_controller = { | |||
320 | .mem_offset = 0x24000000, | 249 | .mem_offset = 0x24000000, |
321 | }; | 250 | }; |
322 | 251 | ||
252 | static u32 ssb_pcicore_pcibus_iobase = 0x100; | ||
253 | static u32 ssb_pcicore_pcibus_membase = SSB_PCI_DMA; | ||
254 | |||
255 | /* This function is called when doing a pci_enable_device(). | ||
256 | * We must first check if the device is a device on the PCI-core bridge. */ | ||
257 | int ssb_pcicore_plat_dev_init(struct pci_dev *d) | ||
258 | { | ||
259 | struct resource *res; | ||
260 | int pos, size; | ||
261 | u32 *base; | ||
262 | |||
263 | if (d->bus->ops != &ssb_pcicore_pciops) { | ||
264 | /* This is not a device on the PCI-core bridge. */ | ||
265 | return -ENODEV; | ||
266 | } | ||
267 | |||
268 | ssb_printk(KERN_INFO "PCI: Fixing up device %s\n", | ||
269 | pci_name(d)); | ||
270 | |||
271 | /* Fix up resource bases */ | ||
272 | for (pos = 0; pos < 6; pos++) { | ||
273 | res = &d->resource[pos]; | ||
274 | if (res->flags & IORESOURCE_IO) | ||
275 | base = &ssb_pcicore_pcibus_iobase; | ||
276 | else | ||
277 | base = &ssb_pcicore_pcibus_membase; | ||
278 | res->flags |= IORESOURCE_PCI_FIXED; | ||
279 | if (res->end) { | ||
280 | size = res->end - res->start + 1; | ||
281 | if (*base & (size - 1)) | ||
282 | *base = (*base + size) & ~(size - 1); | ||
283 | res->start = *base; | ||
284 | res->end = res->start + size - 1; | ||
285 | *base += size; | ||
286 | pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start); | ||
287 | } | ||
288 | /* Fix up PCI bridge BAR0 only */ | ||
289 | if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0) | ||
290 | break; | ||
291 | } | ||
292 | /* Fix up interrupt lines */ | ||
293 | d->irq = ssb_mips_irq(extpci_core->dev) + 2; | ||
294 | pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq); | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | /* Early PCI fixup for a device on the PCI-core bridge. */ | ||
300 | static void ssb_pcicore_fixup_pcibridge(struct pci_dev *dev) | ||
301 | { | ||
302 | u8 lat; | ||
303 | |||
304 | if (dev->bus->ops != &ssb_pcicore_pciops) { | ||
305 | /* This is not a device on the PCI-core bridge. */ | ||
306 | return; | ||
307 | } | ||
308 | if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0) | ||
309 | return; | ||
310 | |||
311 | ssb_printk(KERN_INFO "PCI: Fixing up bridge %s\n", pci_name(dev)); | ||
312 | |||
313 | /* Enable PCI bridge bus mastering and memory space */ | ||
314 | pci_set_master(dev); | ||
315 | if (pcibios_enable_device(dev, ~0) < 0) { | ||
316 | ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n"); | ||
317 | return; | ||
318 | } | ||
319 | |||
320 | /* Enable PCI bridge BAR1 prefetch and burst */ | ||
321 | pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3); | ||
322 | |||
323 | /* Make sure our latency is high enough to handle the devices behind us */ | ||
324 | lat = 168; | ||
325 | ssb_printk(KERN_INFO "PCI: Fixing latency timer of device %s to %u\n", | ||
326 | pci_name(dev), lat); | ||
327 | pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); | ||
328 | } | ||
329 | DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_pcicore_fixup_pcibridge); | ||
330 | |||
331 | /* PCI device IRQ mapping. */ | ||
332 | int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) | ||
333 | { | ||
334 | if (dev->bus->ops != &ssb_pcicore_pciops) { | ||
335 | /* This is not a device on the PCI-core bridge. */ | ||
336 | return -ENODEV; | ||
337 | } | ||
338 | return ssb_mips_irq(extpci_core->dev) + 2; | ||
339 | } | ||
340 | |||
323 | static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc) | 341 | static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc) |
324 | { | 342 | { |
325 | u32 val; | 343 | u32 val; |
@@ -544,15 +562,9 @@ int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc, | |||
544 | u32 intvec; | 562 | u32 intvec; |
545 | 563 | ||
546 | intvec = ssb_read32(pdev, SSB_INTVEC); | 564 | intvec = ssb_read32(pdev, SSB_INTVEC); |
547 | if ((bus->chip_id & 0xFF00) == 0x4400) { | 565 | tmp = ssb_read32(dev, SSB_TPSFLAG); |
548 | /* Workaround: On the BCM44XX the BPFLAG routing | 566 | tmp &= SSB_TPSFLAG_BPFLAG; |
549 | * bit is wrong. Use a hardcoded constant. */ | 567 | intvec |= (1 << tmp); |
550 | intvec |= 0x00000002; | ||
551 | } else { | ||
552 | tmp = ssb_read32(dev, SSB_TPSFLAG); | ||
553 | tmp &= SSB_TPSFLAG_BPFLAG; | ||
554 | intvec |= (1 << tmp); | ||
555 | } | ||
556 | ssb_write32(pdev, SSB_INTVEC, intvec); | 568 | ssb_write32(pdev, SSB_INTVEC, intvec); |
557 | } | 569 | } |
558 | 570 | ||
diff --git a/drivers/ssb/embedded.c b/drivers/ssb/embedded.c index d3ade821555c..7dc3a6b41397 100644 --- a/drivers/ssb/embedded.c +++ b/drivers/ssb/embedded.c | |||
@@ -10,6 +10,9 @@ | |||
10 | 10 | ||
11 | #include <linux/ssb/ssb.h> | 11 | #include <linux/ssb/ssb.h> |
12 | #include <linux/ssb/ssb_embedded.h> | 12 | #include <linux/ssb/ssb_embedded.h> |
13 | #include <linux/ssb/ssb_driver_pci.h> | ||
14 | #include <linux/ssb/ssb_driver_gige.h> | ||
15 | #include <linux/pci.h> | ||
13 | 16 | ||
14 | #include "ssb_private.h" | 17 | #include "ssb_private.h" |
15 | 18 | ||
@@ -130,3 +133,90 @@ u32 ssb_gpio_polarity(struct ssb_bus *bus, u32 mask, u32 value) | |||
130 | return res; | 133 | return res; |
131 | } | 134 | } |
132 | EXPORT_SYMBOL(ssb_gpio_polarity); | 135 | EXPORT_SYMBOL(ssb_gpio_polarity); |
136 | |||
137 | #ifdef CONFIG_SSB_DRIVER_GIGE | ||
138 | static int gige_pci_init_callback(struct ssb_bus *bus, unsigned long data) | ||
139 | { | ||
140 | struct pci_dev *pdev = (struct pci_dev *)data; | ||
141 | struct ssb_device *dev; | ||
142 | unsigned int i; | ||
143 | int res; | ||
144 | |||
145 | for (i = 0; i < bus->nr_devices; i++) { | ||
146 | dev = &(bus->devices[i]); | ||
147 | if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT) | ||
148 | continue; | ||
149 | if (!dev->dev || | ||
150 | !dev->dev->driver || | ||
151 | !device_is_registered(dev->dev)) | ||
152 | continue; | ||
153 | res = ssb_gige_pcibios_plat_dev_init(dev, pdev); | ||
154 | if (res >= 0) | ||
155 | return res; | ||
156 | } | ||
157 | |||
158 | return -ENODEV; | ||
159 | } | ||
160 | #endif /* CONFIG_SSB_DRIVER_GIGE */ | ||
161 | |||
162 | int ssb_pcibios_plat_dev_init(struct pci_dev *dev) | ||
163 | { | ||
164 | int err; | ||
165 | |||
166 | err = ssb_pcicore_plat_dev_init(dev); | ||
167 | if (!err) | ||
168 | return 0; | ||
169 | #ifdef CONFIG_SSB_DRIVER_GIGE | ||
170 | err = ssb_for_each_bus_call((unsigned long)dev, gige_pci_init_callback); | ||
171 | if (err >= 0) | ||
172 | return err; | ||
173 | #endif | ||
174 | /* This is not a PCI device on any SSB device. */ | ||
175 | |||
176 | return -ENODEV; | ||
177 | } | ||
178 | |||
179 | #ifdef CONFIG_SSB_DRIVER_GIGE | ||
180 | static int gige_map_irq_callback(struct ssb_bus *bus, unsigned long data) | ||
181 | { | ||
182 | const struct pci_dev *pdev = (const struct pci_dev *)data; | ||
183 | struct ssb_device *dev; | ||
184 | unsigned int i; | ||
185 | int res; | ||
186 | |||
187 | for (i = 0; i < bus->nr_devices; i++) { | ||
188 | dev = &(bus->devices[i]); | ||
189 | if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT) | ||
190 | continue; | ||
191 | if (!dev->dev || | ||
192 | !dev->dev->driver || | ||
193 | !device_is_registered(dev->dev)) | ||
194 | continue; | ||
195 | res = ssb_gige_map_irq(dev, pdev); | ||
196 | if (res >= 0) | ||
197 | return res; | ||
198 | } | ||
199 | |||
200 | return -ENODEV; | ||
201 | } | ||
202 | #endif /* CONFIG_SSB_DRIVER_GIGE */ | ||
203 | |||
204 | int ssb_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) | ||
205 | { | ||
206 | int res; | ||
207 | |||
208 | /* Check if this PCI device is a device on a SSB bus or device | ||
209 | * and return the IRQ number for it. */ | ||
210 | |||
211 | res = ssb_pcicore_pcibios_map_irq(dev, slot, pin); | ||
212 | if (res >= 0) | ||
213 | return res; | ||
214 | #ifdef CONFIG_SSB_DRIVER_GIGE | ||
215 | res = ssb_for_each_bus_call((unsigned long)dev, gige_map_irq_callback); | ||
216 | if (res >= 0) | ||
217 | return res; | ||
218 | #endif | ||
219 | /* This is not a PCI device on any SSB device. */ | ||
220 | |||
221 | return -ENODEV; | ||
222 | } | ||
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 8003a9e55ac4..7cf8851286b5 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/io.h> | 14 | #include <linux/io.h> |
15 | #include <linux/ssb/ssb.h> | 15 | #include <linux/ssb/ssb.h> |
16 | #include <linux/ssb/ssb_regs.h> | 16 | #include <linux/ssb/ssb_regs.h> |
17 | #include <linux/ssb/ssb_driver_gige.h> | ||
17 | #include <linux/dma-mapping.h> | 18 | #include <linux/dma-mapping.h> |
18 | #include <linux/pci.h> | 19 | #include <linux/pci.h> |
19 | 20 | ||
@@ -68,6 +69,44 @@ found: | |||
68 | } | 69 | } |
69 | #endif /* CONFIG_SSB_PCIHOST */ | 70 | #endif /* CONFIG_SSB_PCIHOST */ |
70 | 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 | |||
91 | int ssb_for_each_bus_call(unsigned long data, | ||
92 | int (*func)(struct ssb_bus *bus, unsigned long data)) | ||
93 | { | ||
94 | struct ssb_bus *bus; | ||
95 | int res; | ||
96 | |||
97 | ssb_buses_lock(); | ||
98 | list_for_each_entry(bus, &buses, list) { | ||
99 | res = func(bus, data); | ||
100 | if (res >= 0) { | ||
101 | ssb_buses_unlock(); | ||
102 | return res; | ||
103 | } | ||
104 | } | ||
105 | ssb_buses_unlock(); | ||
106 | |||
107 | return -ENODEV; | ||
108 | } | ||
109 | |||
71 | static struct ssb_device *ssb_device_get(struct ssb_device *dev) | 110 | static struct ssb_device *ssb_device_get(struct ssb_device *dev) |
72 | { | 111 | { |
73 | if (dev) | 112 | if (dev) |
@@ -81,35 +120,12 @@ static void ssb_device_put(struct ssb_device *dev) | |||
81 | put_device(dev->dev); | 120 | put_device(dev->dev); |
82 | } | 121 | } |
83 | 122 | ||
84 | static int ssb_bus_resume(struct ssb_bus *bus) | ||
85 | { | ||
86 | int err; | ||
87 | |||
88 | ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1); | ||
89 | err = ssb_pcmcia_init(bus); | ||
90 | if (err) { | ||
91 | /* No need to disable XTAL, as we don't have one on PCMCIA. */ | ||
92 | return err; | ||
93 | } | ||
94 | ssb_chipco_resume(&bus->chipco); | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static int ssb_device_resume(struct device *dev) | 123 | static int ssb_device_resume(struct device *dev) |
100 | { | 124 | { |
101 | struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); | 125 | struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); |
102 | struct ssb_driver *ssb_drv; | 126 | struct ssb_driver *ssb_drv; |
103 | struct ssb_bus *bus; | ||
104 | int err = 0; | 127 | int err = 0; |
105 | 128 | ||
106 | bus = ssb_dev->bus; | ||
107 | if (bus->suspend_cnt == bus->nr_devices) { | ||
108 | err = ssb_bus_resume(bus); | ||
109 | if (err) | ||
110 | return err; | ||
111 | } | ||
112 | bus->suspend_cnt--; | ||
113 | if (dev->driver) { | 129 | if (dev->driver) { |
114 | ssb_drv = drv_to_ssb_drv(dev->driver); | 130 | ssb_drv = drv_to_ssb_drv(dev->driver); |
115 | if (ssb_drv && ssb_drv->resume) | 131 | if (ssb_drv && ssb_drv->resume) |
@@ -121,27 +137,10 @@ out: | |||
121 | return err; | 137 | return err; |
122 | } | 138 | } |
123 | 139 | ||
124 | static void ssb_bus_suspend(struct ssb_bus *bus, pm_message_t state) | ||
125 | { | ||
126 | ssb_chipco_suspend(&bus->chipco, state); | ||
127 | ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); | ||
128 | |||
129 | /* Reset HW state information in memory, so that HW is | ||
130 | * completely reinitialized on resume. */ | ||
131 | bus->mapped_device = NULL; | ||
132 | #ifdef CONFIG_SSB_DRIVER_PCICORE | ||
133 | bus->pcicore.setup_done = 0; | ||
134 | #endif | ||
135 | #ifdef CONFIG_SSB_DEBUG | ||
136 | bus->powered_up = 0; | ||
137 | #endif | ||
138 | } | ||
139 | |||
140 | static int ssb_device_suspend(struct device *dev, pm_message_t state) | 140 | static int ssb_device_suspend(struct device *dev, pm_message_t state) |
141 | { | 141 | { |
142 | struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); | 142 | struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); |
143 | struct ssb_driver *ssb_drv; | 143 | struct ssb_driver *ssb_drv; |
144 | struct ssb_bus *bus; | ||
145 | int err = 0; | 144 | int err = 0; |
146 | 145 | ||
147 | if (dev->driver) { | 146 | if (dev->driver) { |
@@ -151,19 +150,46 @@ static int ssb_device_suspend(struct device *dev, pm_message_t state) | |||
151 | if (err) | 150 | if (err) |
152 | goto out; | 151 | goto out; |
153 | } | 152 | } |
153 | out: | ||
154 | return err; | ||
155 | } | ||
156 | |||
157 | int ssb_bus_resume(struct ssb_bus *bus) | ||
158 | { | ||
159 | int err; | ||
160 | |||
161 | /* Reset HW state information in memory, so that HW is | ||
162 | * completely reinitialized. */ | ||
163 | bus->mapped_device = NULL; | ||
164 | #ifdef CONFIG_SSB_DRIVER_PCICORE | ||
165 | bus->pcicore.setup_done = 0; | ||
166 | #endif | ||
154 | 167 | ||
155 | bus = ssb_dev->bus; | 168 | err = ssb_bus_powerup(bus, 0); |
156 | bus->suspend_cnt++; | 169 | if (err) |
157 | if (bus->suspend_cnt == bus->nr_devices) { | 170 | return err; |
158 | /* All devices suspended. Shutdown the bus. */ | 171 | err = ssb_pcmcia_hardware_setup(bus); |
159 | ssb_bus_suspend(bus, state); | 172 | if (err) { |
173 | ssb_bus_may_powerdown(bus); | ||
174 | return err; | ||
160 | } | 175 | } |
176 | ssb_chipco_resume(&bus->chipco); | ||
177 | ssb_bus_may_powerdown(bus); | ||
161 | 178 | ||
162 | out: | 179 | return 0; |
163 | return err; | ||
164 | } | 180 | } |
181 | EXPORT_SYMBOL(ssb_bus_resume); | ||
165 | 182 | ||
166 | #ifdef CONFIG_SSB_PCIHOST | 183 | int ssb_bus_suspend(struct ssb_bus *bus) |
184 | { | ||
185 | ssb_chipco_suspend(&bus->chipco); | ||
186 | ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | EXPORT_SYMBOL(ssb_bus_suspend); | ||
191 | |||
192 | #ifdef CONFIG_SSB_SPROM | ||
167 | int ssb_devices_freeze(struct ssb_bus *bus) | 193 | int ssb_devices_freeze(struct ssb_bus *bus) |
168 | { | 194 | { |
169 | struct ssb_device *dev; | 195 | struct ssb_device *dev; |
@@ -249,7 +275,7 @@ int ssb_devices_thaw(struct ssb_bus *bus) | |||
249 | 275 | ||
250 | return 0; | 276 | return 0; |
251 | } | 277 | } |
252 | #endif /* CONFIG_SSB_PCIHOST */ | 278 | #endif /* CONFIG_SSB_SPROM */ |
253 | 279 | ||
254 | static void ssb_device_shutdown(struct device *dev) | 280 | static void ssb_device_shutdown(struct device *dev) |
255 | { | 281 | { |
@@ -378,7 +404,7 @@ void ssb_bus_unregister(struct ssb_bus *bus) | |||
378 | list_del(&bus->list); | 404 | list_del(&bus->list); |
379 | ssb_buses_unlock(); | 405 | ssb_buses_unlock(); |
380 | 406 | ||
381 | /* ssb_pcmcia_exit(bus); */ | 407 | ssb_pcmcia_exit(bus); |
382 | ssb_pci_exit(bus); | 408 | ssb_pci_exit(bus); |
383 | ssb_iounmap(bus); | 409 | ssb_iounmap(bus); |
384 | } | 410 | } |
@@ -508,6 +534,14 @@ error: | |||
508 | return err; | 534 | return err; |
509 | } | 535 | } |
510 | 536 | ||
537 | static u8 ssb_ssb_read8(struct ssb_device *dev, u16 offset) | ||
538 | { | ||
539 | struct ssb_bus *bus = dev->bus; | ||
540 | |||
541 | offset += dev->core_index * SSB_CORE_SIZE; | ||
542 | return readb(bus->mmio + offset); | ||
543 | } | ||
544 | |||
511 | static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset) | 545 | static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset) |
512 | { | 546 | { |
513 | struct ssb_bus *bus = dev->bus; | 547 | struct ssb_bus *bus = dev->bus; |
@@ -524,6 +558,63 @@ static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset) | |||
524 | return readl(bus->mmio + offset); | 558 | return readl(bus->mmio + offset); |
525 | } | 559 | } |
526 | 560 | ||
561 | #ifdef CONFIG_SSB_BLOCKIO | ||
562 | static void ssb_ssb_block_read(struct ssb_device *dev, void *buffer, | ||
563 | size_t count, u16 offset, u8 reg_width) | ||
564 | { | ||
565 | struct ssb_bus *bus = dev->bus; | ||
566 | void __iomem *addr; | ||
567 | |||
568 | offset += dev->core_index * SSB_CORE_SIZE; | ||
569 | addr = bus->mmio + offset; | ||
570 | |||
571 | switch (reg_width) { | ||
572 | case sizeof(u8): { | ||
573 | u8 *buf = buffer; | ||
574 | |||
575 | while (count) { | ||
576 | *buf = __raw_readb(addr); | ||
577 | buf++; | ||
578 | count--; | ||
579 | } | ||
580 | break; | ||
581 | } | ||
582 | case sizeof(u16): { | ||
583 | __le16 *buf = buffer; | ||
584 | |||
585 | SSB_WARN_ON(count & 1); | ||
586 | while (count) { | ||
587 | *buf = (__force __le16)__raw_readw(addr); | ||
588 | buf++; | ||
589 | count -= 2; | ||
590 | } | ||
591 | break; | ||
592 | } | ||
593 | case sizeof(u32): { | ||
594 | __le32 *buf = buffer; | ||
595 | |||
596 | SSB_WARN_ON(count & 3); | ||
597 | while (count) { | ||
598 | *buf = (__force __le32)__raw_readl(addr); | ||
599 | buf++; | ||
600 | count -= 4; | ||
601 | } | ||
602 | break; | ||
603 | } | ||
604 | default: | ||
605 | SSB_WARN_ON(1); | ||
606 | } | ||
607 | } | ||
608 | #endif /* CONFIG_SSB_BLOCKIO */ | ||
609 | |||
610 | static void ssb_ssb_write8(struct ssb_device *dev, u16 offset, u8 value) | ||
611 | { | ||
612 | struct ssb_bus *bus = dev->bus; | ||
613 | |||
614 | offset += dev->core_index * SSB_CORE_SIZE; | ||
615 | writeb(value, bus->mmio + offset); | ||
616 | } | ||
617 | |||
527 | static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value) | 618 | static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value) |
528 | { | 619 | { |
529 | struct ssb_bus *bus = dev->bus; | 620 | struct ssb_bus *bus = dev->bus; |
@@ -540,12 +631,67 @@ static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value) | |||
540 | writel(value, bus->mmio + offset); | 631 | writel(value, bus->mmio + offset); |
541 | } | 632 | } |
542 | 633 | ||
634 | #ifdef CONFIG_SSB_BLOCKIO | ||
635 | static void ssb_ssb_block_write(struct ssb_device *dev, const void *buffer, | ||
636 | size_t count, u16 offset, u8 reg_width) | ||
637 | { | ||
638 | struct ssb_bus *bus = dev->bus; | ||
639 | void __iomem *addr; | ||
640 | |||
641 | offset += dev->core_index * SSB_CORE_SIZE; | ||
642 | addr = bus->mmio + offset; | ||
643 | |||
644 | switch (reg_width) { | ||
645 | case sizeof(u8): { | ||
646 | const u8 *buf = buffer; | ||
647 | |||
648 | while (count) { | ||
649 | __raw_writeb(*buf, addr); | ||
650 | buf++; | ||
651 | count--; | ||
652 | } | ||
653 | break; | ||
654 | } | ||
655 | case sizeof(u16): { | ||
656 | const __le16 *buf = buffer; | ||
657 | |||
658 | SSB_WARN_ON(count & 1); | ||
659 | while (count) { | ||
660 | __raw_writew((__force u16)(*buf), addr); | ||
661 | buf++; | ||
662 | count -= 2; | ||
663 | } | ||
664 | break; | ||
665 | } | ||
666 | case sizeof(u32): { | ||
667 | const __le32 *buf = buffer; | ||
668 | |||
669 | SSB_WARN_ON(count & 3); | ||
670 | while (count) { | ||
671 | __raw_writel((__force u32)(*buf), addr); | ||
672 | buf++; | ||
673 | count -= 4; | ||
674 | } | ||
675 | break; | ||
676 | } | ||
677 | default: | ||
678 | SSB_WARN_ON(1); | ||
679 | } | ||
680 | } | ||
681 | #endif /* CONFIG_SSB_BLOCKIO */ | ||
682 | |||
543 | /* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */ | 683 | /* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */ |
544 | static const struct ssb_bus_ops ssb_ssb_ops = { | 684 | static const struct ssb_bus_ops ssb_ssb_ops = { |
685 | .read8 = ssb_ssb_read8, | ||
545 | .read16 = ssb_ssb_read16, | 686 | .read16 = ssb_ssb_read16, |
546 | .read32 = ssb_ssb_read32, | 687 | .read32 = ssb_ssb_read32, |
688 | .write8 = ssb_ssb_write8, | ||
547 | .write16 = ssb_ssb_write16, | 689 | .write16 = ssb_ssb_write16, |
548 | .write32 = ssb_ssb_write32, | 690 | .write32 = ssb_ssb_write32, |
691 | #ifdef CONFIG_SSB_BLOCKIO | ||
692 | .block_read = ssb_ssb_block_read, | ||
693 | .block_write = ssb_ssb_block_write, | ||
694 | #endif | ||
549 | }; | 695 | }; |
550 | 696 | ||
551 | static int ssb_fetch_invariants(struct ssb_bus *bus, | 697 | static int ssb_fetch_invariants(struct ssb_bus *bus, |
@@ -628,7 +774,7 @@ out: | |||
628 | err_dequeue: | 774 | err_dequeue: |
629 | list_del(&bus->list); | 775 | list_del(&bus->list); |
630 | err_pcmcia_exit: | 776 | err_pcmcia_exit: |
631 | /* ssb_pcmcia_exit(bus); */ | 777 | ssb_pcmcia_exit(bus); |
632 | err_pci_exit: | 778 | err_pci_exit: |
633 | ssb_pci_exit(bus); | 779 | ssb_pci_exit(bus); |
634 | err_unmap: | 780 | err_unmap: |
@@ -1010,9 +1156,9 @@ u32 ssb_dma_translation(struct ssb_device *dev) | |||
1010 | { | 1156 | { |
1011 | switch (dev->bus->bustype) { | 1157 | switch (dev->bus->bustype) { |
1012 | case SSB_BUSTYPE_SSB: | 1158 | case SSB_BUSTYPE_SSB: |
1159 | case SSB_BUSTYPE_PCMCIA: | ||
1013 | return 0; | 1160 | return 0; |
1014 | case SSB_BUSTYPE_PCI: | 1161 | case SSB_BUSTYPE_PCI: |
1015 | case SSB_BUSTYPE_PCMCIA: | ||
1016 | return SSB_PCI_DMA; | 1162 | return SSB_PCI_DMA; |
1017 | } | 1163 | } |
1018 | return 0; | 1164 | return 0; |
@@ -1161,7 +1307,14 @@ static int __init ssb_modinit(void) | |||
1161 | err = b43_pci_ssb_bridge_init(); | 1307 | err = b43_pci_ssb_bridge_init(); |
1162 | if (err) { | 1308 | if (err) { |
1163 | ssb_printk(KERN_ERR "Broadcom 43xx PCI-SSB-bridge " | 1309 | ssb_printk(KERN_ERR "Broadcom 43xx PCI-SSB-bridge " |
1164 | "initialization failed"); | 1310 | "initialization failed\n"); |
1311 | /* don't fail SSB init because of this */ | ||
1312 | err = 0; | ||
1313 | } | ||
1314 | err = ssb_gige_init(); | ||
1315 | if (err) { | ||
1316 | ssb_printk(KERN_ERR "SSB Broadcom Gigabit Ethernet " | ||
1317 | "driver initialization failed\n"); | ||
1165 | /* don't fail SSB init because of this */ | 1318 | /* don't fail SSB init because of this */ |
1166 | err = 0; | 1319 | err = 0; |
1167 | } | 1320 | } |
@@ -1175,6 +1328,7 @@ fs_initcall(ssb_modinit); | |||
1175 | 1328 | ||
1176 | static void __exit ssb_modexit(void) | 1329 | static void __exit ssb_modexit(void) |
1177 | { | 1330 | { |
1331 | ssb_gige_exit(); | ||
1178 | b43_pci_ssb_bridge_exit(); | 1332 | b43_pci_ssb_bridge_exit(); |
1179 | bus_unregister(&ssb_bustype); | 1333 | bus_unregister(&ssb_bustype); |
1180 | } | 1334 | } |
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index b434df75047f..904b1a8d0885 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) |
@@ -572,6 +574,19 @@ static inline int ssb_pci_assert_buspower(struct ssb_bus *bus) | |||
572 | } | 574 | } |
573 | #endif /* DEBUG */ | 575 | #endif /* DEBUG */ |
574 | 576 | ||
577 | static u8 ssb_pci_read8(struct ssb_device *dev, u16 offset) | ||
578 | { | ||
579 | struct ssb_bus *bus = dev->bus; | ||
580 | |||
581 | if (unlikely(ssb_pci_assert_buspower(bus))) | ||
582 | return 0xFF; | ||
583 | if (unlikely(bus->mapped_device != dev)) { | ||
584 | if (unlikely(ssb_pci_switch_core(bus, dev))) | ||
585 | return 0xFF; | ||
586 | } | ||
587 | return ioread8(bus->mmio + offset); | ||
588 | } | ||
589 | |||
575 | static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset) | 590 | static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset) |
576 | { | 591 | { |
577 | struct ssb_bus *bus = dev->bus; | 592 | struct ssb_bus *bus = dev->bus; |
@@ -598,6 +613,54 @@ static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset) | |||
598 | return ioread32(bus->mmio + offset); | 613 | return ioread32(bus->mmio + offset); |
599 | } | 614 | } |
600 | 615 | ||
616 | #ifdef CONFIG_SSB_BLOCKIO | ||
617 | static void ssb_pci_block_read(struct ssb_device *dev, void *buffer, | ||
618 | size_t count, u16 offset, u8 reg_width) | ||
619 | { | ||
620 | struct ssb_bus *bus = dev->bus; | ||
621 | void __iomem *addr = bus->mmio + offset; | ||
622 | |||
623 | if (unlikely(ssb_pci_assert_buspower(bus))) | ||
624 | goto error; | ||
625 | if (unlikely(bus->mapped_device != dev)) { | ||
626 | if (unlikely(ssb_pci_switch_core(bus, dev))) | ||
627 | goto error; | ||
628 | } | ||
629 | switch (reg_width) { | ||
630 | case sizeof(u8): | ||
631 | ioread8_rep(addr, buffer, count); | ||
632 | break; | ||
633 | case sizeof(u16): | ||
634 | SSB_WARN_ON(count & 1); | ||
635 | ioread16_rep(addr, buffer, count >> 1); | ||
636 | break; | ||
637 | case sizeof(u32): | ||
638 | SSB_WARN_ON(count & 3); | ||
639 | ioread32_rep(addr, buffer, count >> 2); | ||
640 | break; | ||
641 | default: | ||
642 | SSB_WARN_ON(1); | ||
643 | } | ||
644 | |||
645 | return; | ||
646 | error: | ||
647 | memset(buffer, 0xFF, count); | ||
648 | } | ||
649 | #endif /* CONFIG_SSB_BLOCKIO */ | ||
650 | |||
651 | static void ssb_pci_write8(struct ssb_device *dev, u16 offset, u8 value) | ||
652 | { | ||
653 | struct ssb_bus *bus = dev->bus; | ||
654 | |||
655 | if (unlikely(ssb_pci_assert_buspower(bus))) | ||
656 | return; | ||
657 | if (unlikely(bus->mapped_device != dev)) { | ||
658 | if (unlikely(ssb_pci_switch_core(bus, dev))) | ||
659 | return; | ||
660 | } | ||
661 | iowrite8(value, bus->mmio + offset); | ||
662 | } | ||
663 | |||
601 | static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value) | 664 | static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value) |
602 | { | 665 | { |
603 | struct ssb_bus *bus = dev->bus; | 666 | struct ssb_bus *bus = dev->bus; |
@@ -624,79 +687,63 @@ static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value) | |||
624 | iowrite32(value, bus->mmio + offset); | 687 | iowrite32(value, bus->mmio + offset); |
625 | } | 688 | } |
626 | 689 | ||
690 | #ifdef CONFIG_SSB_BLOCKIO | ||
691 | static void ssb_pci_block_write(struct ssb_device *dev, const void *buffer, | ||
692 | size_t count, u16 offset, u8 reg_width) | ||
693 | { | ||
694 | struct ssb_bus *bus = dev->bus; | ||
695 | void __iomem *addr = bus->mmio + offset; | ||
696 | |||
697 | if (unlikely(ssb_pci_assert_buspower(bus))) | ||
698 | return; | ||
699 | if (unlikely(bus->mapped_device != dev)) { | ||
700 | if (unlikely(ssb_pci_switch_core(bus, dev))) | ||
701 | return; | ||
702 | } | ||
703 | switch (reg_width) { | ||
704 | case sizeof(u8): | ||
705 | iowrite8_rep(addr, buffer, count); | ||
706 | break; | ||
707 | case sizeof(u16): | ||
708 | SSB_WARN_ON(count & 1); | ||
709 | iowrite16_rep(addr, buffer, count >> 1); | ||
710 | break; | ||
711 | case sizeof(u32): | ||
712 | SSB_WARN_ON(count & 3); | ||
713 | iowrite32_rep(addr, buffer, count >> 2); | ||
714 | break; | ||
715 | default: | ||
716 | SSB_WARN_ON(1); | ||
717 | } | ||
718 | } | ||
719 | #endif /* CONFIG_SSB_BLOCKIO */ | ||
720 | |||
627 | /* Not "static", as it's used in main.c */ | 721 | /* Not "static", as it's used in main.c */ |
628 | const struct ssb_bus_ops ssb_pci_ops = { | 722 | const struct ssb_bus_ops ssb_pci_ops = { |
723 | .read8 = ssb_pci_read8, | ||
629 | .read16 = ssb_pci_read16, | 724 | .read16 = ssb_pci_read16, |
630 | .read32 = ssb_pci_read32, | 725 | .read32 = ssb_pci_read32, |
726 | .write8 = ssb_pci_write8, | ||
631 | .write16 = ssb_pci_write16, | 727 | .write16 = ssb_pci_write16, |
632 | .write32 = ssb_pci_write32, | 728 | .write32 = ssb_pci_write32, |
729 | #ifdef CONFIG_SSB_BLOCKIO | ||
730 | .block_read = ssb_pci_block_read, | ||
731 | .block_write = ssb_pci_block_write, | ||
732 | #endif | ||
633 | }; | 733 | }; |
634 | 734 | ||
635 | static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, u16 size) | ||
636 | { | ||
637 | int i, pos = 0; | ||
638 | |||
639 | for (i = 0; i < size; i++) | ||
640 | pos += snprintf(buf + pos, buf_len - pos - 1, | ||
641 | "%04X", swab16(sprom[i]) & 0xFFFF); | ||
642 | pos += snprintf(buf + pos, buf_len - pos - 1, "\n"); | ||
643 | |||
644 | return pos + 1; | ||
645 | } | ||
646 | |||
647 | static int hex2sprom(u16 *sprom, const char *dump, size_t len, u16 size) | ||
648 | { | ||
649 | char tmp[5] = { 0 }; | ||
650 | int cnt = 0; | ||
651 | unsigned long parsed; | ||
652 | |||
653 | if (len < size * 2) | ||
654 | return -EINVAL; | ||
655 | |||
656 | while (cnt < size) { | ||
657 | memcpy(tmp, dump, 4); | ||
658 | dump += 4; | ||
659 | parsed = simple_strtoul(tmp, NULL, 16); | ||
660 | sprom[cnt++] = swab16((u16)parsed); | ||
661 | } | ||
662 | |||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev, | 735 | static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev, |
667 | struct device_attribute *attr, | 736 | struct device_attribute *attr, |
668 | char *buf) | 737 | char *buf) |
669 | { | 738 | { |
670 | struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); | 739 | struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); |
671 | struct ssb_bus *bus; | 740 | struct ssb_bus *bus; |
672 | u16 *sprom; | ||
673 | int err = -ENODEV; | ||
674 | ssize_t count = 0; | ||
675 | 741 | ||
676 | bus = ssb_pci_dev_to_bus(pdev); | 742 | bus = ssb_pci_dev_to_bus(pdev); |
677 | if (!bus) | 743 | if (!bus) |
678 | goto out; | 744 | return -ENODEV; |
679 | err = -ENOMEM; | ||
680 | sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); | ||
681 | if (!sprom) | ||
682 | goto out; | ||
683 | |||
684 | /* Use interruptible locking, as the SPROM write might | ||
685 | * be holding the lock for several seconds. So allow userspace | ||
686 | * to cancel operation. */ | ||
687 | err = -ERESTARTSYS; | ||
688 | if (mutex_lock_interruptible(&bus->pci_sprom_mutex)) | ||
689 | goto out_kfree; | ||
690 | sprom_do_read(bus, sprom); | ||
691 | mutex_unlock(&bus->pci_sprom_mutex); | ||
692 | 745 | ||
693 | count = sprom2hex(sprom, buf, PAGE_SIZE, bus->sprom_size); | 746 | return ssb_attr_sprom_show(bus, buf, sprom_do_read); |
694 | err = 0; | ||
695 | |||
696 | out_kfree: | ||
697 | kfree(sprom); | ||
698 | out: | ||
699 | return err ? err : count; | ||
700 | } | 747 | } |
701 | 748 | ||
702 | static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, | 749 | static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, |
@@ -705,55 +752,13 @@ static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, | |||
705 | { | 752 | { |
706 | struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); | 753 | struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); |
707 | struct ssb_bus *bus; | 754 | struct ssb_bus *bus; |
708 | u16 *sprom; | ||
709 | int res = 0, err = -ENODEV; | ||
710 | 755 | ||
711 | bus = ssb_pci_dev_to_bus(pdev); | 756 | bus = ssb_pci_dev_to_bus(pdev); |
712 | if (!bus) | 757 | if (!bus) |
713 | goto out; | 758 | return -ENODEV; |
714 | err = -ENOMEM; | ||
715 | sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); | ||
716 | if (!sprom) | ||
717 | goto out; | ||
718 | err = hex2sprom(sprom, buf, count, bus->sprom_size); | ||
719 | if (err) { | ||
720 | err = -EINVAL; | ||
721 | goto out_kfree; | ||
722 | } | ||
723 | err = sprom_check_crc(sprom, bus->sprom_size); | ||
724 | if (err) { | ||
725 | err = -EINVAL; | ||
726 | goto out_kfree; | ||
727 | } | ||
728 | 759 | ||
729 | /* Use interruptible locking, as the SPROM write might | 760 | return ssb_attr_sprom_store(bus, buf, count, |
730 | * be holding the lock for several seconds. So allow userspace | 761 | sprom_check_crc, sprom_do_write); |
731 | * to cancel operation. */ | ||
732 | err = -ERESTARTSYS; | ||
733 | if (mutex_lock_interruptible(&bus->pci_sprom_mutex)) | ||
734 | goto out_kfree; | ||
735 | err = ssb_devices_freeze(bus); | ||
736 | if (err == -EOPNOTSUPP) { | ||
737 | ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. " | ||
738 | "No suspend support. Is CONFIG_PM enabled?\n"); | ||
739 | goto out_unlock; | ||
740 | } | ||
741 | if (err) { | ||
742 | ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); | ||
743 | goto out_unlock; | ||
744 | } | ||
745 | res = sprom_do_write(bus, sprom); | ||
746 | err = ssb_devices_thaw(bus); | ||
747 | if (err) | ||
748 | ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); | ||
749 | out_unlock: | ||
750 | mutex_unlock(&bus->pci_sprom_mutex); | ||
751 | out_kfree: | ||
752 | kfree(sprom); | ||
753 | out: | ||
754 | if (res) | ||
755 | return res; | ||
756 | return err ? err : count; | ||
757 | } | 762 | } |
758 | 763 | ||
759 | static DEVICE_ATTR(ssb_sprom, 0600, | 764 | static DEVICE_ATTR(ssb_sprom, 0600, |
@@ -780,7 +785,7 @@ int ssb_pci_init(struct ssb_bus *bus) | |||
780 | return 0; | 785 | return 0; |
781 | 786 | ||
782 | pdev = bus->host_pci; | 787 | pdev = bus->host_pci; |
783 | mutex_init(&bus->pci_sprom_mutex); | 788 | mutex_init(&bus->sprom_mutex); |
784 | err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom); | 789 | err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom); |
785 | if (err) | 790 | if (err) |
786 | goto out; | 791 | goto out; |
diff --git a/drivers/ssb/pcihost_wrapper.c b/drivers/ssb/pcihost_wrapper.c index 82a10abef640..e82db4aaa050 100644 --- a/drivers/ssb/pcihost_wrapper.c +++ b/drivers/ssb/pcihost_wrapper.c | |||
@@ -18,6 +18,12 @@ | |||
18 | #ifdef CONFIG_PM | 18 | #ifdef CONFIG_PM |
19 | static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state) | 19 | static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state) |
20 | { | 20 | { |
21 | struct ssb_bus *ssb = pci_get_drvdata(dev); | ||
22 | int err; | ||
23 | |||
24 | err = ssb_bus_suspend(ssb); | ||
25 | if (err) | ||
26 | return err; | ||
21 | pci_save_state(dev); | 27 | pci_save_state(dev); |
22 | pci_disable_device(dev); | 28 | pci_disable_device(dev); |
23 | pci_set_power_state(dev, pci_choose_state(dev, state)); | 29 | pci_set_power_state(dev, pci_choose_state(dev, state)); |
@@ -27,6 +33,7 @@ static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state) | |||
27 | 33 | ||
28 | static int ssb_pcihost_resume(struct pci_dev *dev) | 34 | static int ssb_pcihost_resume(struct pci_dev *dev) |
29 | { | 35 | { |
36 | struct ssb_bus *ssb = pci_get_drvdata(dev); | ||
30 | int err; | 37 | int err; |
31 | 38 | ||
32 | pci_set_power_state(dev, 0); | 39 | pci_set_power_state(dev, 0); |
@@ -34,6 +41,9 @@ static int ssb_pcihost_resume(struct pci_dev *dev) | |||
34 | if (err) | 41 | if (err) |
35 | return err; | 42 | return err; |
36 | pci_restore_state(dev); | 43 | pci_restore_state(dev); |
44 | err = ssb_bus_resume(ssb); | ||
45 | if (err) | ||
46 | return err; | ||
37 | 47 | ||
38 | return 0; | 48 | return 0; |
39 | } | 49 | } |
diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c index 46816cda8b98..24c2a46c1476 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,127 @@ | |||
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_ADDRESS0 0x2E | ||
32 | #define SSB_PCMCIA_ADDRESS1 0x30 | ||
33 | #define SSB_PCMCIA_ADDRESS2 0x32 | ||
34 | #define SSB_PCMCIA_MEMSEG 0x34 | ||
35 | #define SSB_PCMCIA_SPROMCTL 0x36 | ||
36 | #define SSB_PCMCIA_SPROMCTL_IDLE 0 | ||
37 | #define SSB_PCMCIA_SPROMCTL_WRITE 1 | ||
38 | #define SSB_PCMCIA_SPROMCTL_READ 2 | ||
39 | #define SSB_PCMCIA_SPROMCTL_WRITEEN 4 | ||
40 | #define SSB_PCMCIA_SPROMCTL_WRITEDIS 7 | ||
41 | #define SSB_PCMCIA_SPROMCTL_DONE 8 | ||
42 | #define SSB_PCMCIA_SPROM_DATALO 0x38 | ||
43 | #define SSB_PCMCIA_SPROM_DATAHI 0x3A | ||
44 | #define SSB_PCMCIA_SPROM_ADDRLO 0x3C | ||
45 | #define SSB_PCMCIA_SPROM_ADDRHI 0x3E | ||
46 | |||
47 | /* Hardware invariants CIS tuples */ | ||
48 | #define SSB_PCMCIA_CIS 0x80 | ||
49 | #define SSB_PCMCIA_CIS_ID 0x01 | ||
50 | #define SSB_PCMCIA_CIS_BOARDREV 0x02 | ||
51 | #define SSB_PCMCIA_CIS_PA 0x03 | ||
52 | #define SSB_PCMCIA_CIS_PA_PA0B0_LO 0 | ||
53 | #define SSB_PCMCIA_CIS_PA_PA0B0_HI 1 | ||
54 | #define SSB_PCMCIA_CIS_PA_PA0B1_LO 2 | ||
55 | #define SSB_PCMCIA_CIS_PA_PA0B1_HI 3 | ||
56 | #define SSB_PCMCIA_CIS_PA_PA0B2_LO 4 | ||
57 | #define SSB_PCMCIA_CIS_PA_PA0B2_HI 5 | ||
58 | #define SSB_PCMCIA_CIS_PA_ITSSI 6 | ||
59 | #define SSB_PCMCIA_CIS_PA_MAXPOW 7 | ||
60 | #define SSB_PCMCIA_CIS_OEMNAME 0x04 | ||
61 | #define SSB_PCMCIA_CIS_CCODE 0x05 | ||
62 | #define SSB_PCMCIA_CIS_ANTENNA 0x06 | ||
63 | #define SSB_PCMCIA_CIS_ANTGAIN 0x07 | ||
64 | #define SSB_PCMCIA_CIS_BFLAGS 0x08 | ||
65 | #define SSB_PCMCIA_CIS_LEDS 0x09 | ||
66 | |||
67 | /* PCMCIA SPROM size. */ | ||
68 | #define SSB_PCMCIA_SPROM_SIZE 256 | ||
69 | #define SSB_PCMCIA_SPROM_SIZE_BYTES (SSB_PCMCIA_SPROM_SIZE * sizeof(u16)) | ||
70 | |||
71 | |||
72 | /* Write to a PCMCIA configuration register. */ | ||
73 | static int ssb_pcmcia_cfg_write(struct ssb_bus *bus, u8 offset, u8 value) | ||
74 | { | ||
75 | conf_reg_t reg; | ||
76 | int res; | ||
77 | |||
78 | memset(®, 0, sizeof(reg)); | ||
79 | reg.Offset = offset; | ||
80 | reg.Action = CS_WRITE; | ||
81 | reg.Value = value; | ||
82 | res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | ||
83 | if (unlikely(res != CS_SUCCESS)) | ||
84 | return -EBUSY; | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* Read from a PCMCIA configuration register. */ | ||
90 | static int ssb_pcmcia_cfg_read(struct ssb_bus *bus, u8 offset, u8 *value) | ||
91 | { | ||
92 | conf_reg_t reg; | ||
93 | int res; | ||
94 | |||
95 | memset(®, 0, sizeof(reg)); | ||
96 | reg.Offset = offset; | ||
97 | reg.Action = CS_READ; | ||
98 | res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | ||
99 | if (unlikely(res != CS_SUCCESS)) | ||
100 | return -EBUSY; | ||
101 | *value = reg.Value; | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
29 | int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, | 106 | int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, |
30 | u8 coreidx) | 107 | u8 coreidx) |
31 | { | 108 | { |
32 | struct pcmcia_device *pdev = bus->host_pcmcia; | ||
33 | int err; | 109 | int err; |
34 | int attempts = 0; | 110 | int attempts = 0; |
35 | u32 cur_core; | 111 | u32 cur_core; |
36 | conf_reg_t reg; | ||
37 | u32 addr; | 112 | u32 addr; |
38 | u32 read_addr; | 113 | u32 read_addr; |
114 | u8 val; | ||
39 | 115 | ||
40 | addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; | 116 | addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; |
41 | while (1) { | 117 | while (1) { |
42 | reg.Action = CS_WRITE; | 118 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS0, |
43 | reg.Offset = 0x2E; | 119 | (addr & 0x0000F000) >> 12); |
44 | reg.Value = (addr & 0x0000F000) >> 12; | 120 | if (err) |
45 | err = pcmcia_access_configuration_register(pdev, ®); | ||
46 | if (err != CS_SUCCESS) | ||
47 | goto error; | 121 | goto error; |
48 | reg.Offset = 0x30; | 122 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS1, |
49 | reg.Value = (addr & 0x00FF0000) >> 16; | 123 | (addr & 0x00FF0000) >> 16); |
50 | err = pcmcia_access_configuration_register(pdev, ®); | 124 | if (err) |
51 | if (err != CS_SUCCESS) | ||
52 | goto error; | 125 | goto error; |
53 | reg.Offset = 0x32; | 126 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS2, |
54 | reg.Value = (addr & 0xFF000000) >> 24; | 127 | (addr & 0xFF000000) >> 24); |
55 | err = pcmcia_access_configuration_register(pdev, ®); | 128 | if (err) |
56 | if (err != CS_SUCCESS) | ||
57 | goto error; | 129 | goto error; |
58 | 130 | ||
59 | read_addr = 0; | 131 | read_addr = 0; |
60 | 132 | ||
61 | reg.Action = CS_READ; | 133 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS0, &val); |
62 | reg.Offset = 0x2E; | 134 | if (err) |
63 | err = pcmcia_access_configuration_register(pdev, ®); | ||
64 | if (err != CS_SUCCESS) | ||
65 | goto error; | 135 | goto error; |
66 | read_addr |= ((u32)(reg.Value & 0x0F)) << 12; | 136 | read_addr |= ((u32)(val & 0x0F)) << 12; |
67 | reg.Offset = 0x30; | 137 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS1, &val); |
68 | err = pcmcia_access_configuration_register(pdev, ®); | 138 | if (err) |
69 | if (err != CS_SUCCESS) | ||
70 | goto error; | 139 | goto error; |
71 | read_addr |= ((u32)reg.Value) << 16; | 140 | read_addr |= ((u32)val) << 16; |
72 | reg.Offset = 0x32; | 141 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS2, &val); |
73 | err = pcmcia_access_configuration_register(pdev, ®); | 142 | if (err) |
74 | if (err != CS_SUCCESS) | ||
75 | goto error; | 143 | goto error; |
76 | read_addr |= ((u32)reg.Value) << 24; | 144 | read_addr |= ((u32)val) << 24; |
77 | 145 | ||
78 | cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE; | 146 | cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE; |
79 | if (cur_core == coreidx) | 147 | if (cur_core == coreidx) |
80 | break; | 148 | break; |
81 | 149 | ||
150 | err = -ETIMEDOUT; | ||
82 | if (attempts++ > SSB_BAR0_MAX_RETRIES) | 151 | if (attempts++ > SSB_BAR0_MAX_RETRIES) |
83 | goto error; | 152 | goto error; |
84 | udelay(10); | 153 | udelay(10); |
@@ -87,7 +156,7 @@ int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, | |||
87 | return 0; | 156 | return 0; |
88 | error: | 157 | error: |
89 | ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); | 158 | ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); |
90 | return -ENODEV; | 159 | return err; |
91 | } | 160 | } |
92 | 161 | ||
93 | int ssb_pcmcia_switch_core(struct ssb_bus *bus, | 162 | int ssb_pcmcia_switch_core(struct ssb_bus *bus, |
@@ -112,27 +181,21 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus, | |||
112 | int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) | 181 | int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) |
113 | { | 182 | { |
114 | int attempts = 0; | 183 | int attempts = 0; |
115 | conf_reg_t reg; | 184 | int err; |
116 | int res; | 185 | u8 val; |
117 | 186 | ||
118 | SSB_WARN_ON((seg != 0) && (seg != 1)); | 187 | SSB_WARN_ON((seg != 0) && (seg != 1)); |
119 | reg.Offset = 0x34; | ||
120 | reg.Function = 0; | ||
121 | while (1) { | 188 | while (1) { |
122 | reg.Action = CS_WRITE; | 189 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_MEMSEG, seg); |
123 | reg.Value = seg; | 190 | if (err) |
124 | res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | ||
125 | if (unlikely(res != CS_SUCCESS)) | ||
126 | goto error; | 191 | goto error; |
127 | reg.Value = 0xFF; | 192 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_MEMSEG, &val); |
128 | reg.Action = CS_READ; | 193 | if (err) |
129 | res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | ||
130 | if (unlikely(res != CS_SUCCESS)) | ||
131 | goto error; | 194 | goto error; |
132 | 195 | if (val == seg) | |
133 | if (reg.Value == seg) | ||
134 | break; | 196 | break; |
135 | 197 | ||
198 | err = -ETIMEDOUT; | ||
136 | if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES)) | 199 | if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES)) |
137 | goto error; | 200 | goto error; |
138 | udelay(10); | 201 | udelay(10); |
@@ -142,7 +205,7 @@ int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) | |||
142 | return 0; | 205 | return 0; |
143 | error: | 206 | error: |
144 | ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); | 207 | ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); |
145 | return -ENODEV; | 208 | return err; |
146 | } | 209 | } |
147 | 210 | ||
148 | static int select_core_and_segment(struct ssb_device *dev, | 211 | static int select_core_and_segment(struct ssb_device *dev, |
@@ -172,6 +235,22 @@ static int select_core_and_segment(struct ssb_device *dev, | |||
172 | return 0; | 235 | return 0; |
173 | } | 236 | } |
174 | 237 | ||
238 | static u8 ssb_pcmcia_read8(struct ssb_device *dev, u16 offset) | ||
239 | { | ||
240 | struct ssb_bus *bus = dev->bus; | ||
241 | unsigned long flags; | ||
242 | int err; | ||
243 | u8 value = 0xFF; | ||
244 | |||
245 | spin_lock_irqsave(&bus->bar_lock, flags); | ||
246 | err = select_core_and_segment(dev, &offset); | ||
247 | if (likely(!err)) | ||
248 | value = readb(bus->mmio + offset); | ||
249 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
250 | |||
251 | return value; | ||
252 | } | ||
253 | |||
175 | static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) | 254 | static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) |
176 | { | 255 | { |
177 | struct ssb_bus *bus = dev->bus; | 256 | struct ssb_bus *bus = dev->bus; |
@@ -206,6 +285,78 @@ static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) | |||
206 | return (lo | (hi << 16)); | 285 | return (lo | (hi << 16)); |
207 | } | 286 | } |
208 | 287 | ||
288 | #ifdef CONFIG_SSB_BLOCKIO | ||
289 | static void ssb_pcmcia_block_read(struct ssb_device *dev, void *buffer, | ||
290 | size_t count, u16 offset, u8 reg_width) | ||
291 | { | ||
292 | struct ssb_bus *bus = dev->bus; | ||
293 | unsigned long flags; | ||
294 | void __iomem *addr = bus->mmio + offset; | ||
295 | int err; | ||
296 | |||
297 | spin_lock_irqsave(&bus->bar_lock, flags); | ||
298 | err = select_core_and_segment(dev, &offset); | ||
299 | if (unlikely(err)) { | ||
300 | memset(buffer, 0xFF, count); | ||
301 | goto unlock; | ||
302 | } | ||
303 | switch (reg_width) { | ||
304 | case sizeof(u8): { | ||
305 | u8 *buf = buffer; | ||
306 | |||
307 | while (count) { | ||
308 | *buf = __raw_readb(addr); | ||
309 | buf++; | ||
310 | count--; | ||
311 | } | ||
312 | break; | ||
313 | } | ||
314 | case sizeof(u16): { | ||
315 | __le16 *buf = buffer; | ||
316 | |||
317 | SSB_WARN_ON(count & 1); | ||
318 | while (count) { | ||
319 | *buf = (__force __le16)__raw_readw(addr); | ||
320 | buf++; | ||
321 | count -= 2; | ||
322 | } | ||
323 | break; | ||
324 | } | ||
325 | case sizeof(u32): { | ||
326 | __le16 *buf = buffer; | ||
327 | |||
328 | SSB_WARN_ON(count & 3); | ||
329 | while (count) { | ||
330 | *buf = (__force __le16)__raw_readw(addr); | ||
331 | buf++; | ||
332 | *buf = (__force __le16)__raw_readw(addr + 2); | ||
333 | buf++; | ||
334 | count -= 4; | ||
335 | } | ||
336 | break; | ||
337 | } | ||
338 | default: | ||
339 | SSB_WARN_ON(1); | ||
340 | } | ||
341 | unlock: | ||
342 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
343 | } | ||
344 | #endif /* CONFIG_SSB_BLOCKIO */ | ||
345 | |||
346 | static void ssb_pcmcia_write8(struct ssb_device *dev, u16 offset, u8 value) | ||
347 | { | ||
348 | struct ssb_bus *bus = dev->bus; | ||
349 | unsigned long flags; | ||
350 | int err; | ||
351 | |||
352 | spin_lock_irqsave(&bus->bar_lock, flags); | ||
353 | err = select_core_and_segment(dev, &offset); | ||
354 | if (likely(!err)) | ||
355 | writeb(value, bus->mmio + offset); | ||
356 | mmiowb(); | ||
357 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
358 | } | ||
359 | |||
209 | static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) | 360 | static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) |
210 | { | 361 | { |
211 | struct ssb_bus *bus = dev->bus; | 362 | struct ssb_bus *bus = dev->bus; |
@@ -236,26 +387,425 @@ static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) | |||
236 | spin_unlock_irqrestore(&bus->bar_lock, flags); | 387 | spin_unlock_irqrestore(&bus->bar_lock, flags); |
237 | } | 388 | } |
238 | 389 | ||
390 | #ifdef CONFIG_SSB_BLOCKIO | ||
391 | static void ssb_pcmcia_block_write(struct ssb_device *dev, const void *buffer, | ||
392 | size_t count, u16 offset, u8 reg_width) | ||
393 | { | ||
394 | struct ssb_bus *bus = dev->bus; | ||
395 | unsigned long flags; | ||
396 | void __iomem *addr = bus->mmio + offset; | ||
397 | int err; | ||
398 | |||
399 | spin_lock_irqsave(&bus->bar_lock, flags); | ||
400 | err = select_core_and_segment(dev, &offset); | ||
401 | if (unlikely(err)) | ||
402 | goto unlock; | ||
403 | switch (reg_width) { | ||
404 | case sizeof(u8): { | ||
405 | const u8 *buf = buffer; | ||
406 | |||
407 | while (count) { | ||
408 | __raw_writeb(*buf, addr); | ||
409 | buf++; | ||
410 | count--; | ||
411 | } | ||
412 | break; | ||
413 | } | ||
414 | case sizeof(u16): { | ||
415 | const __le16 *buf = buffer; | ||
416 | |||
417 | SSB_WARN_ON(count & 1); | ||
418 | while (count) { | ||
419 | __raw_writew((__force u16)(*buf), addr); | ||
420 | buf++; | ||
421 | count -= 2; | ||
422 | } | ||
423 | break; | ||
424 | } | ||
425 | case sizeof(u32): { | ||
426 | const __le16 *buf = buffer; | ||
427 | |||
428 | SSB_WARN_ON(count & 3); | ||
429 | while (count) { | ||
430 | __raw_writew((__force u16)(*buf), addr); | ||
431 | buf++; | ||
432 | __raw_writew((__force u16)(*buf), addr + 2); | ||
433 | buf++; | ||
434 | count -= 4; | ||
435 | } | ||
436 | break; | ||
437 | } | ||
438 | default: | ||
439 | SSB_WARN_ON(1); | ||
440 | } | ||
441 | unlock: | ||
442 | mmiowb(); | ||
443 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
444 | } | ||
445 | #endif /* CONFIG_SSB_BLOCKIO */ | ||
446 | |||
239 | /* Not "static", as it's used in main.c */ | 447 | /* Not "static", as it's used in main.c */ |
240 | const struct ssb_bus_ops ssb_pcmcia_ops = { | 448 | const struct ssb_bus_ops ssb_pcmcia_ops = { |
449 | .read8 = ssb_pcmcia_read8, | ||
241 | .read16 = ssb_pcmcia_read16, | 450 | .read16 = ssb_pcmcia_read16, |
242 | .read32 = ssb_pcmcia_read32, | 451 | .read32 = ssb_pcmcia_read32, |
452 | .write8 = ssb_pcmcia_write8, | ||
243 | .write16 = ssb_pcmcia_write16, | 453 | .write16 = ssb_pcmcia_write16, |
244 | .write32 = ssb_pcmcia_write32, | 454 | .write32 = ssb_pcmcia_write32, |
455 | #ifdef CONFIG_SSB_BLOCKIO | ||
456 | .block_read = ssb_pcmcia_block_read, | ||
457 | .block_write = ssb_pcmcia_block_write, | ||
458 | #endif | ||
245 | }; | 459 | }; |
246 | 460 | ||
247 | #include <linux/etherdevice.h> | 461 | static int ssb_pcmcia_sprom_command(struct ssb_bus *bus, u8 command) |
462 | { | ||
463 | unsigned int i; | ||
464 | int err; | ||
465 | u8 value; | ||
466 | |||
467 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROMCTL, command); | ||
468 | if (err) | ||
469 | return err; | ||
470 | for (i = 0; i < 1000; i++) { | ||
471 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROMCTL, &value); | ||
472 | if (err) | ||
473 | return err; | ||
474 | if (value & SSB_PCMCIA_SPROMCTL_DONE) | ||
475 | return 0; | ||
476 | udelay(10); | ||
477 | } | ||
478 | |||
479 | return -ETIMEDOUT; | ||
480 | } | ||
481 | |||
482 | /* offset is the 16bit word offset */ | ||
483 | static int ssb_pcmcia_sprom_read(struct ssb_bus *bus, u16 offset, u16 *value) | ||
484 | { | ||
485 | int err; | ||
486 | u8 lo, hi; | ||
487 | |||
488 | offset *= 2; /* Make byte offset */ | ||
489 | |||
490 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO, | ||
491 | (offset & 0x00FF)); | ||
492 | if (err) | ||
493 | return err; | ||
494 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI, | ||
495 | (offset & 0xFF00) >> 8); | ||
496 | if (err) | ||
497 | return err; | ||
498 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_READ); | ||
499 | if (err) | ||
500 | return err; | ||
501 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATALO, &lo); | ||
502 | if (err) | ||
503 | return err; | ||
504 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATAHI, &hi); | ||
505 | if (err) | ||
506 | return err; | ||
507 | *value = (lo | (((u16)hi) << 8)); | ||
508 | |||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | /* offset is the 16bit word offset */ | ||
513 | static int ssb_pcmcia_sprom_write(struct ssb_bus *bus, u16 offset, u16 value) | ||
514 | { | ||
515 | int err; | ||
516 | |||
517 | offset *= 2; /* Make byte offset */ | ||
518 | |||
519 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO, | ||
520 | (offset & 0x00FF)); | ||
521 | if (err) | ||
522 | return err; | ||
523 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI, | ||
524 | (offset & 0xFF00) >> 8); | ||
525 | if (err) | ||
526 | return err; | ||
527 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATALO, | ||
528 | (value & 0x00FF)); | ||
529 | if (err) | ||
530 | return err; | ||
531 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATAHI, | ||
532 | (value & 0xFF00) >> 8); | ||
533 | if (err) | ||
534 | return err; | ||
535 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITE); | ||
536 | if (err) | ||
537 | return err; | ||
538 | msleep(20); | ||
539 | |||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | /* Read the SPROM image. bufsize is in 16bit words. */ | ||
544 | static int ssb_pcmcia_sprom_read_all(struct ssb_bus *bus, u16 *sprom) | ||
545 | { | ||
546 | int err, i; | ||
547 | |||
548 | for (i = 0; i < SSB_PCMCIA_SPROM_SIZE; i++) { | ||
549 | err = ssb_pcmcia_sprom_read(bus, i, &sprom[i]); | ||
550 | if (err) | ||
551 | return err; | ||
552 | } | ||
553 | |||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | /* Write the SPROM image. size is in 16bit words. */ | ||
558 | static int ssb_pcmcia_sprom_write_all(struct ssb_bus *bus, const u16 *sprom) | ||
559 | { | ||
560 | int i, err; | ||
561 | bool failed = 0; | ||
562 | size_t size = SSB_PCMCIA_SPROM_SIZE; | ||
563 | |||
564 | ssb_printk(KERN_NOTICE PFX | ||
565 | "Writing SPROM. Do NOT turn off the power! " | ||
566 | "Please stand by...\n"); | ||
567 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEEN); | ||
568 | if (err) { | ||
569 | ssb_printk(KERN_NOTICE PFX | ||
570 | "Could not enable SPROM write access.\n"); | ||
571 | return -EBUSY; | ||
572 | } | ||
573 | ssb_printk(KERN_NOTICE PFX "[ 0%%"); | ||
574 | msleep(500); | ||
575 | for (i = 0; i < size; i++) { | ||
576 | if (i == size / 4) | ||
577 | ssb_printk("25%%"); | ||
578 | else if (i == size / 2) | ||
579 | ssb_printk("50%%"); | ||
580 | else if (i == (size * 3) / 4) | ||
581 | ssb_printk("75%%"); | ||
582 | else if (i % 2) | ||
583 | ssb_printk("."); | ||
584 | err = ssb_pcmcia_sprom_write(bus, i, sprom[i]); | ||
585 | if (err) { | ||
586 | ssb_printk("\n" KERN_NOTICE PFX | ||
587 | "Failed to write to SPROM.\n"); | ||
588 | failed = 1; | ||
589 | break; | ||
590 | } | ||
591 | } | ||
592 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEDIS); | ||
593 | if (err) { | ||
594 | ssb_printk("\n" KERN_NOTICE PFX | ||
595 | "Could not disable SPROM write access.\n"); | ||
596 | failed = 1; | ||
597 | } | ||
598 | msleep(500); | ||
599 | if (!failed) { | ||
600 | ssb_printk("100%% ]\n"); | ||
601 | ssb_printk(KERN_NOTICE PFX "SPROM written.\n"); | ||
602 | } | ||
603 | |||
604 | return failed ? -EBUSY : 0; | ||
605 | } | ||
606 | |||
607 | static int ssb_pcmcia_sprom_check_crc(const u16 *sprom, size_t size) | ||
608 | { | ||
609 | //TODO | ||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | #define GOTO_ERROR_ON(condition, description) do { \ | ||
614 | if (unlikely(condition)) { \ | ||
615 | error_description = description; \ | ||
616 | goto error; \ | ||
617 | } \ | ||
618 | } while (0) | ||
619 | |||
248 | int ssb_pcmcia_get_invariants(struct ssb_bus *bus, | 620 | int ssb_pcmcia_get_invariants(struct ssb_bus *bus, |
249 | struct ssb_init_invariants *iv) | 621 | struct ssb_init_invariants *iv) |
250 | { | 622 | { |
251 | //TODO | 623 | tuple_t tuple; |
252 | random_ether_addr(iv->sprom.il0mac); | 624 | int res; |
625 | unsigned char buf[32]; | ||
626 | struct ssb_sprom *sprom = &iv->sprom; | ||
627 | struct ssb_boardinfo *bi = &iv->boardinfo; | ||
628 | const char *error_description; | ||
629 | |||
630 | memset(sprom, 0xFF, sizeof(*sprom)); | ||
631 | sprom->revision = 1; | ||
632 | sprom->boardflags_lo = 0; | ||
633 | sprom->boardflags_hi = 0; | ||
634 | |||
635 | /* First fetch the MAC address. */ | ||
636 | memset(&tuple, 0, sizeof(tuple)); | ||
637 | tuple.DesiredTuple = CISTPL_FUNCE; | ||
638 | tuple.TupleData = buf; | ||
639 | tuple.TupleDataMax = sizeof(buf); | ||
640 | res = pcmcia_get_first_tuple(bus->host_pcmcia, &tuple); | ||
641 | GOTO_ERROR_ON(res != CS_SUCCESS, "MAC first tpl"); | ||
642 | res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple); | ||
643 | GOTO_ERROR_ON(res != CS_SUCCESS, "MAC first tpl data"); | ||
644 | while (1) { | ||
645 | GOTO_ERROR_ON(tuple.TupleDataLen < 1, "MAC tpl < 1"); | ||
646 | if (tuple.TupleData[0] == CISTPL_FUNCE_LAN_NODE_ID) | ||
647 | break; | ||
648 | res = pcmcia_get_next_tuple(bus->host_pcmcia, &tuple); | ||
649 | GOTO_ERROR_ON(res != CS_SUCCESS, "MAC next tpl"); | ||
650 | res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple); | ||
651 | GOTO_ERROR_ON(res != CS_SUCCESS, "MAC next tpl data"); | ||
652 | } | ||
653 | GOTO_ERROR_ON(tuple.TupleDataLen != ETH_ALEN + 2, "MAC tpl size"); | ||
654 | memcpy(sprom->il0mac, &tuple.TupleData[2], ETH_ALEN); | ||
655 | |||
656 | /* Fetch the vendor specific tuples. */ | ||
657 | memset(&tuple, 0, sizeof(tuple)); | ||
658 | tuple.DesiredTuple = SSB_PCMCIA_CIS; | ||
659 | tuple.TupleData = buf; | ||
660 | tuple.TupleDataMax = sizeof(buf); | ||
661 | res = pcmcia_get_first_tuple(bus->host_pcmcia, &tuple); | ||
662 | GOTO_ERROR_ON(res != CS_SUCCESS, "VEN first tpl"); | ||
663 | res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple); | ||
664 | GOTO_ERROR_ON(res != CS_SUCCESS, "VEN first tpl data"); | ||
665 | while (1) { | ||
666 | GOTO_ERROR_ON(tuple.TupleDataLen < 1, "VEN tpl < 1"); | ||
667 | switch (tuple.TupleData[0]) { | ||
668 | case SSB_PCMCIA_CIS_ID: | ||
669 | GOTO_ERROR_ON((tuple.TupleDataLen != 5) && | ||
670 | (tuple.TupleDataLen != 7), | ||
671 | "id tpl size"); | ||
672 | bi->vendor = tuple.TupleData[1] | | ||
673 | ((u16)tuple.TupleData[2] << 8); | ||
674 | break; | ||
675 | case SSB_PCMCIA_CIS_BOARDREV: | ||
676 | GOTO_ERROR_ON(tuple.TupleDataLen != 2, | ||
677 | "boardrev tpl size"); | ||
678 | sprom->board_rev = tuple.TupleData[1]; | ||
679 | break; | ||
680 | case SSB_PCMCIA_CIS_PA: | ||
681 | GOTO_ERROR_ON(tuple.TupleDataLen != 9, | ||
682 | "pa tpl size"); | ||
683 | sprom->pa0b0 = tuple.TupleData[1] | | ||
684 | ((u16)tuple.TupleData[2] << 8); | ||
685 | sprom->pa0b1 = tuple.TupleData[3] | | ||
686 | ((u16)tuple.TupleData[4] << 8); | ||
687 | sprom->pa0b2 = tuple.TupleData[5] | | ||
688 | ((u16)tuple.TupleData[6] << 8); | ||
689 | sprom->itssi_a = tuple.TupleData[7]; | ||
690 | sprom->itssi_bg = tuple.TupleData[7]; | ||
691 | sprom->maxpwr_a = tuple.TupleData[8]; | ||
692 | sprom->maxpwr_bg = tuple.TupleData[8]; | ||
693 | break; | ||
694 | case SSB_PCMCIA_CIS_OEMNAME: | ||
695 | /* We ignore this. */ | ||
696 | break; | ||
697 | case SSB_PCMCIA_CIS_CCODE: | ||
698 | GOTO_ERROR_ON(tuple.TupleDataLen != 2, | ||
699 | "ccode tpl size"); | ||
700 | sprom->country_code = tuple.TupleData[1]; | ||
701 | break; | ||
702 | case SSB_PCMCIA_CIS_ANTENNA: | ||
703 | GOTO_ERROR_ON(tuple.TupleDataLen != 2, | ||
704 | "ant tpl size"); | ||
705 | sprom->ant_available_a = tuple.TupleData[1]; | ||
706 | sprom->ant_available_bg = tuple.TupleData[1]; | ||
707 | break; | ||
708 | case SSB_PCMCIA_CIS_ANTGAIN: | ||
709 | GOTO_ERROR_ON(tuple.TupleDataLen != 2, | ||
710 | "antg tpl size"); | ||
711 | sprom->antenna_gain.ghz24.a0 = tuple.TupleData[1]; | ||
712 | sprom->antenna_gain.ghz24.a1 = tuple.TupleData[1]; | ||
713 | sprom->antenna_gain.ghz24.a2 = tuple.TupleData[1]; | ||
714 | sprom->antenna_gain.ghz24.a3 = tuple.TupleData[1]; | ||
715 | sprom->antenna_gain.ghz5.a0 = tuple.TupleData[1]; | ||
716 | sprom->antenna_gain.ghz5.a1 = tuple.TupleData[1]; | ||
717 | sprom->antenna_gain.ghz5.a2 = tuple.TupleData[1]; | ||
718 | sprom->antenna_gain.ghz5.a3 = tuple.TupleData[1]; | ||
719 | break; | ||
720 | case SSB_PCMCIA_CIS_BFLAGS: | ||
721 | GOTO_ERROR_ON(tuple.TupleDataLen != 3, | ||
722 | "bfl tpl size"); | ||
723 | sprom->boardflags_lo = tuple.TupleData[1] | | ||
724 | ((u16)tuple.TupleData[2] << 8); | ||
725 | break; | ||
726 | case SSB_PCMCIA_CIS_LEDS: | ||
727 | GOTO_ERROR_ON(tuple.TupleDataLen != 5, | ||
728 | "leds tpl size"); | ||
729 | sprom->gpio0 = tuple.TupleData[1]; | ||
730 | sprom->gpio1 = tuple.TupleData[2]; | ||
731 | sprom->gpio2 = tuple.TupleData[3]; | ||
732 | sprom->gpio3 = tuple.TupleData[4]; | ||
733 | break; | ||
734 | } | ||
735 | res = pcmcia_get_next_tuple(bus->host_pcmcia, &tuple); | ||
736 | if (res == CS_NO_MORE_ITEMS) | ||
737 | break; | ||
738 | GOTO_ERROR_ON(res != CS_SUCCESS, "VEN next tpl"); | ||
739 | res = pcmcia_get_tuple_data(bus->host_pcmcia, &tuple); | ||
740 | GOTO_ERROR_ON(res != CS_SUCCESS, "VEN next tpl data"); | ||
741 | } | ||
742 | |||
253 | return 0; | 743 | return 0; |
744 | error: | ||
745 | ssb_printk(KERN_ERR PFX | ||
746 | "PCMCIA: Failed to fetch device invariants: %s\n", | ||
747 | error_description); | ||
748 | return -ENODEV; | ||
254 | } | 749 | } |
255 | 750 | ||
256 | int ssb_pcmcia_init(struct ssb_bus *bus) | 751 | static ssize_t ssb_pcmcia_attr_sprom_show(struct device *pcmciadev, |
752 | struct device_attribute *attr, | ||
753 | char *buf) | ||
754 | { | ||
755 | struct pcmcia_device *pdev = | ||
756 | container_of(pcmciadev, struct pcmcia_device, dev); | ||
757 | struct ssb_bus *bus; | ||
758 | |||
759 | bus = ssb_pcmcia_dev_to_bus(pdev); | ||
760 | if (!bus) | ||
761 | return -ENODEV; | ||
762 | |||
763 | return ssb_attr_sprom_show(bus, buf, | ||
764 | ssb_pcmcia_sprom_read_all); | ||
765 | } | ||
766 | |||
767 | static ssize_t ssb_pcmcia_attr_sprom_store(struct device *pcmciadev, | ||
768 | struct device_attribute *attr, | ||
769 | const char *buf, size_t count) | ||
770 | { | ||
771 | struct pcmcia_device *pdev = | ||
772 | container_of(pcmciadev, struct pcmcia_device, dev); | ||
773 | struct ssb_bus *bus; | ||
774 | |||
775 | bus = ssb_pcmcia_dev_to_bus(pdev); | ||
776 | if (!bus) | ||
777 | return -ENODEV; | ||
778 | |||
779 | return ssb_attr_sprom_store(bus, buf, count, | ||
780 | ssb_pcmcia_sprom_check_crc, | ||
781 | ssb_pcmcia_sprom_write_all); | ||
782 | } | ||
783 | |||
784 | static DEVICE_ATTR(ssb_sprom, 0600, | ||
785 | ssb_pcmcia_attr_sprom_show, | ||
786 | ssb_pcmcia_attr_sprom_store); | ||
787 | |||
788 | static int ssb_pcmcia_cor_setup(struct ssb_bus *bus, u8 cor) | ||
789 | { | ||
790 | u8 val; | ||
791 | int err; | ||
792 | |||
793 | err = ssb_pcmcia_cfg_read(bus, cor, &val); | ||
794 | if (err) | ||
795 | return err; | ||
796 | val &= ~COR_SOFT_RESET; | ||
797 | val |= COR_FUNC_ENA | COR_IREQ_ENA | COR_LEVEL_REQ; | ||
798 | err = ssb_pcmcia_cfg_write(bus, cor, val); | ||
799 | if (err) | ||
800 | return err; | ||
801 | msleep(40); | ||
802 | |||
803 | return 0; | ||
804 | } | ||
805 | |||
806 | /* Initialize the PCMCIA hardware. This is called on Init and Resume. */ | ||
807 | int ssb_pcmcia_hardware_setup(struct ssb_bus *bus) | ||
257 | { | 808 | { |
258 | conf_reg_t reg; | ||
259 | int err; | 809 | int err; |
260 | 810 | ||
261 | if (bus->bustype != SSB_BUSTYPE_PCMCIA) | 811 | if (bus->bustype != SSB_BUSTYPE_PCMCIA) |
@@ -264,24 +814,45 @@ int ssb_pcmcia_init(struct ssb_bus *bus) | |||
264 | /* Switch segment to a known state and sync | 814 | /* Switch segment to a known state and sync |
265 | * bus->mapped_pcmcia_seg with hardware state. */ | 815 | * bus->mapped_pcmcia_seg with hardware state. */ |
266 | ssb_pcmcia_switch_segment(bus, 0); | 816 | ssb_pcmcia_switch_segment(bus, 0); |
817 | /* Init the COR register. */ | ||
818 | err = ssb_pcmcia_cor_setup(bus, CISREG_COR); | ||
819 | if (err) | ||
820 | return err; | ||
821 | /* Some cards also need this register to get poked. */ | ||
822 | err = ssb_pcmcia_cor_setup(bus, CISREG_COR + 0x80); | ||
823 | if (err) | ||
824 | return err; | ||
267 | 825 | ||
268 | /* Init IRQ routing */ | 826 | return 0; |
269 | reg.Action = CS_READ; | 827 | } |
270 | reg.Function = 0; | 828 | |
271 | if (bus->chip_id == 0x4306) | 829 | void ssb_pcmcia_exit(struct ssb_bus *bus) |
272 | reg.Offset = 0x00; | 830 | { |
273 | else | 831 | if (bus->bustype != SSB_BUSTYPE_PCMCIA) |
274 | reg.Offset = 0x80; | 832 | return; |
275 | err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | 833 | |
276 | if (err != CS_SUCCESS) | 834 | device_remove_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom); |
835 | } | ||
836 | |||
837 | int ssb_pcmcia_init(struct ssb_bus *bus) | ||
838 | { | ||
839 | int err; | ||
840 | |||
841 | if (bus->bustype != SSB_BUSTYPE_PCMCIA) | ||
842 | return 0; | ||
843 | |||
844 | err = ssb_pcmcia_hardware_setup(bus); | ||
845 | if (err) | ||
277 | goto error; | 846 | goto error; |
278 | reg.Action = CS_WRITE; | 847 | |
279 | reg.Value |= 0x04 | 0x01; | 848 | bus->sprom_size = SSB_PCMCIA_SPROM_SIZE; |
280 | err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | 849 | mutex_init(&bus->sprom_mutex); |
281 | if (err != CS_SUCCESS) | 850 | err = device_create_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom); |
851 | if (err) | ||
282 | goto error; | 852 | goto error; |
283 | 853 | ||
284 | return 0; | 854 | return 0; |
285 | error: | 855 | error: |
286 | return -ENODEV; | 856 | ssb_printk(KERN_ERR PFX "Failed to initialize PCMCIA host device\n"); |
857 | return err; | ||
287 | } | 858 | } |
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 21eca2b5118b..ebc32d8fe15f 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h | |||
@@ -81,6 +81,8 @@ 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 int ssb_pcmcia_hardware_setup(struct ssb_bus *bus); | ||
85 | extern void ssb_pcmcia_exit(struct ssb_bus *bus); | ||
84 | extern int ssb_pcmcia_init(struct ssb_bus *bus); | 86 | extern int ssb_pcmcia_init(struct ssb_bus *bus); |
85 | extern const struct ssb_bus_ops ssb_pcmcia_ops; | 87 | extern const struct ssb_bus_ops ssb_pcmcia_ops; |
86 | #else /* CONFIG_SSB_PCMCIAHOST */ | 88 | #else /* CONFIG_SSB_PCMCIAHOST */ |
@@ -99,6 +101,13 @@ static inline int ssb_pcmcia_switch_segment(struct ssb_bus *bus, | |||
99 | { | 101 | { |
100 | return 0; | 102 | return 0; |
101 | } | 103 | } |
104 | static inline int ssb_pcmcia_hardware_setup(struct ssb_bus *bus) | ||
105 | { | ||
106 | return 0; | ||
107 | } | ||
108 | static inline void ssb_pcmcia_exit(struct ssb_bus *bus) | ||
109 | { | ||
110 | } | ||
102 | static inline int ssb_pcmcia_init(struct ssb_bus *bus) | 111 | static inline int ssb_pcmcia_init(struct ssb_bus *bus) |
103 | { | 112 | { |
104 | return 0; | 113 | return 0; |
@@ -113,11 +122,26 @@ extern int ssb_bus_scan(struct ssb_bus *bus, | |||
113 | extern void ssb_iounmap(struct ssb_bus *ssb); | 122 | extern void ssb_iounmap(struct ssb_bus *ssb); |
114 | 123 | ||
115 | 124 | ||
125 | /* sprom.c */ | ||
126 | extern | ||
127 | ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf, | ||
128 | int (*sprom_read)(struct ssb_bus *bus, u16 *sprom)); | ||
129 | extern | ||
130 | ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, | ||
131 | const char *buf, size_t count, | ||
132 | int (*sprom_check_crc)(const u16 *sprom, size_t size), | ||
133 | int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom)); | ||
134 | |||
135 | |||
116 | /* core.c */ | 136 | /* core.c */ |
117 | extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m); | 137 | extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m); |
118 | extern int ssb_devices_freeze(struct ssb_bus *bus); | 138 | extern int ssb_devices_freeze(struct ssb_bus *bus); |
119 | extern int ssb_devices_thaw(struct ssb_bus *bus); | 139 | extern int ssb_devices_thaw(struct ssb_bus *bus); |
120 | extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev); | 140 | extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev); |
141 | int ssb_for_each_bus_call(unsigned long data, | ||
142 | int (*func)(struct ssb_bus *bus, unsigned long data)); | ||
143 | extern struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev); | ||
144 | |||
121 | 145 | ||
122 | /* b43_pci_bridge.c */ | 146 | /* b43_pci_bridge.c */ |
123 | #ifdef CONFIG_SSB_B43_PCI_BRIDGE | 147 | #ifdef CONFIG_SSB_B43_PCI_BRIDGE |