aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ssb/pcmcia.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ssb/pcmcia.c')
-rw-r--r--drivers/ssb/pcmcia.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c
new file mode 100644
index 000000000000..7c773603b402
--- /dev/null
+++ b/drivers/ssb/pcmcia.c
@@ -0,0 +1,271 @@
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
28int 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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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, &reg);
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;
87error:
88 ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx);
89 return -ENODEV;
90}
91
92int 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
114int 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, &reg);
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, &reg);
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;
145out_unlock:
146 spin_unlock_irqrestore(&bus->bar_lock, flags);
147 return err;
148error:
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. */
157static 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
180static 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
192static 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
204static 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
213static 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 */
226const 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
233int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
234 struct ssb_init_invariants *iv)
235{
236 //TODO
237 return 0;
238}
239
240int 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, &reg);
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, &reg);
265 if (err != CS_SUCCESS)
266 goto error;
267
268 return 0;
269error:
270 return -ENODEV;
271}