summaryrefslogtreecommitdiffstats
path: root/sys/arm/samsung
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm/samsung')
-rw-r--r--sys/arm/samsung/exynos/chrome_ec.c266
-rw-r--r--sys/arm/samsung/exynos/chrome_ec.h36
-rw-r--r--sys/arm/samsung/exynos/chrome_kb.c792
-rw-r--r--sys/arm/samsung/exynos/chrome_kb.h122
-rw-r--r--sys/arm/samsung/exynos/exynos5_combiner.c304
-rw-r--r--sys/arm/samsung/exynos/exynos5_combiner.h29
-rw-r--r--sys/arm/samsung/exynos/exynos5_ehci.c57
-rw-r--r--sys/arm/samsung/exynos/exynos5_fimd.c8
-rw-r--r--sys/arm/samsung/exynos/exynos5_i2c.c476
-rw-r--r--sys/arm/samsung/exynos/exynos5_pad.c716
-rw-r--r--sys/arm/samsung/exynos/exynos5_pad.h29
-rw-r--r--sys/arm/samsung/exynos/files.exynos57
12 files changed, 2811 insertions, 31 deletions
diff --git a/sys/arm/samsung/exynos/chrome_ec.c b/sys/arm/samsung/exynos/chrome_ec.c
new file mode 100644
index 0000000..5ef64ee
--- /dev/null
+++ b/sys/arm/samsung/exynos/chrome_ec.c
@@ -0,0 +1,266 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * 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 EXPREC OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNEC 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 BUSINEC 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 Embedded Controller
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <sys/gpio.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/iicbus/iiconf.h>
+
+#include "iicbus_if.h"
+#include "gpio_if.h"
+
+#include <arm/samsung/exynos/chrome_ec.h>
+
+/* TODO: export to DTS */
+#define OUR_GPIO 177
+#define EC_GPIO 168
+
+struct ec_softc {
+ device_t dev;
+};
+
+struct ec_softc *ec_sc;
+
+/*
+ * bus_claim, bus_release
+ * both functions used for bus arbitration
+ * in multi-master mode
+ */
+
+static int
+bus_claim(struct ec_softc *sc)
+{
+ device_t gpio_dev;
+ int status;
+
+ gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
+ if (gpio_dev == NULL) {
+ device_printf(sc->dev, "cant find gpio_dev\n");
+ return (1);
+ }
+
+ /* Say we want the bus */
+ GPIO_PIN_SET(gpio_dev, OUR_GPIO, GPIO_PIN_LOW);
+
+ /* Check EC decision */
+ GPIO_PIN_GET(gpio_dev, EC_GPIO, &status);
+
+ if (status == 1) {
+ /* Okay. We have bus */
+ return (0);
+ }
+
+ /* EC is master */
+ return (-1);
+}
+
+static int
+bus_release(struct ec_softc *sc)
+{
+ device_t gpio_dev;
+
+ gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
+ if (gpio_dev == NULL) {
+ device_printf(sc->dev, "cant find gpio_dev\n");
+ return (1);
+ }
+
+ GPIO_PIN_SET(gpio_dev, OUR_GPIO, GPIO_PIN_HIGH);
+
+ return (0);
+}
+
+static int
+ec_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Chromebook Embedded Controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+fill_checksum(uint8_t *data_out, int len)
+{
+ int res;
+ int i;
+
+ res = 0;
+ for (i = 0; i < len; i++) {
+ res += data_out[i];
+ }
+
+ data_out[len] = (res & 0xff);
+
+ return (0);
+}
+
+int
+ec_command(uint8_t cmd, uint8_t *dout, uint8_t dout_len,
+ uint8_t *dinp, uint8_t dinp_len)
+{
+ struct ec_softc *sc;
+ uint8_t *msg_dout;
+ uint8_t *msg_dinp;
+ int ret;
+ int i;
+
+ msg_dout = malloc(dout_len + 4, M_DEVBUF, M_NOWAIT);
+ msg_dinp = malloc(dinp_len + 4, M_DEVBUF, M_NOWAIT);
+
+ if (ec_sc == NULL)
+ return (-1);
+
+ sc = ec_sc;
+
+ msg_dout[0] = EC_CMD_VERSION0;
+ msg_dout[1] = cmd;
+ msg_dout[2] = dout_len;
+
+ for (i = 0; i < dout_len; i++) {
+ msg_dout[i + 3] = dout[i];
+ };
+
+ fill_checksum(msg_dout, dout_len + 3);
+
+ struct iic_msg msgs[] = {
+ { 0x1e, IIC_M_WR, dout_len + 4, msg_dout, },
+ { 0x1e, IIC_M_RD, dinp_len + 4, msg_dinp, },
+ };
+
+ ret = iicbus_transfer(sc->dev, msgs, 2);
+ if (ret != 0) {
+ device_printf(sc->dev, "i2c transfer returned %d\n", ret);
+ free(msg_dout, M_DEVBUF);
+ free(msg_dinp, M_DEVBUF);
+ return (-1);
+ }
+
+ for (i = 0; i < dinp_len; i++) {
+ dinp[i] = msg_dinp[i + 3];
+ };
+
+ free(msg_dout, M_DEVBUF);
+ free(msg_dinp, M_DEVBUF);
+ return (0);
+}
+
+int ec_hello(void)
+{
+ uint8_t data_in[4];
+ uint8_t data_out[4];
+
+ data_in[0] = 0x40;
+ data_in[1] = 0x30;
+ data_in[2] = 0x20;
+ data_in[3] = 0x10;
+
+ ec_command(EC_CMD_MKBP_STATE, data_in, 4,
+ data_out, 4);
+
+ return (0);
+}
+
+static int
+ec_attach(device_t dev)
+{
+ struct ec_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ ec_sc = sc;
+
+ /*
+ * Claim the bus.
+ *
+ * We don't know cases when EC is master,
+ * so hold the bus forever for us.
+ *
+ */
+
+ if (bus_claim(sc) != 0) {
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+ec_detach(device_t dev)
+{
+ struct ec_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_release(sc);
+
+ return (0);
+}
+
+static device_method_t ec_methods[] = {
+ DEVMETHOD(device_probe, ec_probe),
+ DEVMETHOD(device_attach, ec_attach),
+ DEVMETHOD(device_detach, ec_detach),
+ { 0, 0 }
+};
+
+static driver_t ec_driver = {
+ "chrome_ec",
+ ec_methods,
+ sizeof(struct ec_softc),
+};
+
+static devclass_t ec_devclass;
+
+DRIVER_MODULE(chrome_ec, iicbus, ec_driver, ec_devclass, 0, 0);
+MODULE_VERSION(chrome_ec, 1);
+MODULE_DEPEND(chrome_ec, iicbus, 1, 1, 1);
diff --git a/sys/arm/samsung/exynos/chrome_ec.h b/sys/arm/samsung/exynos/chrome_ec.h
new file mode 100644
index 0000000..9d3f521
--- /dev/null
+++ b/sys/arm/samsung/exynos/chrome_ec.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#define EC_CMD_HELLO 0x01
+#define EC_CMD_GET_VERSION 0x02
+#define EC_CMD_MKBP_STATE 0x60
+#define EC_CMD_VERSION0 0xdc
+
+int ec_command(uint8_t cmd, uint8_t *dout, uint8_t dout_len,
+ uint8_t *dinp, uint8_t dinp_len);
+int ec_hello(void);
diff --git a/sys/arm/samsung/exynos/chrome_kb.c b/sys/arm/samsung/exynos/chrome_kb.c
new file mode 100644
index 0000000..9129131
--- /dev/null
+++ b/sys/arm/samsung/exynos/chrome_kb.c
@@ -0,0 +1,792 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/kdb.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/mutex.h>
+#include <sys/gpio.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/tty.h>
+#include <sys/kbio.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include "gpio_if.h"
+
+#include <arm/samsung/exynos/chrome_ec.h>
+#include <arm/samsung/exynos/chrome_kb.h>
+
+#include <arm/samsung/exynos/exynos5_combiner.h>
+#include <arm/samsung/exynos/exynos5_pad.h>
+
+#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 <sys/kbio.h>
+#include <dev/kbd/kbdreg.h>
+#include <dev/kbd/kbdtables.h>
+
+#define CKB_NFKEY 12
+#define CKB_FLAG_COMPOSE 0x1
+#define CKB_FLAG_POLLING 0x2
+#define KBD_DRIVER_NAME "ckbd"
+
+/* TODO: take interrupt from DTS */
+#define KB_GPIO_INT 146
+
+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;
+ device_t dev;
+ struct thread *sc_poll_thread;
+
+ uint8_t *scan_local;
+ uint8_t *scan;
+};
+
+/* prototypes */
+static void ckb_set_leds(struct ckb_softc *, uint8_t);
+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);
+}
+
+int scantokey(int i, int j);
+
+int
+scantokey(int i, int j)
+{
+ int k;
+
+ for (k = 0; k < KEYMAP_LEN; k++)
+ if ((keymap[k].col == i) && (keymap[k].row == j))
+ return (keymap[k].key);
+
+ return (0);
+}
+
+/* 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;
+
+ 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) {
+ /* TODO */
+ };
+
+ 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 = scantokey(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);
+
+static int
+parse_dts(struct ckb_softc *sc)
+{
+ phandle_t node;
+ pcell_t dts_value;
+ int len;
+
+ if ((node = ofw_bus_get_node(sc->dev)) == -1)
+ return (ENXIO);
+
+ if ((len = OF_getproplen(node, "keypad,num-rows")) <= 0)
+ return (ENXIO);
+ OF_getprop(node, "keypad,num-rows", &dts_value, len);
+ sc->rows = fdt32_to_cpu(dts_value);
+
+ if ((len = OF_getproplen(node, "keypad,num-columns")) <= 0)
+ return (ENXIO);
+ OF_getprop(node, "keypad,num-columns", &dts_value, len);
+ sc->cols = fdt32_to_cpu(dts_value);
+
+ if ((sc->rows == 0) || (sc->cols == 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;
+
+ if ((error = parse_dts(sc)) != 0)
+ return error;
+
+#if 0
+ device_printf(sc->dev, "Keyboard matrix [%dx%d]\n",
+ sc->cols, sc->rows);
+#endif
+
+ /* TODO: take interrupt from DTS */
+ pad_setup_intr(KB_GPIO_INT, 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")) {
+ device_set_desc(dev, "Chrome EC Keyboard");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static device_method_t chrome_kb_methods[] = {
+ DEVMETHOD(device_probe, chrome_kb_probe),
+ DEVMETHOD(device_attach, chrome_kb_attach),
+ { 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);
diff --git a/sys/arm/samsung/exynos/chrome_kb.h b/sys/arm/samsung/exynos/chrome_kb.h
new file mode 100644
index 0000000..3903a9b
--- /dev/null
+++ b/sys/arm/samsung/exynos/chrome_kb.h
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+void ckb_ec_intr(void *);
+
+struct key {
+ uint8_t row;
+ uint8_t col;
+ uint8_t key;
+};
+
+#define KEYMAP_LEN 75
+
+struct key keymap[KEYMAP_LEN] = {
+ { 0x00, 0x01, 0x7d }, /* lmeta */
+ { 0x00, 0x02, 0x3b }, /* F1 */
+ { 0x00, 0x03, 0x30 }, /* B */
+ { 0x00, 0x04, 0x44 }, /* F10 */
+ { 0x00, 0x06, 0x31 }, /* N */
+ { 0x00, 0x08, 0x0d }, /* = */
+ { 0x00, 0x0a, 0x64 }, /* ralt */
+
+ { 0x01, 0x01, 0x01 }, /* escape */
+ { 0x01, 0x02, 0x3e }, /* F4 */
+ { 0x01, 0x03, 0x22 }, /* G */
+ { 0x01, 0x04, 0x41 }, /* F7 */
+ { 0x01, 0x06, 0x23 }, /* H */
+ { 0x01, 0x08, 0x28 }, /* ' */
+ { 0x01, 0x09, 0x43 }, /* F9 */
+ { 0x01, 0x0b, 0x0e }, /* backspace */
+
+ { 0x02, 0x00, 0x1d }, /* lctrl */
+ { 0x02, 0x01, 0x0f }, /* tab */
+ { 0x02, 0x02, 0x3d }, /* F3 */
+ { 0x02, 0x03, 0x14 }, /* t */
+ { 0x02, 0x04, 0x40 }, /* F6 */
+ { 0x02, 0x05, 0x1b }, /* ] */
+ { 0x02, 0x06, 0x15 }, /* y */
+ { 0x02, 0x07, 0x56 }, /* 102nd */
+ { 0x02, 0x08, 0x1a }, /* [ */
+ { 0x02, 0x09, 0x42 }, /* F8 */
+
+ { 0x03, 0x01, 0x29 }, /* grave */
+ { 0x03, 0x02, 0x3c }, /* F2 */
+ { 0x03, 0x03, 0x06 }, /* 5 */
+ { 0x03, 0x04, 0x3f }, /* F5 */
+ { 0x03, 0x06, 0x07 }, /* 6 */
+ { 0x03, 0x08, 0x0c }, /* - */
+ { 0x03, 0x0b, 0x2b }, /* \ */
+
+ { 0x04, 0x00, 0x61 }, /* rctrl */
+ { 0x04, 0x01, 0x1e }, /* a */
+ { 0x04, 0x02, 0x20 }, /* d */
+ { 0x04, 0x03, 0x21 }, /* f */
+ { 0x04, 0x04, 0x1f }, /* s */
+ { 0x04, 0x05, 0x25 }, /* k */
+ { 0x04, 0x06, 0x24 }, /* j */
+ { 0x04, 0x08, 0x27 }, /* ; */
+ { 0x04, 0x09, 0x26 }, /* l */
+ { 0x04, 0x0a, 0x2b }, /* \ */
+ { 0x04, 0x0b, 0x1c }, /* enter */
+
+ { 0x05, 0x01, 0x2c }, /* z */
+ { 0x05, 0x02, 0x2e }, /* c */
+ { 0x05, 0x03, 0x2f }, /* v */
+ { 0x05, 0x04, 0x2d }, /* x */
+ { 0x05, 0x05, 0x33 }, /* , */
+ { 0x05, 0x06, 0x32 }, /* m */
+ { 0x05, 0x07, 0x2a }, /* lsh */
+ { 0x05, 0x08, 0x35 }, /* / */
+ { 0x05, 0x09, 0x34 }, /* . */
+ { 0x05, 0x0B, 0x39 }, /* space */
+
+ { 0x06, 0x01, 0x02 }, /* 1 */
+ { 0x06, 0x02, 0x04 }, /* 3 */
+ { 0x06, 0x03, 0x05 }, /* 4 */
+ { 0x06, 0x04, 0x03 }, /* 2 */
+ { 0x06, 0x05, 0x09 }, /* 8 */
+ { 0x06, 0x06, 0x08 }, /* 7 */
+ { 0x06, 0x08, 0x0b }, /* 0 */
+ { 0x06, 0x09, 0x0a }, /* 9 */
+ { 0x06, 0x0a, 0x38 }, /* lalt */
+ { 0x06, 0x0b, 0x64 }, /* down */
+ { 0x06, 0x0c, 0x62 }, /* right */
+
+ { 0x07, 0x01, 0x10 }, /* q */
+ { 0x07, 0x02, 0x12 }, /* e */
+ { 0x07, 0x03, 0x13 }, /* r */
+ { 0x07, 0x04, 0x11 }, /* w */
+ { 0x07, 0x05, 0x17 }, /* i */
+ { 0x07, 0x06, 0x16 }, /* u */
+ { 0x07, 0x07, 0x36 }, /* rsh */
+ { 0x07, 0x08, 0x19 }, /* p */
+ { 0x07, 0x09, 0x18 }, /* o */
+ { 0x07, 0x0b, 0x5F }, /* up */
+ { 0x07, 0x0c, 0x61 }, /* left */
+};
diff --git a/sys/arm/samsung/exynos/exynos5_combiner.c b/sys/arm/samsung/exynos/exynos5_combiner.c
new file mode 100644
index 0000000..e7bb896
--- /dev/null
+++ b/sys/arm/samsung/exynos/exynos5_combiner.c
@@ -0,0 +1,304 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * 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 Exynos 5 Interrupt Combiner
+ * Chapter 7, Exynos 5 Dual User's Manual Public Rev 1.00
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/samsung/exynos/exynos5_common.h>
+#include <arm/samsung/exynos/exynos5_combiner.h>
+
+#define NGRP 32
+#define ITABLE_LEN 24
+
+#define IESR(n) (0x10 * n + 0x0) /* Interrupt enable set */
+#define IECR(n) (0x10 * n + 0x4) /* Interrupt enable clear */
+#define ISTR(n) (0x10 * n + 0x8) /* Interrupt status */
+#define IMSR(n) (0x10 * n + 0xC) /* Interrupt masked status */
+#define CIPSR 0x100 /* Combined interrupt pending */
+
+struct combiner_softc {
+ struct resource *res[1 + NGRP];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ void *ih[NGRP];
+ device_t dev;
+};
+
+struct combiner_softc *combiner_sc;
+
+static struct resource_spec combiner_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { SYS_RES_IRQ, 2, RF_ACTIVE },
+ { SYS_RES_IRQ, 3, RF_ACTIVE },
+ { SYS_RES_IRQ, 4, RF_ACTIVE },
+ { SYS_RES_IRQ, 5, RF_ACTIVE },
+ { SYS_RES_IRQ, 6, RF_ACTIVE },
+ { SYS_RES_IRQ, 7, RF_ACTIVE },
+ { SYS_RES_IRQ, 8, RF_ACTIVE },
+ { SYS_RES_IRQ, 9, RF_ACTIVE },
+ { SYS_RES_IRQ, 10, RF_ACTIVE },
+ { SYS_RES_IRQ, 11, RF_ACTIVE },
+ { SYS_RES_IRQ, 12, RF_ACTIVE },
+ { SYS_RES_IRQ, 13, RF_ACTIVE },
+ { SYS_RES_IRQ, 14, RF_ACTIVE },
+ { SYS_RES_IRQ, 15, RF_ACTIVE },
+ { SYS_RES_IRQ, 16, RF_ACTIVE },
+ { SYS_RES_IRQ, 17, RF_ACTIVE },
+ { SYS_RES_IRQ, 18, RF_ACTIVE },
+ { SYS_RES_IRQ, 19, RF_ACTIVE },
+ { SYS_RES_IRQ, 20, RF_ACTIVE },
+ { SYS_RES_IRQ, 21, RF_ACTIVE },
+ { SYS_RES_IRQ, 22, RF_ACTIVE },
+ { SYS_RES_IRQ, 23, RF_ACTIVE },
+ { SYS_RES_IRQ, 24, RF_ACTIVE },
+ { SYS_RES_IRQ, 25, RF_ACTIVE },
+ { SYS_RES_IRQ, 26, RF_ACTIVE },
+ { SYS_RES_IRQ, 27, RF_ACTIVE },
+ { SYS_RES_IRQ, 28, RF_ACTIVE },
+ { SYS_RES_IRQ, 29, RF_ACTIVE },
+ { SYS_RES_IRQ, 30, RF_ACTIVE },
+ { SYS_RES_IRQ, 31, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct combiner_entry {
+ int combiner_id;
+ int bit;
+ char *source_name;
+};
+
+static struct combiner_entry interrupt_table[ITABLE_LEN] = {
+ { 63, 1, "EINT[15]" },
+ { 63, 0, "EINT[14]" },
+ { 62, 1, "EINT[13]" },
+ { 62, 0, "EINT[12]" },
+ { 61, 1, "EINT[11]" },
+ { 61, 0, "EINT[10]" },
+ { 60, 1, "EINT[9]" },
+ { 60, 0, "EINT[8]" },
+ { 59, 1, "EINT[7]" },
+ { 59, 0, "EINT[6]" },
+ { 58, 1, "EINT[5]" },
+ { 58, 0, "EINT[4]" },
+ { 57, 3, "MCT_G3" },
+ { 57, 2, "MCT_G2" },
+ { 57, 1, "EINT[3]" },
+ { 57, 0, "EINT[2]" },
+ { 56, 6, "SYSMMU_G2D[1]" },
+ { 56, 5, "SYSMMU_G2D[0]" },
+ { 56, 2, "SYSMMU_FIMC_LITE1[1]" },
+ { 56, 1, "SYSMMU_FIMC_LITE1[0]" },
+ { 56, 0, "EINT[1]" },
+ { 55, 4, "MCT_G1" },
+ { 55, 3, "MCT_G0" },
+ { 55, 0, "EINT[0]" },
+
+ /* TODO: add groups 54-32 */
+};
+
+struct combined_intr {
+ uint32_t enabled;
+ void (*ih) (void *);
+ void *ih_user;
+};
+
+static struct combined_intr intr_map[32][8];
+
+static void
+combiner_intr(void *arg)
+{
+ struct combiner_softc *sc;
+ void (*ih) (void *);
+ void *ih_user;
+ int enabled;
+ int intrs;
+ int shift;
+ int cirq;
+ int grp;
+ int i,n;
+
+ sc = arg;
+
+ intrs = READ4(sc, CIPSR);
+ for (grp = 0; grp < 32; grp++) {
+ if (intrs & (1 << grp)) {
+ n = (grp / 4);
+ shift = (grp % 4) * 8;
+
+ cirq = READ4(sc, ISTR(n));
+ for (i = 0; i < 8; i++) {
+ if (cirq & (1 << (i + shift))) {
+ ih = intr_map[grp][i].ih;
+ ih_user = intr_map[grp][i].ih_user;
+ enabled = intr_map[grp][i].enabled;
+ if (enabled && (ih != NULL)) {
+ ih(ih_user);
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+combiner_setup_intr(char *source_name, void (*ih)(void *), void *ih_user)
+{
+ struct combiner_entry *entry;
+ struct combined_intr *cirq;
+ struct combiner_softc *sc;
+ int shift;
+ int reg;
+ int grp;
+ int n;
+ int i;
+
+ sc = combiner_sc;
+
+ if (sc == NULL) {
+ device_printf(sc->dev, "Error: combiner is not attached\n");
+ return;
+ }
+
+ entry = NULL;
+
+ for (i = 0; i < ITABLE_LEN; i++) {
+ if (strcmp(interrupt_table[i].source_name, source_name) == 0) {
+ entry = &interrupt_table[i];
+ }
+ }
+
+ if (entry == NULL) {
+ device_printf(sc->dev, "Can't find interrupt name %s\n",
+ source_name);
+ return;
+ }
+
+#if 0
+ device_printf(sc->dev, "Setting up interrupt %s\n", source_name);
+#endif
+
+ grp = entry->combiner_id - 32;
+
+ cirq = &intr_map[grp][entry->bit];
+ cirq->enabled = 1;
+ cirq->ih = ih;
+ cirq->ih_user = ih_user;
+
+ n = grp / 4;
+ shift = (grp % 4) * 8 + entry->bit;
+
+ reg = (1 << shift);
+ WRITE4(sc, IESR(n), reg);
+}
+
+static int
+combiner_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "exynos,combiner"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Samsung Exynos 5 Interrupt Combiner");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+combiner_attach(device_t dev)
+{
+ struct combiner_softc *sc;
+ int err;
+ int i;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ if (bus_alloc_resources(dev, combiner_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ combiner_sc = sc;
+
+ /* Setup interrupt handler */
+ for (i = 0; i < NGRP; i++) {
+ err = bus_setup_intr(dev, sc->res[1+i], INTR_TYPE_BIO | \
+ INTR_MPSAFE, NULL, combiner_intr, sc, &sc->ih[i]);
+ if (err) {
+ device_printf(dev, "Unable to alloc int resource.\n");
+ return (ENXIO);
+ }
+ }
+
+ return (0);
+}
+
+static device_method_t combiner_methods[] = {
+ DEVMETHOD(device_probe, combiner_probe),
+ DEVMETHOD(device_attach, combiner_attach),
+ { 0, 0 }
+};
+
+static driver_t combiner_driver = {
+ "combiner",
+ combiner_methods,
+ sizeof(struct combiner_softc),
+};
+
+static devclass_t combiner_devclass;
+
+DRIVER_MODULE(combiner, simplebus, combiner_driver, combiner_devclass, 0, 0);
diff --git a/sys/arm/samsung/exynos/exynos5_combiner.h b/sys/arm/samsung/exynos/exynos5_combiner.h
new file mode 100644
index 0000000..7be8dd0
--- /dev/null
+++ b/sys/arm/samsung/exynos/exynos5_combiner.h
@@ -0,0 +1,29 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+void combiner_setup_intr(char *source_name, void (*ih)(void *), void *ih_user);
diff --git a/sys/arm/samsung/exynos/exynos5_ehci.c b/sys/arm/samsung/exynos/exynos5_ehci.c
index dc438d5..7dacafa 100644
--- a/sys/arm/samsung/exynos/exynos5_ehci.c
+++ b/sys/arm/samsung/exynos/exynos5_ehci.c
@@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/bus.h>
#include <sys/condvar.h>
#include <sys/rman.h>
+#include <sys/gpio.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
@@ -54,16 +55,14 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <machine/resource.h>
+#include "gpio_if.h"
+
#include "opt_platform.h"
/* GPIO control */
-#define GPIO_CON(x, v) ((v) << ((x) * 4))
-#define GPIO_MASK 0xf
#define GPIO_OUTPUT 1
#define GPIO_INPUT 0
-#define GPX3CON 0x0
-#define GPX3DAT 0x4
-#define PIN_USB 5
+#define PIN_USB 161
/* PWR control */
#define EXYNOS5_PWR_USBHOST_PHY 0x708
@@ -90,16 +89,15 @@ static int exynos_ehci_detach(device_t dev);
static int exynos_ehci_probe(device_t dev);
struct exynos_ehci_softc {
+ device_t dev;
ehci_softc_t base;
- struct resource *res[6];
+ struct resource *res[5];
bus_space_tag_t host_bst;
bus_space_tag_t pwr_bst;
bus_space_tag_t sysreg_bst;
- bus_space_tag_t gpio_bst;
bus_space_handle_t host_bsh;
bus_space_handle_t pwr_bsh;
bus_space_handle_t sysreg_bsh;
- bus_space_handle_t gpio_bsh;
};
@@ -108,7 +106,6 @@ static struct resource_spec exynos_ehci_spec[] = {
{ SYS_RES_MEMORY, 1, RF_ACTIVE },
{ SYS_RES_MEMORY, 2, RF_ACTIVE },
{ SYS_RES_MEMORY, 3, RF_ACTIVE },
- { SYS_RES_MEMORY, 4, RF_ACTIVE },
{ SYS_RES_IRQ, 0, RF_ACTIVE },
{ -1, 0 }
};
@@ -160,19 +157,24 @@ exynos_ehci_probe(device_t dev)
static int
gpio_ctrl(struct exynos_ehci_softc *esc, int dir, int power)
{
- int reg;
+ device_t gpio_dev;
+
+ /* Get the GPIO device, we need this to give power to USB */
+ gpio_dev = devclass_get_device(devclass_find("gpio"), 0);
+ if (gpio_dev == NULL) {
+ device_printf(esc->dev, "cant find gpio_dev\n");
+ return (1);
+ }
- /* Power control */
- reg = bus_space_read_4(esc->gpio_bst, esc->gpio_bsh, GPX3DAT);
- reg &= ~(1 << PIN_USB);
- reg |= (power << PIN_USB);
- bus_space_write_4(esc->gpio_bst, esc->gpio_bsh, GPX3DAT, reg);
+ if (power)
+ GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_HIGH);
+ else
+ GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_LOW);
- /* Input/Output control */
- reg = bus_space_read_4(esc->gpio_bst, esc->gpio_bsh, GPX3CON);
- reg &= ~GPIO_CON(PIN_USB, GPIO_MASK);
- reg |= GPIO_CON(PIN_USB, dir);
- bus_space_write_4(esc->gpio_bst, esc->gpio_bsh, GPX3CON, reg);
+ if (dir)
+ GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_OUTPUT);
+ else
+ GPIO_PIN_SETFLAGS(gpio_dev, PIN_USB, GPIO_PIN_INPUT);
return (0);
}
@@ -224,6 +226,7 @@ exynos_ehci_attach(device_t dev)
int err;
esc = device_get_softc(dev);
+ esc->dev = dev;
sc = &esc->base;
sc->sc_bus.parent = dev;
sc->sc_bus.devices = sc->sc_devices;
@@ -251,10 +254,6 @@ exynos_ehci_attach(device_t dev)
esc->sysreg_bst = rman_get_bustag(esc->res[3]);
esc->sysreg_bsh = rman_get_bushandle(esc->res[3]);
- /* GPIO */
- esc->gpio_bst = rman_get_bustag(esc->res[4]);
- esc->gpio_bsh = rman_get_bushandle(esc->res[4]);
-
/* get all DMA memory */
if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev),
&ehci_iterate_hw_softc))
@@ -272,7 +271,7 @@ exynos_ehci_attach(device_t dev)
phy_init(esc);
/* Setup interrupt handler */
- err = bus_setup_intr(dev, esc->res[5], INTR_TYPE_BIO | INTR_MPSAFE,
+ err = bus_setup_intr(dev, esc->res[4], INTR_TYPE_BIO | INTR_MPSAFE,
NULL, (driver_intr_t *)ehci_interrupt, sc,
&sc->sc_intr_hdl);
if (err) {
@@ -285,7 +284,7 @@ exynos_ehci_attach(device_t dev)
sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
if (!sc->sc_bus.bdev) {
device_printf(dev, "Could not add USB device\n");
- err = bus_teardown_intr(dev, esc->res[5],
+ err = bus_teardown_intr(dev, esc->res[4],
sc->sc_intr_hdl);
if (err)
device_printf(dev, "Could not tear down irq,"
@@ -306,7 +305,7 @@ exynos_ehci_attach(device_t dev)
device_delete_child(dev, sc->sc_bus.bdev);
sc->sc_bus.bdev = NULL;
- err = bus_teardown_intr(dev, esc->res[5],
+ err = bus_teardown_intr(dev, esc->res[4],
sc->sc_intr_hdl);
if (err)
device_printf(dev, "Could not tear down irq,"
@@ -345,8 +344,8 @@ exynos_ehci_detach(device_t dev)
bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl,
EHCI_USBINTR, 0);
- if (esc->res[5] && sc->sc_intr_hdl) {
- err = bus_teardown_intr(dev, esc->res[5],
+ if (esc->res[4] && sc->sc_intr_hdl) {
+ err = bus_teardown_intr(dev, esc->res[4],
sc->sc_intr_hdl);
if (err) {
device_printf(dev, "Could not tear down irq,"
diff --git a/sys/arm/samsung/exynos/exynos5_fimd.c b/sys/arm/samsung/exynos/exynos5_fimd.c
index e56f477..d26330f 100644
--- a/sys/arm/samsung/exynos/exynos5_fimd.c
+++ b/sys/arm/samsung/exynos/exynos5_fimd.c
@@ -47,6 +47,10 @@ __FBSDID("$FreeBSD$");
#include <sys/eventhandler.h>
#include <sys/gpio.h>
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
@@ -351,8 +355,8 @@ fimd_attach(device_t dev)
sc->sc_info.fb_stride = sc->sc_info.fb_width * 2;
sc->sc_info.fb_bpp = sc->sc_info.fb_depth = 16;
sc->sc_info.fb_size = sc->sc_info.fb_height * sc->sc_info.fb_stride;
- sc->sc_info.fb_vbase = (intptr_t)contigmalloc(sc->sc_info.fb_size,
- M_DEVBUF, M_ZERO, 0, ~0, PAGE_SIZE, 0);
+ sc->sc_info.fb_vbase = (intptr_t)kmem_alloc_contig(kernel_arena,
+ sc->sc_info.fb_size, M_ZERO, 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE);
sc->sc_info.fb_pbase = (intptr_t)vtophys(sc->sc_info.fb_vbase);
#if 0
diff --git a/sys/arm/samsung/exynos/exynos5_i2c.c b/sys/arm/samsung/exynos/exynos5_i2c.c
new file mode 100644
index 0000000..bf7548f
--- /dev/null
+++ b/sys/arm/samsung/exynos/exynos5_i2c.c
@@ -0,0 +1,476 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * 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 Exynos 5 Inter-Integrated Circuit (I2C)
+ * Chapter 13, Exynos 5 Dual User's Manual Public Rev 1.00
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+#include "iicbus_if.h"
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/samsung/exynos/exynos5_common.h>
+
+#define I2CCON 0x00 /* Control register */
+#define ACKGEN (1 << 7) /* Acknowledge Enable */
+/*
+ * Source Clock of I2C-bus Transmit Clock Prescaler
+ *
+ * 0 = I2CCLK = fPCLK/16
+ * 1 = I2CCLK = fPCLK/512
+ */
+#define I2CCLK (1 << 6)
+#define IRQ_EN (1 << 5) /* Tx/Rx Interrupt Enable/Disable */
+#define IPEND (1 << 4) /* Tx/Rx Interrupt Pending Flag */
+#define CLKVAL_M 0xf /* Transmit Clock Prescaler Mask */
+#define CLKVAL_S 0
+#define I2CSTAT 0x04 /* Control/status register */
+#define I2CMODE_M 0x3 /* Master/Slave Tx/Rx Mode Select */
+#define I2CMODE_S 6
+#define I2CMODE_SR 0x0 /* Slave Receive Mode */
+#define I2CMODE_ST 0x1 /* Slave Transmit Mode */
+#define I2CMODE_MR 0x2 /* Master Receive Mode */
+#define I2CMODE_MT 0x3 /* Master Transmit Mode */
+#define I2CSTAT_BSY (1 << 5) /* Busy Signal Status bit */
+#define I2C_START_STOP (1 << 5) /* Busy Signal Status bit */
+#define RXTX_EN (1 << 4) /* Data Output Enable/Disable */
+#define ARBST (1 << 3) /* Arbitration status flag */
+#define ADDAS (1 << 2) /* Address-as-slave Status Flag */
+#define ADDZERO (1 << 1) /* Address Zero Status Flag */
+#define ACKRECVD (1 << 0) /* Last-received Bit Status Flag */
+#define I2CADD 0x08 /* Address register */
+#define I2CDS 0x0C /* Transmit/receive data shift */
+#define I2CLC 0x10 /* Multi-master line control */
+#define FILTER_EN (1 << 2) /* Filter Enable bit */
+#define SDAOUT_DELAY_M 0x3 /* SDA Line Delay Length */
+#define SDAOUT_DELAY_S 0
+
+#ifdef DEBUG
+#define DPRINTF(fmt, args...) \
+ printf(fmt, ##args)
+#else
+#define DPRINTF(fmt, args...)
+#endif
+
+static int i2c_start(device_t, u_char, int);
+static int i2c_stop(device_t);
+static int i2c_reset(device_t, u_char, u_char, u_char *);
+static int i2c_read(device_t, char *, int, int *, int, int);
+static int i2c_write(device_t, const char *, int, int *, int);
+
+struct i2c_softc {
+ struct resource *res[2];
+ bus_space_tag_t bst;
+ bus_space_handle_t bsh;
+ device_t dev;
+ device_t iicbus;
+ struct mtx mutex;
+ void *ih;
+ int intr;
+};
+
+static struct resource_spec i2c_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { -1, 0 }
+};
+
+static int
+i2c_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "exynos,i2c"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Samsung Exynos 5 I2C controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+clear_ipend(struct i2c_softc *sc)
+{
+ int reg;
+
+ reg = READ1(sc, I2CCON);
+ reg &= ~(IPEND);
+ WRITE1(sc, I2CCON, reg);
+
+ return (0);
+}
+
+static int
+i2c_attach(device_t dev)
+{
+ struct i2c_softc *sc;
+ int reg;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ mtx_init(&sc->mutex, device_get_nameunit(dev), "I2C", MTX_DEF);
+
+ if (bus_alloc_resources(dev, i2c_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+ sc->bst = rman_get_bustag(sc->res[0]);
+ sc->bsh = rman_get_bushandle(sc->res[0]);
+
+ sc->iicbus = device_add_child(dev, "iicbus", -1);
+ if (sc->iicbus == NULL) {
+ device_printf(dev, "could not add iicbus child");
+ mtx_destroy(&sc->mutex);
+ return (ENXIO);
+ }
+
+ WRITE1(sc, I2CSTAT, 0);
+ WRITE1(sc, I2CADD, 0x00);
+
+ /* Mode */
+ reg = (RXTX_EN);
+ reg |= (I2CMODE_MT << I2CMODE_S);
+ WRITE1(sc, I2CSTAT, reg);
+
+ bus_generic_attach(dev);
+
+ return (0);
+}
+
+static int
+wait_for_iif(struct i2c_softc *sc)
+{
+ int retry;
+ int reg;
+
+ retry = 1000;
+ while (retry --) {
+ reg = READ1(sc, I2CCON);
+ if (reg & IPEND) {
+ return (IIC_NOERR);
+ }
+ DELAY(50);
+ }
+
+ return (IIC_ETIMEOUT);
+}
+
+static int
+wait_for_nibb(struct i2c_softc *sc)
+{
+ int retry;
+
+ retry = 1000;
+ while (retry --) {
+ if ((READ1(sc, I2CSTAT) & I2CSTAT_BSY) == 0)
+ return (IIC_NOERR);
+ DELAY(10);
+ }
+
+ return (IIC_ETIMEOUT);
+}
+
+static int
+is_ack(struct i2c_softc *sc)
+{
+ int stat;
+
+ stat = READ1(sc, I2CSTAT);
+ if (!(stat & 1)) {
+ /* ACK received */
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+i2c_start(device_t dev, u_char slave, int timeout)
+{
+ struct i2c_softc *sc;
+ int error;
+ int reg;
+
+ sc = device_get_softc(dev);
+
+ DPRINTF("i2c start\n");
+
+ mtx_lock(&sc->mutex);
+
+#if 0
+ DPRINTF("I2CCON == 0x%08x\n", READ1(sc, I2CCON));
+ DPRINTF("I2CSTAT == 0x%08x\n", READ1(sc, I2CSTAT));
+#endif
+
+ if (slave & 1) {
+ slave &= ~(1);
+ slave <<= 1;
+ slave |= 1;
+ } else {
+ slave <<= 1;
+ }
+
+ error = wait_for_nibb(sc);
+ if (error) {
+ mtx_unlock(&sc->mutex);
+ DPRINTF("cant i2c start: IIC_EBUSBSY\n");
+ return (IIC_EBUSBSY);
+ }
+
+ reg = READ1(sc, I2CCON);
+ reg |= (IRQ_EN | ACKGEN);
+ WRITE1(sc, I2CCON, reg);
+
+ WRITE1(sc, I2CDS, slave);
+ DELAY(50);
+
+ reg = (RXTX_EN);
+ reg |= I2C_START_STOP;
+ reg |= (I2CMODE_MT << I2CMODE_S);
+ WRITE1(sc, I2CSTAT, reg);
+
+ error = wait_for_iif(sc);
+ if (error) {
+ DPRINTF("cant i2c start: iif error\n");
+
+ mtx_unlock(&sc->mutex);
+ return (error);
+ }
+
+ if (!is_ack(sc)) {
+ DPRINTF("cant i2c start: no ack\n");
+
+ mtx_unlock(&sc->mutex);
+ return (IIC_ENOACK);
+ };
+
+ mtx_unlock(&sc->mutex);
+ return (IIC_NOERR);
+}
+
+static int
+i2c_stop(device_t dev)
+{
+ struct i2c_softc *sc;
+ int reg;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ DPRINTF("i2c stop\n");
+
+ mtx_lock(&sc->mutex);
+
+ reg = READ1(sc, I2CSTAT);
+ int mode = (reg >> I2CMODE_S) & I2CMODE_M;
+
+ reg = (RXTX_EN);
+ reg |= (mode << I2CMODE_S);
+ WRITE1(sc, I2CSTAT, reg);
+
+ clear_ipend(sc);
+
+ error = wait_for_nibb(sc);
+ if (error) {
+ DPRINTF("cant i2c stop: nibb error\n");
+ return (error);
+ }
+
+ mtx_unlock(&sc->mutex);
+ return (IIC_NOERR);
+}
+
+static int
+i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr)
+{
+ struct i2c_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ DPRINTF("i2c reset\n");
+
+ mtx_lock(&sc->mutex);
+
+ /* TODO */
+
+ mtx_unlock(&sc->mutex);
+
+ return (IIC_NOERR);
+}
+
+static int
+i2c_read(device_t dev, char *buf, int len,
+ int *read, int last, int delay)
+{
+ struct i2c_softc *sc;
+ int error;
+ int reg;
+ uint8_t d;
+
+ sc = device_get_softc(dev);
+
+ DPRINTF("i2c read\n");
+
+ reg = (RXTX_EN);
+ reg |= (I2CMODE_MR << I2CMODE_S);
+ reg |= I2C_START_STOP;
+ WRITE1(sc, I2CSTAT, reg);
+
+ *read = 0;
+ mtx_lock(&sc->mutex);
+
+ /* dummy read */
+ READ1(sc, I2CDS);
+
+ DPRINTF("Read ");
+ while (*read < len) {
+
+ /* Do not ack last read */
+ if (*read == (len - 1)) {
+ reg = READ1(sc, I2CCON);
+ reg &= ~(ACKGEN);
+ WRITE1(sc, I2CCON, reg);
+ };
+
+ clear_ipend(sc);
+
+ error = wait_for_iif(sc);
+ if (error) {
+ DPRINTF("cant i2c read: iif error\n");
+ mtx_unlock(&sc->mutex);
+ return (error);
+ }
+
+ d = READ1(sc, I2CDS);
+ DPRINTF("0x%02x ", d);
+ *buf++ = d;
+ (*read)++;
+ }
+ DPRINTF("\n");
+
+ mtx_unlock(&sc->mutex);
+ return (IIC_NOERR);
+}
+
+static int
+i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout)
+{
+ struct i2c_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ DPRINTF("i2c write\n");
+
+ *sent = 0;
+
+ mtx_lock(&sc->mutex);
+
+ DPRINTF("writing ");
+ while (*sent < len) {
+ uint8_t d = *buf++;
+ DPRINTF("0x%02x ", d);
+
+ WRITE1(sc, I2CDS, d);
+ DELAY(50);
+
+ clear_ipend(sc);
+
+ error = wait_for_iif(sc);
+ if (error) {
+ DPRINTF("cant i2c write: iif error\n");
+ mtx_unlock(&sc->mutex);
+ return (error);
+ }
+
+ if (!is_ack(sc)) {
+ DPRINTF("cant i2c write: no ack\n");
+ mtx_unlock(&sc->mutex);
+ return (IIC_ENOACK);
+ };
+
+ (*sent)++;
+ }
+ DPRINTF("\n");
+
+ mtx_unlock(&sc->mutex);
+ return (IIC_NOERR);
+}
+
+static device_method_t i2c_methods[] = {
+ DEVMETHOD(device_probe, i2c_probe),
+ DEVMETHOD(device_attach, i2c_attach),
+
+ DEVMETHOD(iicbus_callback, iicbus_null_callback),
+ DEVMETHOD(iicbus_start, i2c_start),
+ DEVMETHOD(iicbus_stop, i2c_stop),
+ DEVMETHOD(iicbus_reset, i2c_reset),
+ DEVMETHOD(iicbus_read, i2c_read),
+ DEVMETHOD(iicbus_write, i2c_write),
+ DEVMETHOD(iicbus_transfer, iicbus_transfer_gen),
+
+ { 0, 0 }
+};
+
+static driver_t i2c_driver = {
+ "i2c",
+ i2c_methods,
+ sizeof(struct i2c_softc),
+};
+
+static devclass_t i2c_devclass;
+
+DRIVER_MODULE(i2c, simplebus, i2c_driver, i2c_devclass, 0, 0);
+DRIVER_MODULE(iicbus, i2c, iicbus_driver, iicbus_devclass, 0, 0);
diff --git a/sys/arm/samsung/exynos/exynos5_pad.c b/sys/arm/samsung/exynos/exynos5_pad.c
new file mode 100644
index 0000000..f9f38d0
--- /dev/null
+++ b/sys/arm/samsung/exynos/exynos5_pad.c
@@ -0,0 +1,716 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * 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 Exynos 5 Pad Control
+ * Chapter 4, Exynos 5 Dual User's Manual Public Rev 1.00
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+#include <sys/mutex.h>
+#include <sys/gpio.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include "gpio_if.h"
+
+#include <arm/samsung/exynos/exynos5_combiner.h>
+#include <arm/samsung/exynos/exynos5_pad.h>
+
+#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+
+#define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
+
+#define NPORTS 4
+#define NGRP 40
+#define NGPIO 253
+#define NINTS 16
+
+#define PIN_IN 0
+#define PIN_OUT 1
+
+#define READ4(_sc, _port, _reg) \
+ bus_space_read_4(_sc->bst[_port], _sc->bsh[_port], _reg)
+#define WRITE4(_sc, _port, _reg, _val) \
+ bus_space_write_4(_sc->bst[_port], _sc->bsh[_port], _reg, _val)
+
+/*
+ * GPIO interface
+ */
+static int pad_pin_max(device_t, int *);
+static int pad_pin_getcaps(device_t, uint32_t, uint32_t *);
+static int pad_pin_getname(device_t, uint32_t, char *);
+static int pad_pin_getflags(device_t, uint32_t, uint32_t *);
+static int pad_pin_setflags(device_t, uint32_t, uint32_t);
+static int pad_pin_set(device_t, uint32_t, unsigned int);
+static int pad_pin_get(device_t, uint32_t, unsigned int *);
+static int pad_pin_toggle(device_t, uint32_t pin);
+
+struct pad_softc {
+ struct resource *res[NPORTS+4];
+ bus_space_tag_t bst[NPORTS];
+ bus_space_handle_t bsh[NPORTS];
+ struct mtx sc_mtx;
+ int gpio_npins;
+ struct gpio_pin gpio_pins[NGPIO];
+ void *gpio_ih[NPORTS+4];
+ device_t dev;
+};
+
+struct pad_softc *gpio_sc;
+
+static struct resource_spec pad_spec[] = {
+ { SYS_RES_MEMORY, 0, RF_ACTIVE },
+ { SYS_RES_MEMORY, 1, RF_ACTIVE },
+ { SYS_RES_MEMORY, 2, RF_ACTIVE },
+ { SYS_RES_MEMORY, 3, RF_ACTIVE },
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { SYS_RES_IRQ, 2, RF_ACTIVE },
+ { SYS_RES_IRQ, 3, RF_ACTIVE },
+ { -1, 0 }
+};
+
+struct pad_intr {
+ uint32_t enabled;
+ void (*ih) (void *);
+ void *ih_user;
+};
+
+static struct pad_intr intr_map[NGPIO];
+
+struct interrupt_entry {
+ int gpio_number;
+ char *combiner_source_name;
+};
+
+struct interrupt_entry interrupt_table[NINTS] = {
+ { 147, "EINT[15]" },
+ { 146, "EINT[14]" },
+ { 145, "EINT[13]" },
+ { 144, "EINT[12]" },
+ { 143, "EINT[11]" },
+ { 142, "EINT[10]" },
+ { 141, "EINT[9]" },
+ { 140, "EINT[8]" },
+ { 139, "EINT[7]" },
+ { 138, "EINT[6]" },
+ { 137, "EINT[5]" },
+ { 136, "EINT[4]" },
+ { 135, "EINT[3]" },
+ { 134, "EINT[2]" },
+ { 133, "EINT[1]" },
+ { 132, "EINT[0]" },
+};
+
+struct gpio_bank {
+ char *name;
+ uint32_t port;
+ uint32_t con;
+ uint32_t ngpio;
+ uint32_t ext_int_grp;
+ uint32_t ext_con;
+ uint32_t ext_flt_con;
+ uint32_t mask;
+ uint32_t pend;
+};
+
+/*
+ * 253 multi-functional input/output ports
+ */
+
+static struct gpio_bank gpio_map[] = {
+ /* first 132 gpio */
+ { "gpa0", 0, 0x000, 8, 1, 0x700, 0x800, 0x900, 0xA00 },
+ { "gpa1", 0, 0x020, 6, 2, 0x704, 0x808, 0x904, 0xA04 },
+ { "gpa2", 0, 0x040, 8, 3, 0x708, 0x810, 0x908, 0xA08 },
+ { "gpb0", 0, 0x060, 5, 4, 0x70C, 0x818, 0x90C, 0xA0C },
+ { "gpb1", 0, 0x080, 5, 5, 0x710, 0x820, 0x910, 0xA10 },
+ { "gpb2", 0, 0x0A0, 4, 6, 0x714, 0x828, 0x914, 0xA14 },
+ { "gpb3", 0, 0x0C0, 4, 7, 0x718, 0x830, 0x918, 0xA18 },
+ { "gpc0", 0, 0x0E0, 7, 8, 0x71C, 0x838, 0x91C, 0xA1C },
+ { "gpc1", 0, 0x100, 4, 9, 0x720, 0x840, 0x920, 0xA20 },
+ { "gpc2", 0, 0x120, 7, 10, 0x724, 0x848, 0x924, 0xA24 },
+ { "gpc3", 0, 0x140, 7, 11, 0x728, 0x850, 0x928, 0xA28 },
+ { "gpd0", 0, 0x160, 4, 12, 0x72C, 0x858, 0x92C, 0xA2C },
+ { "gpd1", 0, 0x180, 8, 13, 0x730, 0x860, 0x930, 0xA30 },
+ { "gpy0", 0, 0x1A0, 6, 0, 0, 0, 0, 0 },
+ { "gpy1", 0, 0x1C0, 4, 0, 0, 0, 0, 0 },
+ { "gpy2", 0, 0x1E0, 6, 0, 0, 0, 0, 0 },
+ { "gpy3", 0, 0x200, 8, 0, 0, 0, 0, 0 },
+ { "gpy4", 0, 0x220, 8, 0, 0, 0, 0, 0 },
+ { "gpy5", 0, 0x240, 8, 0, 0, 0, 0, 0 },
+ { "gpy6", 0, 0x260, 8, 0, 0, 0, 0, 0 },
+ { "gpc4", 0, 0x2E0, 7, 30, 0x734, 0x868, 0x934, 0xA34 },
+
+ /* next 32 */
+ { "gpx0", 0, 0xC00, 8, 40, 0xE00, 0xE80, 0xF00, 0xF40 },
+ { "gpx1", 0, 0xC20, 8, 41, 0xE04, 0xE88, 0xF04, 0xF44 },
+ { "gpx2", 0, 0xC40, 8, 42, 0xE08, 0xE90, 0xF08, 0xF48 },
+ { "gpx3", 0, 0xC60, 8, 43, 0xE0C, 0xE98, 0xF0C, 0xF4C },
+
+ { "gpe0", 1, 0x000, 8, 14, 0x700, 0x800, 0x900, 0xA00 },
+ { "gpe1", 1, 0x020, 2, 15, 0x704, 0x808, 0x904, 0xA04 },
+ { "gpf0", 1, 0x040, 4, 16, 0x708, 0x810, 0x908, 0xA08 },
+ { "gpf1", 1, 0x060, 4, 17, 0x70C, 0x818, 0x90C, 0xA0C },
+ { "gpg0", 1, 0x080, 8, 18, 0x710, 0x820, 0x910, 0xA10 },
+ { "gpg1", 1, 0x0A0, 8, 19, 0x714, 0x828, 0x914, 0xA14 },
+ { "gpg2", 1, 0x0C0, 2, 20, 0x718, 0x830, 0x918, 0xA18 },
+ { "gph0", 1, 0x0E0, 4, 21, 0x71C, 0x838, 0x91C, 0xA1C },
+ { "gph1", 1, 0x100, 8, 22, 0x720, 0x840, 0x920, 0xA20 },
+
+ { "gpv0", 2, 0x000, 8, 60, 0x700, 0x800, 0x900, 0xA00 },
+ { "gpv1", 2, 0x020, 8, 61, 0x704, 0x808, 0x904, 0xA04 },
+ { "gpv2", 2, 0x060, 8, 62, 0x708, 0x810, 0x908, 0xA08 },
+ { "gpv3", 2, 0x080, 8, 63, 0x70C, 0x818, 0x90C, 0xA0C },
+ { "gpv4", 2, 0x0C0, 2, 64, 0x710, 0x820, 0x910, 0xA10 },
+
+ { "gpz", 3, 0x000, 7, 50, 0x700, 0x800, 0x900, 0xA00 },
+};
+
+static int
+get_bank(int gpio_number, struct gpio_bank *bank, int *pin_shift)
+{
+ int ngpio;
+ int i;
+ int n;
+
+ n = 0;
+ for (i = 0; i < NGRP; i++) {
+ ngpio = gpio_map[i].ngpio;
+
+ if ((n + ngpio) >= gpio_number) {
+ *bank = gpio_map[i];
+ *pin_shift = (gpio_number - n);
+ return (0);
+ };
+
+ n += ngpio;
+ };
+
+ return (-1);
+}
+
+static int
+port_intr(void *arg)
+{
+ struct port_softc *sc;
+
+ sc = arg;
+
+ return (FILTER_HANDLED);
+}
+
+static void
+ext_intr(void *arg)
+{
+ struct pad_softc *sc;
+ void (*ih) (void *);
+ void *ih_user;
+ int ngpio;
+ int found;
+ int reg;
+ int i,j;
+ int n,k;
+
+ sc = arg;
+
+ n = 0;
+ for (i = 0; i < NGRP; i++) {
+ found = 0;
+ ngpio = gpio_map[i].ngpio;
+
+ if (gpio_map[i].pend == 0) {
+ n += ngpio;
+ continue;
+ }
+
+ reg = READ4(sc, gpio_map[i].port, gpio_map[i].pend);
+
+ for (j = 0; j < ngpio; j++) {
+ if (reg & (1 << j)) {
+ found = 1;
+
+ k = (n + j);
+ if (intr_map[k].enabled == 1) {
+ ih = intr_map[k].ih;
+ ih_user = intr_map[k].ih_user;
+ ih(ih_user);
+ }
+ }
+ }
+
+ if (found) {
+ /* ACK */
+ WRITE4(sc, gpio_map[i].port, gpio_map[i].pend, reg);
+ }
+
+ n += ngpio;
+ }
+}
+
+int
+pad_setup_intr(int gpio_number, void (*ih)(void *), void *ih_user)
+{
+ struct interrupt_entry *entry;
+ struct pad_intr *pad_irq;
+ struct gpio_bank bank;
+ struct pad_softc *sc;
+ int pin_shift;
+ int reg;
+ int i;
+
+ sc = gpio_sc;
+
+ if (sc == NULL) {
+ device_printf(sc->dev, "Error: pad is not attached\n");
+ return (-1);
+ }
+
+ if (get_bank(gpio_number, &bank, &pin_shift) != 0)
+ return (-1);
+
+ entry = NULL;
+ for (i = 0; i < NINTS; i++)
+ if (interrupt_table[i].gpio_number == gpio_number)
+ entry = &interrupt_table[i];
+
+ if (entry == NULL) {
+ device_printf(sc->dev, "Cant find interrupt source for %d\n",
+ gpio_number);
+ return (-1);
+ }
+
+#if 0
+ printf("Request interrupt name %s\n", entry->combiner_source_name);
+#endif
+
+ pad_irq = &intr_map[gpio_number];
+ pad_irq->enabled = 1;
+ pad_irq->ih = ih;
+ pad_irq->ih_user = ih_user;
+
+ /* Setup port as external interrupt source */
+ reg = READ4(sc, bank.port, bank.con);
+ reg |= (0xf << (pin_shift * 4));
+#if 0
+ printf("writing 0x%08x to 0x%08x\n", reg, bank.con);
+#endif
+ WRITE4(sc, bank.port, bank.con, reg);
+
+ /*
+ * Configure interrupt pin
+ *
+ * 0x0 = Sets Low level
+ * 0x1 = Sets High level
+ * 0x2 = Triggers Falling edge
+ * 0x3 = Triggers Rising edge
+ * 0x4 = Triggers Both edge
+ *
+ * TODO: add parameter. For now configure as 0x0
+ */
+ reg = READ4(sc, bank.port, bank.ext_con);
+ reg &= ~(0x7 << (pin_shift * 4));
+ WRITE4(sc, bank.port, bank.ext_con, reg);
+
+ /* Unmask */
+ reg = READ4(sc, bank.port, bank.mask);
+ reg &= ~(1 << pin_shift);
+ WRITE4(sc, bank.port, bank.mask, reg);
+
+ combiner_setup_intr(entry->combiner_source_name, ext_intr, sc);
+
+ return (0);
+}
+
+static int
+pad_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "exynos,pad"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Exynos Pad Control");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+pad_attach(device_t dev)
+{
+ struct gpio_bank bank;
+ struct pad_softc *sc;
+ int pin_shift;
+ int reg;
+ int i;
+
+ sc = device_get_softc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ if (bus_alloc_resources(dev, pad_spec, sc->res)) {
+ device_printf(dev, "could not allocate resources\n");
+ return (ENXIO);
+ }
+
+ /* Memory interface */
+
+ for (i = 0; i < NPORTS; i++) {
+ sc->bst[i] = rman_get_bustag(sc->res[i]);
+ sc->bsh[i] = rman_get_bushandle(sc->res[i]);
+ };
+
+ sc->dev = dev;
+ sc->gpio_npins = NGPIO;
+
+ gpio_sc = sc;
+
+ for (i = 0; i < NPORTS; i++) {
+ if ((bus_setup_intr(dev, sc->res[NPORTS + i],
+ INTR_TYPE_BIO | INTR_MPSAFE, port_intr,
+ NULL, sc, &sc->gpio_ih[i]))) {
+ device_printf(dev,
+ "ERROR: Unable to register interrupt handler\n");
+ return (ENXIO);
+ }
+ };
+
+ for (i = 0; i < sc->gpio_npins; i++) {
+ sc->gpio_pins[i].gp_pin = i;
+ sc->gpio_pins[i].gp_caps = DEFAULT_CAPS;
+
+ if (get_bank(i, &bank, &pin_shift) != 0)
+ continue;
+
+ pin_shift *= 4;
+
+ reg = READ4(sc, bank.port, bank.con);
+ if (reg & (PIN_OUT << pin_shift))
+ sc->gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT;
+ else
+ sc->gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
+
+ /* TODO: add other pin statuses */
+
+ snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME,
+ "pad%d.%d", device_get_unit(dev), i);
+ }
+
+ device_add_child(dev, "gpioc", device_get_unit(dev));
+ device_add_child(dev, "gpiobus", device_get_unit(dev));
+
+ return (bus_generic_attach(dev));
+}
+
+static int
+pad_pin_max(device_t dev, int *maxpin)
+{
+
+ *maxpin = NGPIO - 1;
+ return (0);
+}
+
+static int
+pad_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ struct pad_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME);
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+pad_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct pad_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ *caps = sc->gpio_pins[i].gp_caps;
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+pad_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
+{
+ struct pad_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ *flags = sc->gpio_pins[i].gp_flags;
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+pad_pin_get(device_t dev, uint32_t pin, unsigned int *val)
+{
+ struct gpio_bank bank;
+ struct pad_softc *sc;
+ int pin_shift;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ if (get_bank(pin, &bank, &pin_shift) != 0)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ if (READ4(sc, bank.port, bank.con + 0x4) & (1 << pin_shift))
+ *val = 1;
+ else
+ *val = 0;
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+pad_pin_toggle(device_t dev, uint32_t pin)
+{
+ struct gpio_bank bank;
+ struct pad_softc *sc;
+ int pin_shift;
+ int reg;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ if (get_bank(pin, &bank, &pin_shift) != 0)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ reg = READ4(sc, bank.port, bank.con + 0x4);
+ if (reg & (1 << pin_shift))
+ reg &= ~(1 << pin_shift);
+ else
+ reg |= (1 << pin_shift);
+ WRITE4(sc, bank.port, bank.con + 0x4, reg);
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+
+static void
+pad_pin_configure(struct pad_softc *sc, struct gpio_pin *pin,
+ unsigned int flags)
+{
+ struct gpio_bank bank;
+ int pin_shift;
+ int reg;
+
+ GPIO_LOCK(sc);
+
+ /*
+ * Manage input/output
+ */
+ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
+ pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
+
+ if (get_bank(pin->gp_pin, &bank, &pin_shift) != 0)
+ return;
+
+ pin_shift *= 4;
+
+#if 0
+ printf("bank is 0x%08x pin_shift %d\n", bank.con, pin_shift);
+#endif
+
+ if (flags & GPIO_PIN_OUTPUT) {
+ pin->gp_flags |= GPIO_PIN_OUTPUT;
+ reg = READ4(sc, bank.port, bank.con);
+ reg &= ~(0xf << pin_shift);
+ reg |= (PIN_OUT << pin_shift);
+ WRITE4(sc, bank.port, bank.con, reg);
+ } else {
+ pin->gp_flags |= GPIO_PIN_INPUT;
+ reg = READ4(sc, bank.port, bank.con);
+ reg &= ~(0xf << pin_shift);
+ WRITE4(sc, bank.port, bank.con, reg);
+ }
+ }
+
+ GPIO_UNLOCK(sc);
+}
+
+
+static int
+pad_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ struct pad_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ /* Check for unwanted flags. */
+ if ((flags & sc->gpio_pins[i].gp_caps) != flags)
+ return (EINVAL);
+
+ /* Can't mix input/output together */
+ if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) ==
+ (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT))
+ return (EINVAL);
+
+ pad_pin_configure(sc, &sc->gpio_pins[i], flags);
+
+ return (0);
+}
+
+static int
+pad_pin_set(device_t dev, uint32_t pin, unsigned int value)
+{
+ struct pad_softc *sc;
+ struct gpio_bank bank;
+ int pin_shift;
+ int reg;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->gpio_npins; i++) {
+ if (sc->gpio_pins[i].gp_pin == pin)
+ break;
+ }
+
+ if (i >= sc->gpio_npins)
+ return (EINVAL);
+
+ if (get_bank(pin, &bank, &pin_shift) != 0)
+ return (EINVAL);
+
+ GPIO_LOCK(sc);
+ reg = READ4(sc, bank.port, bank.con + 0x4);
+ reg &= ~(PIN_OUT << pin_shift);
+ if (value)
+ reg |= (PIN_OUT << pin_shift);
+ WRITE4(sc, bank.port, bank.con + 0x4, reg);
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static device_method_t pad_methods[] = {
+ DEVMETHOD(device_probe, pad_probe),
+ DEVMETHOD(device_attach, pad_attach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_pin_max, pad_pin_max),
+ DEVMETHOD(gpio_pin_getname, pad_pin_getname),
+ DEVMETHOD(gpio_pin_getcaps, pad_pin_getcaps),
+ DEVMETHOD(gpio_pin_getflags, pad_pin_getflags),
+ DEVMETHOD(gpio_pin_get, pad_pin_get),
+ DEVMETHOD(gpio_pin_toggle, pad_pin_toggle),
+ DEVMETHOD(gpio_pin_setflags, pad_pin_setflags),
+ DEVMETHOD(gpio_pin_set, pad_pin_set),
+ { 0, 0 }
+};
+
+static driver_t pad_driver = {
+ "gpio",
+ pad_methods,
+ sizeof(struct pad_softc),
+};
+
+static devclass_t pad_devclass;
+
+DRIVER_MODULE(pad, simplebus, pad_driver, pad_devclass, 0, 0);
diff --git a/sys/arm/samsung/exynos/exynos5_pad.h b/sys/arm/samsung/exynos/exynos5_pad.h
new file mode 100644
index 0000000..4b83a02
--- /dev/null
+++ b/sys/arm/samsung/exynos/exynos5_pad.h
@@ -0,0 +1,29 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+int pad_setup_intr(int gpio_number, void (*ih)(void *), void *ih_user);
diff --git a/sys/arm/samsung/exynos/files.exynos5 b/sys/arm/samsung/exynos/files.exynos5
index fc68cd3..2bd3a16 100644
--- a/sys/arm/samsung/exynos/files.exynos5
+++ b/sys/arm/samsung/exynos/files.exynos5
@@ -17,8 +17,15 @@ arm/samsung/exynos/exynos5_mct.c standard
arm/samsung/exynos/exynos5_mp.c optional smp
arm/samsung/exynos/exynos5_common.c standard
arm/samsung/exynos/exynos5_machdep.c standard
+arm/samsung/exynos/exynos5_combiner.c standard
+arm/samsung/exynos/exynos5_pad.c optional gpio
arm/samsung/exynos/uart.c optional uart
arm/samsung/exynos/exynos5_ehci.c optional ehci
arm/samsung/exynos/exynos5_fimd.c optional vt
+arm/samsung/exynos/exynos5_i2c.c optional iicbus
+
+# chromebook drivers
+arm/samsung/exynos/chrome_ec.c optional chrome_ec
+arm/samsung/exynos/chrome_kb.c optional chrome_kb
#dev/sdhci/sdhci_fdt.c optional sdhci
OpenPOWER on IntegriCloud