diff options
| author | Michael Buesch <mb@bu3sch.de> | 2007-12-22 16:01:36 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:09:16 -0500 |
| commit | 993e1c780b323736a2cdc24564f35e80ce8d3337 (patch) | |
| tree | 6dd8e11093bc3820351fd83bebfecc6f0ae33fa6 | |
| parent | f3dd3fcc2c79b950801641075b33b86acc372d9b (diff) | |
ssb: Fix PCMCIA lowlevel register access
This fixes lowlevel register access for PCMCIA based devices.
The patch also adds a temporary workaround for the device mac address.
It simply adds generation of a random address. The real SPROM extraction
will follow in another patch.
The temporary workaround will be removed then, but for now it's OK.
Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
| -rw-r--r-- | drivers/ssb/pcmcia.c | 71 | ||||
| -rw-r--r-- | include/linux/ssb/ssb.h | 3 |
2 files changed, 45 insertions, 29 deletions
diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c index bb44a76b3eb5..46816cda8b98 100644 --- a/drivers/ssb/pcmcia.c +++ b/drivers/ssb/pcmcia.c | |||
| @@ -94,7 +94,6 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus, | |||
| 94 | struct ssb_device *dev) | 94 | struct ssb_device *dev) |
| 95 | { | 95 | { |
| 96 | int err; | 96 | int err; |
| 97 | unsigned long flags; | ||
| 98 | 97 | ||
| 99 | #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG | 98 | #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG |
| 100 | ssb_printk(KERN_INFO PFX | 99 | ssb_printk(KERN_INFO PFX |
| @@ -103,11 +102,9 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus, | |||
| 103 | dev->core_index); | 102 | dev->core_index); |
| 104 | #endif | 103 | #endif |
| 105 | 104 | ||
| 106 | spin_lock_irqsave(&bus->bar_lock, flags); | ||
| 107 | err = ssb_pcmcia_switch_coreidx(bus, dev->core_index); | 105 | err = ssb_pcmcia_switch_coreidx(bus, dev->core_index); |
| 108 | if (!err) | 106 | if (!err) |
| 109 | bus->mapped_device = dev; | 107 | bus->mapped_device = dev; |
| 110 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
| 111 | 108 | ||
| 112 | return err; | 109 | return err; |
| 113 | } | 110 | } |
| @@ -115,14 +112,12 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus, | |||
| 115 | int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) | 112 | int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) |
| 116 | { | 113 | { |
| 117 | int attempts = 0; | 114 | int attempts = 0; |
| 118 | unsigned long flags; | ||
| 119 | conf_reg_t reg; | 115 | conf_reg_t reg; |
| 120 | int res, err = 0; | 116 | int res; |
| 121 | 117 | ||
| 122 | SSB_WARN_ON((seg != 0) && (seg != 1)); | 118 | SSB_WARN_ON((seg != 0) && (seg != 1)); |
| 123 | reg.Offset = 0x34; | 119 | reg.Offset = 0x34; |
| 124 | reg.Function = 0; | 120 | reg.Function = 0; |
| 125 | spin_lock_irqsave(&bus->bar_lock, flags); | ||
| 126 | while (1) { | 121 | while (1) { |
| 127 | reg.Action = CS_WRITE; | 122 | reg.Action = CS_WRITE; |
| 128 | reg.Value = seg; | 123 | reg.Value = seg; |
| @@ -143,13 +138,11 @@ int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) | |||
| 143 | udelay(10); | 138 | udelay(10); |
| 144 | } | 139 | } |
| 145 | bus->mapped_pcmcia_seg = seg; | 140 | bus->mapped_pcmcia_seg = seg; |
| 146 | out_unlock: | 141 | |
| 147 | spin_unlock_irqrestore(&bus->bar_lock, flags); | 142 | return 0; |
| 148 | return err; | ||
| 149 | error: | 143 | error: |
| 150 | ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); | 144 | ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); |
| 151 | err = -ENODEV; | 145 | return -ENODEV; |
| 152 | goto out_unlock; | ||
| 153 | } | 146 | } |
| 154 | 147 | ||
| 155 | static int select_core_and_segment(struct ssb_device *dev, | 148 | static int select_core_and_segment(struct ssb_device *dev, |
| @@ -182,22 +175,33 @@ static int select_core_and_segment(struct ssb_device *dev, | |||
| 182 | static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) | 175 | static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) |
| 183 | { | 176 | { |
| 184 | struct ssb_bus *bus = dev->bus; | 177 | struct ssb_bus *bus = dev->bus; |
| 178 | unsigned long flags; | ||
| 179 | int err; | ||
| 180 | u16 value = 0xFFFF; | ||
| 185 | 181 | ||
| 186 | if (unlikely(select_core_and_segment(dev, &offset))) | 182 | spin_lock_irqsave(&bus->bar_lock, flags); |
| 187 | return 0xFFFF; | 183 | err = select_core_and_segment(dev, &offset); |
| 184 | if (likely(!err)) | ||
| 185 | value = readw(bus->mmio + offset); | ||
| 186 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
| 188 | 187 | ||
| 189 | return readw(bus->mmio + offset); | 188 | return value; |
| 190 | } | 189 | } |
| 191 | 190 | ||
| 192 | static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) | 191 | static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) |
| 193 | { | 192 | { |
| 194 | struct ssb_bus *bus = dev->bus; | 193 | struct ssb_bus *bus = dev->bus; |
| 195 | u32 lo, hi; | 194 | unsigned long flags; |
| 195 | int err; | ||
| 196 | u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF; | ||
| 196 | 197 | ||
| 197 | if (unlikely(select_core_and_segment(dev, &offset))) | 198 | spin_lock_irqsave(&bus->bar_lock, flags); |
| 198 | return 0xFFFFFFFF; | 199 | err = select_core_and_segment(dev, &offset); |
| 199 | lo = readw(bus->mmio + offset); | 200 | if (likely(!err)) { |
| 200 | hi = readw(bus->mmio + offset + 2); | 201 | lo = readw(bus->mmio + offset); |
| 202 | hi = readw(bus->mmio + offset + 2); | ||
| 203 | } | ||
| 204 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
| 201 | 205 | ||
| 202 | return (lo | (hi << 16)); | 206 | return (lo | (hi << 16)); |
| 203 | } | 207 | } |
| @@ -205,22 +209,31 @@ static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) | |||
| 205 | static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) | 209 | static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) |
| 206 | { | 210 | { |
| 207 | struct ssb_bus *bus = dev->bus; | 211 | struct ssb_bus *bus = dev->bus; |
| 212 | unsigned long flags; | ||
| 213 | int err; | ||
| 208 | 214 | ||
| 209 | if (unlikely(select_core_and_segment(dev, &offset))) | 215 | spin_lock_irqsave(&bus->bar_lock, flags); |
| 210 | return; | 216 | err = select_core_and_segment(dev, &offset); |
| 211 | writew(value, bus->mmio + offset); | 217 | if (likely(!err)) |
| 218 | writew(value, bus->mmio + offset); | ||
| 219 | mmiowb(); | ||
| 220 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
| 212 | } | 221 | } |
| 213 | 222 | ||
| 214 | static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) | 223 | static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) |
| 215 | { | 224 | { |
| 216 | struct ssb_bus *bus = dev->bus; | 225 | struct ssb_bus *bus = dev->bus; |
| 226 | unsigned long flags; | ||
| 227 | int err; | ||
| 217 | 228 | ||
| 218 | if (unlikely(select_core_and_segment(dev, &offset))) | 229 | spin_lock_irqsave(&bus->bar_lock, flags); |
| 219 | return; | 230 | err = select_core_and_segment(dev, &offset); |
| 220 | writeb((value & 0xFF000000) >> 24, bus->mmio + offset + 3); | 231 | if (likely(!err)) { |
| 221 | writeb((value & 0x00FF0000) >> 16, bus->mmio + offset + 2); | 232 | writew((value & 0x0000FFFF), bus->mmio + offset); |
| 222 | writeb((value & 0x0000FF00) >> 8, bus->mmio + offset + 1); | 233 | writew(((value & 0xFFFF0000) >> 16), bus->mmio + offset + 2); |
| 223 | writeb((value & 0x000000FF) >> 0, bus->mmio + offset + 0); | 234 | } |
| 235 | mmiowb(); | ||
| 236 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
| 224 | } | 237 | } |
| 225 | 238 | ||
| 226 | /* Not "static", as it's used in main.c */ | 239 | /* Not "static", as it's used in main.c */ |
| @@ -231,10 +244,12 @@ const struct ssb_bus_ops ssb_pcmcia_ops = { | |||
| 231 | .write32 = ssb_pcmcia_write32, | 244 | .write32 = ssb_pcmcia_write32, |
| 232 | }; | 245 | }; |
| 233 | 246 | ||
| 247 | #include <linux/etherdevice.h> | ||
| 234 | int ssb_pcmcia_get_invariants(struct ssb_bus *bus, | 248 | int ssb_pcmcia_get_invariants(struct ssb_bus *bus, |
| 235 | struct ssb_init_invariants *iv) | 249 | struct ssb_init_invariants *iv) |
| 236 | { | 250 | { |
| 237 | //TODO | 251 | //TODO |
| 252 | random_ether_addr(iv->sprom.il0mac); | ||
| 238 | return 0; | 253 | return 0; |
| 239 | } | 254 | } |
| 240 | 255 | ||
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 0eaa98424f0a..cacbae531945 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h | |||
| @@ -231,7 +231,8 @@ struct ssb_bus { | |||
| 231 | struct ssb_device *mapped_device; | 231 | struct ssb_device *mapped_device; |
| 232 | /* Currently mapped PCMCIA segment. (bustype == SSB_BUSTYPE_PCMCIA only) */ | 232 | /* Currently mapped PCMCIA segment. (bustype == SSB_BUSTYPE_PCMCIA only) */ |
| 233 | u8 mapped_pcmcia_seg; | 233 | u8 mapped_pcmcia_seg; |
| 234 | /* Lock for core and segment switching. */ | 234 | /* Lock for core and segment switching. |
| 235 | * On PCMCIA-host busses this is used to protect the whole MMIO access. */ | ||
| 235 | spinlock_t bar_lock; | 236 | spinlock_t bar_lock; |
| 236 | 237 | ||
| 237 | /* The bus this backplane is running on. */ | 238 | /* The bus this backplane is running on. */ |
