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