sbus.c revision 1.13 1 /* $NetBSD: sbus.c,v 1.13 2016/07/18 21:54:12 maya Exp $ */
2
3 /*-
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by UCHIYAMA Yasushi.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * PlayStation 2 internal PCMCIA/USB interface unit.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: sbus.c,v 1.13 2016/07/18 21:54:12 maya Exp $");
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41
42 #include <machine/bootinfo.h>
43 #include <machine/autoconf.h>
44
45 #include <playstation2/playstation2/interrupt.h>
46
47 #include <playstation2/ee/eevar.h>
48 #include <playstation2/ee/intcvar.h>
49 #include <playstation2/dev/sbusvar.h>
50 #include <playstation2/dev/sbusreg.h>
51
52 #ifdef DEBUG
53 #define STATIC
54 #else
55 #define STATIC static
56 #endif
57
58 STATIC void sbus_type2_pcmcia_intr_clear(void);
59 STATIC void sbus_type2_pcmcia_intr_enable(void);
60 STATIC void sbus_type2_pcmcia_intr_disable(void);
61 STATIC void sbus_type2_pcmcia_intr_reinstall(void);
62 STATIC void sbus_type3_pcmcia_intr_clear(void);
63 STATIC void sbus_type3_pcmcia_intr_enable(void);
64 STATIC void sbus_type3_pcmcia_intr_disable(void);
65 STATIC void sbus_type3_pcmcia_intr_reinstall(void);
66 STATIC int sbus_spurious_intr(void *);
67
68 STATIC void (*sbus_pcmcia_intr_clear)(void);
69 STATIC void (*sbus_pcmcia_intr_enable)(void);
70 STATIC void (*sbus_pcmcia_intr_disable)(void);
71 STATIC void (*sbus_pcmcia_intr_reinstall)(void);
72
73 STATIC int (*sbus_pcmcia_intr)(void *) = sbus_spurious_intr;
74 STATIC void *sbus_pcmcia_context;
75 STATIC int (*sbus_usb_intr)(void *) = sbus_spurious_intr;
76 STATIC void *sbus_usb_context;
77
78 STATIC void sbus_init(int);
79 STATIC int sbus_intr(void *);
80
81 STATIC int sbus_match(struct device *, cfdata_t, void *);
82 STATIC void sbus_attach(struct device *, struct device *, void *);
83 STATIC int sbus_search(struct device *, cfdata_t,
84 const int *, void *);
85 STATIC int sbus_print(void *, const char *);
86
87 CFATTACH_DECL(sbus, sizeof (struct device),
88 sbus_match, sbus_attach, NULL, NULL);
89
90 extern struct cfdriver sbus_cd;
91 STATIC int __sbus_attached;
92
93 int
94 sbus_match(struct device *parent, cfdata_t cf, void *aux)
95 {
96 struct mainbus_attach_args *ma = aux;
97
98 if (strcmp(ma->ma_name, sbus_cd.cd_name) != 0)
99 return (0);
100
101 return (!__sbus_attached);
102 }
103
104 void
105 sbus_attach(struct device *parent, struct device *self, void *aux)
106 {
107 int type = BOOTINFO_REF(BOOTINFO_PCMCIA_TYPE);
108
109 printf(": controller type %d\n", type);
110
111 /* Initialize SBUS controller */
112 sbus_init(type);
113
114 config_search_ia(sbus_search, self, "sbus", 0);
115 }
116
117 int
118 sbus_search(struct device *parent, cfdata_t cf,
119 const int *ldesc, void *aux)
120 {
121 struct sbus_attach_args sa;
122
123 if (config_match(parent, cf, &sa))
124 config_attach(parent, cf, &sa, sbus_print);
125
126 return (0);
127 }
128
129 int
130 sbus_print(void *aux, const char *pnp)
131 {
132
133 return (pnp ? QUIET : UNCONF);
134 }
135
136 void
137 sbus_init(int type)
138 {
139 /* install model dependent hook */
140 #define SET_PCMCIA_INTR_OPS(x) \
141 sbus_pcmcia_intr_clear = sbus_type##x##_pcmcia_intr_clear; \
142 sbus_pcmcia_intr_enable = sbus_type##x##_pcmcia_intr_enable; \
143 sbus_pcmcia_intr_disable = sbus_type##x##_pcmcia_intr_disable; \
144 sbus_pcmcia_intr_reinstall = sbus_type##x##_pcmcia_intr_reinstall
145
146 switch (type) {
147 default:
148 panic("unknown pcmcia controller type = %d", type);
149 break;
150 case 0:
151 /* FALLTHROUGH */
152 case 1:
153 /* FALLTHROUGH */
154 case 2:
155 SET_PCMCIA_INTR_OPS(2);
156 break;
157 case 3:
158 SET_PCMCIA_INTR_OPS(3);
159 break;
160 }
161 #undef SET_PCMCIA_INTR_OPS
162 /* disable interrupt */
163 (*sbus_pcmcia_intr_disable)();
164
165 /* clear interrupt */
166 (*sbus_pcmcia_intr_clear)();
167 _reg_write_4(SBUS_SMFLG_REG, SMFLG_PCMCIA_INT);
168 _reg_write_4(SBUS_SMFLG_REG, SMFLG_USB_INT);
169
170 /* connect to INTC */
171 intc_intr_establish(I_CH1_SBUS, IPL_BIO, sbus_intr, 0);
172 }
173
174 void *
175 sbus_intr_establish(enum sbus_irq irq, int (*ih_func)(void *), void *ih_arg)
176 {
177 switch (irq) {
178 default:
179 panic("unknown IRQ");
180 break;
181 case SBUS_IRQ_PCMCIA:
182 sbus_pcmcia_intr = ih_func;
183 sbus_pcmcia_context = ih_arg;
184 (*sbus_pcmcia_intr_enable)();
185 break;
186 case SBUS_IRQ_USB:
187 sbus_usb_intr = ih_func;
188 sbus_usb_context = ih_arg;
189 break;
190 }
191
192 return (void *)irq;
193 }
194
195 void
196 sbus_intr_disestablish(void *handle)
197 {
198 int irq = (int)handle;
199
200 switch (irq) {
201 default:
202 panic("unknown IRQ");
203 break;
204 case SBUS_IRQ_PCMCIA:
205 sbus_pcmcia_intr = sbus_spurious_intr;
206 (*sbus_pcmcia_intr_disable)();
207 break;
208 case SBUS_IRQ_USB:
209 sbus_usb_intr = sbus_spurious_intr;
210 break;
211 }
212 }
213
214 int
215 sbus_intr(void *arg)
216 {
217 u_int32_t stat;
218
219 _playstation2_evcnt.sbus.ev_count++;
220 stat = _reg_read_4(SBUS_SMFLG_REG);
221
222 if (stat & SMFLG_PCMCIA_INT) {
223 (*sbus_pcmcia_intr_clear)();
224 _reg_write_4(SBUS_SMFLG_REG, SMFLG_PCMCIA_INT);
225 (*sbus_pcmcia_intr)(sbus_pcmcia_context);
226 }
227
228 if (stat & SMFLG_USB_INT) {
229 _reg_write_4(SBUS_SMFLG_REG, SMFLG_USB_INT);
230 (*sbus_usb_intr)(sbus_usb_context);
231 }
232
233 (*sbus_pcmcia_intr_reinstall)();
234
235 return (1);
236 }
237
238 int
239 sbus_spurious_intr(void *arg)
240 {
241
242 printf("spurious interrupt.\n");
243
244 return (1);
245 }
246
247 /* SCPH-18000 */
248 void
249 sbus_type2_pcmcia_intr_clear()
250 {
251
252 if (_reg_read_2(SBUS_PCMCIA_CSC1_REG16) & 0x080)
253 _reg_write_2(SBUS_PCMCIA_CSC1_REG16, 0xffff);
254 }
255
256 void
257 sbus_type2_pcmcia_intr_enable()
258 {
259
260 _reg_write_2(SBUS_PCMCIA_TIMR_REG16, 0);
261 }
262
263 void
264 sbus_type2_pcmcia_intr_disable()
265 {
266
267 _reg_write_2(SBUS_PCMCIA_TIMR_REG16, 1);
268 }
269
270 void
271 sbus_type2_pcmcia_intr_reinstall()
272 {
273 u_int16_t r = _reg_read_2(SBUS_PCMCIA_TIMR_REG16);
274
275 _reg_write_2(SBUS_PCMCIA_TIMR_REG16, 1);
276 _reg_write_2(SBUS_PCMCIA_TIMR_REG16, r);
277 }
278
279 /* SCPH-30000/35000 */
280 void
281 sbus_type3_pcmcia_intr_clear()
282 {
283 /* nothing */
284 }
285
286 void
287 sbus_type3_pcmcia_intr_enable()
288 {
289
290 _reg_write_2(SBUS_PCMCIA3_TIMR_REG16, 0);
291 }
292
293 void
294 sbus_type3_pcmcia_intr_disable()
295 {
296
297 _reg_write_2(SBUS_PCMCIA3_TIMR_REG16, 1);
298 }
299
300 void
301 sbus_type3_pcmcia_intr_reinstall()
302 {
303 u_int16_t r = _reg_read_2(SBUS_PCMCIA3_TIMR_REG16);
304
305 _reg_write_2(SBUS_PCMCIA3_TIMR_REG16, 1);
306 _reg_write_2(SBUS_PCMCIA3_TIMR_REG16, r);
307 }
308