1 /* $OpenBSD: ssio.c,v 1.8 2022/03/13 08:04:38 mpi Exp $ */
2
3 /*
4 * Copyright (c) 2007 Mark Kettenis
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /*
20 * Driver for the National Semiconductor PC87560 Legacy I/O chip.
21 */
22
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26
27 #include <machine/bus.h>
28 #include <machine/iomod.h>
29
30 #include <dev/pci/pcireg.h>
31 #include <dev/pci/pcivar.h>
32 #include <dev/pci/pcidevs.h>
33 #include <dev/pci/pciidereg.h>
34
35 #include <hppa/dev/ssiovar.h>
36
37 #include "ukbd.h"
38 #if NUKBD > 0
39 #include <dev/usb/ohcireg.h>
40 #include <dev/usb/ukbdvar.h>
41 #endif
42
43 /* PCI config space. */
44 #define SSIO_PCI_DMA_RC2 0x64
45 #define SSIO_PCI_INT_TC1 0x67
46 #define SSIO_PCI_INT_TC2 0x68
47 #define SSIO_PCI_INT_RC1 0x69
48 #define SSIO_PCI_INT_RC2 0x6a
49 #define SSIO_PCI_INT_RC3 0x6b
50 #define SSIO_PCI_INT_RC4 0x6c
51 #define SSIO_PCI_INT_RC5 0x6d
52 #define SSIO_PCI_INT_RC6 0x6e
53 #define SSIO_PCI_INT_RC7 0x6f
54 #define SSIO_PCI_INT_RC8 0x70
55 #define SSIO_PCI_INT_RC9 0x71
56 #define SSIO_PCI_SP1BAR 0x94
57 #define SSIO_PCI_SP2BAR 0x98
58 #define SSIO_PCI_PPBAR 0x9c
59
60 #define SSIO_PCI_INT_TC1_MASK 0xff
61 #define SSIO_PCI_INT_TC1_SHIFT 24
62
63 #define SSIO_PCI_INT_TC2_MASK 0xff
64 #define SSIO_PCI_INT_TC2_SHIFT 0
65
66 #define SSIO_PCI_INT_RC1_MASK 0xff
67 #define SSIO_PCI_INT_RC1_SHIFT 8
68
69 #define SSIO_PCI_INT_RC2_MASK 0xff
70 #define SSIO_PCI_INT_RC2_SHIFT 16
71
72 #define SSIO_PCI_INT_RC3_MASK 0xff
73 #define SSIO_PCI_INT_RC3_SHIFT 24
74
75 #define SSIO_PCI_INT_RC4_MASK 0xff
76 #define SSIO_PCI_INT_RC4_SHIFT 0
77
78 #define SSIO_PCI_INT_RC5_MASK 0xff
79 #define SSIO_PCI_INT_RC5_SHIFT 8
80
81 #define SSIO_PCI_INT_RC6_MASK 0xff
82 #define SSIO_PCI_INT_RC6_SHIFT 16
83
84 #define SSIO_PCI_INT_RC7_MASK 0xff
85 #define SSIO_PCI_INT_RC7_SHIFT 24
86
87 #define SSIO_PCI_INT_RC8_MASK 0xff
88 #define SSIO_PCI_INT_RC8_SHIFT 0
89
90 #define SSIO_PCI_INT_RC9_MASK 0xff
91 #define SSIO_PCI_INT_RC9_SHIFT 8
92
93 /* Cascaded i8259-compatible PICs. */
94 #define SSIO_PIC1 0x20
95 #define SSIO_PIC2 0xa0
96 #define SSIO_NINTS 16
97
98 int ssio_match(struct device *, void *, void *);
99 void ssio_attach(struct device *, struct device *, void *);
100
101 struct ssio_iv {
102 int (*handler)(void *);
103 void *arg;
104 };
105
106 struct ssio_iv ssio_intr_table[SSIO_NINTS];
107
108 struct ssio_softc {
109 struct device sc_dev;
110
111 bus_space_tag_t sc_iot;
112 bus_space_handle_t sc_ic1h;
113 bus_space_handle_t sc_ic2h;
114 void *sc_ih;
115 };
116
117 const struct cfattach ssio_ca = {
118 sizeof(struct ssio_softc), ssio_match, ssio_attach
119 };
120
121 struct cfdriver ssio_cd = {
122 NULL, "ssio", DV_DULL
123 };
124
125 const struct pci_matchid ssio_devices[] = {
126 { PCI_VENDOR_NS, PCI_PRODUCT_NS_PC87560 }
127 };
128
129 int ssio_intr(void *);
130 int ssio_print(void *, const char *);
131
132 int
ssio_match(struct device * parent,void * match,void * aux)133 ssio_match(struct device *parent, void *match, void *aux)
134 {
135 struct pci_attach_args *pa = aux;
136 pcireg_t bhlc, id;
137 pcitag_t tag;
138
139 /*
140 * The firmware doesn't always switch the IDE function into native
141 * mode. So we do that ourselves since it makes life much simpler.
142 * Note that we have to do this in the match function since the
143 * Legacy I/O function attaches after the IDE function.
144 */
145 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_NS &&
146 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_NS_PC87415) {
147 bhlc = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG);
148 if (!PCI_HDRTYPE_MULTIFN(bhlc))
149 return (0);
150
151 tag = pci_make_tag(pa->pa_pc, pa->pa_bus, pa->pa_device, 1);
152 id = pci_conf_read(pa->pa_pc, tag, PCI_ID_REG);
153 if (PCI_VENDOR(id) != PCI_VENDOR_NS ||
154 PCI_PRODUCT(id) != PCI_PRODUCT_NS_PC87560)
155 return (0);
156
157 pa->pa_class |= PCIIDE_INTERFACE_PCI(0) << PCI_INTERFACE_SHIFT;
158 pa->pa_class |= PCIIDE_INTERFACE_PCI(1) << PCI_INTERFACE_SHIFT;
159 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_CLASS_REG,
160 pa->pa_class);
161 return (0);
162 }
163
164 return (pci_matchbyid((struct pci_attach_args *)aux, ssio_devices,
165 sizeof(ssio_devices) / sizeof (ssio_devices[0])));
166 }
167
168 void
ssio_attach(struct device * parent,struct device * self,void * aux)169 ssio_attach(struct device *parent, struct device *self, void *aux)
170 {
171 struct ssio_softc *sc = (void *)self;
172 struct pci_attach_args *pa = aux;
173 struct ssio_attach_args saa;
174 pci_intr_handle_t ih;
175 const char *intrstr;
176 pcireg_t reg;
177 #if NUKBD > 0
178 pcitag_t tag;
179 #endif
180
181 sc->sc_iot = pa->pa_iot;
182 if (bus_space_map(sc->sc_iot, SSIO_PIC1, 2, 0, &sc->sc_ic1h)) {
183 printf(": unable to map PIC1 registers\n");
184 return;
185 }
186 if (bus_space_map(sc->sc_iot, SSIO_PIC2, 2, 0, &sc->sc_ic2h)) {
187 printf(": unable to map PIC2 registers\n");
188 goto unmap_ic1;
189 }
190
191 if (pci_intr_map(pa, &ih)) {
192 printf(": unable to map interrupt\n");
193 goto unmap_ic2;
194 }
195 intrstr = pci_intr_string(pa->pa_pc, ih);
196 /* XXX Probably should be IPL_NESTED. */
197 sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_TTY, ssio_intr,
198 sc, sc->sc_dev.dv_xname);
199 if (sc->sc_ih == NULL) {
200 printf(": couldn't establish interrupt\n");
201 goto unmap_ic2;
202 }
203
204 printf(": %s\n", intrstr);
205
206 /*
207 * We use the following interrupt mapping:
208 *
209 * USB (INTD#) IRQ 1
210 * IDE Channel 1 IRQ 5
211 * Serial Port 1 IRQ 4
212 * Serial Port 2 IRQ 3
213 * Parallel Port IRQ 7
214 *
215 * USB and IDE are set to level triggered, all others to edge
216 * triggered.
217 *
218 * We disable all other interrupts since we don't need them.
219 */
220 reg = pci_conf_read(pa->pa_pc, pa->pa_tag, SSIO_PCI_DMA_RC2);
221 reg &= ~(SSIO_PCI_INT_TC1_MASK << SSIO_PCI_INT_TC1_SHIFT);
222 reg |= 0x22 << SSIO_PCI_INT_TC1_SHIFT;
223 pci_conf_write(pa->pa_pc, pa->pa_tag, SSIO_PCI_DMA_RC2, reg);
224
225 reg = 0;
226 reg |= 0x34 << SSIO_PCI_INT_RC1_SHIFT; /* SP1, SP2 */
227 reg |= 0x07 << SSIO_PCI_INT_RC2_SHIFT; /* PP */
228 reg |= 0x05 << SSIO_PCI_INT_RC3_SHIFT; /* IDE1 */
229 pci_conf_write(pa->pa_pc, pa->pa_tag, SSIO_PCI_INT_TC2, reg);
230
231 reg = 0;
232 reg |= 0x10 << SSIO_PCI_INT_RC5_SHIFT; /* INTD# (USB) */
233 pci_conf_write(pa->pa_pc, pa->pa_tag, SSIO_PCI_INT_RC4, reg);
234
235 /* Program PIC1. */
236 bus_space_write_1(sc->sc_iot, sc->sc_ic1h, 0, 0x11);
237 bus_space_write_1(sc->sc_iot, sc->sc_ic1h, 1, 0x00);
238 bus_space_write_1(sc->sc_iot, sc->sc_ic1h, 1, 0x04);
239 bus_space_write_1(sc->sc_iot, sc->sc_ic1h, 1, 0x01);
240
241 /* Priority (3-7,0-2). */
242 bus_space_write_1(sc->sc_iot, sc->sc_ic1h, 0, 0xc2);
243
244 /* Program PIC2. */
245 bus_space_write_1(sc->sc_iot, sc->sc_ic2h, 0, 0x11);
246 bus_space_write_1(sc->sc_iot, sc->sc_ic2h, 1, 0x00);
247 bus_space_write_1(sc->sc_iot, sc->sc_ic2h, 1, 0x02);
248 bus_space_write_1(sc->sc_iot, sc->sc_ic2h, 1, 0x01);
249
250 /* Unmask all interrupts. */
251 bus_space_write_1(sc->sc_iot, sc->sc_ic1h, 1, 0x00);
252 bus_space_write_1(sc->sc_iot, sc->sc_ic2h, 1, 0x00);
253
254 /* Serial Port 1. */
255 saa.saa_name = "com";
256 saa.saa_iot = sc->sc_iot;
257 saa.saa_iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, SSIO_PCI_SP1BAR);
258 saa.saa_iobase &= 0xfffffffe;
259 saa.saa_irq = 4;
260 config_found(self, &saa, ssio_print);
261
262 /* Serial Port 2. */
263 saa.saa_name = "com";
264 saa.saa_iot = sc->sc_iot;
265 saa.saa_iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, SSIO_PCI_SP2BAR);
266 saa.saa_iobase &= 0xfffffffe;
267 saa.saa_irq = 3;
268 config_found(self, &saa, ssio_print);
269
270 /* Parallel Port. */
271 saa.saa_name = "lpt";
272 saa.saa_iot = sc->sc_iot;
273 saa.saa_iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, SSIO_PCI_PPBAR);
274 saa.saa_iobase &= 0xfffffffe;
275 saa.saa_irq = 7;
276 config_found(self, &saa, ssio_print);
277
278 #if NUKBD > 0
279 /*
280 * If a USB keybard is used for console input, the firmware passes
281 * the mmio address of the USB controller the keyboard is attached
282 * to. Since we know the USB controller is function 2 on the same
283 * device and comes right after us (we're function 1 remember),
284 * this is a convenient spot to mark the USB keyboard as console
285 * if the address matches.
286 */
287 tag = pci_make_tag(pa->pa_pc, pa->pa_bus, pa->pa_device, 2);
288 reg = pci_conf_read(pa->pa_pc, tag, PCI_CBMEM);
289
290 if (PAGE0->mem_kbd.pz_class == PCL_KEYBD &&
291 PAGE0->mem_kbd.pz_hpa == reg)
292 ukbd_cnattach();
293 #endif
294
295 return;
296
297 unmap_ic2:
298 bus_space_unmap(sc->sc_iot, sc->sc_ic2h, 2);
299 unmap_ic1:
300 bus_space_unmap(sc->sc_iot, sc->sc_ic1h, 2);
301 }
302
303 int
ssio_intr(void * v)304 ssio_intr(void *v)
305 {
306 struct ssio_softc *sc = v;
307 struct ssio_iv *iv;
308 int claimed = 0;
309 int irq, isr;
310
311 /* Poll for interrupt. */
312 bus_space_write_1(sc->sc_iot, sc->sc_ic1h, 0, 0x0c);
313 irq = bus_space_read_1(sc->sc_iot, sc->sc_ic1h, 0);
314 irq &= 0x07;
315
316 if (irq == 7) {
317 bus_space_write_1(sc->sc_iot, sc->sc_ic1h, 0, 0x0b);
318 isr = bus_space_read_1(sc->sc_iot, sc->sc_ic1h, 0);
319 if ((isr & 0x80) == 0)
320 /* Spurious interrupt. */
321 return (0);
322 }
323
324 iv = &ssio_intr_table[irq];
325 if (iv->handler)
326 claimed = iv->handler(iv->arg);
327
328 /* Signal EOI. */
329 bus_space_write_1(sc->sc_iot, sc->sc_ic1h, 0, 0x60 | (irq & 0x0f));
330
331 return (claimed);
332 }
333
334 void *
ssio_intr_establish(int pri,int irq,int (* handler)(void *),void * arg,const char * name)335 ssio_intr_establish(int pri, int irq, int (*handler)(void *), void *arg,
336 const char *name)
337 {
338 struct ssio_iv *iv;
339
340 if (irq < 0 || irq >= SSIO_NINTS || ssio_intr_table[irq].handler)
341 return (NULL);
342
343 iv = &ssio_intr_table[irq];
344 iv->handler = handler;
345 iv->arg = arg;
346
347 return (iv);
348 }
349
350 int
ssio_print(void * aux,const char * pnp)351 ssio_print(void *aux, const char *pnp)
352 {
353 struct ssio_attach_args *saa = aux;
354
355 if (pnp)
356 printf("%s at %s", saa->saa_name, pnp);
357 if (saa->saa_iobase) {
358 printf(" offset %lx", saa->saa_iobase);
359 if (!pnp && saa->saa_irq >= 0)
360 printf(" irq %d", saa->saa_irq);
361 }
362
363 return (UNCONF);
364 }
365