xref: /linux/drivers/ssb/pcmcia.c (revision 2b8232ce512105e28453f301d1510de8363bccd1)
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, &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;
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, &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;
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, &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;
269 error:
270 	return -ENODEV;
271 }
272