Index: head/sys/arm/samsung/exynos/chrome_kb.c =================================================================== --- head/sys/arm/samsung/exynos/chrome_kb.c (revision 355270) +++ head/sys/arm/samsung/exynos/chrome_kb.c (revision 355271) @@ -1,921 +1,920 @@ /*- * Copyright (c) 2014 Ruslan Bukin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Samsung Chromebook Keyboard */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include "gpio_if.h" #include #include #include #include #define CKB_LOCK() mtx_lock(&Giant) #define CKB_UNLOCK() mtx_unlock(&Giant) #ifdef INVARIANTS /* * Assert that the lock is held in all contexts * where the code can be executed. */ #define CKB_LOCK_ASSERT() mtx_assert(&Giant, MA_OWNED) /* * Assert that the lock is held in the contexts * where it really has to be so. */ #define CKB_CTX_LOCK_ASSERT() \ do { \ if (!kdb_active && panicstr == NULL) \ mtx_assert(&Giant, MA_OWNED); \ } while (0) #else #define CKB_LOCK_ASSERT() (void)0 #define CKB_CTX_LOCK_ASSERT() (void)0 #endif /* * Define a stub keyboard driver in case one hasn't been * compiled into the kernel */ #include #include #include #define CKB_NFKEY 12 #define CKB_FLAG_COMPOSE 0x1 #define CKB_FLAG_POLLING 0x2 #define KBD_DRIVER_NAME "ckbd" struct ckb_softc { keyboard_t sc_kbd; keymap_t sc_keymap; accentmap_t sc_accmap; fkeytab_t sc_fkeymap[CKB_NFKEY]; struct resource* sc_mem_res; struct resource* sc_irq_res; void* sc_intr_hl; int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int sc_state; /* shift/lock key state */ int sc_accents; /* accent key index (> 0) */ int sc_flags; /* flags */ struct callout sc_repeat_callout; int sc_repeat_key; int sc_repeating; int flag; int rows; int cols; int gpio; device_t dev; device_t gpio_dev; struct thread *sc_poll_thread; uint16_t *keymap; uint8_t *scan_local; uint8_t *scan; }; /* prototypes */ static int ckb_set_typematic(keyboard_t *, int); static uint32_t ckb_read_char(keyboard_t *, int); static void ckb_clear_state(keyboard_t *); static int ckb_ioctl(keyboard_t *, u_long, caddr_t); static int ckb_enable(keyboard_t *); static int ckb_disable(keyboard_t *); static void ckb_repeat(void *arg) { struct ckb_softc *sc; sc = arg; if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) { if (sc->sc_repeat_key != -1) { sc->sc_repeating = 1; sc->sc_kbd.kb_callback.kc_func(&sc->sc_kbd, KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg); } } } /* detect a keyboard, not used */ static int ckb__probe(int unit, void *arg, int flags) { return (ENXIO); } /* reset and initialize the device, not used */ static int ckb_init(int unit, keyboard_t **kbdp, void *arg, int flags) { return (ENXIO); } /* test the interface to the device, not used */ static int ckb_test_if(keyboard_t *kbd) { return (0); } /* finish using this keyboard, not used */ static int ckb_term(keyboard_t *kbd) { return (ENXIO); } /* keyboard interrupt routine, not used */ static int ckb_intr(keyboard_t *kbd, void *arg) { return (0); } /* lock the access to the keyboard, not used */ static int ckb_lock(keyboard_t *kbd, int lock) { return (1); } /* clear the internal state of the keyboard */ static void ckb_clear_state(keyboard_t *kbd) { struct ckb_softc *sc; sc = kbd->kb_data; CKB_CTX_LOCK_ASSERT(); sc->sc_flags &= ~(CKB_FLAG_COMPOSE | CKB_FLAG_POLLING); sc->sc_state &= LOCK_MASK; /* preserve locking key state */ sc->sc_accents = 0; } /* save the internal state, not used */ static int ckb_get_state(keyboard_t *kbd, void *buf, size_t len) { return (len == 0) ? 1 : -1; } /* set the internal state, not used */ static int ckb_set_state(keyboard_t *kbd, void *buf, size_t len) { return (EINVAL); } /* check if data is waiting */ static int ckb_check(keyboard_t *kbd) { struct ckb_softc *sc; int i; sc = kbd->kb_data; CKB_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (0); if (sc->sc_flags & CKB_FLAG_POLLING) { return (1); } for (i = 0; i < sc->cols; i++) if (sc->scan_local[i] != sc->scan[i]) { return (1); } if (sc->sc_repeating) return (1); return (0); } /* check if char is waiting */ static int ckb_check_char_locked(keyboard_t *kbd) { CKB_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (0); return (ckb_check(kbd)); } static int ckb_check_char(keyboard_t *kbd) { int result; CKB_LOCK(); result = ckb_check_char_locked(kbd); CKB_UNLOCK(); return (result); } /* read one byte from the keyboard if it's allowed */ /* Currently unused. */ static int ckb_read(keyboard_t *kbd, int wait) { CKB_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (-1); printf("Implement ME: %s\n", __func__); return (0); } static uint16_t keymap_read(struct ckb_softc *sc, int col, int row) { KASSERT(sc->keymap != NULL, ("keymap_read: no keymap")); if (col >= 0 && col < sc->cols && row >= 0 && row < sc->rows) { return sc->keymap[row * sc->cols + col]; } return (0); } static int keymap_write(struct ckb_softc *sc, int col, int row, uint16_t key) { KASSERT(sc->keymap != NULL, ("keymap_write: no keymap")); if (col >= 0 && col < sc->cols && row >= 0 && row < sc->rows) { sc->keymap[row * sc->cols + col] = key; return (0); } return (-1); } /* read char from the keyboard */ static uint32_t ckb_read_char_locked(keyboard_t *kbd, int wait) { struct ckb_softc *sc; int i,j; uint16_t key; int oldbit; int newbit; int status; sc = kbd->kb_data; CKB_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (NOKEY); if (sc->sc_repeating) { sc->sc_repeating = 0; callout_reset(&sc->sc_repeat_callout, hz / 10, ckb_repeat, sc); return (sc->sc_repeat_key); } if (sc->sc_flags & CKB_FLAG_POLLING) { for (;;) { GPIO_PIN_GET(sc->gpio_dev, sc->gpio, &status); if (status == 0) { if (ec_command(EC_CMD_MKBP_STATE, sc->scan, sc->cols, sc->scan, sc->cols)) { return (NOKEY); } break; } if (!wait) { return (NOKEY); } DELAY(1000); } } for (i = 0; i < sc->cols; i++) { for (j = 0; j < sc->rows; j++) { oldbit = (sc->scan_local[i] & (1 << j)); newbit = (sc->scan[i] & (1 << j)); if (oldbit == newbit) continue; key = keymap_read(sc, i, j); if (key == 0) { continue; } if (newbit > 0) { /* key pressed */ sc->scan_local[i] |= (1 << j); /* setup repeating */ sc->sc_repeat_key = key; callout_reset(&sc->sc_repeat_callout, hz / 2, ckb_repeat, sc); } else { /* key released */ sc->scan_local[i] &= ~(1 << j); /* release flag */ key |= 0x80; /* unsetup repeating */ sc->sc_repeat_key = -1; callout_stop(&sc->sc_repeat_callout); } return (key); } } return (NOKEY); } /* Currently wait is always false. */ static uint32_t ckb_read_char(keyboard_t *kbd, int wait) { uint32_t keycode; CKB_LOCK(); keycode = ckb_read_char_locked(kbd, wait); CKB_UNLOCK(); return (keycode); } /* some useful control functions */ static int ckb_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg) { struct ckb_softc *sc; int i; sc = kbd->kb_data; CKB_LOCK_ASSERT(); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = sc->sc_mode; break; case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (sc->sc_mode != K_XLATE) { /* make lock key state and LED state match */ sc->sc_state &= ~LOCK_MASK; sc->sc_state |= KBD_LED_VAL(kbd); } /* FALLTHROUGH */ case K_RAW: case K_CODE: if (sc->sc_mode != *(int *)arg) { if ((sc->sc_flags & CKB_FLAG_POLLING) == 0) ckb_clear_state(kbd); sc->sc_mode = *(int *)arg; } break; default: return (EINVAL); } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; case KDSETLED: /* set keyboard LED */ /* NOTE: lock key state in "sc_state" won't be changed */ if (*(int *)arg & ~LOCK_MASK) return (EINVAL); i = *(int *)arg; /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ if (sc->sc_mode == K_XLATE && kbd->kb_keymap->n_keys > ALTGR_OFFSET) { if (i & ALKED) i |= CLKED; else i &= ~CLKED; } if (KBD_HAS_DEVICE(kbd)) { /* Configure LED */ } KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = sc->sc_state & LOCK_MASK; break; case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { return (EINVAL); } sc->sc_state &= ~LOCK_MASK; sc->sc_state |= *(int *)arg; /* set LEDs and quit */ return (ckb_ioctl(kbd, KDSETLED, arg)); case KDSETREPEAT: /* set keyboard repeat rate (new * interface) */ if (!KBD_HAS_DEVICE(kbd)) { return (0); } if (((int *)arg)[1] < 0) { return (EINVAL); } if (((int *)arg)[0] < 0) { return (EINVAL); } if (((int *)arg)[0] < 200) /* fastest possible value */ kbd->kb_delay1 = 200; else kbd->kb_delay1 = ((int *)arg)[0]; kbd->kb_delay2 = ((int *)arg)[1]; return (0); case KDSETRAD: /* set keyboard repeat rate (old * interface) */ return (ckb_set_typematic(kbd, *(int *)arg)); case PIO_KEYMAP: /* set keyboard translation table */ case OPIO_KEYMAP: /* set keyboard translation table * (compat) */ case PIO_KEYMAPENT: /* set keyboard translation table * entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ sc->sc_accents = 0; /* FALLTHROUGH */ default: return (genkbd_commonioctl(kbd, cmd, arg)); } return (0); } static int ckb_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { int result; /* * XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any * context where printf(9) can be called, which among other things * includes interrupt filters and threads with any kinds of locks * already held. For this reason it would be dangerous to acquire * the Giant here unconditionally. On the other hand we have to * have it to handle the ioctl. * So we make our best effort to auto-detect whether we can grab * the Giant or not. Blame syscons(4) for this. */ switch (cmd) { case KDGKBSTATE: case KDSKBSTATE: case KDSETLED: if (!mtx_owned(&Giant) && !SCHEDULER_STOPPED()) return (EDEADLK); /* best I could come up with */ /* FALLTHROUGH */ default: CKB_LOCK(); result = ckb_ioctl_locked(kbd, cmd, arg); CKB_UNLOCK(); return (result); } } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int ckb_enable(keyboard_t *kbd) { CKB_LOCK(); KBD_ACTIVATE(kbd); CKB_UNLOCK(); return (0); } /* disallow the access to the device */ static int ckb_disable(keyboard_t *kbd) { CKB_LOCK(); KBD_DEACTIVATE(kbd); CKB_UNLOCK(); return (0); } /* local functions */ static int ckb_set_typematic(keyboard_t *kbd, int code) { static const int delays[] = {250, 500, 750, 1000}; static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126, 136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440, 472, 504}; if (code & ~0x7f) { return (EINVAL); } kbd->kb_delay1 = delays[(code >> 5) & 3]; kbd->kb_delay2 = rates[code & 0x1f]; return (0); } static int ckb_poll(keyboard_t *kbd, int on) { struct ckb_softc *sc; sc = kbd->kb_data; CKB_LOCK(); if (on) { sc->sc_flags |= CKB_FLAG_POLLING; sc->sc_poll_thread = curthread; } else { sc->sc_flags &= ~CKB_FLAG_POLLING; } CKB_UNLOCK(); return (0); } /* local functions */ static int dummy_kbd_configure(int flags); keyboard_switch_t ckbdsw = { .probe = &ckb__probe, .init = &ckb_init, .term = &ckb_term, .intr = &ckb_intr, .test_if = &ckb_test_if, .enable = &ckb_enable, .disable = &ckb_disable, .read = &ckb_read, .check = &ckb_check, .read_char = &ckb_read_char, .check_char = &ckb_check_char, .ioctl = &ckb_ioctl, .lock = &ckb_lock, .clear_state = &ckb_clear_state, .get_state = &ckb_get_state, .set_state = &ckb_set_state, .get_fkeystr = &genkbd_get_fkeystr, .poll = &ckb_poll, .diag = &genkbd_diag, }; static int dummy_kbd_configure(int flags) { return (0); } KEYBOARD_DRIVER(ckbd, ckbdsw, dummy_kbd_configure); /* * Parses 'keymap' into sc->keymap. * Requires sc->cols and sc->rows to be set. */ static int parse_keymap(struct ckb_softc *sc, pcell_t *keymap, size_t len) { int i; sc->keymap = malloc(sc->cols * sc->rows * sizeof(sc->keymap[0]), M_DEVBUF, M_NOWAIT | M_ZERO); if (sc->keymap == NULL) { return (ENOMEM); } for (i = 0; i < len; i++) { /* * Return value is ignored, we just write whatever fits into * specified number of rows and columns and silently ignore * everything else. * Keymap entries follow this format: 0xRRCCKKKK * RR - row number, CC - column number, KKKK - key code */ keymap_write(sc, (keymap[i] >> 16) & 0xff, (keymap[i] >> 24) & 0xff, keymap[i] & 0xffff); } return (0); } /* Allocates a new array for keymap and returns it in 'keymap'. */ static int read_keymap(phandle_t node, const char *prop, pcell_t **keymap, size_t *len) { if ((*len = OF_getproplen(node, prop)) <= 0) { return (ENXIO); } if ((*keymap = malloc(*len, M_DEVBUF, M_NOWAIT)) == NULL) { return (ENOMEM); } if (OF_getencprop(node, prop, *keymap, *len) != *len) { return (ENXIO); } return (0); } static int parse_dts(struct ckb_softc *sc) { phandle_t node; pcell_t dts_value; pcell_t *keymap; int len, ret; const char *keymap_prop = NULL; if ((node = ofw_bus_get_node(sc->dev)) == -1) return (ENXIO); if ((len = OF_getproplen(node, "google,key-rows")) <= 0) return (ENXIO); OF_getencprop(node, "google,key-rows", &dts_value, len); sc->rows = dts_value; if ((len = OF_getproplen(node, "google,key-columns")) <= 0) return (ENXIO); OF_getencprop(node, "google,key-columns", &dts_value, len); sc->cols = dts_value; if ((len = OF_getproplen(node, "freebsd,intr-gpio")) <= 0) return (ENXIO); OF_getencprop(node, "freebsd,intr-gpio", &dts_value, len); sc->gpio = dts_value; if (OF_hasprop(node, "freebsd,keymap")) { keymap_prop = "freebsd,keymap"; device_printf(sc->dev, "using FreeBSD-specific keymap from FDT\n"); } else if (OF_hasprop(node, "linux,keymap")) { keymap_prop = "linux,keymap"; device_printf(sc->dev, "using Linux keymap from FDT\n"); } else { device_printf(sc->dev, "using built-in keymap\n"); } if (keymap_prop != NULL) { if ((ret = read_keymap(node, keymap_prop, &keymap, &len))) { device_printf(sc->dev, "failed to read keymap from FDT: %d\n", ret); return (ret); } ret = parse_keymap(sc, keymap, len); free(keymap, M_DEVBUF); if (ret) { return (ret); } } else { if ((ret = parse_keymap(sc, default_keymap, KEYMAP_LEN))) { return (ret); } } if ((sc->rows == 0) || (sc->cols == 0) || (sc->gpio == 0)) return (ENXIO); return (0); } void ckb_ec_intr(void *arg) { struct ckb_softc *sc; sc = arg; if (sc->sc_flags & CKB_FLAG_POLLING) return; ec_command(EC_CMD_MKBP_STATE, sc->scan, sc->cols, sc->scan, sc->cols); (sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg); }; static int chrome_kb_attach(device_t dev) { struct ckb_softc *sc; keyboard_t *kbd; int error; int rid; int i; sc = device_get_softc(dev); sc->dev = dev; sc->keymap = NULL; if ((error = parse_dts(sc)) != 0) return error; sc->gpio_dev = devclass_get_device(devclass_find("gpio"), 0); if (sc->gpio_dev == NULL) { device_printf(sc->dev, "Can't find gpio device.\n"); return (ENXIO); } #if 0 device_printf(sc->dev, "Keyboard matrix [%dx%d]\n", sc->cols, sc->rows); #endif pad_setup_intr(sc->gpio, ckb_ec_intr, sc); kbd = &sc->sc_kbd; rid = 0; sc->scan_local = malloc(sc->cols, M_DEVBUF, M_NOWAIT); sc->scan = malloc(sc->cols, M_DEVBUF, M_NOWAIT); for (i = 0; i < sc->cols; i++) { sc->scan_local[i] = 0; sc->scan[i] = 0; } kbd_init_struct(kbd, KBD_DRIVER_NAME, KB_OTHER, device_get_unit(dev), 0, 0, 0); kbd->kb_data = (void *)sc; sc->sc_keymap = key_map; sc->sc_accmap = accent_map; for (i = 0; i < CKB_NFKEY; i++) { sc->sc_fkeymap[i] = fkey_tab[i]; } kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap, sc->sc_fkeymap, CKB_NFKEY); KBD_FOUND_DEVICE(kbd); ckb_clear_state(kbd); KBD_PROBE_DONE(kbd); callout_init(&sc->sc_repeat_callout, 0); KBD_INIT_DONE(kbd); if (kbd_register(kbd) < 0) { return (ENXIO); } KBD_CONFIG_DONE(kbd); return (0); } static int chrome_kb_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_is_compatible(dev, "google,cros-ec-keyb") || ofw_bus_is_compatible(dev, "google,mkbp-keyb")) { device_set_desc(dev, "Chrome EC Keyboard"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int chrome_kb_detach(device_t dev) { struct ckb_softc *sc; sc = device_get_softc(dev); if (sc->keymap != NULL) { free(sc->keymap, M_DEVBUF); } return 0; } static device_method_t chrome_kb_methods[] = { DEVMETHOD(device_probe, chrome_kb_probe), DEVMETHOD(device_attach, chrome_kb_attach), DEVMETHOD(device_detach, chrome_kb_detach), { 0, 0 } }; static driver_t chrome_kb_driver = { "chrome_kb", chrome_kb_methods, sizeof(struct ckb_softc), }; static devclass_t chrome_kb_devclass; DRIVER_MODULE(chrome_kb, simplebus, chrome_kb_driver, chrome_kb_devclass, 0, 0); Index: head/sys/arm/samsung/exynos/exynos_uart.c =================================================================== --- head/sys/arm/samsung/exynos/exynos_uart.c (revision 355270) +++ head/sys/arm/samsung/exynos/exynos_uart.c (revision 355271) @@ -1,390 +1,389 @@ /* * Copyright (c) 2003 Marcel Moolenaar * Copyright (c) 2007-2009 Andrew Turner * Copyright (c) 2013 Ruslan Bukin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include "uart_if.h" #define DEF_CLK 100000000 static int sscomspeed(long, long); static int exynos4210_uart_param(struct uart_bas *, int, int, int, int); /* * Low-level UART interface. */ static int exynos4210_probe(struct uart_bas *bas); static void exynos4210_init(struct uart_bas *bas, int, int, int, int); static void exynos4210_term(struct uart_bas *bas); static void exynos4210_putc(struct uart_bas *bas, int); static int exynos4210_rxready(struct uart_bas *bas); static int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx); extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static int sscomspeed(long speed, long frequency) { int x; if (speed <= 0 || frequency <= 0) return (-1); x = (frequency / 16) / speed; return (x-1); } static int exynos4210_uart_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { int brd, ulcon; ulcon = 0; switch(databits) { case 5: ulcon |= ULCON_LENGTH_5; break; case 6: ulcon |= ULCON_LENGTH_6; break; case 7: ulcon |= ULCON_LENGTH_7; break; case 8: ulcon |= ULCON_LENGTH_8; break; default: return (EINVAL); } switch (parity) { case UART_PARITY_NONE: ulcon |= ULCON_PARITY_NONE; break; case UART_PARITY_ODD: ulcon |= ULCON_PARITY_ODD; break; case UART_PARITY_EVEN: ulcon |= ULCON_PARITY_EVEN; break; case UART_PARITY_MARK: case UART_PARITY_SPACE: default: return (EINVAL); } if (stopbits == 2) ulcon |= ULCON_STOP; uart_setreg(bas, SSCOM_ULCON, ulcon); brd = sscomspeed(baudrate, bas->rclk); uart_setreg(bas, SSCOM_UBRDIV, brd); return (0); } struct uart_ops uart_exynos4210_ops = { .probe = exynos4210_probe, .init = exynos4210_init, .term = exynos4210_term, .putc = exynos4210_putc, .rxready = exynos4210_rxready, .getc = exynos4210_getc, }; static int exynos4210_probe(struct uart_bas *bas) { return (0); } static void exynos4210_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { if (bas->rclk == 0) bas->rclk = DEF_CLK; KASSERT(bas->rclk != 0, ("exynos4210_init: Invalid rclk")); uart_setreg(bas, SSCOM_UCON, 0); uart_setreg(bas, SSCOM_UFCON, UFCON_TXTRIGGER_8 | UFCON_RXTRIGGER_8 | UFCON_TXFIFO_RESET | UFCON_RXFIFO_RESET | UFCON_FIFO_ENABLE); exynos4210_uart_param(bas, baudrate, databits, stopbits, parity); /* Enable UART. */ uart_setreg(bas, SSCOM_UCON, UCON_TXMODE_INT | UCON_RXMODE_INT | UCON_TOINT); uart_setreg(bas, SSCOM_UMCON, UMCON_RTS); } static void exynos4210_term(struct uart_bas *bas) { /* XXX */ } static void exynos4210_putc(struct uart_bas *bas, int c) { while ((bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) & UFSTAT_TXFULL) == UFSTAT_TXFULL) continue; uart_setreg(bas, SSCOM_UTXH, c); } static int exynos4210_rxready(struct uart_bas *bas) { return ((uart_getreg(bas, SSCOM_UTRSTAT) & UTRSTAT_RXREADY) == UTRSTAT_RXREADY); } static int exynos4210_getc(struct uart_bas *bas, struct mtx *mtx) { int utrstat; utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT); while (!(utrstat & UTRSTAT_RXREADY)) { utrstat = bus_space_read_1(bas->bst, bas->bsh, SSCOM_UTRSTAT); continue; } return (bus_space_read_1(bas->bst, bas->bsh, SSCOM_URXH)); } static int exynos4210_bus_probe(struct uart_softc *sc); static int exynos4210_bus_attach(struct uart_softc *sc); static int exynos4210_bus_flush(struct uart_softc *, int); static int exynos4210_bus_getsig(struct uart_softc *); static int exynos4210_bus_ioctl(struct uart_softc *, int, intptr_t); static int exynos4210_bus_ipend(struct uart_softc *); static int exynos4210_bus_param(struct uart_softc *, int, int, int, int); static int exynos4210_bus_receive(struct uart_softc *); static int exynos4210_bus_setsig(struct uart_softc *, int); static int exynos4210_bus_transmit(struct uart_softc *); static kobj_method_t exynos4210_methods[] = { KOBJMETHOD(uart_probe, exynos4210_bus_probe), KOBJMETHOD(uart_attach, exynos4210_bus_attach), KOBJMETHOD(uart_flush, exynos4210_bus_flush), KOBJMETHOD(uart_getsig, exynos4210_bus_getsig), KOBJMETHOD(uart_ioctl, exynos4210_bus_ioctl), KOBJMETHOD(uart_ipend, exynos4210_bus_ipend), KOBJMETHOD(uart_param, exynos4210_bus_param), KOBJMETHOD(uart_receive, exynos4210_bus_receive), KOBJMETHOD(uart_setsig, exynos4210_bus_setsig), KOBJMETHOD(uart_transmit, exynos4210_bus_transmit), {0, 0 } }; int exynos4210_bus_probe(struct uart_softc *sc) { sc->sc_txfifosz = 16; sc->sc_rxfifosz = 16; return (0); } static int exynos4210_bus_attach(struct uart_softc *sc) { sc->sc_hwiflow = 0; sc->sc_hwoflow = 0; return (0); } static int exynos4210_bus_transmit(struct uart_softc *sc) { int i; int reg; uart_lock(sc->sc_hwmtx); for (i = 0; i < sc->sc_txdatasz; i++) { exynos4210_putc(&sc->sc_bas, sc->sc_txbuf[i]); uart_barrier(&sc->sc_bas); } sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); /* unmask TX interrupt */ reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM); reg &= ~(1 << 2); bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg); return (0); } static int exynos4210_bus_setsig(struct uart_softc *sc, int sig) { return (0); } static int exynos4210_bus_receive(struct uart_softc *sc) { struct uart_bas *bas; bas = &sc->sc_bas; while (bus_space_read_4(bas->bst, bas->bsh, SSCOM_UFSTAT) & UFSTAT_RXCOUNT) uart_rx_put(sc, uart_getreg(&sc->sc_bas, SSCOM_URXH)); return (0); } static int exynos4210_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { int error; if (sc->sc_bas.rclk == 0) sc->sc_bas.rclk = DEF_CLK; KASSERT(sc->sc_bas.rclk != 0, ("exynos4210_init: Invalid rclk")); uart_lock(sc->sc_hwmtx); error = exynos4210_uart_param(&sc->sc_bas, baudrate, databits, stopbits, parity); uart_unlock(sc->sc_hwmtx); return (error); } static int exynos4210_bus_ipend(struct uart_softc *sc) { uint32_t ints; uint32_t txempty, rxready; int reg; int ipend; uart_lock(sc->sc_hwmtx); ints = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP); bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTP, ints); txempty = (1 << 2); rxready = (1 << 0); ipend = 0; if ((ints & txempty) > 0) { if (sc->sc_txbusy != 0) ipend |= SER_INT_TXIDLE; /* mask TX interrupt */ reg = bus_space_read_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM); reg |= (1 << 2); bus_space_write_4(sc->sc_bas.bst, sc->sc_bas.bsh, SSCOM_UINTM, reg); } if ((ints & rxready) > 0) { ipend |= SER_INT_RXREADY; } uart_unlock(sc->sc_hwmtx); return (ipend); } static int exynos4210_bus_flush(struct uart_softc *sc, int what) { return (0); } static int exynos4210_bus_getsig(struct uart_softc *sc) { return (0); } static int exynos4210_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { return (EINVAL); } static struct uart_class uart_exynos4210_class = { "exynos4210 class", exynos4210_methods, 1, .uc_ops = &uart_exynos4210_ops, .uc_range = 8, .uc_rclk = 0, .uc_rshift = 0 }; static struct ofw_compat_data compat_data[] = { {"exynos", (uintptr_t)&uart_exynos4210_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); Index: head/sys/arm/versatile/pl050.c =================================================================== --- head/sys/arm/versatile/pl050.c (revision 355270) +++ head/sys/arm/versatile/pl050.c (revision 355271) @@ -1,743 +1,744 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2012 Oleksandr Tymoshenko * All rights reserved. * * Based on dev/usb/input/ukbd.c * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include +#include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #define KMI_LOCK() mtx_lock(&Giant) #define KMI_UNLOCK() mtx_unlock(&Giant) #ifdef INVARIANTS /* * Assert that the lock is held in all contexts * where the code can be executed. */ #define KMI_LOCK_ASSERT() mtx_assert(&Giant, MA_OWNED) /* * Assert that the lock is held in the contexts * where it really has to be so. */ #define KMI_CTX_LOCK_ASSERT() \ do { \ if (!kdb_active && panicstr == NULL) \ mtx_assert(&Giant, MA_OWNED); \ } while (0) #else #define KMI_LOCK_ASSERT() (void)0 #define KMI_CTX_LOCK_ASSERT() (void)0 #endif #define KMICR 0x00 #define KMICR_TYPE_NONPS2 (1 << 5) #define KMICR_RXINTREN (1 << 4) #define KMICR_TXINTREN (1 << 3) #define KMICR_EN (1 << 2) #define KMICR_FKMID (1 << 1) #define KMICR_FKMIC (1 << 0) #define KMISTAT 0x04 #define KMISTAT_TXEMPTY (1 << 6) #define KMISTAT_TXBUSY (1 << 5) #define KMISTAT_RXFULL (1 << 4) #define KMISTAT_RXBUSY (1 << 3) #define KMISTAT_RXPARITY (1 << 2) #define KMISTAT_KMIC (1 << 1) #define KMISTAT_KMID (1 << 0) #define KMIDATA 0x08 #define KMICLKDIV 0x0C #define KMIIR 0x10 #define KMIIR_TXINTR (1 << 1) #define KMIIR_RXINTR (1 << 0) #define KMI_DRIVER_NAME "kmi" #define KMI_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */ #define SET_SCANCODE_SET 0xf0 struct kmi_softc { device_t sc_dev; keyboard_t sc_kbd; keymap_t sc_keymap; accentmap_t sc_accmap; fkeytab_t sc_fkeymap[KMI_NFKEY]; struct resource* sc_mem_res; struct resource* sc_irq_res; void* sc_intr_hl; int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int sc_state; /* shift/lock key state */ int sc_accents; /* accent key index (> 0) */ uint32_t sc_flags; /* flags */ #define KMI_FLAG_COMPOSE 0x00000001 #define KMI_FLAG_POLLING 0x00000002 struct thread *sc_poll_thread; }; /* Read/Write macros for Timer used as timecounter */ #define pl050_kmi_read_4(sc, reg) \ bus_read_4((sc)->sc_mem_res, (reg)) #define pl050_kmi_write_4(sc, reg, val) \ bus_write_4((sc)->sc_mem_res, (reg), (val)) /* prototypes */ static void kmi_set_leds(struct kmi_softc *, uint8_t); static int kmi_set_typematic(keyboard_t *, int); static uint32_t kmi_read_char(keyboard_t *, int); static void kmi_clear_state(keyboard_t *); static int kmi_ioctl(keyboard_t *, u_long, caddr_t); static int kmi_enable(keyboard_t *); static int kmi_disable(keyboard_t *); static int kmi_attached = 0; /* early keyboard probe, not supported */ static int kmi_configure(int flags) { return (0); } /* detect a keyboard, not used */ static int kmi_probe(int unit, void *arg, int flags) { return (ENXIO); } /* reset and initialize the device, not used */ static int kmi_init(int unit, keyboard_t **kbdp, void *arg, int flags) { return (ENXIO); } /* test the interface to the device, not used */ static int kmi_test_if(keyboard_t *kbd) { return (0); } /* finish using this keyboard, not used */ static int kmi_term(keyboard_t *kbd) { return (ENXIO); } /* keyboard interrupt routine, not used */ static int kmi_intr(keyboard_t *kbd, void *arg) { return (0); } /* lock the access to the keyboard, not used */ static int kmi_lock(keyboard_t *kbd, int lock) { return (1); } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int kmi_enable(keyboard_t *kbd) { KMI_LOCK(); KBD_ACTIVATE(kbd); KMI_UNLOCK(); return (0); } /* disallow the access to the device */ static int kmi_disable(keyboard_t *kbd) { KMI_LOCK(); KBD_DEACTIVATE(kbd); KMI_UNLOCK(); return (0); } /* check if data is waiting */ static int kmi_check(keyboard_t *kbd) { struct kmi_softc *sc = kbd->kb_data; uint32_t reg; KMI_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (0); reg = pl050_kmi_read_4(sc, KMIIR); return (reg & KMIIR_RXINTR); } /* check if char is waiting */ static int kmi_check_char_locked(keyboard_t *kbd) { KMI_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (0); return (kmi_check(kbd)); } static int kmi_check_char(keyboard_t *kbd) { int result; KMI_LOCK(); result = kmi_check_char_locked(kbd); KMI_UNLOCK(); return (result); } /* read one byte from the keyboard if it's allowed */ /* Currently unused. */ static int kmi_read(keyboard_t *kbd, int wait) { KMI_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (-1); ++(kbd->kb_count); printf("Implement ME: %s\n", __func__); return (0); } /* read char from the keyboard */ static uint32_t kmi_read_char_locked(keyboard_t *kbd, int wait) { struct kmi_softc *sc = kbd->kb_data; uint32_t reg, data; KMI_CTX_LOCK_ASSERT(); if (!KBD_IS_ACTIVE(kbd)) return (NOKEY); reg = pl050_kmi_read_4(sc, KMIIR); if (reg & KMIIR_RXINTR) { data = pl050_kmi_read_4(sc, KMIDATA); return (data); } ++kbd->kb_count; return (NOKEY); } /* Currently wait is always false. */ static uint32_t kmi_read_char(keyboard_t *kbd, int wait) { uint32_t keycode; KMI_LOCK(); keycode = kmi_read_char_locked(kbd, wait); KMI_UNLOCK(); return (keycode); } /* some useful control functions */ static int kmi_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg) { struct kmi_softc *sc = kbd->kb_data; int i; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; #endif KMI_LOCK_ASSERT(); switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = sc->sc_mode; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 7): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (sc->sc_mode != K_XLATE) { /* make lock key state and LED state match */ sc->sc_state &= ~LOCK_MASK; sc->sc_state |= KBD_LED_VAL(kbd); } /* FALLTHROUGH */ case K_RAW: case K_CODE: if (sc->sc_mode != *(int *)arg) { if ((sc->sc_flags & KMI_FLAG_POLLING) == 0) kmi_clear_state(kbd); sc->sc_mode = *(int *)arg; } break; default: return (EINVAL); } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 66): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETLED: /* set keyboard LED */ /* NOTE: lock key state in "sc_state" won't be changed */ if (*(int *)arg & ~LOCK_MASK) return (EINVAL); i = *(int *)arg; /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ if (sc->sc_mode == K_XLATE && kbd->kb_keymap->n_keys > ALTGR_OFFSET) { if (i & ALKED) i |= CLKED; else i &= ~CLKED; } if (KBD_HAS_DEVICE(kbd)) kmi_set_leds(sc, i); KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = sc->sc_state & LOCK_MASK; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 20): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { return (EINVAL); } sc->sc_state &= ~LOCK_MASK; sc->sc_state |= *(int *)arg; /* set LEDs and quit */ return (kmi_ioctl(kbd, KDSETLED, arg)); case KDSETREPEAT: /* set keyboard repeat rate (new * interface) */ if (!KBD_HAS_DEVICE(kbd)) { return (0); } if (((int *)arg)[1] < 0) { return (EINVAL); } if (((int *)arg)[0] < 0) { return (EINVAL); } if (((int *)arg)[0] < 200) /* fastest possible value */ kbd->kb_delay1 = 200; else kbd->kb_delay1 = ((int *)arg)[0]; kbd->kb_delay2 = ((int *)arg)[1]; return (0); #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 67): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETRAD: /* set keyboard repeat rate (old * interface) */ return (kmi_set_typematic(kbd, *(int *)arg)); case PIO_KEYMAP: /* set keyboard translation table */ case OPIO_KEYMAP: /* set keyboard translation table * (compat) */ case PIO_KEYMAPENT: /* set keyboard translation table * entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ sc->sc_accents = 0; /* FALLTHROUGH */ default: return (genkbd_commonioctl(kbd, cmd, arg)); } return (0); } static int kmi_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { int result; /* * XXX KDGKBSTATE, KDSKBSTATE and KDSETLED can be called from any * context where printf(9) can be called, which among other things * includes interrupt filters and threads with any kinds of locks * already held. For this reason it would be dangerous to acquire * the Giant here unconditionally. On the other hand we have to * have it to handle the ioctl. * So we make our best effort to auto-detect whether we can grab * the Giant or not. Blame syscons(4) for this. */ switch (cmd) { case KDGKBSTATE: case KDSKBSTATE: case KDSETLED: if (!mtx_owned(&Giant) && !SCHEDULER_STOPPED()) return (EDEADLK); /* best I could come up with */ /* FALLTHROUGH */ default: KMI_LOCK(); result = kmi_ioctl_locked(kbd, cmd, arg); KMI_UNLOCK(); return (result); } } /* clear the internal state of the keyboard */ static void kmi_clear_state(keyboard_t *kbd) { struct kmi_softc *sc = kbd->kb_data; KMI_CTX_LOCK_ASSERT(); sc->sc_flags &= ~(KMI_FLAG_COMPOSE | KMI_FLAG_POLLING); sc->sc_state &= LOCK_MASK; /* preserve locking key state */ sc->sc_accents = 0; } /* save the internal state, not used */ static int kmi_get_state(keyboard_t *kbd, void *buf, size_t len) { return (len == 0) ? 1 : -1; } /* set the internal state, not used */ static int kmi_set_state(keyboard_t *kbd, void *buf, size_t len) { return (EINVAL); } static int kmi_poll(keyboard_t *kbd, int on) { struct kmi_softc *sc = kbd->kb_data; KMI_LOCK(); if (on) { sc->sc_flags |= KMI_FLAG_POLLING; sc->sc_poll_thread = curthread; } else { sc->sc_flags &= ~KMI_FLAG_POLLING; } KMI_UNLOCK(); return (0); } /* local functions */ static void kmi_set_leds(struct kmi_softc *sc, uint8_t leds) { KMI_LOCK_ASSERT(); /* start transfer, if not already started */ printf("Implement me: %s\n", __func__); } static int kmi_set_typematic(keyboard_t *kbd, int code) { static const int delays[] = {250, 500, 750, 1000}; static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126, 136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440, 472, 504}; if (code & ~0x7f) { return (EINVAL); } kbd->kb_delay1 = delays[(code >> 5) & 3]; kbd->kb_delay2 = rates[code & 0x1f]; return (0); } static keyboard_switch_t kmisw = { .probe = &kmi_probe, .init = &kmi_init, .term = &kmi_term, .intr = &kmi_intr, .test_if = &kmi_test_if, .enable = &kmi_enable, .disable = &kmi_disable, .read = &kmi_read, .check = &kmi_check, .read_char = &kmi_read_char, .check_char = &kmi_check_char, .ioctl = &kmi_ioctl, .lock = &kmi_lock, .clear_state = &kmi_clear_state, .get_state = &kmi_get_state, .set_state = &kmi_set_state, .get_fkeystr = &genkbd_get_fkeystr, .poll = &kmi_poll, .diag = &genkbd_diag, }; KEYBOARD_DRIVER(kmi, kmisw, kmi_configure); static void pl050_kmi_intr(void *arg) { struct kmi_softc *sc = arg; uint32_t c; KMI_CTX_LOCK_ASSERT(); if ((sc->sc_flags & KMI_FLAG_POLLING) != 0) return; if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) { /* let the callback function process the input */ (sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg); } else { /* read and discard the input, no one is waiting for it */ do { c = kmi_read_char_locked(&sc->sc_kbd, 0); } while (c != NOKEY); } } static int pl050_kmi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); /* * PL050 is plain PS2 port that pushes bytes to/from computer * VersatilePB has two such ports and QEMU simulates keyboard * connected to port #0 and mouse connected to port #1. This * information can't be obtained from device tree so we just * hardcode this knowledge here. We attach keyboard driver to * port #0 and ignore port #1 */ if (kmi_attached) return (ENXIO); if (ofw_bus_is_compatible(dev, "arm,pl050")) { device_set_desc(dev, "PL050 Keyboard/Mouse Interface"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int pl050_kmi_attach(device_t dev) { struct kmi_softc *sc = device_get_softc(dev); keyboard_t *kbd; int rid; int i; uint32_t ack; sc->sc_dev = dev; kbd = &sc->sc_kbd; rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } /* Request the IRQ resources */ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq_res == NULL) { device_printf(dev, "Error: could not allocate irq resources\n"); return (ENXIO); } /* Setup and enable the timer */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_CLK, NULL, pl050_kmi_intr, sc, &sc->sc_intr_hl) != 0) { bus_release_resource(dev, SYS_RES_IRQ, rid, sc->sc_irq_res); device_printf(dev, "Unable to setup the clock irq handler.\n"); return (ENXIO); } /* TODO: clock & divisor */ pl050_kmi_write_4(sc, KMICR, KMICR_EN); pl050_kmi_write_4(sc, KMIDATA, SET_SCANCODE_SET); /* read out ACK */ ack = pl050_kmi_read_4(sc, KMIDATA); /* Set Scan Code set 1 (XT) */ pl050_kmi_write_4(sc, KMIDATA, 1); /* read out ACK */ ack = pl050_kmi_read_4(sc, KMIDATA); pl050_kmi_write_4(sc, KMICR, KMICR_EN | KMICR_RXINTREN); kbd_init_struct(kbd, KMI_DRIVER_NAME, KB_OTHER, device_get_unit(dev), 0, 0, 0); kbd->kb_data = (void *)sc; sc->sc_keymap = key_map; sc->sc_accmap = accent_map; for (i = 0; i < KMI_NFKEY; i++) { sc->sc_fkeymap[i] = fkey_tab[i]; } kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap, sc->sc_fkeymap, KMI_NFKEY); KBD_FOUND_DEVICE(kbd); kmi_clear_state(kbd); KBD_PROBE_DONE(kbd); KBD_INIT_DONE(kbd); if (kbd_register(kbd) < 0) { goto detach; } KBD_CONFIG_DONE(kbd); #ifdef KBD_INSTALL_CDEV if (kbd_attach(kbd)) { goto detach; } #endif if (bootverbose) { genkbd_diag(kbd, bootverbose); } kmi_attached = 1; return (0); detach: return (ENXIO); } static device_method_t pl050_kmi_methods[] = { DEVMETHOD(device_probe, pl050_kmi_probe), DEVMETHOD(device_attach, pl050_kmi_attach), { 0, 0 } }; static driver_t pl050_kmi_driver = { "kmi", pl050_kmi_methods, sizeof(struct kmi_softc), }; static devclass_t pl050_kmi_devclass; DRIVER_MODULE(pl050_kmi, simplebus, pl050_kmi_driver, pl050_kmi_devclass, 0, 0); Index: head/sys/arm/xilinx/uart_dev_cdnc.c =================================================================== --- head/sys/arm/xilinx/uart_dev_cdnc.c (revision 355270) +++ head/sys/arm/xilinx/uart_dev_cdnc.c (revision 355271) @@ -1,718 +1,717 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005 M. Warner Losh * Copyright (c) 2005 Olivier Houchard * Copyright (c) 2012 Thomas Skibo * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* A driver for the Cadence AMBA UART as used by the Xilinx Zynq-7000. * * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. * (v1.4) November 16, 2012. Xilinx doc UG585. UART is covered in Ch. 19 * and register definitions are in appendix B.33. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include -#include #include #include #include #include #include #include "uart_if.h" #define UART_FIFO_SIZE 64 #define RD4(bas, reg) \ bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg))) #define WR4(bas, reg, value) \ bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg)), \ (value)) /* Register definitions for Cadence UART Controller. */ #define CDNC_UART_CTRL_REG 0x00 /* Control Register. */ #define CDNC_UART_CTRL_REG_STOPBRK (1<<8) #define CDNC_UART_CTRL_REG_STARTBRK (1<<7) #define CDNC_UART_CTRL_REG_TORST (1<<6) #define CDNC_UART_CTRL_REG_TX_DIS (1<<5) #define CDNC_UART_CTRL_REG_TX_EN (1<<4) #define CDNC_UART_CTRL_REG_RX_DIS (1<<3) #define CDNC_UART_CTRL_REG_RX_EN (1<<2) #define CDNC_UART_CTRL_REG_TXRST (1<<1) #define CDNC_UART_CTRL_REG_RXRST (1<<0) #define CDNC_UART_MODE_REG 0x04 /* Mode Register. */ #define CDNC_UART_MODE_REG_CHMOD_R_LOOP (3<<8) /* [9:8] - channel mode */ #define CDNC_UART_MODE_REG_CHMOD_L_LOOP (2<<8) #define CDNC_UART_MODE_REG_CHMOD_AUTECHO (1<<8) #define CDNC_UART_MODE_REG_STOP2 (2<<6) /* [7:6] - stop bits */ #define CDNC_UART_MODE_REG_PAR_NONE (4<<3) /* [5:3] - parity type */ #define CDNC_UART_MODE_REG_PAR_MARK (3<<3) #define CDNC_UART_MODE_REG_PAR_SPACE (2<<3) #define CDNC_UART_MODE_REG_PAR_ODD (1<<3) #define CDNC_UART_MODE_REG_PAR_EVEN (0<<3) #define CDNC_UART_MODE_REG_6BIT (3<<1) /* [2:1] - character len */ #define CDNC_UART_MODE_REG_7BIT (2<<1) #define CDNC_UART_MODE_REG_8BIT (0<<1) #define CDNC_UART_MODE_REG_CLKSEL (1<<0) #define CDNC_UART_IEN_REG 0x08 /* Interrupt registers. */ #define CDNC_UART_IDIS_REG 0x0C #define CDNC_UART_IMASK_REG 0x10 #define CDNC_UART_ISTAT_REG 0x14 #define CDNC_UART_INT_TXOVR (1<<12) #define CDNC_UART_INT_TXNRLYFUL (1<<11) /* tx "nearly" full */ #define CDNC_UART_INT_TXTRIG (1<<10) #define CDNC_UART_INT_DMSI (1<<9) /* delta modem status */ #define CDNC_UART_INT_RXTMOUT (1<<8) #define CDNC_UART_INT_PARITY (1<<7) #define CDNC_UART_INT_FRAMING (1<<6) #define CDNC_UART_INT_RXOVR (1<<5) #define CDNC_UART_INT_TXFULL (1<<4) #define CDNC_UART_INT_TXEMPTY (1<<3) #define CDNC_UART_INT_RXFULL (1<<2) #define CDNC_UART_INT_RXEMPTY (1<<1) #define CDNC_UART_INT_RXTRIG (1<<0) #define CDNC_UART_INT_ALL 0x1FFF #define CDNC_UART_BAUDGEN_REG 0x18 #define CDNC_UART_RX_TIMEO_REG 0x1C #define CDNC_UART_RX_WATER_REG 0x20 #define CDNC_UART_MODEM_CTRL_REG 0x24 #define CDNC_UART_MODEM_CTRL_REG_FCM (1<<5) /* automatic flow control */ #define CDNC_UART_MODEM_CTRL_REG_RTS (1<<1) #define CDNC_UART_MODEM_CTRL_REG_DTR (1<<0) #define CDNC_UART_MODEM_STAT_REG 0x28 #define CDNC_UART_MODEM_STAT_REG_FCMS (1<<8) /* flow control mode (rw) */ #define CDNC_UART_MODEM_STAT_REG_DCD (1<<7) #define CDNC_UART_MODEM_STAT_REG_RI (1<<6) #define CDNC_UART_MODEM_STAT_REG_DSR (1<<5) #define CDNC_UART_MODEM_STAT_REG_CTS (1<<4) #define CDNC_UART_MODEM_STAT_REG_DDCD (1<<3) /* change in DCD (w1tc) */ #define CDNC_UART_MODEM_STAT_REG_TERI (1<<2) /* trail edge ring (w1tc) */ #define CDNC_UART_MODEM_STAT_REG_DDSR (1<<1) /* change in DSR (w1tc) */ #define CDNC_UART_MODEM_STAT_REG_DCTS (1<<0) /* change in CTS (w1tc) */ #define CDNC_UART_CHAN_STAT_REG 0x2C /* Channel status register. */ #define CDNC_UART_CHAN_STAT_REG_TXNRLYFUL (1<<14) /* tx "nearly" full */ #define CDNC_UART_CHAN_STAT_REG_TXTRIG (1<<13) #define CDNC_UART_CHAN_STAT_REG_FDELT (1<<12) #define CDNC_UART_CHAN_STAT_REG_TXACTIVE (1<<11) #define CDNC_UART_CHAN_STAT_REG_RXACTIVE (1<<10) #define CDNC_UART_CHAN_STAT_REG_TXFULL (1<<4) #define CDNC_UART_CHAN_STAT_REG_TXEMPTY (1<<3) #define CDNC_UART_CHAN_STAT_REG_RXEMPTY (1<<1) #define CDNC_UART_CHAN_STAT_REG_RXTRIG (1<<0) #define CDNC_UART_FIFO 0x30 /* Data FIFO (tx and rx) */ #define CDNC_UART_BAUDDIV_REG 0x34 #define CDNC_UART_FLOWDEL_REG 0x38 #define CDNC_UART_TX_WATER_REG 0x44 /* * Low-level UART interface. */ static int cdnc_uart_probe(struct uart_bas *bas); static void cdnc_uart_init(struct uart_bas *bas, int, int, int, int); static void cdnc_uart_term(struct uart_bas *bas); static void cdnc_uart_putc(struct uart_bas *bas, int); static int cdnc_uart_rxready(struct uart_bas *bas); static int cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx); extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; static struct uart_ops cdnc_uart_ops = { .probe = cdnc_uart_probe, .init = cdnc_uart_init, .term = cdnc_uart_term, .putc = cdnc_uart_putc, .rxready = cdnc_uart_rxready, .getc = cdnc_uart_getc, }; #define SIGCHG(c, i, s, d) \ if (c) { \ i |= (i & s) ? s : s | d; \ } else { \ i = (i & s) ? (i & ~s) | d : i; \ } static int cdnc_uart_probe(struct uart_bas *bas) { return (0); } static int cdnc_uart_set_baud(struct uart_bas *bas, int baudrate) { uint32_t baudgen, bauddiv; uint32_t best_bauddiv, best_baudgen, best_error; uint32_t baud_out, err; best_bauddiv = 0; best_baudgen = 0; best_error = ~0; /* Try all possible bauddiv values and pick best match. */ for (bauddiv = 4; bauddiv <= 255; bauddiv++) { baudgen = (bas->rclk + (baudrate * (bauddiv + 1)) / 2) / (baudrate * (bauddiv + 1)); if (baudgen < 1 || baudgen > 0xffff) continue; baud_out = bas->rclk / (baudgen * (bauddiv + 1)); err = baud_out > baudrate ? baud_out - baudrate : baudrate - baud_out; if (err < best_error) { best_error = err; best_bauddiv = bauddiv; best_baudgen = baudgen; } } if (best_bauddiv > 0) { WR4(bas, CDNC_UART_BAUDDIV_REG, best_bauddiv); WR4(bas, CDNC_UART_BAUDGEN_REG, best_baudgen); return (0); } else return (-1); /* out of range */ } static int cdnc_uart_set_params(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { uint32_t mode_reg_value = 0; switch (databits) { case 6: mode_reg_value |= CDNC_UART_MODE_REG_6BIT; break; case 7: mode_reg_value |= CDNC_UART_MODE_REG_7BIT; break; case 8: default: mode_reg_value |= CDNC_UART_MODE_REG_8BIT; break; } if (stopbits == 2) mode_reg_value |= CDNC_UART_MODE_REG_STOP2; switch (parity) { case UART_PARITY_MARK: mode_reg_value |= CDNC_UART_MODE_REG_PAR_MARK; break; case UART_PARITY_SPACE: mode_reg_value |= CDNC_UART_MODE_REG_PAR_SPACE; break; case UART_PARITY_ODD: mode_reg_value |= CDNC_UART_MODE_REG_PAR_ODD; break; case UART_PARITY_EVEN: mode_reg_value |= CDNC_UART_MODE_REG_PAR_EVEN; break; case UART_PARITY_NONE: default: mode_reg_value |= CDNC_UART_MODE_REG_PAR_NONE; break; } WR4(bas, CDNC_UART_MODE_REG, mode_reg_value); if (baudrate > 0 && cdnc_uart_set_baud(bas, baudrate) < 0) return (EINVAL); return(0); } static void cdnc_uart_hw_init(struct uart_bas *bas) { /* Reset RX and TX. */ WR4(bas, CDNC_UART_CTRL_REG, CDNC_UART_CTRL_REG_RXRST | CDNC_UART_CTRL_REG_TXRST); /* Interrupts all off. */ WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_ALL); WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_ALL); /* Clear delta bits. */ WR4(bas, CDNC_UART_MODEM_STAT_REG, CDNC_UART_MODEM_STAT_REG_DDCD | CDNC_UART_MODEM_STAT_REG_TERI | CDNC_UART_MODEM_STAT_REG_DDSR | CDNC_UART_MODEM_STAT_REG_DCTS); /* RX FIFO water level, stale timeout */ WR4(bas, CDNC_UART_RX_WATER_REG, UART_FIFO_SIZE/2); WR4(bas, CDNC_UART_RX_TIMEO_REG, 10); /* TX FIFO water level (not used.) */ WR4(bas, CDNC_UART_TX_WATER_REG, UART_FIFO_SIZE/2); /* Bring RX and TX online. */ WR4(bas, CDNC_UART_CTRL_REG, CDNC_UART_CTRL_REG_RX_EN | CDNC_UART_CTRL_REG_TX_EN | CDNC_UART_CTRL_REG_TORST | CDNC_UART_CTRL_REG_STOPBRK); /* Set DTR and RTS. */ WR4(bas, CDNC_UART_MODEM_CTRL_REG, CDNC_UART_MODEM_CTRL_REG_DTR | CDNC_UART_MODEM_CTRL_REG_RTS); } /* * Initialize this device for use as a console. */ static void cdnc_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, int parity) { /* Initialize hardware. */ cdnc_uart_hw_init(bas); /* Set baudrate, parameters. */ (void)cdnc_uart_set_params(bas, baudrate, databits, stopbits, parity); } /* * Free resources now that we're no longer the console. This appears to * be never called, and I'm unsure quite what to do if I am called. */ static void cdnc_uart_term(struct uart_bas *bas) { /* XXX */ } /* * Put a character of console output (so we do it here polling rather than * interrutp driven). */ static void cdnc_uart_putc(struct uart_bas *bas, int c) { /* Wait for room. */ while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) & CDNC_UART_CHAN_STAT_REG_TXFULL) != 0) ; WR4(bas, CDNC_UART_FIFO, c); while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) & CDNC_UART_CHAN_STAT_REG_TXEMPTY) == 0) ; } /* * Check for a character available. */ static int cdnc_uart_rxready(struct uart_bas *bas) { return ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0); } /* * Block waiting for a character. */ static int cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx) { int c; uart_lock(mtx); while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & CDNC_UART_CHAN_STAT_REG_RXEMPTY) != 0) { uart_unlock(mtx); DELAY(4); uart_lock(mtx); } c = RD4(bas, CDNC_UART_FIFO); uart_unlock(mtx); c &= 0xff; return (c); } /*****************************************************************************/ /* * High-level UART interface. */ static int cdnc_uart_bus_probe(struct uart_softc *sc); static int cdnc_uart_bus_attach(struct uart_softc *sc); static int cdnc_uart_bus_flush(struct uart_softc *, int); static int cdnc_uart_bus_getsig(struct uart_softc *); static int cdnc_uart_bus_ioctl(struct uart_softc *, int, intptr_t); static int cdnc_uart_bus_ipend(struct uart_softc *); static int cdnc_uart_bus_param(struct uart_softc *, int, int, int, int); static int cdnc_uart_bus_receive(struct uart_softc *); static int cdnc_uart_bus_setsig(struct uart_softc *, int); static int cdnc_uart_bus_transmit(struct uart_softc *); static void cdnc_uart_bus_grab(struct uart_softc *); static void cdnc_uart_bus_ungrab(struct uart_softc *); static kobj_method_t cdnc_uart_bus_methods[] = { KOBJMETHOD(uart_probe, cdnc_uart_bus_probe), KOBJMETHOD(uart_attach, cdnc_uart_bus_attach), KOBJMETHOD(uart_flush, cdnc_uart_bus_flush), KOBJMETHOD(uart_getsig, cdnc_uart_bus_getsig), KOBJMETHOD(uart_ioctl, cdnc_uart_bus_ioctl), KOBJMETHOD(uart_ipend, cdnc_uart_bus_ipend), KOBJMETHOD(uart_param, cdnc_uart_bus_param), KOBJMETHOD(uart_receive, cdnc_uart_bus_receive), KOBJMETHOD(uart_setsig, cdnc_uart_bus_setsig), KOBJMETHOD(uart_transmit, cdnc_uart_bus_transmit), KOBJMETHOD(uart_grab, cdnc_uart_bus_grab), KOBJMETHOD(uart_ungrab, cdnc_uart_bus_ungrab), KOBJMETHOD_END }; int cdnc_uart_bus_probe(struct uart_softc *sc) { sc->sc_txfifosz = UART_FIFO_SIZE; sc->sc_rxfifosz = UART_FIFO_SIZE; sc->sc_hwiflow = 0; sc->sc_hwoflow = 0; device_set_desc(sc->sc_dev, "Cadence UART"); return (0); } static int cdnc_uart_bus_attach(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; struct uart_devinfo *di; if (sc->sc_sysdev != NULL) { di = sc->sc_sysdev; (void)cdnc_uart_set_params(bas, di->baudrate, di->databits, di->stopbits, di->parity); } else cdnc_uart_hw_init(bas); (void)cdnc_uart_bus_getsig(sc); /* Enable interrupts. */ WR4(bas, CDNC_UART_IEN_REG, CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT | CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | CDNC_UART_INT_DMSI); return (0); } static int cdnc_uart_bus_transmit(struct uart_softc *sc) { int i; struct uart_bas *bas = &sc->sc_bas; uart_lock(sc->sc_hwmtx); /* Clear sticky TXEMPTY status bit. */ WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_TXEMPTY); for (i = 0; i < sc->sc_txdatasz; i++) WR4(bas, CDNC_UART_FIFO, sc->sc_txbuf[i]); /* Enable TX empty interrupt. */ WR4(bas, CDNC_UART_IEN_REG, CDNC_UART_INT_TXEMPTY); sc->sc_txbusy = 1; uart_unlock(sc->sc_hwmtx); return (0); } static int cdnc_uart_bus_setsig(struct uart_softc *sc, int sig) { struct uart_bas *bas = &sc->sc_bas; uint32_t new, old, modem_ctrl; do { old = sc->sc_hwsig; new = old; if (sig & SER_DDTR) { SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); } if (sig & SER_DRTS) { SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); } } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); uart_lock(sc->sc_hwmtx); modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG) & ~(CDNC_UART_MODEM_CTRL_REG_DTR | CDNC_UART_MODEM_CTRL_REG_RTS); if ((new & SER_DTR) != 0) modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_DTR; if ((new & SER_RTS) != 0) modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS; WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl); uart_unlock(sc->sc_hwmtx); return (0); } static int cdnc_uart_bus_receive(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; uint32_t status; int c, c_status = 0; uart_lock(sc->sc_hwmtx); /* Check for parity or framing errors and clear the status bits. */ status = RD4(bas, CDNC_UART_ISTAT_REG); if ((status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY)) != 0) { WR4(bas, CDNC_UART_ISTAT_REG, status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY)); if ((status & CDNC_UART_INT_PARITY) != 0) c_status |= UART_STAT_PARERR; if ((status & CDNC_UART_INT_FRAMING) != 0) c_status |= UART_STAT_FRAMERR; } while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0) { c = RD4(bas, CDNC_UART_FIFO) & 0xff; #ifdef KDB /* Detect break and drop into debugger. */ if (c == 0 && (c_status & UART_STAT_FRAMERR) != 0 && sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) { kdb_break(); WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_FRAMING); } #endif uart_rx_put(sc, c | c_status); } uart_unlock(sc->sc_hwmtx); return (0); } static int cdnc_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, int stopbits, int parity) { return (cdnc_uart_set_params(&sc->sc_bas, baudrate, databits, stopbits, parity)); } static int cdnc_uart_bus_ipend(struct uart_softc *sc) { int ipend = 0; struct uart_bas *bas = &sc->sc_bas; uint32_t istatus; uart_lock(sc->sc_hwmtx); istatus = RD4(bas, CDNC_UART_ISTAT_REG); /* Clear interrupt bits. */ WR4(bas, CDNC_UART_ISTAT_REG, istatus & (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT | CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | CDNC_UART_INT_TXEMPTY | CDNC_UART_INT_DMSI)); /* Receive data. */ if ((istatus & (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT)) != 0) ipend |= SER_INT_RXREADY; /* Transmit fifo empty. */ if (sc->sc_txbusy && (istatus & CDNC_UART_INT_TXEMPTY) != 0) { /* disable txempty interrupt. */ WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_TXEMPTY); ipend |= SER_INT_TXIDLE; } /* TX Overflow. */ if ((istatus & CDNC_UART_INT_TXOVR) != 0) ipend |= SER_INT_OVERRUN; /* RX Overflow. */ if ((istatus & CDNC_UART_INT_RXOVR) != 0) ipend |= SER_INT_OVERRUN; /* Modem signal change. */ if ((istatus & CDNC_UART_INT_DMSI) != 0) { WR4(bas, CDNC_UART_MODEM_STAT_REG, CDNC_UART_MODEM_STAT_REG_DDCD | CDNC_UART_MODEM_STAT_REG_TERI | CDNC_UART_MODEM_STAT_REG_DDSR | CDNC_UART_MODEM_STAT_REG_DCTS); ipend |= SER_INT_SIGCHG; } uart_unlock(sc->sc_hwmtx); return (ipend); } static int cdnc_uart_bus_flush(struct uart_softc *sc, int what) { return (0); } static int cdnc_uart_bus_getsig(struct uart_softc *sc) { struct uart_bas *bas = &sc->sc_bas; uint32_t new, old, sig; uint8_t modem_status; do { old = sc->sc_hwsig; sig = old; uart_lock(sc->sc_hwmtx); modem_status = RD4(bas, CDNC_UART_MODEM_STAT_REG); uart_unlock(sc->sc_hwmtx); SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DSR, sig, SER_DSR, SER_DDSR); SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_CTS, sig, SER_CTS, SER_DCTS); SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DCD, sig, SER_DCD, SER_DDCD); SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_RI, sig, SER_RI, SER_DRI); new = sig & ~SER_MASK_DELTA; } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); return (sig); } static int cdnc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) { struct uart_bas *bas = &sc->sc_bas; uint32_t uart_ctrl, modem_ctrl; int error = 0; uart_lock(sc->sc_hwmtx); switch (request) { case UART_IOCTL_BREAK: uart_ctrl = RD4(bas, CDNC_UART_CTRL_REG); if (data) { uart_ctrl |= CDNC_UART_CTRL_REG_STARTBRK; uart_ctrl &= ~CDNC_UART_CTRL_REG_STOPBRK; } else { uart_ctrl |= CDNC_UART_CTRL_REG_STOPBRK; uart_ctrl &= ~CDNC_UART_CTRL_REG_STARTBRK; } WR4(bas, CDNC_UART_CTRL_REG, uart_ctrl); break; case UART_IOCTL_IFLOW: modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG); if (data) modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS; else modem_ctrl &= ~CDNC_UART_MODEM_CTRL_REG_RTS; WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl); break; default: error = EINVAL; break; } uart_unlock(sc->sc_hwmtx); return (error); } static void cdnc_uart_bus_grab(struct uart_softc *sc) { /* Enable interrupts. */ WR4(&sc->sc_bas, CDNC_UART_IEN_REG, CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | CDNC_UART_INT_DMSI); } static void cdnc_uart_bus_ungrab(struct uart_softc *sc) { /* Enable interrupts. */ WR4(&sc->sc_bas, CDNC_UART_IEN_REG, CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT | CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | CDNC_UART_INT_DMSI); } static struct uart_class uart_cdnc_class = { "cdnc_uart", cdnc_uart_bus_methods, sizeof(struct uart_softc), .uc_ops = &cdnc_uart_ops, .uc_range = 8 }; static struct ofw_compat_data compat_data[] = { {"cadence,uart", (uintptr_t)&uart_cdnc_class}, {"cdns,uart-r1p12", (uintptr_t)&uart_cdnc_class}, {"xlnx,xuartps", (uintptr_t)&uart_cdnc_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); Index: head/sys/dev/gpio/gpiokeys.c =================================================================== --- head/sys/dev/gpio/gpiokeys.c (revision 355270) +++ head/sys/dev/gpio/gpiokeys.c (revision 355271) @@ -1,1017 +1,1016 @@ /*- * Copyright (c) 2015-2016 Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include "opt_kbd.h" #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #define KBD_DRIVER_NAME "gpiokeys" #define GPIOKEYS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define GPIOKEYS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define GPIOKEYS_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ "gpiokeys", MTX_DEF) #define GPIOKEYS_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx); #define GPIOKEYS_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define GPIOKEY_LOCK(_key) mtx_lock(&(_key)->mtx) #define GPIOKEY_UNLOCK(_key) mtx_unlock(&(_key)->mtx) #define GPIOKEY_LOCK_INIT(_key) \ mtx_init(&(_key)->mtx, "gpiokey", "gpiokey", MTX_DEF) #define GPIOKEY_LOCK_DESTROY(_key) mtx_destroy(&(_key)->mtx); #define KEY_PRESS 0 #define KEY_RELEASE 0x80 #define SCAN_PRESS 0 #define SCAN_RELEASE 0x80 #define SCAN_CHAR(c) ((c) & 0x7f) #define GPIOKEYS_GLOBAL_NMOD 8 /* units */ #define GPIOKEYS_GLOBAL_NKEYCODE 6 /* units */ #define GPIOKEYS_GLOBAL_IN_BUF_SIZE (2*(GPIOKEYS_GLOBAL_NMOD + (2*GPIOKEYS_GLOBAL_NKEYCODE))) /* bytes */ #define GPIOKEYS_GLOBAL_IN_BUF_FULL (GPIOKEYS_GLOBAL_IN_BUF_SIZE / 2) /* bytes */ #define GPIOKEYS_GLOBAL_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */ #define GPIOKEYS_GLOBAL_BUFFER_SIZE 64 /* bytes */ #define AUTOREPEAT_DELAY 250 #define AUTOREPEAT_REPEAT 34 struct gpiokeys_softc; struct gpiokey { struct gpiokeys_softc *parent_sc; gpio_pin_t pin; int irq_rid; struct resource *irq_res; void *intr_hl; struct mtx mtx; uint32_t keycode; int autorepeat; struct callout debounce_callout; struct callout repeat_callout; int repeat_delay; int repeat; int debounce_interval; }; struct gpiokeys_softc { device_t sc_dev; struct mtx sc_mtx; struct gpiokey *sc_keys; int sc_total_keys; keyboard_t sc_kbd; keymap_t sc_keymap; accentmap_t sc_accmap; fkeytab_t sc_fkeymap[GPIOKEYS_GLOBAL_NFKEY]; uint32_t sc_input[GPIOKEYS_GLOBAL_IN_BUF_SIZE]; /* input buffer */ uint32_t sc_time_ms; #define GPIOKEYS_GLOBAL_FLAG_POLLING 0x00000002 uint32_t sc_flags; /* flags */ int sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ int sc_state; /* shift/lock key state */ int sc_accents; /* accent key index (> 0) */ int sc_kbd_size; uint16_t sc_inputs; uint16_t sc_inputhead; uint16_t sc_inputtail; uint8_t sc_kbd_id; }; /* gpio-keys device */ static int gpiokeys_probe(device_t); static int gpiokeys_attach(device_t); static int gpiokeys_detach(device_t); /* kbd methods prototypes */ static int gpiokeys_set_typematic(keyboard_t *, int); static uint32_t gpiokeys_read_char(keyboard_t *, int); static void gpiokeys_clear_state(keyboard_t *); static int gpiokeys_ioctl(keyboard_t *, u_long, caddr_t); static int gpiokeys_enable(keyboard_t *); static int gpiokeys_disable(keyboard_t *); static void gpiokeys_event_keyinput(struct gpiokeys_softc *); static void gpiokeys_put_key(struct gpiokeys_softc *sc, uint32_t key) { GPIOKEYS_ASSERT_LOCKED(sc); if (sc->sc_inputs < GPIOKEYS_GLOBAL_IN_BUF_SIZE) { sc->sc_input[sc->sc_inputtail] = key; ++(sc->sc_inputs); ++(sc->sc_inputtail); if (sc->sc_inputtail >= GPIOKEYS_GLOBAL_IN_BUF_SIZE) { sc->sc_inputtail = 0; } } else { device_printf(sc->sc_dev, "input buffer is full\n"); } } static void gpiokeys_key_event(struct gpiokeys_softc *sc, uint16_t keycode, int pressed) { uint32_t key; key = keycode & SCAN_KEYCODE_MASK; if (!pressed) key |= KEY_RELEASE; GPIOKEYS_LOCK(sc); if (keycode & SCAN_PREFIX_E0) gpiokeys_put_key(sc, 0xe0); else if (keycode & SCAN_PREFIX_E1) gpiokeys_put_key(sc, 0xe1); gpiokeys_put_key(sc, key); GPIOKEYS_UNLOCK(sc); gpiokeys_event_keyinput(sc); } static void gpiokey_autorepeat(void *arg) { struct gpiokey *key; key = arg; if (key->keycode == GPIOKEY_NONE) return; gpiokeys_key_event(key->parent_sc, key->keycode, 1); callout_reset(&key->repeat_callout, key->repeat, gpiokey_autorepeat, key); } static void gpiokey_debounced_intr(void *arg) { struct gpiokey *key; bool active; key = arg; if (key->keycode == GPIOKEY_NONE) return; gpio_pin_is_active(key->pin, &active); if (active) { gpiokeys_key_event(key->parent_sc, key->keycode, 1); if (key->autorepeat) { callout_reset(&key->repeat_callout, key->repeat_delay, gpiokey_autorepeat, key); } } else { if (key->autorepeat && callout_pending(&key->repeat_callout)) callout_stop(&key->repeat_callout); gpiokeys_key_event(key->parent_sc, key->keycode, 0); } } static void gpiokey_intr(void *arg) { struct gpiokey *key; int debounce_ticks; key = arg; GPIOKEY_LOCK(key); debounce_ticks = (hz * key->debounce_interval) / 1000; if (debounce_ticks == 0) debounce_ticks = 1; if (!callout_pending(&key->debounce_callout)) callout_reset(&key->debounce_callout, debounce_ticks, gpiokey_debounced_intr, key); GPIOKEY_UNLOCK(key); } static void gpiokeys_attach_key(struct gpiokeys_softc *sc, phandle_t node, struct gpiokey *key) { pcell_t prop; char *name; uint32_t code; int err; const char *key_name; GPIOKEY_LOCK_INIT(key); key->parent_sc = sc; callout_init_mtx(&key->debounce_callout, &key->mtx, 0); callout_init_mtx(&key->repeat_callout, &key->mtx, 0); name = NULL; if (OF_getprop_alloc(node, "label", (void **)&name) == -1) OF_getprop_alloc(node, "name", (void **)&name); if (name != NULL) key_name = name; else key_name = "unknown"; key->autorepeat = OF_hasprop(node, "autorepeat"); key->repeat_delay = (hz * AUTOREPEAT_DELAY) / 1000; if (key->repeat_delay == 0) key->repeat_delay = 1; key->repeat = (hz * AUTOREPEAT_REPEAT) / 1000; if (key->repeat == 0) key->repeat = 1; if ((OF_getprop(node, "debounce-interval", &prop, sizeof(prop))) > 0) key->debounce_interval = fdt32_to_cpu(prop); else key->debounce_interval = 5; if ((OF_getprop(node, "freebsd,code", &prop, sizeof(prop))) > 0) key->keycode = fdt32_to_cpu(prop); else if ((OF_getprop(node, "linux,code", &prop, sizeof(prop))) > 0) { code = fdt32_to_cpu(prop); key->keycode = gpiokey_map_linux_code(code); if (key->keycode == GPIOKEY_NONE) device_printf(sc->sc_dev, "<%s> failed to map linux,code value 0x%x\n", key_name, code); } else device_printf(sc->sc_dev, "<%s> no linux,code or freebsd,code property\n", key_name); err = gpio_pin_get_by_ofw_idx(sc->sc_dev, node, 0, &key->pin); if (err) { device_printf(sc->sc_dev, "<%s> failed to map pin\n", key_name); if (name) OF_prop_free(name); return; } key->irq_res = gpio_alloc_intr_resource(sc->sc_dev, &key->irq_rid, RF_ACTIVE, key->pin, GPIO_INTR_EDGE_BOTH); if (!key->irq_res) { device_printf(sc->sc_dev, "<%s> cannot allocate interrupt\n", key_name); gpio_pin_release(key->pin); key->pin = NULL; if (name) OF_prop_free(name); return; } if (bus_setup_intr(sc->sc_dev, key->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, gpiokey_intr, key, &key->intr_hl) != 0) { device_printf(sc->sc_dev, "<%s> unable to setup the irq handler\n", key_name); bus_release_resource(sc->sc_dev, SYS_RES_IRQ, key->irq_rid, key->irq_res); gpio_pin_release(key->pin); key->pin = NULL; key->irq_res = NULL; if (name) OF_prop_free(name); return; } if (bootverbose) device_printf(sc->sc_dev, "<%s> code=%08x, autorepeat=%d, "\ "repeat=%d, repeat_delay=%d\n", key_name, key->keycode, key->autorepeat, key->repeat, key->repeat_delay); if (name) OF_prop_free(name); } static void gpiokeys_detach_key(struct gpiokeys_softc *sc, struct gpiokey *key) { GPIOKEY_LOCK(key); if (key->intr_hl) bus_teardown_intr(sc->sc_dev, key->irq_res, key->intr_hl); if (key->irq_res) bus_release_resource(sc->sc_dev, SYS_RES_IRQ, key->irq_rid, key->irq_res); if (callout_pending(&key->repeat_callout)) callout_drain(&key->repeat_callout); if (callout_pending(&key->debounce_callout)) callout_drain(&key->debounce_callout); if (key->pin) gpio_pin_release(key->pin); GPIOKEY_UNLOCK(key); GPIOKEY_LOCK_DESTROY(key); } static int gpiokeys_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "gpio-keys")) return (ENXIO); device_set_desc(dev, "GPIO keyboard"); return (0); } static int gpiokeys_attach(device_t dev) { int unit; struct gpiokeys_softc *sc; keyboard_t *kbd; phandle_t keys, child; int total_keys; if ((keys = ofw_bus_get_node(dev)) == -1) return (ENXIO); sc = device_get_softc(dev); sc->sc_dev = dev; kbd = &sc->sc_kbd; GPIOKEYS_LOCK_INIT(sc); unit = device_get_unit(dev); kbd_init_struct(kbd, KBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0); kbd->kb_data = (void *)sc; sc->sc_mode = K_XLATE; sc->sc_keymap = key_map; sc->sc_accmap = accent_map; kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap, sc->sc_fkeymap, GPIOKEYS_GLOBAL_NFKEY); KBD_FOUND_DEVICE(kbd); gpiokeys_clear_state(kbd); KBD_PROBE_DONE(kbd); KBD_INIT_DONE(kbd); if (kbd_register(kbd) < 0) { goto detach; } KBD_CONFIG_DONE(kbd); gpiokeys_enable(kbd); #ifdef KBD_INSTALL_CDEV if (kbd_attach(kbd)) { goto detach; } #endif if (bootverbose) { genkbd_diag(kbd, 1); } total_keys = 0; /* Traverse the 'gpio-keys' node and count keys */ for (child = OF_child(keys); child != 0; child = OF_peer(child)) { if (!OF_hasprop(child, "gpios")) continue; total_keys++; } if (total_keys) { sc->sc_keys = malloc(sizeof(struct gpiokey) * total_keys, M_DEVBUF, M_WAITOK | M_ZERO); sc->sc_total_keys = 0; /* Traverse the 'gpio-keys' node and count keys */ for (child = OF_child(keys); child != 0; child = OF_peer(child)) { if (!OF_hasprop(child, "gpios")) continue; gpiokeys_attach_key(sc, child ,&sc->sc_keys[sc->sc_total_keys]); sc->sc_total_keys++; } } return (0); detach: gpiokeys_detach(dev); return (ENXIO); } static int gpiokeys_detach(device_t dev) { struct gpiokeys_softc *sc; keyboard_t *kbd; int i; sc = device_get_softc(dev); for (i = 0; i < sc->sc_total_keys; i++) gpiokeys_detach_key(sc, &sc->sc_keys[i]); kbd = kbd_get_keyboard(kbd_find_keyboard(KBD_DRIVER_NAME, device_get_unit(dev))); #ifdef KBD_INSTALL_CDEV kbd_detach(kbd); #endif kbd_unregister(kbd); GPIOKEYS_LOCK_DESTROY(sc); if (sc->sc_keys) free(sc->sc_keys, M_DEVBUF); return (0); } /* early keyboard probe, not supported */ static int gpiokeys_configure(int flags) { return (0); } /* detect a keyboard, not used */ static int gpiokeys__probe(int unit, void *arg, int flags) { return (ENXIO); } /* reset and initialize the device, not used */ static int gpiokeys_init(int unit, keyboard_t **kbdp, void *arg, int flags) { return (ENXIO); } /* test the interface to the device, not used */ static int gpiokeys_test_if(keyboard_t *kbd) { return (0); } /* finish using this keyboard, not used */ static int gpiokeys_term(keyboard_t *kbd) { return (ENXIO); } /* keyboard interrupt routine, not used */ static int gpiokeys_intr(keyboard_t *kbd, void *arg) { return (0); } /* lock the access to the keyboard, not used */ static int gpiokeys_lock(keyboard_t *kbd, int lock) { return (1); } /* * Enable the access to the device; until this function is called, * the client cannot read from the keyboard. */ static int gpiokeys_enable(keyboard_t *kbd) { struct gpiokeys_softc *sc; sc = kbd->kb_data; GPIOKEYS_LOCK(sc); KBD_ACTIVATE(kbd); GPIOKEYS_UNLOCK(sc); return (0); } /* disallow the access to the device */ static int gpiokeys_disable(keyboard_t *kbd) { struct gpiokeys_softc *sc; sc = kbd->kb_data; GPIOKEYS_LOCK(sc); KBD_DEACTIVATE(kbd); GPIOKEYS_UNLOCK(sc); return (0); } static void gpiokeys_do_poll(struct gpiokeys_softc *sc, uint8_t wait) { KASSERT((sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) != 0, ("gpiokeys_do_poll called when not polling\n")); GPIOKEYS_ASSERT_LOCKED(sc); if (!kdb_active && !SCHEDULER_STOPPED()) { while (sc->sc_inputs == 0) { kern_yield(PRI_UNCHANGED); if (!wait) break; } return; } while ((sc->sc_inputs == 0) && wait) { printf("POLL!\n"); } } /* check if data is waiting */ static int gpiokeys_check(keyboard_t *kbd) { struct gpiokeys_softc *sc = kbd->kb_data; GPIOKEYS_ASSERT_LOCKED(sc); if (!KBD_IS_ACTIVE(kbd)) return (0); if (sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) gpiokeys_do_poll(sc, 0); if (sc->sc_inputs > 0) { return (1); } return (0); } /* check if char is waiting */ static int gpiokeys_check_char_locked(keyboard_t *kbd) { if (!KBD_IS_ACTIVE(kbd)) return (0); return (gpiokeys_check(kbd)); } static int gpiokeys_check_char(keyboard_t *kbd) { int result; struct gpiokeys_softc *sc = kbd->kb_data; GPIOKEYS_LOCK(sc); result = gpiokeys_check_char_locked(kbd); GPIOKEYS_UNLOCK(sc); return (result); } static int32_t gpiokeys_get_key(struct gpiokeys_softc *sc, uint8_t wait) { int32_t c; KASSERT((!kdb_active && !SCHEDULER_STOPPED()) || (sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) != 0, ("not polling in kdb or panic\n")); GPIOKEYS_ASSERT_LOCKED(sc); if (sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) gpiokeys_do_poll(sc, wait); if (sc->sc_inputs == 0) { c = -1; } else { c = sc->sc_input[sc->sc_inputhead]; --(sc->sc_inputs); ++(sc->sc_inputhead); if (sc->sc_inputhead >= GPIOKEYS_GLOBAL_IN_BUF_SIZE) { sc->sc_inputhead = 0; } } return (c); } /* read one byte from the keyboard if it's allowed */ static int gpiokeys_read(keyboard_t *kbd, int wait) { struct gpiokeys_softc *sc = kbd->kb_data; int32_t keycode; if (!KBD_IS_ACTIVE(kbd)) return (-1); /* XXX */ keycode = gpiokeys_get_key(sc, (wait == FALSE) ? 0 : 1); if (!KBD_IS_ACTIVE(kbd) || (keycode == -1)) return (-1); ++(kbd->kb_count); return (keycode); } /* read char from the keyboard */ static uint32_t gpiokeys_read_char_locked(keyboard_t *kbd, int wait) { struct gpiokeys_softc *sc = kbd->kb_data; uint32_t action; uint32_t keycode; if (!KBD_IS_ACTIVE(kbd)) return (NOKEY); next_code: /* see if there is something in the keyboard port */ /* XXX */ keycode = gpiokeys_get_key(sc, (wait == FALSE) ? 0 : 1); ++kbd->kb_count; /* return the byte as is for the K_RAW mode */ if (sc->sc_mode == K_RAW) { return (keycode); } /* return the key code in the K_CODE mode */ /* XXX: keycode |= SCAN_RELEASE; */ if (sc->sc_mode == K_CODE) { return (keycode); } /* keycode to key action */ action = genkbd_keyaction(kbd, SCAN_CHAR(keycode), (keycode & SCAN_RELEASE), &sc->sc_state, &sc->sc_accents); if (action == NOKEY) { goto next_code; } return (action); } /* Currently wait is always false. */ static uint32_t gpiokeys_read_char(keyboard_t *kbd, int wait) { uint32_t keycode; struct gpiokeys_softc *sc = kbd->kb_data; GPIOKEYS_LOCK(sc); keycode = gpiokeys_read_char_locked(kbd, wait); GPIOKEYS_UNLOCK(sc); return (keycode); } /* some useful control functions */ static int gpiokeys_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg) { struct gpiokeys_softc *sc = kbd->kb_data; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) int ival; #endif switch (cmd) { case KDGKBMODE: /* get keyboard mode */ *(int *)arg = sc->sc_mode; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 7): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBMODE: /* set keyboard mode */ switch (*(int *)arg) { case K_XLATE: if (sc->sc_mode != K_XLATE) { /* make lock key state and LED state match */ sc->sc_state &= ~LOCK_MASK; sc->sc_state |= KBD_LED_VAL(kbd); } /* FALLTHROUGH */ case K_RAW: case K_CODE: if (sc->sc_mode != *(int *)arg) { if ((sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) == 0) gpiokeys_clear_state(kbd); sc->sc_mode = *(int *)arg; } break; default: return (EINVAL); } break; case KDGETLED: /* get keyboard LED */ *(int *)arg = KBD_LED_VAL(kbd); break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 66): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETLED: /* set keyboard LED */ KBD_LED_VAL(kbd) = *(int *)arg; break; case KDGKBSTATE: /* get lock key state */ *(int *)arg = sc->sc_state & LOCK_MASK; break; #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 20): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSKBSTATE: /* set lock key state */ if (*(int *)arg & ~LOCK_MASK) { return (EINVAL); } sc->sc_state &= ~LOCK_MASK; sc->sc_state |= *(int *)arg; return (0); case KDSETREPEAT: /* set keyboard repeat rate (new * interface) */ if (!KBD_HAS_DEVICE(kbd)) { return (0); } if (((int *)arg)[1] < 0) { return (EINVAL); } if (((int *)arg)[0] < 0) { return (EINVAL); } if (((int *)arg)[0] < 200) /* fastest possible value */ kbd->kb_delay1 = 200; else kbd->kb_delay1 = ((int *)arg)[0]; kbd->kb_delay2 = ((int *)arg)[1]; return (0); #if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD4) || defined(COMPAT_43) case _IO('K', 67): ival = IOCPARM_IVAL(arg); arg = (caddr_t)&ival; /* FALLTHROUGH */ #endif case KDSETRAD: /* set keyboard repeat rate (old * interface) */ return (gpiokeys_set_typematic(kbd, *(int *)arg)); case PIO_KEYMAP: /* set keyboard translation table */ case OPIO_KEYMAP: /* set keyboard translation table * (compat) */ case PIO_KEYMAPENT: /* set keyboard translation table * entry */ case PIO_DEADKEYMAP: /* set accent key translation table */ sc->sc_accents = 0; /* FALLTHROUGH */ default: return (genkbd_commonioctl(kbd, cmd, arg)); } return (0); } static int gpiokeys_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { int result; struct gpiokeys_softc *sc; sc = kbd->kb_data; /* * XXX Check if someone is calling us from a critical section: */ if (curthread->td_critnest != 0) return (EDEADLK); GPIOKEYS_LOCK(sc); result = gpiokeys_ioctl_locked(kbd, cmd, arg); GPIOKEYS_UNLOCK(sc); return (result); } /* clear the internal state of the keyboard */ static void gpiokeys_clear_state(keyboard_t *kbd) { struct gpiokeys_softc *sc = kbd->kb_data; sc->sc_flags &= ~(GPIOKEYS_GLOBAL_FLAG_POLLING); sc->sc_state &= LOCK_MASK; /* preserve locking key state */ sc->sc_accents = 0; } /* get the internal state, not used */ static int gpiokeys_get_state(keyboard_t *kbd, void *buf, size_t len) { return (len == 0) ? 1 : -1; } /* set the internal state, not used */ static int gpiokeys_set_state(keyboard_t *kbd, void *buf, size_t len) { return (EINVAL); } static int gpiokeys_poll(keyboard_t *kbd, int on) { struct gpiokeys_softc *sc = kbd->kb_data; GPIOKEYS_LOCK(sc); if (on) sc->sc_flags |= GPIOKEYS_GLOBAL_FLAG_POLLING; else sc->sc_flags &= ~GPIOKEYS_GLOBAL_FLAG_POLLING; GPIOKEYS_UNLOCK(sc); return (0); } static int gpiokeys_set_typematic(keyboard_t *kbd, int code) { static const int delays[] = {250, 500, 750, 1000}; static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126, 136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440, 472, 504}; if (code & ~0x7f) { return (EINVAL); } kbd->kb_delay1 = delays[(code >> 5) & 3]; kbd->kb_delay2 = rates[code & 0x1f]; return (0); } static void gpiokeys_event_keyinput(struct gpiokeys_softc *sc) { int c; if ((sc->sc_flags & GPIOKEYS_GLOBAL_FLAG_POLLING) != 0) return; if (KBD_IS_ACTIVE(&sc->sc_kbd) && KBD_IS_BUSY(&sc->sc_kbd)) { /* let the callback function process the input */ (sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT, sc->sc_kbd.kb_callback.kc_arg); } else { /* read and discard the input, no one is waiting for it */ do { c = gpiokeys_read_char(&sc->sc_kbd, 0); } while (c != NOKEY); } } static keyboard_switch_t gpiokeyssw = { .probe = &gpiokeys__probe, .init = &gpiokeys_init, .term = &gpiokeys_term, .intr = &gpiokeys_intr, .test_if = &gpiokeys_test_if, .enable = &gpiokeys_enable, .disable = &gpiokeys_disable, .read = &gpiokeys_read, .check = &gpiokeys_check, .read_char = &gpiokeys_read_char, .check_char = &gpiokeys_check_char, .ioctl = &gpiokeys_ioctl, .lock = &gpiokeys_lock, .clear_state = &gpiokeys_clear_state, .get_state = &gpiokeys_get_state, .set_state = &gpiokeys_set_state, .get_fkeystr = &genkbd_get_fkeystr, .poll = &gpiokeys_poll, .diag = &genkbd_diag, }; KEYBOARD_DRIVER(gpiokeys, gpiokeyssw, gpiokeys_configure); static int gpiokeys_driver_load(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: kbd_add_driver(&gpiokeys_kbd_driver); break; case MOD_UNLOAD: kbd_delete_driver(&gpiokeys_kbd_driver); break; } return (0); } static devclass_t gpiokeys_devclass; static device_method_t gpiokeys_methods[] = { DEVMETHOD(device_probe, gpiokeys_probe), DEVMETHOD(device_attach, gpiokeys_attach), DEVMETHOD(device_detach, gpiokeys_detach), DEVMETHOD_END }; static driver_t gpiokeys_driver = { "gpiokeys", gpiokeys_methods, sizeof(struct gpiokeys_softc), }; DRIVER_MODULE(gpiokeys, simplebus, gpiokeys_driver, gpiokeys_devclass, gpiokeys_driver_load, 0); MODULE_VERSION(gpiokeys, 1); Index: head/sys/dev/quicc/quicc_bfe_fdt.c =================================================================== --- head/sys/dev/quicc/quicc_bfe_fdt.c (revision 355270) +++ head/sys/dev/quicc/quicc_bfe_fdt.c (revision 355271) @@ -1,93 +1,92 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2006 Juniper Networks. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include -#include #include #include #include #include static int quicc_fdt_probe(device_t dev); static device_method_t quicc_fdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, quicc_fdt_probe), DEVMETHOD(device_attach, quicc_bfe_attach), DEVMETHOD(device_detach, quicc_bfe_detach), DEVMETHOD(bus_alloc_resource, quicc_bus_alloc_resource), DEVMETHOD(bus_release_resource, quicc_bus_release_resource), DEVMETHOD(bus_get_resource, quicc_bus_get_resource), DEVMETHOD(bus_read_ivar, quicc_bus_read_ivar), DEVMETHOD(bus_setup_intr, quicc_bus_setup_intr), DEVMETHOD(bus_teardown_intr, quicc_bus_teardown_intr), DEVMETHOD_END }; static driver_t quicc_fdt_driver = { quicc_driver_name, quicc_fdt_methods, sizeof(struct quicc_softc), }; static int quicc_fdt_probe(device_t dev) { phandle_t par; pcell_t clock; if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,cpm2")) return (ENXIO); par = OF_parent(ofw_bus_get_node(dev)); if (OF_getprop(par, "bus-frequency", &clock, sizeof(clock)) <= 0) clock = 0; return (quicc_bfe_probe(dev, (uintptr_t)clock)); } DRIVER_MODULE(quicc, simplebus, quicc_fdt_driver, quicc_devclass, 0, 0); Index: head/sys/dev/rp/rp_isa.c =================================================================== --- head/sys/dev/rp/rp_isa.c (revision 355270) +++ head/sys/dev/rp/rp_isa.c (revision 355271) @@ -1,507 +1,508 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) Comtrol Corporation * All rights reserved. * * ISA-specific part separated from: * sys/i386/isa/rp.c,v 1.33 1999/09/28 11:45:27 phk Exp * * Redistribution and use in source and binary forms, with or without * modification, are permitted prodived that the follwoing conditions * are met. * 1. Redistributions of source code must retain the above copyright * notive, this list of conditions and the following disclainer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials prodided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Comtrol Corporation. * 4. The name of Comtrol Corporation may not be used to endorse or * promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL COMTROL CORPORATION BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include -#include #include #include +#include #include +#include #include #include #include #include #define ROCKET_C #include #include #include /* ISA-specific part of CONTROLLER_t */ struct ISACONTROLLER_T { int MBaseIO; /* rid of the Mudbac controller for this controller */ int MReg0IO; /* offset0 of the Mudbac controller for this controller */ int MReg1IO; /* offset1 of the Mudbac controller for this controller */ int MReg2IO; /* offset2 of the Mudbac controller for this controller */ int MReg3IO; /* offset3 of the Mudbac controller for this controller */ Byte_t MReg2; Byte_t MReg3; }; typedef struct ISACONTROLLER_T ISACONTROLLER_t; #define ISACTL(ctlp) ((ISACONTROLLER_t *)((ctlp)->bus_ctlp)) /*************************************************************************** Function: sControllerEOI Purpose: Strobe the MUDBAC's End Of Interrupt bit. Call: sControllerEOI(MudbacCtlP,CtlP) CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure CONTROLLER_T *CtlP; Ptr to controller structure */ #define sControllerEOI(MudbacCtlP,CtlP) \ rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg2IO,ISACTL(CtlP)->MReg2 | INT_STROB) /*************************************************************************** Function: sDisAiop Purpose: Disable I/O access to an AIOP Call: sDisAiop(MudbacCtlP,CtlP) CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure CONTROLLER_T *CtlP; Ptr to controller structure int AiopNum; Number of AIOP on controller */ #define sDisAiop(MudbacCtlP,CtlP,AIOPNUM) \ { \ ISACTL(CtlP)->MReg3 &= rp_sBitMapClrTbl[AIOPNUM]; \ rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg3IO,ISACTL(CtlP)->MReg3); \ } /*************************************************************************** Function: sEnAiop Purpose: Enable I/O access to an AIOP Call: sEnAiop(MudbacCtlP,CtlP) CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure CONTROLLER_T *CtlP; Ptr to controller structure int AiopNum; Number of AIOP on controller */ #define sEnAiop(MudbacCtlP,CtlP,AIOPNUM) \ { \ ISACTL(CtlP)->MReg3 |= rp_sBitMapSetTbl[AIOPNUM]; \ rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg3IO,ISACTL(CtlP)->MReg3); \ } /*************************************************************************** Function: sGetControllerIntStatus Purpose: Get the controller interrupt status Call: sGetControllerIntStatus(MudbacCtlP,CtlP) CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure CONTROLLER_T *CtlP; Ptr to controller structure Return: Byte_t: The controller interrupt status in the lower 4 bits. Bits 0 through 3 represent AIOP's 0 through 3 respectively. If a bit is set that AIOP is interrupting. Bits 4 through 7 will always be cleared. */ #define sGetControllerIntStatus(MudbacCtlP,CtlP) \ (rp_readio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg1IO) & 0x0f) static devclass_t rp_devclass; static CONTROLLER_t *rp_controller; static int rp_nisadevs; static int rp_probe(device_t dev); static int rp_attach(device_t dev); static void rp_isareleaseresource(CONTROLLER_t *ctlp); static int sInitController(CONTROLLER_T *CtlP, CONTROLLER_T *MudbacCtlP, int AiopNum, int IRQNum, Byte_t Frequency, int PeriodicOnly); static rp_aiop2rid_t rp_isa_aiop2rid; static rp_aiop2off_t rp_isa_aiop2off; static rp_ctlmask_t rp_isa_ctlmask; static int rp_probe(device_t dev) { int unit; CONTROLLER_t *controller; int num_aiops; CONTROLLER_t *ctlp; int retval; /* * We have no PnP RocketPort cards. * (At least according to LINT) */ if (isa_get_logicalid(dev) != 0) return (ENXIO); /* We need IO port resource to configure an ISA device. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 0) return (ENXIO); unit = device_get_unit(dev); if (unit >= 4) { device_printf(dev, "rpprobe: unit number %d invalid.\n", unit); return (ENXIO); } device_printf(dev, "probing for RocketPort(ISA) unit %d.\n", unit); ctlp = device_get_softc(dev); bzero(ctlp, sizeof(*ctlp)); ctlp->dev = dev; ctlp->aiop2rid = rp_isa_aiop2rid; ctlp->aiop2off = rp_isa_aiop2off; ctlp->ctlmask = rp_isa_ctlmask; /* The IO ports of AIOPs for an ISA controller are discrete. */ ctlp->io_num = 1; ctlp->io_rid = malloc(sizeof(*(ctlp->io_rid)) * MAX_AIOPS_PER_BOARD, M_DEVBUF, M_NOWAIT | M_ZERO); ctlp->io = malloc(sizeof(*(ctlp->io)) * MAX_AIOPS_PER_BOARD, M_DEVBUF, M_NOWAIT | M_ZERO); if (ctlp->io_rid == NULL || ctlp->io == NULL) { device_printf(dev, "rp_attach: Out of memory.\n"); retval = ENOMEM; goto nogo; } ctlp->bus_ctlp = malloc(sizeof(ISACONTROLLER_t) * 1, M_DEVBUF, M_NOWAIT | M_ZERO); if (ctlp->bus_ctlp == NULL) { device_printf(dev, "rp_attach: Out of memory.\n"); retval = ENOMEM; goto nogo; } ctlp->io_rid[0] = 0; if (rp_controller != NULL) { controller = rp_controller; ctlp->io[0] = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &ctlp->io_rid[0], 0x40, RF_ACTIVE); } else { controller = rp_controller = ctlp; ctlp->io[0] = bus_alloc_resource_anywhere(dev, SYS_RES_IOPORT, &ctlp->io_rid[0], 0x44, RF_ACTIVE); } if (ctlp->io[0] == NULL) { device_printf(dev, "rp_attach: Resource not available.\n"); retval = ENXIO; goto nogo; } num_aiops = sInitController(ctlp, controller, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); if (num_aiops <= 0) { device_printf(dev, "board%d init failed.\n", unit); retval = ENXIO; goto nogo; } if (rp_controller == NULL) rp_controller = controller; rp_nisadevs++; device_set_desc(dev, "RocketPort ISA"); return (0); nogo: rp_isareleaseresource(ctlp); return (retval); } static int rp_attach(device_t dev) { int unit; int num_ports, num_aiops; int aiop; CONTROLLER_t *ctlp; int retval; unit = device_get_unit(dev); ctlp = device_get_softc(dev); #ifdef notdef num_aiops = sInitController(ctlp, rp_controller, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); #else num_aiops = ctlp->NumAiop; #endif /* notdef */ num_ports = 0; for(aiop=0; aiop < num_aiops; aiop++) { sResetAiopByNum(ctlp, aiop); sEnAiop(rp_controller, ctlp, aiop); num_ports += sGetAiopNumChan(ctlp, aiop); } retval = rp_attachcommon(ctlp, num_aiops, num_ports); if (retval != 0) goto nogo; return (0); nogo: rp_isareleaseresource(ctlp); return (retval); } static void rp_isareleaseresource(CONTROLLER_t *ctlp) { int i; rp_releaseresource(ctlp); if (ctlp == rp_controller) rp_controller = NULL; if (ctlp->io != NULL) { for (i = 0 ; i < MAX_AIOPS_PER_BOARD ; i++) if (ctlp->io[i] != NULL) bus_release_resource(ctlp->dev, SYS_RES_IOPORT, ctlp->io_rid[i], ctlp->io[i]); free(ctlp->io, M_DEVBUF); } if (ctlp->io_rid != NULL) free(ctlp->io_rid, M_DEVBUF); if (rp_controller != NULL && rp_controller->io[ISACTL(ctlp)->MBaseIO] != NULL) { bus_release_resource(rp_controller->dev, SYS_RES_IOPORT, rp_controller->io_rid[ISACTL(ctlp)->MBaseIO], rp_controller->io[ISACTL(ctlp)->MBaseIO]); rp_controller->io[ISACTL(ctlp)->MBaseIO] = NULL; rp_controller->io_rid[ISACTL(ctlp)->MBaseIO] = 0; } if (ctlp->bus_ctlp != NULL) free(ctlp->bus_ctlp, M_DEVBUF); } /*************************************************************************** Function: sInitController Purpose: Initialization of controller global registers and controller structure. Call: sInitController(CtlP,MudbacCtlP,AiopNum, IRQNum,Frequency,PeriodicOnly) CONTROLLER_T *CtlP; Ptr to controller structure CONTROLLER_T *MudbacCtlP; Ptr to Mudbac controller structure int AiopNum; Number of Aiops int IRQNum; Interrupt Request number. Can be any of the following: 0: Disable global interrupts 3: IRQ 3 4: IRQ 4 5: IRQ 5 9: IRQ 9 10: IRQ 10 11: IRQ 11 12: IRQ 12 15: IRQ 15 Byte_t Frequency: A flag identifying the frequency of the periodic interrupt, can be any one of the following: FREQ_DIS - periodic interrupt disabled FREQ_137HZ - 137 Hertz FREQ_69HZ - 69 Hertz FREQ_34HZ - 34 Hertz FREQ_17HZ - 17 Hertz FREQ_9HZ - 9 Hertz FREQ_4HZ - 4 Hertz If IRQNum is set to 0 the Frequency parameter is overidden, it is forced to a value of FREQ_DIS. int PeriodicOnly: TRUE if all interrupts except the periodic interrupt are to be blocked. FALSE is both the periodic interrupt and other channel interrupts are allowed. If IRQNum is set to 0 the PeriodicOnly parameter is overidden, it is forced to a value of FALSE. Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller initialization failed. Comments: If periodic interrupts are to be disabled but AIOP interrupts are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE. If interrupts are to be completely disabled set IRQNum to 0. Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an invalid combination. This function performs initialization of global interrupt modes, but it does not actually enable global interrupts. To enable and disable global interrupts use functions sEnGlobalInt() and sDisGlobalInt(). Enabling of global interrupts is normally not done until all other initializations are complete. Even if interrupts are globally enabled, they must also be individually enabled for each channel that is to generate interrupts. Warnings: No range checking on any of the parameters is done. No context switches are allowed while executing this function. After this function all AIOPs on the controller are disabled, they can be enabled with sEnAiop(). */ static int sInitController( CONTROLLER_T *CtlP, CONTROLLER_T *MudbacCtlP, int AiopNum, int IRQNum, Byte_t Frequency, int PeriodicOnly) { int i; int ctl_base, aiop_base, aiop_size; CtlP->CtlID = CTLID_0001; /* controller release 1 */ ISACTL(CtlP)->MBaseIO = rp_nisadevs; if (MudbacCtlP->io[ISACTL(CtlP)->MBaseIO] != NULL) { ISACTL(CtlP)->MReg0IO = 0x40 + 0; ISACTL(CtlP)->MReg1IO = 0x40 + 1; ISACTL(CtlP)->MReg2IO = 0x40 + 2; ISACTL(CtlP)->MReg3IO = 0x40 + 3; } else { MudbacCtlP->io_rid[ISACTL(CtlP)->MBaseIO] = ISACTL(CtlP)->MBaseIO; ctl_base = rman_get_start(MudbacCtlP->io[0]) + 0x40 + 0x400 * rp_nisadevs; MudbacCtlP->io[ISACTL(CtlP)->MBaseIO] = bus_alloc_resource(MudbacCtlP->dev, SYS_RES_IOPORT, &CtlP->io_rid[ISACTL(CtlP)->MBaseIO], ctl_base, ctl_base + 3, 4, RF_ACTIVE); ISACTL(CtlP)->MReg0IO = 0; ISACTL(CtlP)->MReg1IO = 1; ISACTL(CtlP)->MReg2IO = 2; ISACTL(CtlP)->MReg3IO = 3; } #if 1 ISACTL(CtlP)->MReg2 = 0; /* interrupt disable */ ISACTL(CtlP)->MReg3 = 0; /* no periodic interrupts */ #else if(sIRQMap[IRQNum] == 0) /* interrupts globally disabled */ { ISACTL(CtlP)->MReg2 = 0; /* interrupt disable */ ISACTL(CtlP)->MReg3 = 0; /* no periodic interrupts */ } else { ISACTL(CtlP)->MReg2 = sIRQMap[IRQNum]; /* set IRQ number */ ISACTL(CtlP)->MReg3 = Frequency; /* set frequency */ if(PeriodicOnly) /* periodic interrupt only */ { ISACTL(CtlP)->MReg3 |= PERIODIC_ONLY; } } #endif rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg2IO,ISACTL(CtlP)->MReg2); rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO,ISACTL(CtlP)->MReg3IO,ISACTL(CtlP)->MReg3); sControllerEOI(MudbacCtlP,CtlP); /* clear EOI if warm init */ /* Init AIOPs */ CtlP->NumAiop = 0; for(i=0; i < AiopNum; i++) { if (CtlP->io[i] == NULL) { CtlP->io_rid[i] = i; aiop_base = rman_get_start(CtlP->io[0]) + 0x400 * i; if (rp_nisadevs == 0) aiop_size = 0x44; else aiop_size = 0x40; CtlP->io[i] = bus_alloc_resource(CtlP->dev, SYS_RES_IOPORT, &CtlP->io_rid[i], aiop_base, aiop_base + aiop_size - 1, aiop_size, RF_ACTIVE); } else aiop_base = rman_get_start(CtlP->io[i]); rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO, ISACTL(CtlP)->MReg2IO, ISACTL(CtlP)->MReg2 | (i & 0x03)); /* AIOP index */ rp_writeio1(MudbacCtlP,ISACTL(CtlP)->MBaseIO, ISACTL(CtlP)->MReg0IO, (Byte_t)(aiop_base >> 6)); /* set up AIOP I/O in MUDBAC */ sEnAiop(MudbacCtlP,CtlP,i); /* enable the AIOP */ CtlP->AiopID[i] = sReadAiopID(CtlP, i); /* read AIOP ID */ if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ { sDisAiop(MudbacCtlP,CtlP,i); /* disable AIOP */ bus_release_resource(CtlP->dev, SYS_RES_IOPORT, CtlP->io_rid[i], CtlP->io[i]); CtlP->io[i] = NULL; break; /* done looking for AIOPs */ } CtlP->AiopNumChan[i] = sReadAiopNumChan(CtlP, i); /* num channels in AIOP */ rp_writeaiop2(CtlP,i,_INDX_ADDR,_CLK_PRE); /* clock prescaler */ rp_writeaiop1(CtlP,i,_INDX_DATA,CLOCK_PRESC); CtlP->NumAiop++; /* bump count of AIOPs */ sDisAiop(MudbacCtlP,CtlP,i); /* disable AIOP */ } if(CtlP->NumAiop == 0) return(-1); else return(CtlP->NumAiop); } /* * ARGSUSED * Maps (aiop, offset) to rid. */ static int rp_isa_aiop2rid(int aiop, int offset) { /* rid equals to aiop for an ISA controller. */ return aiop; } /* * ARGSUSED * Maps (aiop, offset) to the offset of resource. */ static int rp_isa_aiop2off(int aiop, int offset) { /* Each aiop has its own resource. */ return offset; } /* Read the int status for an ISA controller. */ static unsigned char rp_isa_ctlmask(CONTROLLER_t *ctlp) { return sGetControllerIntStatus(rp_controller,ctlp); } static device_method_t rp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rp_probe), DEVMETHOD(device_attach, rp_attach), { 0, 0 } }; static driver_t rp_driver = { "rp", rp_methods, sizeof(CONTROLLER_t), }; /* * rp can be attached to an isa bus. */ DRIVER_MODULE(rp, isa, rp_driver, rp_devclass, 0, 0); Index: head/sys/dev/rp/rp_pci.c =================================================================== --- head/sys/dev/rp/rp_pci.c (revision 355270) +++ head/sys/dev/rp/rp_pci.c (revision 355271) @@ -1,367 +1,368 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) Comtrol Corporation * All rights reserved. * * PCI-specific part separated from: * sys/i386/isa/rp.c,v 1.33 1999/09/28 11:45:27 phk Exp * * Redistribution and use in source and binary forms, with or without * modification, are permitted prodived that the follwoing conditions * are met. * 1. Redistributions of source code must retain the above copyright * notive, this list of conditions and the following disclainer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials prodided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Comtrol Corporation. * 4. The name of Comtrol Corporation may not be used to endorse or * promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL COMTROL CORPORATION BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include -#include #include #include +#include #include +#include #include #include #include #include #define ROCKET_C #include #include #include #include /* PCI IDs */ #define RP_VENDOR_ID 0x11FE #define RP_DEVICE_ID_32I 0x0001 #define RP_DEVICE_ID_8I 0x0002 #define RP_DEVICE_ID_16I 0x0003 #define RP_DEVICE_ID_4Q 0x0004 #define RP_DEVICE_ID_8O 0x0005 #define RP_DEVICE_ID_8J 0x0006 #define RP_DEVICE_ID_4J 0x0007 #define RP_DEVICE_ID_6M 0x000C #define RP_DEVICE_ID_4M 0x000D #define RP_DEVICE_ID_UPCI_32 0x0801 #define RP_DEVICE_ID_UPCI_16 0x0803 #define RP_DEVICE_ID_UPCI_8O 0x0805 /************************************************************************** MUDBAC remapped for PCI **************************************************************************/ #define _CFG_INT_PCI 0x40 #define _PCI_INT_FUNC 0x3A #define PCI_STROB 0x2000 #define INTR_EN_PCI 0x0010 /*************************************************************************** Function: sPCIControllerEOI Purpose: Strobe the MUDBAC's End Of Interrupt bit. Call: sPCIControllerEOI(CtlP) CONTROLLER_T *CtlP; Ptr to controller structure */ #define sPCIControllerEOI(CtlP) rp_writeio2(CtlP, 0, _PCI_INT_FUNC, PCI_STROB) /*************************************************************************** Function: sPCIGetControllerIntStatus Purpose: Get the controller interrupt status Call: sPCIGetControllerIntStatus(CtlP) CONTROLLER_T *CtlP; Ptr to controller structure Return: Byte_t: The controller interrupt status in the lower 4 bits. Bits 0 through 3 represent AIOP's 0 through 3 respectively. If a bit is set that AIOP is interrupting. Bits 4 through 7 will always be cleared. */ #define sPCIGetControllerIntStatus(CTLP) ((rp_readio2(CTLP, 0, _PCI_INT_FUNC) >> 8) & 0x1f) static devclass_t rp_devclass; static int rp_pciprobe(device_t dev); static int rp_pciattach(device_t dev); #ifdef notdef static int rp_pcidetach(device_t dev); static int rp_pcishutdown(device_t dev); #endif /* notdef */ static void rp_pcireleaseresource(CONTROLLER_t *ctlp); static int sPCIInitController( CONTROLLER_t *CtlP, int AiopNum, int IRQNum, Byte_t Frequency, int PeriodicOnly, int VendorDevice); static rp_aiop2rid_t rp_pci_aiop2rid; static rp_aiop2off_t rp_pci_aiop2off; static rp_ctlmask_t rp_pci_ctlmask; /* * The following functions are the pci-specific part * of rp driver. */ static int rp_pciprobe(device_t dev) { char *s; s = NULL; if (pci_get_vendor(dev) == RP_VENDOR_ID) s = "RocketPort PCI"; if (s != NULL) { device_set_desc(dev, s); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int rp_pciattach(device_t dev) { int num_ports, num_aiops; int aiop; CONTROLLER_t *ctlp; int unit; int retval; ctlp = device_get_softc(dev); bzero(ctlp, sizeof(*ctlp)); ctlp->dev = dev; unit = device_get_unit(dev); ctlp->aiop2rid = rp_pci_aiop2rid; ctlp->aiop2off = rp_pci_aiop2off; ctlp->ctlmask = rp_pci_ctlmask; /* The IO ports of AIOPs for a PCI controller are continuous. */ ctlp->io_num = 1; ctlp->io_rid = malloc(sizeof(*(ctlp->io_rid)) * ctlp->io_num, M_DEVBUF, M_NOWAIT | M_ZERO); ctlp->io = malloc(sizeof(*(ctlp->io)) * ctlp->io_num, M_DEVBUF, M_NOWAIT | M_ZERO); if (ctlp->io_rid == NULL || ctlp->io == NULL) { device_printf(dev, "rp_pciattach: Out of memory.\n"); retval = ENOMEM; goto nogo; } ctlp->bus_ctlp = NULL; switch (pci_get_device(dev)) { case RP_DEVICE_ID_UPCI_16: case RP_DEVICE_ID_UPCI_32: case RP_DEVICE_ID_UPCI_8O: ctlp->io_rid[0] = PCIR_BAR(2); break; default: ctlp->io_rid[0] = PCIR_BAR(0); break; } ctlp->io[0] = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &ctlp->io_rid[0], RF_ACTIVE); if(ctlp->io[0] == NULL) { device_printf(dev, "ioaddr mapping failed for RocketPort(PCI).\n"); retval = ENXIO; goto nogo; } num_aiops = sPCIInitController(ctlp, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0, pci_get_device(dev)); num_ports = 0; for(aiop=0; aiop < num_aiops; aiop++) { sResetAiopByNum(ctlp, aiop); num_ports += sGetAiopNumChan(ctlp, aiop); } retval = rp_attachcommon(ctlp, num_aiops, num_ports); if (retval != 0) goto nogo; return (0); nogo: rp_pcireleaseresource(ctlp); return (retval); } static int rp_pcidetach(device_t dev) { CONTROLLER_t *ctlp; ctlp = device_get_softc(dev); rp_pcireleaseresource(ctlp); return (0); } static int rp_pcishutdown(device_t dev) { CONTROLLER_t *ctlp; ctlp = device_get_softc(dev); rp_pcireleaseresource(ctlp); return (0); } static void rp_pcireleaseresource(CONTROLLER_t *ctlp) { rp_releaseresource(ctlp); if (ctlp->io != NULL) { if (ctlp->io[0] != NULL) bus_release_resource(ctlp->dev, SYS_RES_IOPORT, ctlp->io_rid[0], ctlp->io[0]); free(ctlp->io, M_DEVBUF); ctlp->io = NULL; } if (ctlp->io_rid != NULL) { free(ctlp->io_rid, M_DEVBUF); ctlp->io = NULL; } } static int sPCIInitController( CONTROLLER_t *CtlP, int AiopNum, int IRQNum, Byte_t Frequency, int PeriodicOnly, int VendorDevice) { int i; CtlP->CtlID = CTLID_0001; /* controller release 1 */ sPCIControllerEOI(CtlP); /* Init AIOPs */ CtlP->NumAiop = 0; for(i=0; i < AiopNum; i++) { /*device_printf(CtlP->dev, "aiop %d.\n", i);*/ CtlP->AiopID[i] = sReadAiopID(CtlP, i); /* read AIOP ID */ /*device_printf(CtlP->dev, "ID = %d.\n", CtlP->AiopID[i]);*/ if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ { break; /* done looking for AIOPs */ } switch( VendorDevice ) { case RP_DEVICE_ID_4Q: case RP_DEVICE_ID_4J: case RP_DEVICE_ID_4M: CtlP->AiopNumChan[i] = 4; break; case RP_DEVICE_ID_6M: CtlP->AiopNumChan[i] = 6; break; case RP_DEVICE_ID_8O: case RP_DEVICE_ID_8J: case RP_DEVICE_ID_8I: case RP_DEVICE_ID_16I: case RP_DEVICE_ID_32I: CtlP->AiopNumChan[i] = 8; break; default: #ifdef notdef CtlP->AiopNumChan[i] = 8; #else CtlP->AiopNumChan[i] = sReadAiopNumChan(CtlP, i); #endif /* notdef */ break; } /*device_printf(CtlP->dev, "%d channels.\n", CtlP->AiopNumChan[i]);*/ rp_writeaiop2(CtlP, i, _INDX_ADDR,_CLK_PRE); /* clock prescaler */ /*device_printf(CtlP->dev, "configuring clock prescaler.\n");*/ rp_writeaiop1(CtlP, i, _INDX_DATA,CLOCK_PRESC); /*device_printf(CtlP->dev, "configured clock prescaler.\n");*/ CtlP->NumAiop++; /* bump count of AIOPs */ } if(CtlP->NumAiop == 0) return(-1); else return(CtlP->NumAiop); } /* * ARGSUSED * Maps (aiop, offset) to rid. */ static int rp_pci_aiop2rid(int aiop, int offset) { /* Always return zero for a PCI controller. */ return 0; } /* * ARGSUSED * Maps (aiop, offset) to the offset of resource. */ static int rp_pci_aiop2off(int aiop, int offset) { /* Each AIOP reserves 0x40 bytes. */ return aiop * 0x40 + offset; } /* Read the int status for a PCI controller. */ static unsigned char rp_pci_ctlmask(CONTROLLER_t *ctlp) { return sPCIGetControllerIntStatus(ctlp); } static device_method_t rp_pcimethods[] = { /* Device interface */ DEVMETHOD(device_probe, rp_pciprobe), DEVMETHOD(device_attach, rp_pciattach), DEVMETHOD(device_detach, rp_pcidetach), DEVMETHOD(device_shutdown, rp_pcishutdown), { 0, 0 } }; static driver_t rp_pcidriver = { "rp", rp_pcimethods, sizeof(CONTROLLER_t), }; /* * rp can be attached to a pci bus. */ DRIVER_MODULE(rp, pci, rp_pcidriver, rp_devclass, 0, 0); Index: head/sys/powerpc/psim/uart_iobus.c =================================================================== --- head/sys/powerpc/psim/uart_iobus.c (revision 355270) +++ head/sys/powerpc/psim/uart_iobus.c (revision 355271) @@ -1,89 +1,88 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright 2002 by Peter Grehan. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * PSIM local bus 16550 */ #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include static int uart_iobus_probe(device_t dev); static device_method_t uart_iobus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_iobus_probe), DEVMETHOD(device_attach, uart_bus_attach), DEVMETHOD(device_detach, uart_bus_detach), { 0, 0 } }; static driver_t uart_iobus_driver = { uart_driver_name, uart_iobus_methods, sizeof(struct uart_softc), }; static int uart_iobus_probe(device_t dev) { struct uart_softc *sc; char *type; type = iobus_get_name(dev); if (strncmp(type, "com", 3) != 0) return (ENXIO); sc = device_get_softc(dev); sc->sc_class = &uart_ns8250_class; device_set_desc(dev, "PSIM serial port"); return (uart_bus_probe(dev, 0, 0, 0, 0, 0, 0)); } DRIVER_MODULE(uart, iobus, uart_iobus_driver, uart_devclass, 0, 0);