1 /* $NetBSD: sbus.c,v 1.12 2014/03/31 11:25:49 martin 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.12 2014/03/31 11:25:49 martin 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 *, struct cfdata *, void *); 82 STATIC void sbus_attach(struct device *, struct device *, void *); 83 STATIC int sbus_search(struct device *, struct cfdata *, 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, struct cfdata *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, struct cfdata *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