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);