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