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 Also: - Use new gpio dev class in EHCI driver - Expand device tree information
This commit is contained in:
parent
a3fcf1f8d9
commit
657fae63fc
@ -20,6 +20,11 @@
|
||||
include "EXYNOS5250.common"
|
||||
ident CHROMEBOOK
|
||||
|
||||
hints "CHROMEBOOK.hints"
|
||||
|
||||
device chrome_ec # Chrome Embedded Controller
|
||||
device chrome_kb # Chrome Keyboard
|
||||
|
||||
# Framebuffer
|
||||
device vt
|
||||
device kbdmux
|
||||
|
5
sys/arm/conf/CHROMEBOOK.hints
Normal file
5
sys/arm/conf/CHROMEBOOK.hints
Normal file
@ -0,0 +1,5 @@
|
||||
# $FreeBSD$
|
||||
|
||||
# Chrome Embedded Controller
|
||||
hint.chrome_ec.0.at="iicbus0"
|
||||
hint.chrome_ec.0.addr=0x1e
|
253
sys/arm/samsung/exynos/chrome_ec.c
Normal file
253
sys/arm/samsung/exynos/chrome_ec.c
Normal file
@ -0,0 +1,253 @@
|
||||
/*-
|
||||
* 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 device_method_t ec_methods[] = {
|
||||
DEVMETHOD(device_probe, ec_probe),
|
||||
DEVMETHOD(device_attach, ec_attach),
|
||||
{ 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);
|
36
sys/arm/samsung/exynos/chrome_ec.h
Normal file
36
sys/arm/samsung/exynos/chrome_ec.h
Normal file
@ -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);
|
792
sys/arm/samsung/exynos/chrome_kb.c
Normal file
792
sys/arm/samsung/exynos/chrome_kb.c
Normal file
@ -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);
|
122
sys/arm/samsung/exynos/chrome_kb.h
Normal file
122
sys/arm/samsung/exynos/chrome_kb.h
Normal file
@ -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 */
|
||||
};
|
304
sys/arm/samsung/exynos/exynos5_combiner.c
Normal file
304
sys/arm/samsung/exynos/exynos5_combiner.c
Normal file
@ -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);
|
29
sys/arm/samsung/exynos/exynos5_combiner.h
Normal file
29
sys/arm/samsung/exynos/exynos5_combiner.h
Normal file
@ -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);
|
@ -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;
|
||||
|
||||
/* 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);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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 (power)
|
||||
GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_HIGH);
|
||||
else
|
||||
GPIO_PIN_SET(gpio_dev, PIN_USB, GPIO_PIN_LOW);
|
||||
|
||||
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,"
|
||||
|
476
sys/arm/samsung/exynos/exynos5_i2c.c
Normal file
476
sys/arm/samsung/exynos/exynos5_i2c.c
Normal file
@ -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);
|
716
sys/arm/samsung/exynos/exynos5_pad.c
Normal file
716
sys/arm/samsung/exynos/exynos5_pad.c
Normal file
@ -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);
|
29
sys/arm/samsung/exynos/exynos5_pad.h
Normal file
29
sys/arm/samsung/exynos/exynos5_pad.h
Normal file
@ -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);
|
@ -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
|
||||
|
@ -38,6 +38,14 @@
|
||||
reg = < 0x40000000 0x80000000 >; /* 2G */
|
||||
};
|
||||
|
||||
SOC: Exynos5@0 {
|
||||
|
||||
pad0: pad@11400000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
chosen {
|
||||
stdin = &serial2;
|
||||
stdout = &serial2;
|
||||
|
@ -40,6 +40,10 @@
|
||||
|
||||
SOC: Exynos5@0 {
|
||||
|
||||
pad0: pad@11400000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
fimd0: fimd@14400000 {
|
||||
status = "okay";
|
||||
|
||||
@ -50,6 +54,15 @@
|
||||
panel-backlight-pin = < 25 >;
|
||||
};
|
||||
|
||||
i2c4: i2c@12CA0000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
keyboard-controller {
|
||||
compatible = "google,cros-ec-keyb";
|
||||
keypad,num-rows = <8>;
|
||||
keypad,num-columns = <13>;
|
||||
};
|
||||
};
|
||||
|
||||
chosen {
|
||||
|
@ -57,6 +57,16 @@
|
||||
#interrupt-cells = <1>;
|
||||
};
|
||||
|
||||
combiner: interrupt-controller@10440000 {
|
||||
compatible = "exynos,combiner";
|
||||
reg = <0x10440000 0x1000>;
|
||||
interrupts = < 32 33 34 35 36 37 38 39
|
||||
40 41 42 43 44 45 46 47
|
||||
48 49 50 51 52 53 54 55
|
||||
56 57 58 59 60 61 62 63 >;
|
||||
interrupt-parent = <&GIC>;
|
||||
};
|
||||
|
||||
clk0: clk@10010000 {
|
||||
compatible = "exynos,clk";
|
||||
reg = < 0x10020000 0x20000 >;
|
||||
@ -81,13 +91,23 @@
|
||||
clock-frequency = <24000000>;
|
||||
};
|
||||
|
||||
pad0: pad@11400000 {
|
||||
compatible = "exynos,pad";
|
||||
status = "disabled";
|
||||
reg = <0x11400000 0x1000>, /* gpio left */
|
||||
<0x13400000 0x1000>, /* gpio right */
|
||||
<0x10D10000 0x1000>, /* gpio c2c */
|
||||
<0x03860000 0x1000>;
|
||||
interrupts = < 78 77 82 79 >;
|
||||
interrupt-parent = <&GIC>;
|
||||
};
|
||||
|
||||
usb@12110000 {
|
||||
compatible = "exynos,usb-ehci", "usb-ehci";
|
||||
reg = <0x12110000 0x1000>, /* EHCI */
|
||||
<0x12130000 0x1000>, /* EHCI host ctrl */
|
||||
<0x10040000 0x1000>, /* Power */
|
||||
<0x10050230 0x10>, /* Sysreg */
|
||||
<0x11400C60 0x10>; /* GPIO left */
|
||||
<0x10050230 0x10>; /* Sysreg */
|
||||
interrupts = < 103 >;
|
||||
interrupt-parent = <&GIC>;
|
||||
};
|
||||
@ -167,6 +187,70 @@
|
||||
current-speed = <115200>;
|
||||
};
|
||||
|
||||
i2c0: i2c@12C60000 {
|
||||
compatible = "exynos,i2c";
|
||||
status = "disabled";
|
||||
reg = <0x12C60000 0x10000>;
|
||||
interrupts = < 88 >;
|
||||
interrupt-parent = <&GIC>;
|
||||
};
|
||||
|
||||
i2c1: i2c@12C70000 {
|
||||
compatible = "exynos,i2c";
|
||||
status = "disabled";
|
||||
reg = <0x12C70000 0x10000>;
|
||||
interrupts = < 89 >;
|
||||
interrupt-parent = <&GIC>;
|
||||
};
|
||||
|
||||
i2c2: i2c@12C80000 {
|
||||
compatible = "exynos,i2c";
|
||||
status = "disabled";
|
||||
reg = <0x12C80000 0x10000>;
|
||||
interrupts = < 90 >;
|
||||
interrupt-parent = <&GIC>;
|
||||
};
|
||||
|
||||
i2c3: i2c@12C90000 {
|
||||
compatible = "exynos,i2c";
|
||||
status = "disabled";
|
||||
reg = <0x12C90000 0x10000>;
|
||||
interrupts = < 91 >;
|
||||
interrupt-parent = <&GIC>;
|
||||
};
|
||||
|
||||
i2c4: i2c@12CA0000 {
|
||||
compatible = "exynos,i2c";
|
||||
status = "disabled";
|
||||
reg = <0x12CA0000 0x10000>;
|
||||
interrupts = < 92 >;
|
||||
interrupt-parent = <&GIC>;
|
||||
};
|
||||
|
||||
i2c5: i2c@12CB0000 {
|
||||
compatible = "exynos,i2c";
|
||||
status = "disabled";
|
||||
reg = <0x12CB0000 0x10000>;
|
||||
interrupts = < 93 >;
|
||||
interrupt-parent = <&GIC>;
|
||||
};
|
||||
|
||||
i2c6: i2c@12CC0000 {
|
||||
compatible = "exynos,i2c";
|
||||
status = "disabled";
|
||||
reg = <0x12CC0000 0x10000>;
|
||||
interrupts = < 94 >;
|
||||
interrupt-parent = <&GIC>;
|
||||
};
|
||||
|
||||
i2c7: i2c@12CD0000 {
|
||||
compatible = "exynos,i2c";
|
||||
status = "disabled";
|
||||
reg = <0x12CD0000 0x10000>;
|
||||
interrupts = < 95 >;
|
||||
interrupt-parent = <&GIC>;
|
||||
};
|
||||
|
||||
fimd0: fimd@14400000 {
|
||||
compatible = "exynos,fimd";
|
||||
status = "disabled";
|
||||
|
Loading…
x
Reference in New Issue
Block a user