summaryrefslogtreecommitdiffstats
path: root/sys/arm/samsung
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2014-05-17 19:37:04 +0000
committerian <ian@FreeBSD.org>2014-05-17 19:37:04 +0000
commitac96a624aeb31b613fe5d1210998d31e29bcb2df (patch)
tree65562490066ab0b940ac6f4fea27daf43356cb54 /sys/arm/samsung
parent91f6841891ec7c87458d26652e835aeeff9a52df (diff)
downloadFreeBSD-src-ac96a624aeb31b613fe5d1210998d31e29bcb2df.zip
FreeBSD-src-ac96a624aeb31b613fe5d1210998d31e29bcb2df.tar.gz
MFC 263910, 263913, 263914, 263933, 263934, 263935, 263936, 263981, 263982,
Add more flags for the fpexc register from the ARM1176JZF-S Manual Initialise fpscr to a sane value when we create the pcb. This sets NaNs to be the default NaN and for denormalised numbers to be flushed to zero. VFP fixes/cleanups for ARM11: * Save the required VFP registers on context switch. If the exception bit is set we need to save and restore the FPINST register, and if the fp2v bit is also set we need to save and restore FPINST2. * Move saving and restoring the floating point control registers to C. * Clear the fpexc exception and fp2v flags on a floating-point exception. * Signal a SIGFPE if the fpexc exception flag is set on an undefined instruction. This is how the ARM core signals to software there is a floating-point exception. Add Cortex-A15 cpu id revisions. Exynos/Arndale... - Merge SoC-common parts - Enable iicbus device - Directly call kmem_alloc_contig to allocate framebuffer memory and pass VM_MEMATTR_UNCACHEABLE (no-cache, no-buffer). This fixes screen refreshing problem when data is updated too slowly. - Add support for keyboard used in Samsung Chromebook (ARM machine) Support covers device drivers for: - Interrupt Combiner - gpio/pad, External Interrupts Controller (pad) - I2C Interface - Chrome Embedded Controller - Chrome Keyboard - Use new gpio dev class in EHCI driver - Expand device tree information - Release i2c bus on detach.
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