1 /* 2 * Sonics Silicon Backplane 3 * PCMCIA-Hostbus related functions 4 * 5 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 6 * Copyright 2007 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/delay.h> 13 #include <linux/io.h> 14 15 #include <pcmcia/cs_types.h> 16 #include <pcmcia/cs.h> 17 #include <pcmcia/cistpl.h> 18 #include <pcmcia/ciscode.h> 19 #include <pcmcia/ds.h> 20 #include <pcmcia/cisreg.h> 21 22 #include "ssb_private.h" 23 24 25 /* Define the following to 1 to enable a printk on each coreswitch. */ 26 #define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 0 27 28 29 int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, 30 u8 coreidx) 31 { 32 struct pcmcia_device *pdev = bus->host_pcmcia; 33 int err; 34 int attempts = 0; 35 u32 cur_core; 36 conf_reg_t reg; 37 u32 addr; 38 u32 read_addr; 39 40 addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; 41 while (1) { 42 reg.Action = CS_WRITE; 43 reg.Offset = 0x2E; 44 reg.Value = (addr & 0x0000F000) >> 12; 45 err = pcmcia_access_configuration_register(pdev, ®); 46 if (err != CS_SUCCESS) 47 goto error; 48 reg.Offset = 0x30; 49 reg.Value = (addr & 0x00FF0000) >> 16; 50 err = pcmcia_access_configuration_register(pdev, ®); 51 if (err != CS_SUCCESS) 52 goto error; 53 reg.Offset = 0x32; 54 reg.Value = (addr & 0xFF000000) >> 24; 55 err = pcmcia_access_configuration_register(pdev, ®); 56 if (err != CS_SUCCESS) 57 goto error; 58 59 read_addr = 0; 60 61 reg.Action = CS_READ; 62 reg.Offset = 0x2E; 63 err = pcmcia_access_configuration_register(pdev, ®); 64 if (err != CS_SUCCESS) 65 goto error; 66 read_addr |= ((u32)(reg.Value & 0x0F)) << 12; 67 reg.Offset = 0x30; 68 err = pcmcia_access_configuration_register(pdev, ®); 69 if (err != CS_SUCCESS) 70 goto error; 71 read_addr |= ((u32)reg.Value) << 16; 72 reg.Offset = 0x32; 73 err = pcmcia_access_configuration_register(pdev, ®); 74 if (err != CS_SUCCESS) 75 goto error; 76 read_addr |= ((u32)reg.Value) << 24; 77 78 cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE; 79 if (cur_core == coreidx) 80 break; 81 82 if (attempts++ > SSB_BAR0_MAX_RETRIES) 83 goto error; 84 udelay(10); 85 } 86 87 return 0; 88 error: 89 ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); 90 return -ENODEV; 91 } 92 93 int ssb_pcmcia_switch_core(struct ssb_bus *bus, 94 struct ssb_device *dev) 95 { 96 int err; 97 98 #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 99 ssb_printk(KERN_INFO PFX 100 "Switching to %s core, index %d\n", 101 ssb_core_name(dev->id.coreid), 102 dev->core_index); 103 #endif 104 105 err = ssb_pcmcia_switch_coreidx(bus, dev->core_index); 106 if (!err) 107 bus->mapped_device = dev; 108 109 return err; 110 } 111 112 int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) 113 { 114 int attempts = 0; 115 conf_reg_t reg; 116 int res; 117 118 SSB_WARN_ON((seg != 0) && (seg != 1)); 119 reg.Offset = 0x34; 120 reg.Function = 0; 121 while (1) { 122 reg.Action = CS_WRITE; 123 reg.Value = seg; 124 res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); 125 if (unlikely(res != CS_SUCCESS)) 126 goto error; 127 reg.Value = 0xFF; 128 reg.Action = CS_READ; 129 res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); 130 if (unlikely(res != CS_SUCCESS)) 131 goto error; 132 133 if (reg.Value == seg) 134 break; 135 136 if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES)) 137 goto error; 138 udelay(10); 139 } 140 bus->mapped_pcmcia_seg = seg; 141 142 return 0; 143 error: 144 ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); 145 return -ENODEV; 146 } 147 148 static int select_core_and_segment(struct ssb_device *dev, 149 u16 *offset) 150 { 151 struct ssb_bus *bus = dev->bus; 152 int err; 153 u8 need_segment; 154 155 if (*offset >= 0x800) { 156 *offset -= 0x800; 157 need_segment = 1; 158 } else 159 need_segment = 0; 160 161 if (unlikely(dev != bus->mapped_device)) { 162 err = ssb_pcmcia_switch_core(bus, dev); 163 if (unlikely(err)) 164 return err; 165 } 166 if (unlikely(need_segment != bus->mapped_pcmcia_seg)) { 167 err = ssb_pcmcia_switch_segment(bus, need_segment); 168 if (unlikely(err)) 169 return err; 170 } 171 172 return 0; 173 } 174 175 static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) 176 { 177 struct ssb_bus *bus = dev->bus; 178 unsigned long flags; 179 int err; 180 u16 value = 0xFFFF; 181 182 spin_lock_irqsave(&bus->bar_lock, flags); 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); 187 188 return value; 189 } 190 191 static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) 192 { 193 struct ssb_bus *bus = dev->bus; 194 unsigned long flags; 195 int err; 196 u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF; 197 198 spin_lock_irqsave(&bus->bar_lock, flags); 199 err = select_core_and_segment(dev, &offset); 200 if (likely(!err)) { 201 lo = readw(bus->mmio + offset); 202 hi = readw(bus->mmio + offset + 2); 203 } 204 spin_unlock_irqrestore(&bus->bar_lock, flags); 205 206 return (lo | (hi << 16)); 207 } 208 209 static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) 210 { 211 struct ssb_bus *bus = dev->bus; 212 unsigned long flags; 213 int err; 214 215 spin_lock_irqsave(&bus->bar_lock, flags); 216 err = select_core_and_segment(dev, &offset); 217 if (likely(!err)) 218 writew(value, bus->mmio + offset); 219 mmiowb(); 220 spin_unlock_irqrestore(&bus->bar_lock, flags); 221 } 222 223 static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) 224 { 225 struct ssb_bus *bus = dev->bus; 226 unsigned long flags; 227 int err; 228 229 spin_lock_irqsave(&bus->bar_lock, flags); 230 err = select_core_and_segment(dev, &offset); 231 if (likely(!err)) { 232 writew((value & 0x0000FFFF), bus->mmio + offset); 233 writew(((value & 0xFFFF0000) >> 16), bus->mmio + offset + 2); 234 } 235 mmiowb(); 236 spin_unlock_irqrestore(&bus->bar_lock, flags); 237 } 238 239 /* Not "static", as it's used in main.c */ 240 const struct ssb_bus_ops ssb_pcmcia_ops = { 241 .read16 = ssb_pcmcia_read16, 242 .read32 = ssb_pcmcia_read32, 243 .write16 = ssb_pcmcia_write16, 244 .write32 = ssb_pcmcia_write32, 245 }; 246 247 #include <linux/etherdevice.h> 248 int ssb_pcmcia_get_invariants(struct ssb_bus *bus, 249 struct ssb_init_invariants *iv) 250 { 251 //TODO 252 random_ether_addr(iv->sprom.il0mac); 253 return 0; 254 } 255 256 int ssb_pcmcia_init(struct ssb_bus *bus) 257 { 258 conf_reg_t reg; 259 int err; 260 261 if (bus->bustype != SSB_BUSTYPE_PCMCIA) 262 return 0; 263 264 /* Switch segment to a known state and sync 265 * bus->mapped_pcmcia_seg with hardware state. */ 266 ssb_pcmcia_switch_segment(bus, 0); 267 268 /* Init IRQ routing */ 269 reg.Action = CS_READ; 270 reg.Function = 0; 271 if (bus->chip_id == 0x4306) 272 reg.Offset = 0x00; 273 else 274 reg.Offset = 0x80; 275 err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); 276 if (err != CS_SUCCESS) 277 goto error; 278 reg.Action = CS_WRITE; 279 reg.Value |= 0x04 | 0x01; 280 err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); 281 if (err != CS_SUCCESS) 282 goto error; 283 284 return 0; 285 error: 286 return -ENODEV; 287 } 288