diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2009-08-30 19:30:51 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2009-08-30 19:30:51 +0000 |
commit | 25aec09563340fddf243450ef5287b5908a15f9b (patch) | |
tree | 68e80257ce9b9efbb438d69b2dd7d8d738e576b0 /sys | |
parent | 8acf7ad7da20700612be54fb6215c4129d3f5908 (diff) |
When the `machine' commands part of the hppa bootloader were written, we
were only running on the non-PCI models. Many years have passed since then...
Update the bus walking code to support dino and elroy PCI bridges, in order
to identify PCI graphics devices and USB controllers (in order to support
USB keyboards). Also recognize com@ssio, so that high-class four digit B and C
machines (and probably four digit J as well) can configure console from the
boot blocks correctly.
Of course, this is of questionable usefulness, since these console related
routines were written with the 712 in mind, which has a castrated ROM preventing
the user from configuring a serial console. Yet providing a consistent
feature set never hurts.
(three digit J series still need some love, due to the different way of
walking the buses; to be addressed in a later commit, soon)
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/hppa/stand/libsa/cmd_hppa.c | 346 |
1 files changed, 272 insertions, 74 deletions
diff --git a/sys/arch/hppa/stand/libsa/cmd_hppa.c b/sys/arch/hppa/stand/libsa/cmd_hppa.c index 6b0837e847f..ff7c4e19454 100644 --- a/sys/arch/hppa/stand/libsa/cmd_hppa.c +++ b/sys/arch/hppa/stand/libsa/cmd_hppa.c @@ -1,7 +1,7 @@ -/* $OpenBSD: cmd_hppa.c,v 1.8 2004/04/07 18:24:20 mickey Exp $ */ +/* $OpenBSD: cmd_hppa.c,v 1.9 2009/08/30 19:30:50 miod Exp $ */ /* - * Copyright (c) 2002 Miodrag Vallat + * Copyright (c) 2002, 2009 Miodrag Vallat * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,8 +26,6 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -/*#define DEBUG*/ - #include <sys/param.h> /* would come from <sys/param.h> if -D_KERNEL */ #define offsetof(s, e) ((size_t)&((s *)0)->e) @@ -36,6 +34,10 @@ #include <machine/pdc.h> #include <arch/hppa/dev/cpudevs.h> +#include <arch/hppa/dev/elroyreg.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> #include <libsa.h> #include "cmd.h" @@ -69,45 +71,39 @@ struct consoledev { #define PS2 1 #define HIL 2 -#define SERIAL 3 -#define GRAPHICS 4 +#define USB 3 +#define SERIAL 4 +#define GRAPHICS 5 -/* max. serial ports */ #define MAX_SERIALS 4 -/* max. HIL and PS2 */ -#define MAX_KEYBOARDS 2 -/* max. heads */ +#define MAX_KEYBOARDS 4 #define MAX_GRAPHICS 4 struct consoledev serials[MAX_SERIALS]; struct consoledev keyboards[MAX_KEYBOARDS]; struct consoledev graphics[MAX_GRAPHICS]; -/* Relaxed device comparison */ -#define MATCH(dev1, dev2) \ - (dev1).dp_mod == (dev2).dp_mod && \ - (dev1).dp_bc[0] == (dev2).dp_bc[0] && \ - (dev1).dp_bc[1] == (dev2).dp_bc[1] && \ - (dev1).dp_bc[2] == (dev2).dp_bc[2] && \ - (dev1).dp_bc[3] == (dev2).dp_bc[3] && \ - (dev1).dp_bc[4] == (dev2).dp_bc[4] && \ - (dev1).dp_bc[5] == (dev2).dp_bc[5] - int walked; -void bus_walk(struct device_path *); -void register_device(struct consoledev *, int, - struct device_path *, struct iodc_data *, int, int); - -int Xconsole(void); -void print_console(void); -int set_graphics(struct device_path *, int, char *); -int set_serial(struct device_path *, int, char *); -int set_console(struct device_path *); - -int Xkeyboard(void); -void print_keyboard(void); -int set_keyboard(struct device_path *); +void bus_walk(struct device_path *, int); +uint32_t dino_conf_read(u_int, int, int, u_int); +uint32_t elroy_conf_read(u_int, int, int, u_int); +int path_match(struct device_path *, struct device_path *); +void path_shift(struct device_path *, int); +void pci_bus_walk(struct device_path *, struct iodc_data *, + struct pdc_memmap *); +void register_device(struct consoledev *, int, struct device_path *, + struct iodc_data *, int, int); + +int Xconsole(void); +void print_console(void); +int set_graphics(struct device_path *, int, char *); +int set_serial(struct device_path *, int, char *); +int set_console(struct device_path *); + +int Xkeyboard(void); +void print_keyboard(void); +int set_keyboard(struct device_path *); struct cmd_table cmd_machine[] = { { "console", CMDT_CMD, Xconsole }, @@ -116,7 +112,7 @@ struct cmd_table cmd_machine[] = { }; /* value to console speed table */ -int i_speeds[] = { +const int i_speeds[] = { 50, 75, 110, @@ -135,7 +131,7 @@ int i_speeds[] = { 230400, }; -char *c_speeds[] = { +const char *c_speeds[] = { "50", "75", "110", @@ -155,7 +151,7 @@ char *c_speeds[] = { }; /* values to console parity table */ -char *parities[] = { +const char *parities[] = { "none", "odd", "<unknown parity>", @@ -196,7 +192,7 @@ print_console() /* look for a serial console */ for (port = i = 0; i < MAX_SERIALS; i++) - if (MATCH(serials[i].dp, sstor.ss_console)) { + if (path_match(&serials[i].dp, &sstor.ss_console)) { port = i + 1; break; } @@ -207,7 +203,7 @@ print_console() */ for (port = i = 0; i < MAX_GRAPHICS; i++) - if (MATCH(graphics[i].dp, sstor.ss_console)) { + if (path_match(&graphics[i].dp, &sstor.ss_console)) { port = i; break; } @@ -269,15 +265,13 @@ set_graphics(console, port, arg) if (*digit >= '0' && *digit <= '9') mode = 10 * mode + (*digit - '0'); else { - printf("invalid mode specification, %s\n", - arg); + printf("invalid mode specification, %s\n", arg); return 0; } } if (mode <= 0) { - printf("invalid mode specification, %s\n", - arg); + printf("invalid mode specification, %s\n", arg); return 0; } } @@ -286,7 +280,7 @@ set_graphics(console, port, arg) * If we are just changing the mode of the same graphics * console, check that our mode is in the valid range. */ - if (MATCH(graphics[port].dp, sstor.ss_console)) { + if (path_match(&graphics[port].dp, &sstor.ss_console)) { maxmode = sstor.ss_console.dp_layers[1]; /* pick back same mode if unspecified */ @@ -445,7 +439,7 @@ Xconsole() /* walk the device list if not already done */ if (walked == 0) { - bus_walk(NULL); + bus_walk(NULL, MAXMODBUS); walked++; } @@ -468,8 +462,8 @@ Xconsole() CONSOLEOFFSET, &sstor.ss_console, sizeof(sstor.ss_console)); if (rc != 0) { - printf("failed to save console settings, error %d\n", - rc); + printf("failed to save console" + " settings, error %d\n", rc); /* read sstor again for safety */ (*pdc)(PDC_STABLE, PDC_STABLE_READ, CONSOLEOFFSET, &sstor.ss_console, @@ -519,12 +513,15 @@ print_keyboard() printf("Keyboard path: "); for (type = i = 0; i < MAX_KEYBOARDS; i++) - if (MATCH(keyboards[i].dp, sstor.ss_keyboard)) { + if (path_match(&keyboards[i].dp, &sstor.ss_keyboard)) { type = keyboards[i].type; break; } switch (type) { + case USB: + printf("usb"); + break; case HIL: printf("hil"); break; @@ -551,7 +548,10 @@ set_keyboard(keyboard) type = HIL; else if (strcmp(arg, "ps2") == 0) type = PS2; + else if (strcmp(arg, "usb") == 0) + type = USB; else { + /* XXX should probably handle multiple USB controllers */ printf("invalid device specification, %s\n", arg); return 0; } @@ -574,7 +574,7 @@ Xkeyboard() /* walk the device list if not already done */ if (walked == 0) { - bus_walk(NULL); + bus_walk(NULL, MAXMODBUS); walked++; } @@ -597,8 +597,8 @@ Xkeyboard() KEYBOARDOFFSET, &sstor.ss_keyboard, sizeof(sstor.ss_keyboard)); if (rc != 0) { - printf("failed to save keyboard settings, error %d\n", - rc); + printf("failed to save keyboard" + " settings, error %d\n", rc); /* read sstor again for safety */ (*pdc)(PDC_STABLE, PDC_STABLE_READ, KEYBOARDOFFSET, &sstor.ss_keyboard, @@ -625,28 +625,25 @@ Xkeyboard() * serial ports, keyboard and graphics devices as they are found. */ void -bus_walk(struct device_path *idp) +bus_walk(struct device_path *idp, int maxmod) { struct device_path dp; struct pdc_memmap memmap; struct iodc_data mptr; int err, i, kluge_ps2 = 0; /* kluge, see below */ - for (i = 0; i < MAXMODBUS; i++) { - + for (i = 0; i < maxmod; i++) { if (idp) { - dp.dp_bc[0] = idp->dp_bc[1]; - dp.dp_bc[1] = idp->dp_bc[2]; - dp.dp_bc[2] = idp->dp_bc[3]; - dp.dp_bc[3] = idp->dp_bc[4]; - dp.dp_bc[4] = idp->dp_bc[5]; - dp.dp_bc[5] = idp->dp_mod; + dp = *idp; + path_shift(&dp, i); } else { + dp.dp_flags = 0; dp.dp_bc[0] = dp.dp_bc[1] = dp.dp_bc[2] = dp.dp_bc[3] = dp.dp_bc[4] = dp.dp_bc[5] = -1; + dp.dp_mod = i; + bzero(&dp.dp_layers, sizeof dp.dp_layers); } - dp.dp_mod = i; if ((pdc)(PDC_MEMMAP, PDC_MEMMAP_HPA, &memmap, &dp) < 0 && (pdc)(PDC_SYSMAP, PDC_SYSMAP_HPA, &memmap, &dp) < 0) continue; @@ -662,6 +659,7 @@ bus_walk(struct device_path *idp) dp.dp_bc[4], dp.dp_bc[5], dp.dp_flags, dp.dp_mod, mptr.iodc_type, mptr.iodc_sv_model); #endif + /* * If the device can be considered as a valid rs232, * graphics console or keyboard, register it. @@ -678,21 +676,19 @@ bus_walk(struct device_path *idp) */ switch (mptr.iodc_type) { case HPPA_TYPE_BCPORT: - bus_walk(&dp); + bus_walk(&dp, MAXMODBUS); break; - case HPPA_TYPE_BHA: case HPPA_TYPE_BRIDGE: - /* if there was no phantomas here */ - if (dp.dp_bc[5] == -1) { - dp.dp_bc[0] = dp.dp_bc[1]; - dp.dp_bc[1] = dp.dp_bc[2]; - dp.dp_bc[2] = dp.dp_bc[3]; - dp.dp_bc[3] = dp.dp_bc[4]; - dp.dp_bc[4] = dp.dp_bc[5]; - dp.dp_bc[5] = dp.dp_mod; - dp.dp_mod = 0; + if (mptr.iodc_sv_model == HPPA_BRIDGE_DINO) { + pci_bus_walk(&dp, &mptr, &memmap); + break; } - bus_walk(&dp); + /* FALLTHROUGH */ + case HPPA_TYPE_BHA: + /* if there was no phantomas(4) here */ + if (dp.dp_bc[5] == -1) + path_shift(&dp, 0); + bus_walk(&dp, MAXMODBUS); break; case HPPA_TYPE_ADIRECT: switch (mptr.iodc_sv_model) { @@ -717,7 +713,7 @@ bus_walk(struct device_path *idp) register_device(keyboards, MAX_KEYBOARDS, &dp, &mptr, HIL, 0); break; - case HPPA_FIO_RS232: + case HPPA_FIO_RS232: /* com@gsc */ register_device(serials, MAX_SERIALS, &dp, &mptr, SERIAL, 0); break; @@ -739,7 +735,7 @@ bus_walk(struct device_path *idp) &dp, &mptr, PS2, 1); kluge_ps2++; break; - case HPPA_FIO_GRS232: + case HPPA_FIO_GRS232: /* com@dino, com@gsc */ { int j, first; @@ -770,12 +766,214 @@ bus_walk(struct device_path *idp) register_device(graphics, MAX_GRAPHICS, &dp, &mptr, GRAPHICS, 1); break; +#if 0 /* can these really be used as console? */ + case HPPA_FIO_GRJ16: /* com@gsc */ + register_device(serials, MAX_SERIALS, + &dp, &mptr, SERIAL, 0); + break; +#endif + } + break; + case HPPA_TYPE_IOA: + switch (mptr.iodc_sv_model) { + case HPPA_IOA_UTURN: + bus_walk(&dp, MAXMODBUS - 1); + break; } break; } } } +/* + * PCI bus walker. + * The PDC device enumeration stops at the PCI bridge level, however + * in order to properly handle console path on systems with PCI graphics + * and USB controllers, it is necessary to dig further. + * + * Note that there are apparently PDC routines to access bridge configuration + * space, but I have yet to find documentation about them. + * + * We ignore multi-function devices and subordinate PCI busses here, since + * PDC PCI device paths stop at the PCI device number, and subordinate + * busses are unlikely to be configured by the PDC. + */ + +#define ELROY_MODEL 0x78 +#define DINO_PAMR 0x804 +#define DINO_CFG_ADDR 0x64 +#define DINO_CFG_DATA 0x68 + +void +pci_bus_walk(struct device_path *idp, struct iodc_data *mptr, + struct pdc_memmap *memmap) +{ + struct device_path dp; + int dev, fn, nfuncs; + uint32_t id, bhlcr, class; + uint32_t (*conf_read)(u_int, int, int, u_int); + + if (mptr->iodc_model == ELROY_MODEL) + conf_read = elroy_conf_read; + else + conf_read = dino_conf_read; + + for (dev = 0; dev < 32; dev++) { + id = (*conf_read)(memmap->hpa, dev, 0, PCI_ID_REG); + + if (PCI_VENDOR(id) == PCI_VENDOR_INVALID || PCI_VENDOR(id) == 0) + continue; + + bhlcr = (*conf_read)(memmap->hpa, dev, 0, PCI_BHLC_REG); + nfuncs = PCI_HDRTYPE_MULTIFN(bhlcr) ? 8 : 1; + + for (fn = 0; fn < nfuncs; fn++) { + dp = *idp; + path_shift(&dp, dev); + path_shift(&dp, fn); + + if (fn != 0) + id = (*conf_read)(memmap->hpa, dev, fn, + PCI_ID_REG); + class = (*conf_read)(memmap->hpa, dev, fn, + PCI_CLASS_REG); + + /* + * We are only interested in two kinds of devices + * here: sti graphics, and USB controllers. + */ + if (PCI_CLASS(class) == PCI_CLASS_SERIALBUS && + PCI_SUBCLASS(class) == PCI_SUBCLASS_SERIALBUS_USB) { + /* + * Note about the last parameter of the + * register_device() call below being zero: + * machines with USB keyboards have neither + * PS/2 nor HIL controllers, so it doesn't + * matter what order the USB controllers are + * in. + * However machines with PS/2 keyboards + * might have an USB PCI card plugged in, + * which better appear after the PS/2 + * keyboard. + */ + register_device(keyboards, MAX_KEYBOARDS, + &dp, mptr, USB, 0); + continue; + } + + switch (PCI_VENDOR(id)) { + case PCI_VENDOR_HP: + switch (PCI_PRODUCT(id)) { + case PCI_PRODUCT_HP_VISUALIZE_EG: + case PCI_PRODUCT_HP_VISUALIZE_FX2: + case PCI_PRODUCT_HP_VISUALIZE_FX4: + case PCI_PRODUCT_HP_VISUALIZE_FX6: + case PCI_PRODUCT_HP_VISUALIZE_FXE: + register_device(graphics, MAX_GRAPHICS, + &dp, mptr, GRAPHICS, 0); + break; + } + break; + case PCI_VENDOR_NS: + if (PCI_PRODUCT(id) == PCI_PRODUCT_NS_PC87560) { + /* serial_2 */ + path_shift(&dp, 2); + register_device(serials, MAX_SERIALS, + &dp, mptr, SERIAL, 1); + /* serial_1 */ + dp.dp_mod = 1; + register_device(serials, MAX_SERIALS, + &dp, mptr, SERIAL, 1); + } + break; + } + } + } +} + +uint32_t +dino_conf_read(u_int hpa, int dev, int fn, u_int reg) +{ + volatile uint32_t *dino = (volatile uint32_t *)hpa; + uint32_t pamr; + uint32_t addr, id; + + addr = (dev << 11) | (fn << 8) | reg; + + pamr = dino[DINO_PAMR / 4]; + dino[DINO_PAMR / 4] = 0; + dino[DINO_CFG_ADDR / 4] = addr; + id = dino[DINO_CFG_DATA / 4]; + dino[DINO_PAMR / 4] = pamr; + + return letoh32(id); +} + +uint32_t +elroy_conf_read(u_int hpa, int dev, int fn, u_int reg) +{ + volatile struct elroy_regs *elroy = (volatile struct elroy_regs *)hpa; + uint32_t arb_mask, err_cfg, control; + uint32_t addr, id; + + addr = (dev << 11) | (fn << 8) | reg; + + arb_mask = *(volatile uint32_t *)&elroy->arb_mask; + err_cfg = *(volatile uint32_t *)&elroy->err_cfg; + control = *(volatile uint32_t *)&elroy->control; + + if (arb_mask == 0) + *(volatile uint32_t *)&elroy->arb_mask = + htole32(ELROY_ARB_ENABLE); + *(volatile uint32_t *)&elroy->err_cfg = err_cfg | + htole32(ELROY_ERRCFG_SMART | ELROY_ERRCFG_CM); + *(volatile uint32_t *)&elroy->control = + (control | htole32(ELROY_CONTROL_CE)) & ~htole32(ELROY_CONTROL_HF); + + *(volatile uint32_t *)&elroy->pci_conf_addr = htole32(addr); + addr = *(volatile uint32_t *)&elroy->pci_conf_addr; + id = *(volatile uint32_t *)&elroy->pci_conf_data; + + *(volatile uint32_t *)&elroy->control = + control | htole32(ELROY_CONTROL_CE | ELROY_CONTROL_CL); + *(volatile uint32_t *)&elroy->control = control; + *(volatile uint32_t *)&elroy->err_cfg = err_cfg; + if (arb_mask == 0) + *(volatile uint32_t *)&elroy->arb_mask = arb_mask; + + return letoh32(id); +} + +/* + * Relaxed device comparison + */ +int +path_match(struct device_path *dev1, struct device_path *dev2) +{ + return dev1->dp_mod == dev2->dp_mod && + dev1->dp_bc[0] == dev2->dp_bc[0] && + dev1->dp_bc[1] == dev2->dp_bc[1] && + dev1->dp_bc[2] == dev2->dp_bc[2] && + dev1->dp_bc[3] == dev2->dp_bc[3] && + dev1->dp_bc[4] == dev2->dp_bc[4] && + dev1->dp_bc[5] == dev2->dp_bc[5]; +} + +/* + * Shift a device path, inserting a new value as dp_mod. + */ +void +path_shift(struct device_path *dp, int nmod) +{ + dp->dp_bc[0] = dp->dp_bc[1]; + dp->dp_bc[1] = dp->dp_bc[2]; + dp->dp_bc[2] = dp->dp_bc[3]; + dp->dp_bc[3] = dp->dp_bc[4]; + dp->dp_bc[4] = dp->dp_bc[5]; + dp->dp_bc[5] = dp->dp_mod; + dp->dp_mod = nmod; +} + void register_device(devlist, cnt, dp, mptr, type, first) struct consoledev *devlist; |