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 |= (reg.Value & 0xF) << 12; 67 reg.Offset = 0x30; 68 err = pcmcia_access_configuration_register(pdev, ®); 69 if (err != CS_SUCCESS) 70 goto error; 71 read_addr |= 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 |= 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 unsigned long flags; 98 99 #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 100 ssb_printk(KERN_INFO PFX 101 "Switching to %s core, index %d\n", 102 ssb_core_name(dev->id.coreid), 103 dev->core_index); 104 #endif 105 106 spin_lock_irqsave(&bus->bar_lock, flags); 107 err = ssb_pcmcia_switch_coreidx(bus, dev->core_index); 108 if (!err) 109 bus->mapped_device = dev; 110 spin_unlock_irqrestore(&bus->bar_lock, flags); 111 112 return err; 113 } 114 115 int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) 116 { 117 int attempts = 0; 118 unsigned long flags; 119 conf_reg_t reg; 120 int res, err = 0; 121 122 SSB_WARN_ON((seg != 0) && (seg != 1)); 123 reg.Offset = 0x34; 124 reg.Function = 0; 125 spin_lock_irqsave(&bus->bar_lock, flags); 126 while (1) { 127 reg.Action = CS_WRITE; 128 reg.Value = seg; 129 res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); 130 if (unlikely(res != CS_SUCCESS)) 131 goto error; 132 reg.Value = 0xFF; 133 reg.Action = CS_READ; 134 res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); 135 if (unlikely(res != CS_SUCCESS)) 136 goto error; 137 138 if (reg.Value == seg) 139 break; 140 141 if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES)) 142 goto error; 143 udelay(10); 144 } 145 bus->mapped_pcmcia_seg = seg; 146 out_unlock: 147 spin_unlock_irqrestore(&bus->bar_lock, flags); 148 return err; 149 error: 150 ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); 151 err = -ENODEV; 152 goto out_unlock; 153 } 154 155 /* These are the main device register access functions. 156 * do_select_core is inline to have the likely hotpath inline. 157 * All unlikely codepaths are out-of-line. */ 158 static inline int do_select_core(struct ssb_bus *bus, 159 struct ssb_device *dev, 160 u16 *offset) 161 { 162 int err; 163 u8 need_seg = (*offset >= 0x800) ? 1 : 0; 164 165 if (unlikely(dev != bus->mapped_device)) { 166 err = ssb_pcmcia_switch_core(bus, dev); 167 if (unlikely(err)) 168 return err; 169 } 170 if (unlikely(need_seg != bus->mapped_pcmcia_seg)) { 171 err = ssb_pcmcia_switch_segment(bus, need_seg); 172 if (unlikely(err)) 173 return err; 174 } 175 if (need_seg == 1) 176 *offset -= 0x800; 177 178 return 0; 179 } 180 181 static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) 182 { 183 struct ssb_bus *bus = dev->bus; 184 u16 x; 185 186 if (unlikely(do_select_core(bus, dev, &offset))) 187 return 0xFFFF; 188 x = readw(bus->mmio + offset); 189 190 return x; 191 } 192 193 static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) 194 { 195 struct ssb_bus *bus = dev->bus; 196 u32 x; 197 198 if (unlikely(do_select_core(bus, dev, &offset))) 199 return 0xFFFFFFFF; 200 x = readl(bus->mmio + offset); 201 202 return x; 203 } 204 205 static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) 206 { 207 struct ssb_bus *bus = dev->bus; 208 209 if (unlikely(do_select_core(bus, dev, &offset))) 210 return; 211 writew(value, bus->mmio + offset); 212 } 213 214 static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) 215 { 216 struct ssb_bus *bus = dev->bus; 217 218 if (unlikely(do_select_core(bus, dev, &offset))) 219 return; 220 readw(bus->mmio + offset); 221 writew(value >> 16, bus->mmio + offset + 2); 222 readw(bus->mmio + offset); 223 writew(value, bus->mmio + offset); 224 } 225 226 /* Not "static", as it's used in main.c */ 227 const struct ssb_bus_ops ssb_pcmcia_ops = { 228 .read16 = ssb_pcmcia_read16, 229 .read32 = ssb_pcmcia_read32, 230 .write16 = ssb_pcmcia_write16, 231 .write32 = ssb_pcmcia_write32, 232 }; 233 234 int ssb_pcmcia_get_invariants(struct ssb_bus *bus, 235 struct ssb_init_invariants *iv) 236 { 237 //TODO 238 return 0; 239 } 240 241 int ssb_pcmcia_init(struct ssb_bus *bus) 242 { 243 conf_reg_t reg; 244 int err; 245 246 if (bus->bustype != SSB_BUSTYPE_PCMCIA) 247 return 0; 248 249 /* Switch segment to a known state and sync 250 * bus->mapped_pcmcia_seg with hardware state. */ 251 ssb_pcmcia_switch_segment(bus, 0); 252 253 /* Init IRQ routing */ 254 reg.Action = CS_READ; 255 reg.Function = 0; 256 if (bus->chip_id == 0x4306) 257 reg.Offset = 0x00; 258 else 259 reg.Offset = 0x80; 260 err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); 261 if (err != CS_SUCCESS) 262 goto error; 263 reg.Action = CS_WRITE; 264 reg.Value |= 0x04 | 0x01; 265 err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); 266 if (err != CS_SUCCESS) 267 goto error; 268 269 return 0; 270 error: 271 return -ENODEV; 272 } 273