Annotation of sys/arch/hppa/dev/ssio.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: ssio.c,v 1.6 2007/07/05 11:28:30 kettenis 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: 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
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
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
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 *
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
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 offset\n", 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: }
CVSweb