diff options
author | Brandon Mercer <bmercer@cvs.openbsd.org> | 2015-01-26 02:48:25 +0000 |
---|---|---|
committer | Brandon Mercer <bmercer@cvs.openbsd.org> | 2015-01-26 02:48:25 +0000 |
commit | 34a8368bc8175eb2a24ea327ff945835ca6e9fd7 (patch) | |
tree | 4b8275ea7f46530e8f70b212bc76e20c17ac639c | |
parent | 5c9fb779da79fd7e7451055c874fc31295b22d06 (diff) |
Import the exynos work into tree. This is from Bitrig. Discussed with and OK jsg, also OK from krw.
28 files changed, 7163 insertions, 0 deletions
diff --git a/sys/arch/armv7/exynos/crosec.c b/sys/arch/armv7/exynos/crosec.c new file mode 100644 index 00000000000..21178b48534 --- /dev/null +++ b/sys/arch/armv7/exynos/crosec.c @@ -0,0 +1,274 @@ +/* $OpenBSD: crosec.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/sensors.h> +#include <sys/malloc.h> + +#include <armv7/exynos/crosecvar.h> + +#ifdef DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +int cros_ec_match(struct device *, void *, void *); +void cros_ec_attach(struct device *, struct device *, void *); + +int cros_ec_send_command(struct cros_ec_softc *, uint8_t, + int, const void *, int, uint8_t **, int); +int cros_ec_command(struct cros_ec_softc *, uint8_t, + int, const void *, int, void *, int); +int cros_ec_command_inptr(struct cros_ec_softc *, uint8_t, + int, const void *, int, uint8_t **, int); +int cros_ec_i2c_command(struct cros_ec_softc *, uint8_t, + int, const uint8_t *, int, uint8_t **, int); +int cros_ec_calc_checksum(const uint8_t *, int); + +struct cfattach crosec_ca = { + sizeof(struct cros_ec_softc), cros_ec_match, cros_ec_attach +}; + +struct cfdriver crosec_cd = { + NULL, "crosec", DV_DULL +}; + +int +cros_ec_match(struct device *parent, void *match, void *aux) +{ + struct i2c_attach_args *ia = aux; + + if (strcmp(ia->ia_name, "crosec") == 0) + return 1; + return 0; +} + +void +cros_ec_attach(struct device *parent, struct device *self, void *aux) +{ + struct cros_ec_softc *sc = (struct cros_ec_softc *)self; + struct i2c_attach_args *ia = aux; + + sc->sc_tag = ia->ia_tag; + sc->sc_addr = ia->ia_addr; + + printf("\n"); + + if (cros_ec_check_version(sc)) { + printf("%s: could not initialize ChromeOS EC\n", __func__); + return; + } + + if (cros_ec_init_keyboard(sc)) { + printf("%s: could not initialize keyboard\n", __func__); + return; + } +} + +int +cros_ec_check_version(struct cros_ec_softc *sc) +{ + struct ec_params_hello req; + struct ec_response_hello *resp; + + sc->cmd_version_is_supported = 1; + if (cros_ec_command_inptr(sc, EC_CMD_HELLO, 0, &req, sizeof(req), + (uint8_t **)&resp, sizeof(*resp)) > 0) { + /* new version supported */ + sc->cmd_version_is_supported = 1; + } else { + printf("%s: old EC interface not supported\n", __func__); + return -1; + } + + return 0; +} + +int +cros_ec_i2c_command(struct cros_ec_softc *sc, uint8_t cmd, int cmd_version, + const uint8_t *out, int out_len, uint8_t **in, int in_len) +{ + int out_bytes, in_bytes, ret; + uint8_t *ptr = sc->out; + uint8_t *inptr = sc->in; + + inptr += sizeof(uint64_t); /* returned data should be 64-bit aligned */ + if (!sc->cmd_version_is_supported) { + /* old-style */ + *ptr++ = cmd; + out_bytes = out_len + 1; + in_bytes = in_len + 2; + inptr--; /* status byte */ + } else { + /* new-style */ + *ptr++ = EC_CMD_VERSION0 + cmd_version; + *ptr++ = cmd; + *ptr++ = out_len; + out_bytes = out_len + 4; + in_bytes = in_len + 3; + inptr -= 2; /* status byte, length */ + } + memcpy(ptr, out, out_len); + ptr += out_len; + + if (sc->cmd_version_is_supported) + *ptr++ = (uint8_t) + cros_ec_calc_checksum(sc->out, out_len + 3); + + iic_acquire_bus(sc->sc_tag, 0); + ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, + sc->sc_addr, NULL, 0, &sc->out, out_bytes, 0); + if (ret) { + DPRINTF(("%s: I2C write failed\n", __func__)); + iic_release_bus(sc->sc_tag, 0); + return -1; + } + + ret = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_addr, NULL, 0, inptr, in_bytes, 0); + if (ret) { + DPRINTF(("%s: I2C read failed\n", __func__)); + iic_release_bus(sc->sc_tag, 0); + return -1; + } + + iic_release_bus(sc->sc_tag, 0); + + if (*inptr != EC_RES_SUCCESS) { + DPRINTF(("%s: bad result\n", __func__)); + return -(int)*inptr; + } + + if (sc->cmd_version_is_supported) { + int len, csum; + + len = inptr[1]; + if (len > sizeof(sc->in)) { + DPRINTF(("%s: Received length too large\n", __func__)); + return -1; + } + csum = cros_ec_calc_checksum(inptr, 2 + len); + if (csum != inptr[2 + len]) { + DPRINTF(("%s: Invalid checksum\n", __func__)); + return -1; + } + in_len = min(in_len, len); + } + + *in = sc->in + sizeof(uint64_t); + + return in_len; +} + +int +cros_ec_send_command(struct cros_ec_softc *sc, uint8_t cmd, int cmd_version, + const void *out, int out_len, uint8_t **in, int in_len) +{ + return cros_ec_i2c_command(sc, cmd, cmd_version, + (const uint8_t *)out, out_len, in, in_len); +} + +int +cros_ec_command(struct cros_ec_softc *sc, uint8_t cmd, + int cmd_version, const void *out, int out_len, + void *in, int in_len) { + uint8_t *in_buffer; + int len; + + len = cros_ec_command_inptr(sc, cmd, cmd_version, out, out_len, + &in_buffer, in_len); + + if (len > 0) { + if (in && in_buffer) { + len = min(in_len, len); + memmove(in, in_buffer, len); + } + } + return len; +} + +int +cros_ec_command_inptr(struct cros_ec_softc *sc, uint8_t cmd, + int cmd_version, const void *out, int out_len, + uint8_t **inp, int in_len) { + uint8_t *in; + int len; + + len = cros_ec_send_command(sc, cmd, cmd_version, + (const uint8_t *)out, out_len, &in, in_len); + + /* Wait for the command to complete. */ + if (len == -EC_RES_IN_PROGRESS) { + struct ec_response_get_comms_status *resp; + + do { + int ret; + + delay(50000); + cros_ec_send_command(sc, EC_CMD_GET_COMMS_STATUS, 0, + NULL, 0, + (uint8_t **)&resp, sizeof(*resp)); + if (ret < 0) + return ret; + + //timeout CROS_EC_CMD_TIMEOUT_MS + //return -EC_RES_TIMEOUT + } while (resp->flags & EC_COMMS_STATUS_PROCESSING); + + /* Let's get the response. */ + len = cros_ec_send_command(sc, EC_CMD_RESEND_RESPONSE, 0, + out, out_len, &in, in_len); + } + + if (inp != NULL) + *inp = in; + + return len; +} + +int +cros_ec_calc_checksum(const uint8_t *data, int size) +{ + int csum, i; + + for (i = csum = 0; i < size; i++) + csum += data[i]; + return csum & 0xff; +} + +int +cros_ec_scan_keyboard(struct cros_ec_softc *sc, uint8_t *scan, int len) +{ + if (cros_ec_command(sc, EC_CMD_CROS_EC_STATE, 0, NULL, 0, scan, + len) < len) + return -1; + + return 0; +} + +int +cros_ec_info(struct cros_ec_softc *sc, struct ec_response_cros_ec_info *info) +{ + if (cros_ec_command(sc, EC_CMD_CROS_EC_INFO, 0, NULL, 0, info, + sizeof(*info)) < sizeof(*info)) + return -1; + + return 0; +} diff --git a/sys/arch/armv7/exynos/crosec_kbd.c b/sys/arch/armv7/exynos/crosec_kbd.c new file mode 100644 index 00000000000..cc8e0151b15 --- /dev/null +++ b/sys/arch/armv7/exynos/crosec_kbd.c @@ -0,0 +1,249 @@ +/* $OpenBSD: crosec_kbd.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/sensors.h> +#include <sys/malloc.h> + +#include <armv7/exynos/crosecvar.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wskbdvar.h> +#include <dev/wscons/wsksymdef.h> +#include <dev/wscons/wsksymvar.h> + +int cros_ec_get_keystate(struct cros_ec_softc *); + +int cros_ec_keyboard_enable(void *, int); +void cros_ec_keyboard_set_leds(void *, int); +int cros_ec_keyboard_ioctl(void *, u_long, caddr_t, int, struct proc *); + +struct wskbd_accessops cros_ec_keyboard_accessops = { + cros_ec_keyboard_enable, + cros_ec_keyboard_set_leds, + cros_ec_keyboard_ioctl, +}; + +void cros_ec_keyboard_cngetc(void *, u_int *, int *); +void cros_ec_keyboard_cnpollc(void *, int); + +struct wskbd_consops cros_ec_keyboard_consops = { + cros_ec_keyboard_cngetc, + cros_ec_keyboard_cnpollc, +}; + + +/* XXX: assumes 8 rows, 13 cols, FDT */ +#define KC(n) KS_KEYCODE(n) +static const keysym_t cros_ec_keyboard_keydesc_us[] = { + KC(1), KS_Caps_Lock, + KC(3), KS_b, + KC(6), KS_n, + KC(10), KS_Alt_R, + KC(14), KS_Escape, + KC(16), KS_g, + KC(19), KS_h, + KC(24), KS_BackSpace, + KC(26), KS_Control_L, + KC(27), KS_Tab, + KC(29), KS_t, + KC(32), KS_y, + KC(42), KS_5, + KC(45), KS_6, + KC(52), KS_Control_R, + KC(53), KS_a, + KC(54), KS_d, + KC(55), KS_f, + KC(56), KS_s, + KC(57), KS_k, + KC(58), KS_j, + KC(61), KS_l, + KC(63), KS_Return, + KC(66), KS_z, + KC(67), KS_c, + KC(68), KS_v, + KC(69), KS_x, + KC(70), KS_comma, + KC(71), KS_m, + KC(72), KS_Shift_L, + KC(74), KS_period, + KC(76), KS_space, + KC(79), KS_1, + KC(80), KS_3, + KC(81), KS_4, + KC(82), KS_2, + KC(83), KS_8, + KC(84), KS_7, + KC(86), KS_0, + KC(87), KS_9, + KC(88), KS_Alt_L, + KC(92), KS_q, + KC(93), KS_e, + KC(94), KS_r, + KC(95), KS_w, + KC(96), KS_i, + KC(97), KS_u, + KC(98), KS_Shift_R, + KC(99), KS_p, + KC(100), KS_o, +}; + +#define KBD_MAP(name, base, map) \ + { name, base, sizeof(map)/sizeof(keysym_t), map } +static const struct wscons_keydesc cros_ec_keyboard_keydesctab[] = { + KBD_MAP(KB_US, 0, cros_ec_keyboard_keydesc_us), + {0, 0, 0, 0} +}; +struct wskbd_mapdata cros_ec_keyboard_keymapdata = { + cros_ec_keyboard_keydesctab, + KB_US, +}; + +int +cros_ec_init_keyboard(struct cros_ec_softc *sc) +{ + struct ec_response_cros_ec_info info; + struct wskbddev_attach_args a; + + if (cros_ec_info(sc, &info)) { + printf("%s: could not read KBC info\n", __func__); + return (-1); + } + + sc->keyboard.rows = info.rows; + sc->keyboard.cols = info.cols; + sc->keyboard.switches = info.switches; + sc->keyboard.state = (uint8_t *)malloc(info.rows*info.cols, + M_DEVBUF, M_WAITOK|M_ZERO); + if (sc->keyboard.state == NULL) + panic("%s: no memory available for keyboard states", __func__); + + /* XXX: ghosting */ + +#if 0 + while (sc->keyboard.state != NULL) { + cros_ec_get_keystate(sc); + delay(100000); + } +#endif + + wskbd_cnattach(&cros_ec_keyboard_consops, sc, &cros_ec_keyboard_keymapdata); + a.console = 1; + + a.keymap = &cros_ec_keyboard_keymapdata; + a.accessops = &cros_ec_keyboard_accessops; + a.accesscookie = sc; + + sc->keyboard.wskbddev = config_found((void *)sc, &a, wskbddevprint); + + return 0; +} + +int +cros_ec_get_keystate(struct cros_ec_softc *sc) +{ + int col, row; + uint8_t state[sc->keyboard.cols]; + cros_ec_scan_keyboard(sc, state, sc->keyboard.cols); + for (col = 0; col < sc->keyboard.cols; col++) { + for (row = 0; row < sc->keyboard.rows; row++) { + int off = row*sc->keyboard.cols; + int pressed = !!(state[col] & (1 << row)); + if (pressed && !sc->keyboard.state[off+col]) { + //printf("row %d col %d id %d pressed\n", row, col, off+col); + sc->keyboard.state[off+col] = 1; + if (sc->keyboard.polling) + return off+col; + wskbd_input(sc->keyboard.wskbddev, WSCONS_EVENT_KEY_DOWN, off+col); + } else if (!pressed && sc->keyboard.state[off+col]) { + //printf("row %d col %d id %d released\n", row, col, off+col); + sc->keyboard.state[off+col] = 0; + if (sc->keyboard.polling) + return off+col; + wskbd_input(sc->keyboard.wskbddev, WSCONS_EVENT_KEY_UP, off+col); + } else if (sc->keyboard.state[off+col]) { + //printf("row %d col %d id %d repeated\n", row, col, off+col); + if (sc->keyboard.polling) + return off+col; + } + } + } + return (-1); +} + +void +cros_ec_keyboard_cngetc(void *v, u_int *type, int *data) +{ + struct cros_ec_softc *sc = v; + int key; + + sc->keyboard.polling = 1; + while ((key = cros_ec_get_keystate(sc)) == -1) { + delay(10000); + } + sc->keyboard.polling = 0; + + *data = key; + *type = sc->keyboard.state[key] ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; +} + +void +cros_ec_keyboard_cnpollc(void *v, int on) +{ +} + +int +cros_ec_keyboard_enable(void *v, int on) +{ + return 0; +} + +void +cros_ec_keyboard_set_leds(void *v, int on) +{ +} + +int +cros_ec_keyboard_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) +{ +#ifdef WSDISPLAY_COMPAT_RAWKBD + struct cros_ec_softc *sc = v; +#endif + + switch (cmd) { + + case WSKBDIO_GTYPE: + *(int *)data = WSKBD_TYPE_ZAURUS; + return 0; + case WSKBDIO_SETLEDS: + return 0; + case WSKBDIO_GETLEDS: + *(int *)data = 0; + return 0; +#ifdef WSDISPLAY_COMPAT_RAWKBD + case WSKBDIO_SETMODE: + sc->rawkbd = *(int *)data == WSKBD_RAW; + return (0); +#endif + + } + /* kbdioctl(...); */ + + return -1; +} diff --git a/sys/arch/armv7/exynos/crosecvar.h b/sys/arch/armv7/exynos/crosecvar.h new file mode 100644 index 00000000000..1f869c3b83b --- /dev/null +++ b/sys/arch/armv7/exynos/crosecvar.h @@ -0,0 +1,61 @@ +/* $OpenBSD: crosecvar.h,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CROSECVAR_H +#define CROSECVAR_H + +#include <dev/i2c/i2cvar.h> +#include <armv7/exynos/ec_commands.h> + +/* message sizes */ +#define MSG_HEADER 0xec +#define MSG_HEADER_BYTES 3 +#define MSG_TRAILER_BYTES 2 +#define MSG_PROTO_BYTES (MSG_HEADER_BYTES + MSG_TRAILER_BYTES) +#define MSG_BYTES (EC_HOST_PARAM_SIZE + MSG_PROTO_BYTES) +#define MSG_BYTES_ALIGNED ((MSG_BYTES+8) & ~8) + +#define min(a,b) (((a)<(b))?(a):(b)) + +struct cros_ec_softc { + struct device sc_dev; + i2c_tag_t sc_tag; + i2c_addr_t sc_addr; + + int cmd_version_is_supported; + struct { + int rows; + int cols; + int switches; + uint8_t *state; + + /* wskbd bits */ + struct device *wskbddev; + int rawkbd; + int polling; + } keyboard; + uint8_t in[MSG_BYTES_ALIGNED]; + uint8_t out[MSG_BYTES_ALIGNED]; +}; + +int cros_ec_check_version(struct cros_ec_softc *); +int cros_ec_scan_keyboard(struct cros_ec_softc *, uint8_t *, int); +int cros_ec_info(struct cros_ec_softc *, struct ec_response_cros_ec_info *); + +int cros_ec_init_keyboard(struct cros_ec_softc *); + +#endif /* !CROSECVAR_H */ diff --git a/sys/arch/armv7/exynos/ec_commands.h b/sys/arch/armv7/exynos/ec_commands.h new file mode 100644 index 00000000000..8dc2df07133 --- /dev/null +++ b/sys/arch/armv7/exynos/ec_commands.h @@ -0,0 +1,1500 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Host communication command constants for Chrome EC */ + +#ifndef __CROS_EC_COMMANDS_H +#define __CROS_EC_COMMANDS_H + +/* + * Protocol overview + * + * request: CMD [ P0 P1 P2 ... Pn S ] + * response: ERR [ P0 P1 P2 ... Pn S ] + * + * where the bytes are defined as follow : + * - CMD is the command code. (defined by EC_CMD_ constants) + * - ERR is the error code. (defined by EC_RES_ constants) + * - Px is the optional payload. + * it is not sent if the error code is not success. + * (defined by ec_params_ and ec_response_ structures) + * - S is the checksum which is the sum of all payload bytes. + * + * On LPC, CMD and ERR are sent/received at EC_LPC_ADDR_KERNEL|USER_CMD + * and the payloads are sent/received at EC_LPC_ADDR_KERNEL|USER_PARAM. + * On I2C, all bytes are sent serially in the same message. + */ + +/* Current version of this protocol */ +#define EC_PROTO_VERSION 0x00000002 + +/* Command version mask */ +#define EC_VER_MASK(version) (1UL << (version)) + +/* I/O addresses for ACPI commands */ +#define EC_LPC_ADDR_ACPI_DATA 0x62 +#define EC_LPC_ADDR_ACPI_CMD 0x66 + +/* I/O addresses for host command */ +#define EC_LPC_ADDR_HOST_DATA 0x200 +#define EC_LPC_ADDR_HOST_CMD 0x204 + +/* I/O addresses for host command args and params */ +#define EC_LPC_ADDR_HOST_ARGS 0x800 +#define EC_LPC_ADDR_HOST_PARAM 0x804 +#define EC_HOST_PARAM_SIZE 0x0fc /* Size of param area in bytes */ + +/* EC command register bit functions */ +#define EC_LPC_CMDR_DATA (1 << 0) /* Data ready for host to read */ +#define EC_LPC_CMDR_PENDING (1 << 1) /* Write pending to EC */ +#define EC_LPC_CMDR_BUSY (1 << 2) /* EC is busy processing a command */ +#define EC_LPC_CMDR_CMD (1 << 3) /* Last host write was a command */ +#define EC_LPC_CMDR_ACPI_BRST (1 << 4) /* Burst mode (not used) */ +#define EC_LPC_CMDR_SCI (1 << 5) /* SCI event is pending */ +#define EC_LPC_CMDR_SMI (1 << 6) /* SMI event is pending */ + +#define EC_LPC_ADDR_MEMMAP 0x900 +#define EC_MEMMAP_SIZE 255 /* ACPI IO buffer max is 255 bytes */ +#define EC_MEMMAP_TEXT_MAX 8 /* Size of a string in the memory map */ + +/* The offset address of each type of data in mapped memory. */ +#define EC_MEMMAP_TEMP_SENSOR 0x00 /* Temp sensors */ +#define EC_MEMMAP_FAN 0x10 /* Fan speeds */ +#define EC_MEMMAP_TEMP_SENSOR_B 0x18 /* Temp sensors (second set) */ +#define EC_MEMMAP_ID 0x20 /* 'E' 'C' */ +#define EC_MEMMAP_ID_VERSION 0x22 /* Version of data in 0x20 - 0x2f */ +#define EC_MEMMAP_THERMAL_VERSION 0x23 /* Version of data in 0x00 - 0x1f */ +#define EC_MEMMAP_BATTERY_VERSION 0x24 /* Version of data in 0x40 - 0x7f */ +#define EC_MEMMAP_SWITCHES_VERSION 0x25 /* Version of data in 0x30 - 0x33 */ +#define EC_MEMMAP_EVENTS_VERSION 0x26 /* Version of data in 0x34 - 0x3f */ +#define EC_MEMMAP_HOST_CMD_FLAGS 0x27 /* Host command interface flags */ +#define EC_MEMMAP_SWITCHES 0x30 +#define EC_MEMMAP_HOST_EVENTS 0x34 +#define EC_MEMMAP_BATT_VOLT 0x40 /* Battery Present Voltage */ +#define EC_MEMMAP_BATT_RATE 0x44 /* Battery Present Rate */ +#define EC_MEMMAP_BATT_CAP 0x48 /* Battery Remaining Capacity */ +#define EC_MEMMAP_BATT_FLAG 0x4c /* Battery State, defined below */ +#define EC_MEMMAP_BATT_DCAP 0x50 /* Battery Design Capacity */ +#define EC_MEMMAP_BATT_DVLT 0x54 /* Battery Design Voltage */ +#define EC_MEMMAP_BATT_LFCC 0x58 /* Battery Last Full Charge Capacity */ +#define EC_MEMMAP_BATT_CCNT 0x5c /* Battery Cycle Count */ +#define EC_MEMMAP_BATT_MFGR 0x60 /* Battery Manufacturer String */ +#define EC_MEMMAP_BATT_MODEL 0x68 /* Battery Model Number String */ +#define EC_MEMMAP_BATT_SERIAL 0x70 /* Battery Serial Number String */ +#define EC_MEMMAP_BATT_TYPE 0x78 /* Battery Type String */ + +/* Number of temp sensors at EC_MEMMAP_TEMP_SENSOR */ +#define EC_TEMP_SENSOR_ENTRIES 16 +/* + * Number of temp sensors at EC_MEMMAP_TEMP_SENSOR_B. + * + * Valid only if EC_MEMMAP_THERMAL_VERSION returns >= 2. + */ +#define EC_TEMP_SENSOR_B_ENTRIES 8 +#define EC_TEMP_SENSOR_NOT_PRESENT 0xff +#define EC_TEMP_SENSOR_ERROR 0xfe +#define EC_TEMP_SENSOR_NOT_POWERED 0xfd +#define EC_TEMP_SENSOR_NOT_CALIBRATED 0xfc +/* + * The offset of temperature value stored in mapped memory. This allows + * reporting a temperature range of 200K to 454K = -73C to 181C. + */ +#define EC_TEMP_SENSOR_OFFSET 200 + +#define EC_FAN_SPEED_ENTRIES 4 /* Number of fans at EC_MEMMAP_FAN */ +#define EC_FAN_SPEED_NOT_PRESENT 0xffff /* Entry not present */ +#define EC_FAN_SPEED_STALLED 0xfffe /* Fan stalled */ + +/* Battery bit flags at EC_MEMMAP_BATT_FLAG. */ +#define EC_BATT_FLAG_AC_PRESENT 0x01 +#define EC_BATT_FLAG_BATT_PRESENT 0x02 +#define EC_BATT_FLAG_DISCHARGING 0x04 +#define EC_BATT_FLAG_CHARGING 0x08 +#define EC_BATT_FLAG_LEVEL_CRITICAL 0x10 + +/* Switch flags at EC_MEMMAP_SWITCHES */ +#define EC_SWITCH_LID_OPEN 0x01 +#define EC_SWITCH_POWER_BUTTON_PRESSED 0x02 +#define EC_SWITCH_WRITE_PROTECT_DISABLED 0x04 +/* Was recovery requested via keyboard; now unused. */ +#define EC_SWITCH_IGNORE1 0x08 +/* Recovery requested via dedicated signal (from servo board) */ +#define EC_SWITCH_DEDICATED_RECOVERY 0x10 +/* Was fake developer mode switch; now unused. Remove in next refactor. */ +#define EC_SWITCH_IGNORE0 0x20 + +/* Host command interface flags */ +/* Host command interface supports LPC args (LPC interface only) */ +#define EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED 0x01 + +/* Wireless switch flags */ +#define EC_WIRELESS_SWITCH_WLAN 0x01 +#define EC_WIRELESS_SWITCH_BLUETOOTH 0x02 + +/* + * This header file is used in coreboot both in C and ACPI code. The ACPI code + * is pre-processed to handle constants but the ASL compiler is unable to + * handle actual C code so keep it separate. + */ +#ifndef __ACPI__ + +/* + * Define __packed if someone hasn't beat us to it. Linux kernel style + * checking prefers __packed over __attribute__((packed)). + */ +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +/* LPC command status byte masks */ +/* EC has written a byte in the data register and host hasn't read it yet */ +#define EC_LPC_STATUS_TO_HOST 0x01 +/* Host has written a command/data byte and the EC hasn't read it yet */ +#define EC_LPC_STATUS_FROM_HOST 0x02 +/* EC is processing a command */ +#define EC_LPC_STATUS_PROCESSING 0x04 +/* Last write to EC was a command, not data */ +#define EC_LPC_STATUS_LAST_CMD 0x08 +/* EC is in burst mode. Unsupported by Chrome EC, so this bit is never set */ +#define EC_LPC_STATUS_BURST_MODE 0x10 +/* SCI event is pending (requesting SCI query) */ +#define EC_LPC_STATUS_SCI_PENDING 0x20 +/* SMI event is pending (requesting SMI query) */ +#define EC_LPC_STATUS_SMI_PENDING 0x40 +/* (reserved) */ +#define EC_LPC_STATUS_RESERVED 0x80 + +/* + * EC is busy. This covers both the EC processing a command, and the host has + * written a new command but the EC hasn't picked it up yet. + */ +#define EC_LPC_STATUS_BUSY_MASK \ + (EC_LPC_STATUS_FROM_HOST | EC_LPC_STATUS_PROCESSING) + +/* Host command response codes */ +enum ec_status { + EC_RES_SUCCESS = 0, + EC_RES_INVALID_COMMAND = 1, + EC_RES_ERROR = 2, + EC_RES_INVALID_PARAM = 3, + EC_RES_ACCESS_DENIED = 4, + EC_RES_INVALID_RESPONSE = 5, + EC_RES_INVALID_VERSION = 6, + EC_RES_INVALID_CHECKSUM = 7, + EC_RES_IN_PROGRESS = 8, /* Accepted, command in progress */ + EC_RES_UNAVAILABLE = 9, /* No response available */ + EC_RES_TIMEOUT = 10, /* We got a timeout */ + EC_RES_OVERFLOW = 11, /* Table / data overflow */ +}; + +/* + * Host event codes. Note these are 1-based, not 0-based, because ACPI query + * EC command uses code 0 to mean "no event pending". We explicitly specify + * each value in the enum listing so they won't change if we delete/insert an + * item or rearrange the list (it needs to be stable across platforms, not + * just within a single compiled instance). + */ +enum host_event_code { + EC_HOST_EVENT_LID_CLOSED = 1, + EC_HOST_EVENT_LID_OPEN = 2, + EC_HOST_EVENT_POWER_BUTTON = 3, + EC_HOST_EVENT_AC_CONNECTED = 4, + EC_HOST_EVENT_AC_DISCONNECTED = 5, + EC_HOST_EVENT_BATTERY_LOW = 6, + EC_HOST_EVENT_BATTERY_CRITICAL = 7, + EC_HOST_EVENT_BATTERY = 8, + EC_HOST_EVENT_THERMAL_THRESHOLD = 9, + EC_HOST_EVENT_THERMAL_OVERLOAD = 10, + EC_HOST_EVENT_THERMAL = 11, + EC_HOST_EVENT_USB_CHARGER = 12, + EC_HOST_EVENT_KEY_PRESSED = 13, + /* + * EC has finished initializing the host interface. The host can check + * for this event following sending a EC_CMD_REBOOT_EC command to + * determine when the EC is ready to accept subsequent commands. + */ + EC_HOST_EVENT_INTERFACE_READY = 14, + /* Keyboard recovery combo has been pressed */ + EC_HOST_EVENT_KEYBOARD_RECOVERY = 15, + + /* Shutdown due to thermal overload */ + EC_HOST_EVENT_THERMAL_SHUTDOWN = 16, + /* Shutdown due to battery level too low */ + EC_HOST_EVENT_BATTERY_SHUTDOWN = 17, + + /* + * The high bit of the event mask is not used as a host event code. If + * it reads back as set, then the entire event mask should be + * considered invalid by the host. This can happen when reading the + * raw event status via EC_MEMMAP_HOST_EVENTS but the LPC interface is + * not initialized on the EC, or improperly configured on the host. + */ + EC_HOST_EVENT_INVALID = 32 +}; +/* Host event mask */ +#define EC_HOST_EVENT_MASK(event_code) (1UL << ((event_code) - 1)) + +/* Arguments at EC_LPC_ADDR_HOST_ARGS */ +struct ec_lpc_host_args { + uint8_t flags; + uint8_t command_version; + uint8_t data_size; + /* + * Checksum; sum of command + flags + command_version + data_size + + * all params/response data bytes. + */ + uint8_t checksum; +} __packed; + +/* Flags for ec_lpc_host_args.flags */ +/* + * Args are from host. Data area at EC_LPC_ADDR_HOST_PARAM contains command + * params. + * + * If EC gets a command and this flag is not set, this is an old-style command. + * Command version is 0 and params from host are at EC_LPC_ADDR_OLD_PARAM with + * unknown length. EC must respond with an old-style response (that is, + * withouth setting EC_HOST_ARGS_FLAG_TO_HOST). + */ +#define EC_HOST_ARGS_FLAG_FROM_HOST 0x01 +/* + * Args are from EC. Data area at EC_LPC_ADDR_HOST_PARAM contains response. + * + * If EC responds to a command and this flag is not set, this is an old-style + * response. Command version is 0 and response data from EC is at + * EC_LPC_ADDR_OLD_PARAM with unknown length. + */ +#define EC_HOST_ARGS_FLAG_TO_HOST 0x02 + +/* + * Notes on commands: + * + * Each command is an 8-byte command value. Commands which take params or + * return response data specify structs for that data. If no struct is + * specified, the command does not input or output data, respectively. + * Parameter/response length is implicit in the structs. Some underlying + * communication protocols (I2C, SPI) may add length or checksum headers, but + * those are implementation-dependent and not defined here. + */ + +/*****************************************************************************/ +/* General / test commands */ + +/* + * Get protocol version, used to deal with non-backward compatible protocol + * changes. + */ +#define EC_CMD_PROTO_VERSION 0x00 + +struct ec_response_proto_version { + uint32_t version; +} __packed; + +/* + * Hello. This is a simple command to test the EC is responsive to + * commands. + */ +#define EC_CMD_HELLO 0x01 + +struct ec_params_hello { + uint32_t in_data; /* Pass anything here */ +} __packed; + +struct ec_response_hello { + uint32_t out_data; /* Output will be in_data + 0x01020304 */ +} __packed; + +/* Get version number */ +#define EC_CMD_GET_VERSION 0x02 + +enum ec_current_image { + EC_IMAGE_UNKNOWN = 0, + EC_IMAGE_RO, + EC_IMAGE_RW +}; + +struct ec_response_get_version { + /* Null-terminated version strings for RO, RW */ + char version_string_ro[32]; + char version_string_rw[32]; + char reserved[32]; /* Was previously RW-B string */ + uint32_t current_image; /* One of ec_current_image */ +} __packed; + +/* Read test */ +#define EC_CMD_READ_TEST 0x03 + +struct ec_params_read_test { + uint32_t offset; /* Starting value for read buffer */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +struct ec_response_read_test { + uint32_t data[32]; +} __packed; + +/* + * Get build information + * + * Response is null-terminated string. + */ +#define EC_CMD_GET_BUILD_INFO 0x04 + +/* Get chip info */ +#define EC_CMD_GET_CHIP_INFO 0x05 + +struct ec_response_get_chip_info { + /* Null-terminated strings */ + char vendor[32]; + char name[32]; + char revision[32]; /* Mask version */ +} __packed; + +/* Get board HW version */ +#define EC_CMD_GET_BOARD_VERSION 0x06 + +struct ec_response_board_version { + uint16_t board_version; /* A monotonously incrementing number. */ +} __packed; + +/* + * Read memory-mapped data. + * + * This is an alternate interface to memory-mapped data for bus protocols + * which don't support direct-mapped memory - I2C, SPI, etc. + * + * Response is params.size bytes of data. + */ +#define EC_CMD_READ_MEMMAP 0x07 + +struct ec_params_read_memmap { + uint8_t offset; /* Offset in memmap (EC_MEMMAP_*) */ + uint8_t size; /* Size to read in bytes */ +} __packed; + +/* Read versions supported for a command */ +#define EC_CMD_GET_CMD_VERSIONS 0x08 + +struct ec_params_get_cmd_versions { + uint8_t cmd; /* Command to check */ +} __packed; + +struct ec_response_get_cmd_versions { + /* + * Mask of supported versions; use EC_VER_MASK() to compare with a + * desired version. + */ + uint32_t version_mask; +} __packed; + +/* + * Check EC communcations status (busy). This is needed on i2c/spi but not + * on lpc since it has its own out-of-band busy indicator. + * + * lpc must read the status from the command register. Attempting this on + * lpc will overwrite the args/parameter space and corrupt its data. + */ +#define EC_CMD_GET_COMMS_STATUS 0x09 + +/* Avoid using ec_status which is for return values */ +enum ec_comms_status { + EC_COMMS_STATUS_PROCESSING = 1 << 0, /* Processing cmd */ +}; + +struct ec_response_get_comms_status { + uint32_t flags; /* Mask of enum ec_comms_status */ +} __packed; + + +/*****************************************************************************/ +/* Flash commands */ + +/* Get flash info */ +#define EC_CMD_FLASH_INFO 0x10 + +struct ec_response_flash_info { + /* Usable flash size, in bytes */ + uint32_t flash_size; + /* + * Write block size. Write offset and size must be a multiple + * of this. + */ + uint32_t write_block_size; + /* + * Erase block size. Erase offset and size must be a multiple + * of this. + */ + uint32_t erase_block_size; + /* + * Protection block size. Protection offset and size must be a + * multiple of this. + */ + uint32_t protect_block_size; +} __packed; + +/* + * Read flash + * + * Response is params.size bytes of data. + */ +#define EC_CMD_FLASH_READ 0x11 + +struct ec_params_flash_read { + uint32_t offset; /* Byte offset to read */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +/* Write flash */ +#define EC_CMD_FLASH_WRITE 0x12 + +struct ec_params_flash_write { + uint32_t offset; /* Byte offset to write */ + uint32_t size; /* Size to write in bytes */ + /* + * Data to write. Could really use EC_PARAM_SIZE - 8, but tidiest to + * use a power of 2 so writes stay aligned. + */ + uint8_t data[64]; +} __packed; + +/* Erase flash */ +#define EC_CMD_FLASH_ERASE 0x13 + +struct ec_params_flash_erase { + uint32_t offset; /* Byte offset to erase */ + uint32_t size; /* Size to erase in bytes */ +} __packed; + +/* + * Get/set flash protection. + * + * If mask!=0, sets/clear the requested bits of flags. Depending on the + * firmware write protect GPIO, not all flags will take effect immediately; + * some flags require a subsequent hard reset to take effect. Check the + * returned flags bits to see what actually happened. + * + * If mask=0, simply returns the current flags state. + */ +#define EC_CMD_FLASH_PROTECT 0x15 +#define EC_VER_FLASH_PROTECT 1 /* Command version 1 */ + +/* Flags for flash protection */ +/* RO flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_RO_AT_BOOT (1 << 0) +/* + * RO flash code protected now. If this bit is set, at-boot status cannot + * be changed. + */ +#define EC_FLASH_PROTECT_RO_NOW (1 << 1) +/* Entire flash code protected now, until reboot. */ +#define EC_FLASH_PROTECT_ALL_NOW (1 << 2) +/* Flash write protect GPIO is asserted now */ +#define EC_FLASH_PROTECT_GPIO_ASSERTED (1 << 3) +/* Error - at least one bank of flash is stuck locked, and cannot be unlocked */ +#define EC_FLASH_PROTECT_ERROR_STUCK (1 << 4) +/* + * Error - flash protection is in inconsistent state. At least one bank of + * flash which should be protected is not protected. Usually fixed by + * re-requesting the desired flags, or by a hard reset if that fails. + */ +#define EC_FLASH_PROTECT_ERROR_INCONSISTENT (1 << 5) +/* Entile flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_ALL_AT_BOOT (1 << 6) + +struct ec_params_flash_protect { + uint32_t mask; /* Bits in flags to apply */ + uint32_t flags; /* New flags to apply */ +} __packed; + +struct ec_response_flash_protect { + /* Current value of flash protect flags */ + uint32_t flags; + /* + * Flags which are valid on this platform. This allows the caller + * to distinguish between flags which aren't set vs. flags which can't + * be set on this platform. + */ + uint32_t valid_flags; + /* Flags which can be changed given the current protection state */ + uint32_t writable_flags; +} __packed; + +/* + * Note: commands 0x14 - 0x19 version 0 were old commands to get/set flash + * write protect. These commands may be reused with version > 0. + */ + +/* Get the region offset/size */ +#define EC_CMD_FLASH_REGION_INFO 0x16 +#define EC_VER_FLASH_REGION_INFO 1 + +enum ec_flash_region { + /* Region which holds read-only EC image */ + EC_FLASH_REGION_RO, + /* Region which holds rewritable EC image */ + EC_FLASH_REGION_RW, + /* + * Region which should be write-protected in the factory (a superset of + * EC_FLASH_REGION_RO) + */ + EC_FLASH_REGION_WP_RO, +}; + +struct ec_params_flash_region_info { + uint32_t region; /* enum ec_flash_region */ +} __packed; + +struct ec_response_flash_region_info { + uint32_t offset; + uint32_t size; +} __packed; + +/* Read/write VbNvContext */ +#define EC_CMD_VBNV_CONTEXT 0x17 +#define EC_VER_VBNV_CONTEXT 1 +#define EC_VBNV_BLOCK_SIZE 16 + +enum ec_vbnvcontext_op { + EC_VBNV_CONTEXT_OP_READ, + EC_VBNV_CONTEXT_OP_WRITE, +}; + +struct ec_params_vbnvcontext { + uint32_t op; + uint8_t block[EC_VBNV_BLOCK_SIZE]; +} __packed; + +struct ec_response_vbnvcontext { + uint8_t block[EC_VBNV_BLOCK_SIZE]; +} __packed; + +/*****************************************************************************/ +/* PWM commands */ + +/* Get fan target RPM */ +#define EC_CMD_PWM_GET_FAN_TARGET_RPM 0x20 + +struct ec_response_pwm_get_fan_rpm { + uint32_t rpm; +} __packed; + +/* Set target fan RPM */ +#define EC_CMD_PWM_SET_FAN_TARGET_RPM 0x21 + +struct ec_params_pwm_set_fan_target_rpm { + uint32_t rpm; +} __packed; + +/* Get keyboard backlight */ +#define EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT 0x22 + +struct ec_response_pwm_get_keyboard_backlight { + uint8_t percent; + uint8_t enabled; +} __packed; + +/* Set keyboard backlight */ +#define EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT 0x23 + +struct ec_params_pwm_set_keyboard_backlight { + uint8_t percent; +} __packed; + +/* Set target fan PWM duty cycle */ +#define EC_CMD_PWM_SET_FAN_DUTY 0x24 + +struct ec_params_pwm_set_fan_duty { + uint32_t percent; +} __packed; + +/*****************************************************************************/ +/* + * Lightbar commands. This looks worse than it is. Since we only use one HOST + * command to say "talk to the lightbar", we put the "and tell it to do X" part + * into a subcommand. We'll make separate structs for subcommands with + * different input args, so that we know how much to expect. + */ +#define EC_CMD_LIGHTBAR_CMD 0x28 + +struct rgb_s { + uint8_t r, g, b; +}; + +#define LB_BATTERY_LEVELS 4 +/* List of tweakable parameters. NOTE: It's __packed so it can be sent in a + * host command, but the alignment is the same regardless. Keep it that way. + */ +struct lightbar_params { + /* Timing */ + int google_ramp_up; + int google_ramp_down; + int s3s0_ramp_up; + int s0_tick_delay[2]; /* AC=0/1 */ + int s0a_tick_delay[2]; /* AC=0/1 */ + int s0s3_ramp_down; + int s3_sleep_for; + int s3_ramp_up; + int s3_ramp_down; + + /* Oscillation */ + uint8_t new_s0; + uint8_t osc_min[2]; /* AC=0/1 */ + uint8_t osc_max[2]; /* AC=0/1 */ + uint8_t w_ofs[2]; /* AC=0/1 */ + + /* Brightness limits based on the backlight and AC. */ + uint8_t bright_bl_off_fixed[2]; /* AC=0/1 */ + uint8_t bright_bl_on_min[2]; /* AC=0/1 */ + uint8_t bright_bl_on_max[2]; /* AC=0/1 */ + + /* Battery level thresholds */ + uint8_t battery_threshold[LB_BATTERY_LEVELS - 1]; + + /* Map [AC][battery_level] to color index */ + uint8_t s0_idx[2][LB_BATTERY_LEVELS]; /* AP is running */ + uint8_t s3_idx[2][LB_BATTERY_LEVELS]; /* AP is sleeping */ + + /* Color palette */ + struct rgb_s color[8]; /* 0-3 are Google colors */ +} __packed; + +struct ec_params_lightbar { + uint8_t cmd; /* Command (see enum lightbar_command) */ + union { + struct { + /* no args */ + } dump, off, on, init, get_seq, get_params; + + struct num { + uint8_t num; + } brightness, seq, demo; + + struct reg { + uint8_t ctrl, reg, value; + } reg; + + struct rgb { + uint8_t led, red, green, blue; + } rgb; + + struct lightbar_params set_params; + }; +} __packed; + +struct ec_response_lightbar { + union { + struct dump { + struct { + uint8_t reg; + uint8_t ic0; + uint8_t ic1; + } vals[23]; + } dump; + + struct get_seq { + uint8_t num; + } get_seq; + + struct lightbar_params get_params; + + struct { + /* no return params */ + } off, on, init, brightness, seq, reg, rgb, demo, set_params; + }; +} __packed; + +/* Lightbar commands */ +enum lightbar_command { + LIGHTBAR_CMD_DUMP = 0, + LIGHTBAR_CMD_OFF = 1, + LIGHTBAR_CMD_ON = 2, + LIGHTBAR_CMD_INIT = 3, + LIGHTBAR_CMD_BRIGHTNESS = 4, + LIGHTBAR_CMD_SEQ = 5, + LIGHTBAR_CMD_REG = 6, + LIGHTBAR_CMD_RGB = 7, + LIGHTBAR_CMD_GET_SEQ = 8, + LIGHTBAR_CMD_DEMO = 9, + LIGHTBAR_CMD_GET_PARAMS = 10, + LIGHTBAR_CMD_SET_PARAMS = 11, + LIGHTBAR_NUM_CMDS +}; + +/*****************************************************************************/ +/* Verified boot commands */ + +/* + * Note: command code 0x29 version 0 was VBOOT_CMD in Link EVT; it may be + * reused for other purposes with version > 0. + */ + +/* Verified boot hash command */ +#define EC_CMD_VBOOT_HASH 0x2A + +struct ec_params_vboot_hash { + uint8_t cmd; /* enum ec_vboot_hash_cmd */ + uint8_t hash_type; /* enum ec_vboot_hash_type */ + uint8_t nonce_size; /* Nonce size; may be 0 */ + uint8_t reserved0; /* Reserved; set 0 */ + uint32_t offset; /* Offset in flash to hash */ + uint32_t size; /* Number of bytes to hash */ + uint8_t nonce_data[64]; /* Nonce data; ignored if nonce_size=0 */ +} __packed; + +struct ec_response_vboot_hash { + uint8_t status; /* enum ec_vboot_hash_status */ + uint8_t hash_type; /* enum ec_vboot_hash_type */ + uint8_t digest_size; /* Size of hash digest in bytes */ + uint8_t reserved0; /* Ignore; will be 0 */ + uint32_t offset; /* Offset in flash which was hashed */ + uint32_t size; /* Number of bytes hashed */ + uint8_t hash_digest[64]; /* Hash digest data */ +} __packed; + +enum ec_vboot_hash_cmd { + EC_VBOOT_HASH_GET = 0, /* Get current hash status */ + EC_VBOOT_HASH_ABORT = 1, /* Abort calculating current hash */ + EC_VBOOT_HASH_START = 2, /* Start computing a new hash */ + EC_VBOOT_HASH_RECALC = 3, /* Synchronously compute a new hash */ +}; + +enum ec_vboot_hash_type { + EC_VBOOT_HASH_TYPE_SHA256 = 0, /* SHA-256 */ +}; + +enum ec_vboot_hash_status { + EC_VBOOT_HASH_STATUS_NONE = 0, /* No hash (not started, or aborted) */ + EC_VBOOT_HASH_STATUS_DONE = 1, /* Finished computing a hash */ + EC_VBOOT_HASH_STATUS_BUSY = 2, /* Busy computing a hash */ +}; + +/* + * Special values for offset for EC_VBOOT_HASH_START and EC_VBOOT_HASH_RECALC. + * If one of these is specified, the EC will automatically update offset and + * size to the correct values for the specified image (RO or RW). + */ +#define EC_VBOOT_HASH_OFFSET_RO 0xfffffffe +#define EC_VBOOT_HASH_OFFSET_RW 0xfffffffd + +/*****************************************************************************/ +/* USB charging control commands */ + +/* Set USB port charging mode */ +#define EC_CMD_USB_CHARGE_SET_MODE 0x30 + +struct ec_params_usb_charge_set_mode { + uint8_t usb_port_id; + uint8_t mode; +} __packed; + +/*****************************************************************************/ +/* Persistent storage for host */ + +/* Maximum bytes that can be read/written in a single command */ +#define EC_PSTORE_SIZE_MAX 64 + +/* Get persistent storage info */ +#define EC_CMD_PSTORE_INFO 0x40 + +struct ec_response_pstore_info { + /* Persistent storage size, in bytes */ + uint32_t pstore_size; + /* Access size; read/write offset and size must be a multiple of this */ + uint32_t access_size; +} __packed; + +/* + * Read persistent storage + * + * Response is params.size bytes of data. + */ +#define EC_CMD_PSTORE_READ 0x41 + +struct ec_params_pstore_read { + uint32_t offset; /* Byte offset to read */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +/* Write persistent storage */ +#define EC_CMD_PSTORE_WRITE 0x42 + +struct ec_params_pstore_write { + uint32_t offset; /* Byte offset to write */ + uint32_t size; /* Size to write in bytes */ + uint8_t data[EC_PSTORE_SIZE_MAX]; +} __packed; + +/*****************************************************************************/ +/* Real-time clock */ + +/* RTC params and response structures */ +struct ec_params_rtc { + uint32_t time; +} __packed; + +struct ec_response_rtc { + uint32_t time; +} __packed; + +/* These use ec_response_rtc */ +#define EC_CMD_RTC_GET_VALUE 0x44 +#define EC_CMD_RTC_GET_ALARM 0x45 + +/* These all use ec_params_rtc */ +#define EC_CMD_RTC_SET_VALUE 0x46 +#define EC_CMD_RTC_SET_ALARM 0x47 + +/*****************************************************************************/ +/* Port80 log access */ + +/* Get last port80 code from previous boot */ +#define EC_CMD_PORT80_LAST_BOOT 0x48 + +struct ec_response_port80_last_boot { + uint16_t code; +} __packed; + +/*****************************************************************************/ +/* Thermal engine commands */ + +/* Set thershold value */ +#define EC_CMD_THERMAL_SET_THRESHOLD 0x50 + +struct ec_params_thermal_set_threshold { + uint8_t sensor_type; + uint8_t threshold_id; + uint16_t value; +} __packed; + +/* Get threshold value */ +#define EC_CMD_THERMAL_GET_THRESHOLD 0x51 + +struct ec_params_thermal_get_threshold { + uint8_t sensor_type; + uint8_t threshold_id; +} __packed; + +struct ec_response_thermal_get_threshold { + uint16_t value; +} __packed; + +/* Toggle automatic fan control */ +#define EC_CMD_THERMAL_AUTO_FAN_CTRL 0x52 + +/* Get TMP006 calibration data */ +#define EC_CMD_TMP006_GET_CALIBRATION 0x53 + +struct ec_params_tmp006_get_calibration { + uint8_t index; +} __packed; + +struct ec_response_tmp006_get_calibration { + float s0; + float b0; + float b1; + float b2; +} __packed; + +/* Set TMP006 calibration data */ +#define EC_CMD_TMP006_SET_CALIBRATION 0x54 + +struct ec_params_tmp006_set_calibration { + uint8_t index; + uint8_t reserved[3]; /* Reserved; set 0 */ + float s0; + float b0; + float b1; + float b2; +} __packed; + +/*****************************************************************************/ +/* CROS_EC - Matrix KeyBoard Protocol */ + +/* + * Read key state + * + * Returns raw data for keyboard cols; see ec_response_cros_ec_info.cols for + * expected response size. + */ +#define EC_CMD_CROS_EC_STATE 0x60 + +/* Provide information about the matrix : number of rows and columns */ +#define EC_CMD_CROS_EC_INFO 0x61 + +struct ec_response_cros_ec_info { + uint32_t rows; + uint32_t cols; + uint8_t switches; +} __packed; + +/* Simulate key press */ +#define EC_CMD_CROS_EC_SIMULATE_KEY 0x62 + +struct ec_params_cros_ec_simulate_key { + uint8_t col; + uint8_t row; + uint8_t pressed; +} __packed; + +/* Configure keyboard scanning */ +#define EC_CMD_CROS_EC_SET_CONFIG 0x64 +#define EC_CMD_CROS_EC_GET_CONFIG 0x65 + +/* flags */ +enum cros_ec_config_flags { + EC_CROS_EC_FLAGS_ENABLE = 1, /* Enable keyboard scanning */ +}; + +enum cros_ec_config_valid { + EC_CROS_EC_VALID_SCAN_PERIOD = 1 << 0, + EC_CROS_EC_VALID_POLL_TIMEOUT = 1 << 1, + EC_CROS_EC_VALID_MIN_POST_SCAN_DELAY = 1 << 3, + EC_CROS_EC_VALID_OUTPUT_SETTLE = 1 << 4, + EC_CROS_EC_VALID_DEBOUNCE_DOWN = 1 << 5, + EC_CROS_EC_VALID_DEBOUNCE_UP = 1 << 6, + EC_CROS_EC_VALID_FIFO_MAX_DEPTH = 1 << 7, +}; + +/* Configuration for our key scanning algorithm */ +struct ec_cros_ec_config { + uint32_t valid_mask; /* valid fields */ + uint8_t flags; /* some flags (enum cros_ec_config_flags) */ + uint8_t valid_flags; /* which flags are valid */ + uint16_t scan_period_us; /* period between start of scans */ + /* revert to interrupt mode after no activity for this long */ + uint32_t poll_timeout_us; + /* + * minimum post-scan relax time. Once we finish a scan we check + * the time until we are due to start the next one. If this time is + * shorter this field, we use this instead. + */ + uint16_t min_post_scan_delay_us; + /* delay between setting up output and waiting for it to settle */ + uint16_t output_settle_us; + uint16_t debounce_down_us; /* time for debounce on key down */ + uint16_t debounce_up_us; /* time for debounce on key up */ + /* maximum depth to allow for fifo (0 = no keyscan output) */ + uint8_t fifo_max_depth; +} __packed; + +struct ec_params_cros_ec_set_config { + struct ec_cros_ec_config config; +} __packed; + +struct ec_response_cros_ec_get_config { + struct ec_cros_ec_config config; +} __packed; + +/* Run the key scan emulation */ +#define EC_CMD_KEYSCAN_SEQ_CTRL 0x66 + +enum ec_keyscan_seq_cmd { + EC_KEYSCAN_SEQ_STATUS = 0, /* Get status information */ + EC_KEYSCAN_SEQ_CLEAR = 1, /* Clear sequence */ + EC_KEYSCAN_SEQ_ADD = 2, /* Add item to sequence */ + EC_KEYSCAN_SEQ_START = 3, /* Start running sequence */ + EC_KEYSCAN_SEQ_COLLECT = 4, /* Collect sequence summary data */ +}; + +enum ec_collect_flags { + /* + * Indicates this scan was processed by the EC. Due to timing, some + * scans may be skipped. + */ + EC_KEYSCAN_SEQ_FLAG_DONE = 1 << 0, +}; + +struct ec_collect_item { + uint8_t flags; /* some flags (enum ec_collect_flags) */ +}; + +struct ec_params_keyscan_seq_ctrl { + uint8_t cmd; /* Command to send (enum ec_keyscan_seq_cmd) */ + union { + struct { + uint8_t active; /* still active */ + uint8_t num_items; /* number of items */ + /* Current item being presented */ + uint8_t cur_item; + } status; + struct { + /* + * Absolute time for this scan, measured from the + * start of the sequence. + */ + uint32_t time_us; + uint8_t scan[0]; /* keyscan data */ + } add; + struct { + uint8_t start_item; /* First item to return */ + uint8_t num_items; /* Number of items to return */ + } collect; + }; +} __packed; + +struct ec_result_keyscan_seq_ctrl { + union { + struct { + uint8_t num_items; /* Number of items */ + /* Data for each item */ + struct ec_collect_item item[0]; + } collect; + }; +} __packed; + +/*****************************************************************************/ +/* Temperature sensor commands */ + +/* Read temperature sensor info */ +#define EC_CMD_TEMP_SENSOR_GET_INFO 0x70 + +struct ec_params_temp_sensor_get_info { + uint8_t id; +} __packed; + +struct ec_response_temp_sensor_get_info { + char sensor_name[32]; + uint8_t sensor_type; +} __packed; + +/*****************************************************************************/ + +/* + * Note: host commands 0x80 - 0x87 are reserved to avoid conflict with ACPI + * commands accidentally sent to the wrong interface. See the ACPI section + * below. + */ + +/*****************************************************************************/ +/* Host event commands */ + +/* + * Host event mask params and response structures, shared by all of the host + * event commands below. + */ +struct ec_params_host_event_mask { + uint32_t mask; +} __packed; + +struct ec_response_host_event_mask { + uint32_t mask; +} __packed; + +/* These all use ec_response_host_event_mask */ +#define EC_CMD_HOST_EVENT_GET_B 0x87 +#define EC_CMD_HOST_EVENT_GET_SMI_MASK 0x88 +#define EC_CMD_HOST_EVENT_GET_SCI_MASK 0x89 +#define EC_CMD_HOST_EVENT_GET_WAKE_MASK 0x8d + +/* These all use ec_params_host_event_mask */ +#define EC_CMD_HOST_EVENT_SET_SMI_MASK 0x8a +#define EC_CMD_HOST_EVENT_SET_SCI_MASK 0x8b +#define EC_CMD_HOST_EVENT_CLEAR 0x8c +#define EC_CMD_HOST_EVENT_SET_WAKE_MASK 0x8e +#define EC_CMD_HOST_EVENT_CLEAR_B 0x8f + +/*****************************************************************************/ +/* Switch commands */ + +/* Enable/disable LCD backlight */ +#define EC_CMD_SWITCH_ENABLE_BKLIGHT 0x90 + +struct ec_params_switch_enable_backlight { + uint8_t enabled; +} __packed; + +/* Enable/disable WLAN/Bluetooth */ +#define EC_CMD_SWITCH_ENABLE_WIRELESS 0x91 + +struct ec_params_switch_enable_wireless { + uint8_t enabled; +} __packed; + +/*****************************************************************************/ +/* GPIO commands. Only available on EC if write protect has been disabled. */ + +/* Set GPIO output value */ +#define EC_CMD_GPIO_SET 0x92 + +struct ec_params_gpio_set { + char name[32]; + uint8_t val; +} __packed; + +/* Get GPIO value */ +#define EC_CMD_GPIO_GET 0x93 + +struct ec_params_gpio_get { + char name[32]; +} __packed; +struct ec_response_gpio_get { + uint8_t val; +} __packed; + +/*****************************************************************************/ +/* I2C commands. Only available when flash write protect is unlocked. */ + +/* Read I2C bus */ +#define EC_CMD_I2C_READ 0x94 + +struct ec_params_i2c_read { + uint16_t addr; /* 8-bit address (7-bit shifted << 1) */ + uint8_t read_size; /* Either 8 or 16. */ + uint8_t port; + uint8_t offset; +} __packed; +struct ec_response_i2c_read { + uint16_t data; +} __packed; + +/* Write I2C bus */ +#define EC_CMD_I2C_WRITE 0x95 + +struct ec_params_i2c_write { + uint16_t data; + uint16_t addr; /* 8-bit address (7-bit shifted << 1) */ + uint8_t write_size; /* Either 8 or 16. */ + uint8_t port; + uint8_t offset; +} __packed; + +/*****************************************************************************/ +/* Charge state commands. Only available when flash write protect unlocked. */ + +/* Force charge state machine to stop in idle mode */ +#define EC_CMD_CHARGE_FORCE_IDLE 0x96 + +struct ec_params_force_idle { + uint8_t enabled; +} __packed; + +/*****************************************************************************/ +/* Console commands. Only available when flash write protect is unlocked. */ + +/* Snapshot console output buffer for use by EC_CMD_CONSOLE_READ. */ +#define EC_CMD_CONSOLE_SNAPSHOT 0x97 + +/* + * Read next chunk of data from saved snapshot. + * + * Response is null-terminated string. Empty string, if there is no more + * remaining output. + */ +#define EC_CMD_CONSOLE_READ 0x98 + +/*****************************************************************************/ + +/* + * Cut off battery power output if the battery supports. + * + * For unsupported battery, just don't implement this command and lets EC + * return EC_RES_INVALID_COMMAND. + */ +#define EC_CMD_BATTERY_CUT_OFF 0x99 + +/*****************************************************************************/ +/* USB port mux control. */ + +/* + * Switch USB mux or return to automatic switching. + */ +#define EC_CMD_USB_MUX 0x9a + +struct ec_params_usb_mux { + uint8_t mux; +} __packed; + +/*****************************************************************************/ +/* LDOs / FETs control. */ + +enum ec_ldo_state { + EC_LDO_STATE_OFF = 0, /* the LDO / FET is shut down */ + EC_LDO_STATE_ON = 1, /* the LDO / FET is ON / providing power */ +}; + +/* + * Switch on/off a LDO. + */ +#define EC_CMD_LDO_SET 0x9b + +struct ec_params_ldo_set { + uint8_t index; + uint8_t state; +} __packed; + +/* + * Get LDO state. + */ +#define EC_CMD_LDO_GET 0x9c + +struct ec_params_ldo_get { + uint8_t index; +} __packed; + +struct ec_response_ldo_get { + uint8_t state; +} __packed; + +/*****************************************************************************/ +/* Power info. */ + +/* + * Get power info. + */ +#define EC_CMD_POWER_INFO 0x9d + +struct ec_response_power_info { + uint32_t usb_dev_type; + uint16_t voltage_ac; + uint16_t voltage_system; + uint16_t current_system; + uint16_t usb_current_limit; +} __packed; + +/*****************************************************************************/ +/* I2C passthru command */ + +#define EC_CMD_I2C_PASSTHRU 0x9e + +/* Slave address is 10 (not 7) bit */ +#define EC_I2C_FLAG_10BIT (1 << 16) + +/* Read data; if not present, message is a write */ +#define EC_I2C_FLAG_READ (1 << 15) + +/* Mask for address */ +#define EC_I2C_ADDR_MASK 0x3ff + +#define EC_I2C_STATUS_NAK (1 << 0) /* Transfer was not acknowledged */ +#define EC_I2C_STATUS_TIMEOUT (1 << 1) /* Timeout during transfer */ + +/* Any error */ +#define EC_I2C_STATUS_ERROR (EC_I2C_STATUS_NAK | EC_I2C_STATUS_TIMEOUT) + +struct ec_params_i2c_passthru_msg { + uint16_t addr_flags; /* I2C slave address (7 or 10 bits) and flags */ + uint16_t len; /* Number of bytes to write*/ +} __packed; + +struct ec_params_i2c_passthru { + uint8_t port; /* I2C port number */ + uint8_t num_msgs; /* Number of messages */ + struct ec_params_i2c_passthru_msg msg[]; + /* Data for all messages is concatenated here */ +} __packed; + +struct ec_response_i2c_passthru { + uint8_t i2c_status; /* Status flags (EC_I2C_STATUS_...) */ + uint8_t num_msgs; /* Number of messages processed */ + uint8_t data[]; /* Data for all messages concatenated here */ +} __packed; + + +/*****************************************************************************/ +/* Temporary debug commands. TODO: remove this crosbug.com/p/13849 */ + +/* + * Dump charge state machine context. + * + * Response is a binary dump of charge state machine context. + */ +#define EC_CMD_CHARGE_DUMP 0xa0 + +/* + * Set maximum battery charging current. + */ +#define EC_CMD_CHARGE_CURRENT_LIMIT 0xa1 + +struct ec_params_current_limit { + uint32_t limit; /* in mA */ +} __packed; + +/* + * Set maximum external power current. + */ +#define EC_CMD_EXT_POWER_CURRENT_LIMIT 0xa2 + +struct ec_params_ext_power_current_limit { + uint32_t limit; /* in mA */ +} __packed; + +/*****************************************************************************/ +/* Smart battery pass-through */ + +/* Get / Set 16-bit smart battery registers */ +#define EC_CMD_SB_READ_WORD 0xb0 +#define EC_CMD_SB_WRITE_WORD 0xb1 + +/* Get / Set string smart battery parameters + * formatted as SMBUS "block". + */ +#define EC_CMD_SB_READ_BLOCK 0xb2 +#define EC_CMD_SB_WRITE_BLOCK 0xb3 + +struct ec_params_sb_rd { + uint8_t reg; +} __packed; + +struct ec_response_sb_rd_word { + uint16_t value; +} __packed; + +struct ec_params_sb_wr_word { + uint8_t reg; + uint16_t value; +} __packed; + +struct ec_response_sb_rd_block { + uint8_t data[32]; +} __packed; + +struct ec_params_sb_wr_block { + uint8_t reg; + uint16_t data[32]; +} __packed; + +/*****************************************************************************/ +/* System commands */ + +/* + * TODO: this is a confusing name, since it doesn't necessarily reboot the EC. + * Rename to "set image" or something similar. + */ +#define EC_CMD_REBOOT_EC 0xd2 + +/* Command */ +enum ec_reboot_cmd { + EC_REBOOT_CANCEL = 0, /* Cancel a pending reboot */ + EC_REBOOT_JUMP_RO = 1, /* Jump to RO without rebooting */ + EC_REBOOT_JUMP_RW = 2, /* Jump to RW without rebooting */ + /* (command 3 was jump to RW-B) */ + EC_REBOOT_COLD = 4, /* Cold-reboot */ + EC_REBOOT_DISABLE_JUMP = 5, /* Disable jump until next reboot */ + EC_REBOOT_HIBERNATE = 6 /* Hibernate EC */ +}; + +/* Flags for ec_params_reboot_ec.reboot_flags */ +#define EC_REBOOT_FLAG_RESERVED0 (1 << 0) /* Was recovery request */ +#define EC_REBOOT_FLAG_ON_AP_SHUTDOWN (1 << 1) /* Reboot after AP shutdown */ + +struct ec_params_reboot_ec { + uint8_t cmd; /* enum ec_reboot_cmd */ + uint8_t flags; /* See EC_REBOOT_FLAG_* */ +} __packed; + +/* + * Get information on last EC panic. + * + * Returns variable-length platform-dependent panic information. See panic.h + * for details. + */ +#define EC_CMD_GET_PANIC_INFO 0xd3 + +/*****************************************************************************/ +/* + * ACPI commands + * + * These are valid ONLY on the ACPI command/data port. + */ + +/* + * ACPI Read Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + * - Write EC_CMD_ACPI_READ to EC_LPC_ADDR_ACPI_CMD + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write address to EC_LPC_ADDR_ACPI_DATA + * - Wait for EC_LPC_CMDR_DATA bit to set + * - Read value from EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_READ 0x80 + +/* + * ACPI Write Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + * - Write EC_CMD_ACPI_WRITE to EC_LPC_ADDR_ACPI_CMD + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write address to EC_LPC_ADDR_ACPI_DATA + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write value to EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_WRITE 0x81 + +/* + * ACPI Query Embedded Controller + * + * This clears the lowest-order bit in the currently pending host events, and + * sets the result code to the 1-based index of the bit (event 0x00000001 = 1, + * event 0x80000000 = 32), or 0 if no event was pending. + */ +#define EC_CMD_ACPI_QUERY_EVENT 0x84 + +/* Valid addresses in ACPI memory space, for read/write commands */ +/* Memory space version; set to EC_ACPI_MEM_VERSION_CURRENT */ +#define EC_ACPI_MEM_VERSION 0x00 +/* + * Test location; writing value here updates test compliment byte to (0xff - + * value). + */ +#define EC_ACPI_MEM_TEST 0x01 +/* Test compliment; writes here are ignored. */ +#define EC_ACPI_MEM_TEST_COMPLIMENT 0x02 +/* Keyboard backlight brightness percent (0 - 100) */ +#define EC_ACPI_MEM_KEYBOARD_BACKLIGHT 0x03 + +/* Current version of ACPI memory address space */ +#define EC_ACPI_MEM_VERSION_CURRENT 1 + + +/*****************************************************************************/ +/* + * Special commands + * + * These do not follow the normal rules for commands. See each command for + * details. + */ + +/* + * Reboot NOW + * + * This command will work even when the EC LPC interface is busy, because the + * reboot command is processed at interrupt level. Note that when the EC + * reboots, the host will reboot too, so there is no response to this command. + * + * Use EC_CMD_REBOOT_EC to reboot the EC more politely. + */ +#define EC_CMD_REBOOT 0xd1 /* Think "die" */ + +/* + * Resend last response (not supported on LPC). + * + * Returns EC_RES_UNAVAILABLE if there is no response available - for example, + * there was no previous command, or the previous command's response was too + * big to save. + */ +#define EC_CMD_RESEND_RESPONSE 0xdb + +/* + * This header byte on a command indicate version 0. Any header byte less + * than this means that we are talking to an old EC which doesn't support + * versioning. In that case, we assume version 0. + * + * Header bytes greater than this indicate a later version. For example, + * EC_CMD_VERSION0 + 1 means we are using version 1. + * + * The old EC interface must not use commands 0dc or higher. + */ +#define EC_CMD_VERSION0 0xdc + +#endif /* !__ACPI__ */ + +#endif /* __CROS_EC_COMMANDS_H */ diff --git a/sys/arch/armv7/exynos/exclock.c b/sys/arch/armv7/exynos/exclock.c new file mode 100644 index 00000000000..85f595a9a2e --- /dev/null +++ b/sys/arch/armv7/exynos/exclock.c @@ -0,0 +1,300 @@ +/* $OpenBSD: exclock.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/sysctl.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> +#include <machine/intr.h> +#include <machine/bus.h> +#include <machine/fdt.h> +#include <armv7/armv7/armv7var.h> + +/* registers */ +#define CLOCK_APLL_CON0 0x0100 +#define CLOCK_APLL_CON1 0x0104 +#define CLOCK_BPLL_CON0 0x0110 +#define CLOCK_BPLL_CON1 0x0114 +#define CLOCK_EPLL_CON0 0x0130 +#define CLOCK_EPLL_CON1 0x0134 +#define CLOCK_EPLL_CON2 0x0138 +#define CLOCK_VPLL_CON0 0x0140 +#define CLOCK_VPLL_CON1 0x0144 +#define CLOCK_VPLL_CON2 0x0148 +#define CLOCK_CLK_DIV_CPU0 0x0500 +#define CLOCK_CLK_DIV_CPU1 0x0504 +#define CLOCK_CLK_DIV_TOP0 0x0510 +#define CLOCK_CLK_DIV_TOP1 0x0514 +#define CLOCK_PLL_DIV2_SEL 0x0A24 +#define CLOCK_MPLL_CON0 0x4100 +#define CLOCK_MPLL_CON1 0x4104 + +/* bits and bytes */ +#define MPLL_FOUT_SEL_SHIFT 0x4 +#define MPLL_FOUT_SEL_MASK 0x1 +#define BPLL_FOUT_SEL_SHIFT 0x0 +#define BPLL_FOUT_SEL_MASK 0x1 + +#define HCLK_FREQ 24000 + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +struct exclock_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +enum clocks { + /* OSC */ + OSC, /* 24 MHz OSC */ + + /* PLLs */ + APLL, /* ARM core clock */ + MPLL, /* System bus clock for memory controller */ + BPLL, /* Graphic 3D processor clock and 1066 MHz clock for memory controller if necessary */ + CPLL, /* Multi Format Video Hardware Codec clock */ + GPLL, /* Graphic 3D processor clock or other clocks for DVFS flexibility */ + EPLL, /* Audio interface clocks and clocks for other external device interfaces */ + VPLL, /* dithered PLL, helps to reduce the EMI of display and camera */ +}; + +struct exclock_softc *exclock_sc; + +int exclock_match(struct device *parent, void *v, void *aux); +void exclock_attach(struct device *parent, struct device *self, void *args); +int exclock_cpuspeed(int *); +unsigned int exclock_decode_pll_clk(enum clocks, unsigned int, unsigned int); +unsigned int exclock_get_pll_clk(enum clocks); +unsigned int exclock_get_armclk(void); +unsigned int exclock_get_i2cclk(void); + +struct cfattach exclock_ca = { + sizeof (struct exclock_softc), NULL, exclock_attach +}; +struct cfattach exclock_fdt_ca = { + sizeof (struct exclock_softc), exclock_match, exclock_attach +}; + +struct cfdriver exclock_cd = { + NULL, "exclock", DV_DULL +}; + +int +exclock_match(struct device *parent, void *v, void *aux) +{ + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("samsung,exynos5250-clock", aa->aa_node)) + return 1; + + return 0; +} + +void +exclock_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct exclock_softc *sc = (struct exclock_softc *) self; + struct fdt_memory mem; + + exclock_sc = sc; + sc->sc_iot = aa->aa_iot; + if (aa->aa_node) { + if (fdt_get_memory_address(aa->aa_node, 0, &mem)) + panic("%s: could not extract memory data from FDT", + __func__); + } else { + mem.addr = aa->aa_dev->mem[0].addr; + mem.size = aa->aa_dev->mem[0].size; + } + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + printf(": Exynos 5 CPU freq: %d MHz", + exclock_get_armclk() / 1000); + + printf("\n"); + + cpu_cpuspeed = exclock_cpuspeed; +} + +int +exclock_cpuspeed(int *freq) +{ + *freq = exclock_get_armclk() / 1000; + return (0); +} + +unsigned int +exclock_decode_pll_clk(enum clocks pll, unsigned int r, unsigned int k) +{ + uint32_t m, p, s = 0, mask, fout, freq; + /* + * APLL_CON: MIDV [25:16] + * MPLL_CON: MIDV [25:16] + * EPLL_CON: MIDV [24:16] + * VPLL_CON: MIDV [24:16] + * BPLL_CON: MIDV [25:16]: Exynos5 + */ + + switch (pll) + { + case APLL: + case MPLL: + case BPLL: + mask = 0x3ff; + break; + default: + mask = 0x1ff; + } + + m = (r >> 16) & mask; + + /* PDIV [13:8] */ + p = (r >> 8) & 0x3f; + /* SDIV [2:0] */ + s = r & 0x7; + + freq = HCLK_FREQ; + + if (pll == EPLL) { + k = k & 0xffff; + /* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */ + fout = (m + k / 65536) * (freq / (p * (1 << s))); + } else if (pll == VPLL) { + k = k & 0xfff; + /* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */ + fout = (m + k / 1024) * (freq / (p * (1 << s))); + } else { + /* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */ + fout = m * (freq / (p * (1 << s))); + } + + return fout; +} + +unsigned int +exclock_get_pll_clk(enum clocks pll) +{ + struct exclock_softc *sc = exclock_sc; + uint32_t freq; + + switch (pll) { + case APLL: + freq = exclock_decode_pll_clk(pll, + HREAD4(sc, CLOCK_APLL_CON0), + 0); + break; + case MPLL: + freq = exclock_decode_pll_clk(pll, + HREAD4(sc, CLOCK_MPLL_CON0), + 0); + break; + case BPLL: + freq = exclock_decode_pll_clk(pll, + HREAD4(sc, CLOCK_BPLL_CON0), + 0); + break; + case EPLL: + freq = exclock_decode_pll_clk(pll, + HREAD4(sc, CLOCK_EPLL_CON0), + HREAD4(sc, CLOCK_EPLL_CON1)); + break; + case VPLL: + freq = exclock_decode_pll_clk(pll, + HREAD4(sc, CLOCK_VPLL_CON0), + HREAD4(sc, CLOCK_VPLL_CON1)); + break; + default: + return 0; + } + + /* + * According to the user manual, in EVT1 MPLL and BPLL always gives + * 1.6GHz clock, so divide by 2 to get 800MHz MPLL clock. + */ + if (pll == MPLL || pll == BPLL) { + uint32_t freq_sel; + uint32_t pll_div2_sel = HREAD4(sc, CLOCK_PLL_DIV2_SEL); + + switch (pll) { + case MPLL: + freq_sel = (pll_div2_sel >> MPLL_FOUT_SEL_SHIFT) + & MPLL_FOUT_SEL_MASK; + break; + case BPLL: + freq_sel = (pll_div2_sel >> BPLL_FOUT_SEL_SHIFT) + & BPLL_FOUT_SEL_MASK; + break; + default: + freq_sel = -1; + break; + } + + if (freq_sel == 0) + freq /= 2; + } + + return freq; +} + +unsigned int +exclock_get_armclk() +{ + struct exclock_softc *sc = exclock_sc; + uint32_t div, armclk, arm_ratio, arm2_ratio; + + div = HREAD4(sc, CLOCK_CLK_DIV_CPU0); + + /* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */ + arm_ratio = (div >> 0) & 0x7; + arm2_ratio = (div >> 28) & 0x7; + + armclk = exclock_get_pll_clk(APLL) / (arm_ratio + 1); + armclk /= (arm2_ratio + 1); + + return armclk; +} + +unsigned int +exclock_get_i2cclk() +{ + struct exclock_softc *sc = exclock_sc; + uint32_t aclk_66, aclk_66_pre, div, ratio; + + div = HREAD4(sc, CLOCK_CLK_DIV_TOP1); + ratio = (div >> 24) & 0x7; + aclk_66_pre = exclock_get_pll_clk(MPLL) / (ratio + 1); + div = HREAD4(sc, CLOCK_CLK_DIV_TOP0); + ratio = (div >> 0) & 0x7; + aclk_66 = aclk_66_pre / (ratio + 1); + + return aclk_66; +} diff --git a/sys/arch/armv7/exynos/exclockvar.h b/sys/arch/armv7/exynos/exclockvar.h new file mode 100644 index 00000000000..a5e3fd7d66f --- /dev/null +++ b/sys/arch/armv7/exynos/exclockvar.h @@ -0,0 +1,23 @@ +/* $OpenBSD: exclockvar.h,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef EXCLOCKVAR_H +#define EXCLOCKVAR_H + +unsigned int exclock_get_i2cclk(void); + +#endif /* EXCLOCKVAR_H */ diff --git a/sys/arch/armv7/exynos/exdisplay.c b/sys/arch/armv7/exynos/exdisplay.c new file mode 100644 index 00000000000..6c7663faa2d --- /dev/null +++ b/sys/arch/armv7/exynos/exdisplay.c @@ -0,0 +1,290 @@ +/* $OpenBSD: exdisplay.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> + +#include <dev/cons.h> +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wsdisplayvar.h> +#include <dev/wscons/wscons_callbacks.h> +#include <dev/wsfont/wsfont.h> +#include <dev/rasops/rasops.h> + +#include <machine/intr.h> +#include <machine/bus.h> +#include <machine/fdt.h> +#include <armv7/armv7/armv7var.h> + +/* registers */ + +struct exdisplay_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + struct rasops_info *ro; +}; + +int exdisplay_match(struct device *parent, void *v, void *aux); +void exdisplay_attach(struct device *parent, struct device *self, void *args); +int exdisplay_cnattach(bus_space_tag_t iot, bus_addr_t iobase, size_t size); +void exdisplay_setup_rasops(struct rasops_info *rinfo, struct wsscreen_descr *descr); + +struct cfattach exdisplay_ca = { + sizeof (struct exdisplay_softc), NULL, exdisplay_attach +}; +struct cfattach exdisplay_fdt_ca = { + sizeof (struct exdisplay_softc), exdisplay_match, exdisplay_attach +}; + +struct cfdriver exdisplay_cd = { + NULL, "exdisplay", DV_DULL +}; + +int exdisplay_wsioctl(void *, u_long, caddr_t, int, struct proc *); +paddr_t exdisplay_wsmmap(void *, off_t, int); +int exdisplay_alloc_screen(void *, const struct wsscreen_descr *, + void **, int *, int *, long *); +void exdisplay_free_screen(void *, void *); +int exdisplay_show_screen(void *, void *, int, + void (*)(void *, int, int), void *); +void exdisplay_doswitch(void *, void *); +int exdisplay_load_font(void *, void *, struct wsdisplay_font *); +int exdisplay_list_font(void *, struct wsdisplay_font *); +int exdisplay_getchar(void *, int, int, struct wsdisplay_charcell *); +void exdisplay_burner(void *, u_int, u_int); + +struct rasops_info exdisplay_ri; +struct wsscreen_descr exdisplay_stdscreen = { + "std" +}; + +const struct wsscreen_descr *exdisplay_scrlist[] = { + &exdisplay_stdscreen, +}; + +struct wsscreen_list exdisplay_screenlist = { + nitems(exdisplay_scrlist), exdisplay_scrlist +}; + +struct wsdisplay_accessops exdisplay_accessops = { + .ioctl = exdisplay_wsioctl, + .mmap = exdisplay_wsmmap, + .alloc_screen = exdisplay_alloc_screen, + .free_screen = exdisplay_free_screen, + .show_screen = exdisplay_show_screen, + .getchar = exdisplay_getchar, + .load_font = exdisplay_load_font, + .list_font = exdisplay_list_font, + .burn_screen = exdisplay_burner +}; + +int +exdisplay_match(struct device *parent, void *v, void *aux) +{ + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("samsung,exynos5250-fimd", aa->aa_node)) + return 1; + + return 0; +} + +void +exdisplay_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct exdisplay_softc *sc = (struct exdisplay_softc *) self; + struct wsemuldisplaydev_attach_args waa; + struct rasops_info *ri = &exdisplay_ri; + struct fdt_memory mem; + + if (aa->aa_node == NULL) { + printf(": not configured without FDT\n"); + return; + } + + sc->sc_iot = aa->aa_iot; + if (fdt_get_memory_address(aa->aa_node, 0, &mem)) + panic("%s: could not extract memory data from FDT", + __func__); + + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + printf("\n"); + +#if notyet + /* FIXME: Set up framebuffer instead of re-using. */ + if (!fdt_find_compatible("simple-framebuffer")) { + long defattr; + + ri->ri_bits = (u_char *)sc->sc_fbioh; + exdisplay_setup_rasops(ri, &exdisplay_stdscreen); + + ri->ri_ops.alloc_attr(ri->ri_active, 0, 0, 0, &defattr); + wsdisplay_cnattach(&exdisplay_stdscreen, ri->ri_active, + 0, 0, defattr); + } +#endif + + sc->ro = ri; + + waa.console = 1; + waa.scrdata = &exdisplay_screenlist; + waa.accessops = &exdisplay_accessops; + waa.accesscookie = sc; + waa.defaultscreens = 0; + + printf("%s: %dx%d\n", sc->sc_dev.dv_xname, ri->ri_width, ri->ri_height); + + config_found(self, &waa, wsemuldisplaydevprint); +} + +int +exdisplay_cnattach(bus_space_tag_t iot, bus_addr_t iobase, size_t size) +{ + struct wsscreen_descr *descr = &exdisplay_stdscreen; + struct rasops_info *ri = &exdisplay_ri; + long defattr; + + if (bus_space_map(iot, iobase, size, 0, (bus_space_handle_t *)&ri->ri_bits)) + return ENOMEM; + + exdisplay_setup_rasops(ri, descr); + + /* assumes 16 bpp */ + ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr); + + wsdisplay_cnattach(descr, ri, ri->ri_ccol, ri->ri_crow, defattr); + + return 0; +} + +void +exdisplay_setup_rasops(struct rasops_info *rinfo, struct wsscreen_descr *descr) +{ + rinfo->ri_flg = RI_CLEAR; + rinfo->ri_depth = 16; + rinfo->ri_width = 1366; + rinfo->ri_height = 768; + rinfo->ri_stride = rinfo->ri_width * rinfo->ri_depth / 8; + + /* swap B and R */ + if (rinfo->ri_depth == 16) { + rinfo->ri_rnum = 5; + rinfo->ri_rpos = 11; + rinfo->ri_gnum = 6; + rinfo->ri_gpos = 5; + rinfo->ri_bnum = 5; + rinfo->ri_bpos = 0; + } + + wsfont_init(); + rinfo->ri_wsfcookie = wsfont_find(NULL, 8, 0, 0); + wsfont_lock(rinfo->ri_wsfcookie, &rinfo->ri_font, + WSDISPLAY_FONTORDER_L2R, WSDISPLAY_FONTORDER_L2R); + + /* get rasops to compute screen size the first time */ + rasops_init(rinfo, 200, 200); + + descr->nrows = rinfo->ri_rows; + descr->ncols = rinfo->ri_cols; + descr->capabilities = rinfo->ri_caps; + descr->textops = &rinfo->ri_ops; +} + +int +exdisplay_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + return (-1); +} + +paddr_t +exdisplay_wsmmap(void *v, off_t off, int prot) +{ + return (-1); +} + +int +exdisplay_alloc_screen(void *v, const struct wsscreen_descr *type, + void **cookiep, int *curxp, int *curyp, long *attrp) +{ + struct exdisplay_softc *sc = v; + struct rasops_info *ri = sc->ro; + + return rasops_alloc_screen(ri, cookiep, curxp, curyp, attrp); +} + +void +exdisplay_free_screen(void *v, void *cookie) +{ + struct exdisplay_softc *sc = v; + struct rasops_info *ri = sc->ro; + + return rasops_free_screen(ri, cookie); +} + +int +exdisplay_show_screen(void *v, void *cookie, int waitok, + void (*cb)(void *, int, int), void *cbarg) +{ + return (0); +} + +void +exdisplay_doswitch(void *v, void *dummy) +{ +} + +int +exdisplay_getchar(void *v, int row, int col, struct wsdisplay_charcell *cell) +{ + struct exdisplay_softc *sc = v; + struct rasops_info *ri = sc->ro; + + return rasops_getchar(ri, row, col, cell); +} + +int +exdisplay_load_font(void *v, void *cookie, struct wsdisplay_font *font) +{ + struct exdisplay_softc *sc = v; + struct rasops_info *ri = sc->ro; + + return rasops_load_font(ri, cookie, font); +} + +int +exdisplay_list_font(void *v, struct wsdisplay_font *font) +{ + struct exdisplay_softc *sc = v; + struct rasops_info *ri = sc->ro; + + return rasops_list_font(ri, font); +} + +void +exdisplay_burner(void *v, u_int on, u_int flags) +{ +} diff --git a/sys/arch/armv7/exynos/exdisplayvar.h b/sys/arch/armv7/exynos/exdisplayvar.h new file mode 100644 index 00000000000..853e683423d --- /dev/null +++ b/sys/arch/armv7/exynos/exdisplayvar.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +int exdisplay_cnattach(bus_space_tag_t iot, bus_addr_t iobase, size_t size); diff --git a/sys/arch/armv7/exynos/exdog.c b/sys/arch/armv7/exynos/exdog.c new file mode 100644 index 00000000000..d5e285a49a8 --- /dev/null +++ b/sys/arch/armv7/exynos/exdog.c @@ -0,0 +1,146 @@ +/* $OpenBSD: exdog.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> +#include <machine/bus.h> +#include <machine/fdt.h> +#include <machine/intr.h> +#include <armv7/armv7/armv7var.h> + +/* registers */ +#define WTCON 0x00 +#define WTDAT 0x04 +#define WTCNT 0x08 +#define WTCLRINT 0x0C + +/* bits and bytes */ +#define WTCON_RESET (1 << 0) +#define WTCON_INT (1 << 2) +#define WTCON_CLKSEL_16 (0x0 << 3) +#define WTCON_CLKSEL_32 (0x1 << 3) +#define WTCON_CLKSEL_64 (0x2 << 3) +#define WTCON_CLKSEL_128 (0x3 << 3) +#define WTCON_EN (1 << 5) +#define WTCON_PRESCALER(x) (((x) & 0xff) << 8) + +struct exdog_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +struct exdog_softc *exdog_sc; + +int exdog_match(struct device *parent, void *v, void *aux); +void exdog_attach(struct device *parent, struct device *self, void *args); +void exdog_stop(void); +void exdog_reset(void); + +struct cfattach exdog_ca = { + sizeof (struct exdog_softc), NULL, exdog_attach +}; +struct cfattach exdog_fdt_ca = { + sizeof (struct exdog_softc), exdog_match, exdog_attach +}; + +struct cfdriver exdog_cd = { + NULL, "exdog", DV_DULL +}; + +int +exdog_match(struct device *parent, void *v, void *aux) +{ + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("samsung,exynos5250-wdt", aa->aa_node)) + return 1; + + return 0; +} + +void +exdog_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct exdog_softc *sc = (struct exdog_softc *) self; + struct fdt_memory mem; + + sc->sc_iot = aa->aa_iot; + if (aa->aa_node) { + if (fdt_get_memory_address(aa->aa_node, 0, &mem)) + panic("%s: could not extract memory data from FDT", + __func__); + } else { + mem.addr = aa->aa_dev->mem[0].addr; + mem.size = aa->aa_dev->mem[0].size; + } + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + printf("\n"); + exdog_sc = sc; +} + +void +exdog_stop() +{ + uint32_t wtcon; + + if (exdog_sc == NULL) + return; + + wtcon = bus_space_read_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTCON); + + wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET); + + bus_space_write_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTCON, wtcon); +} + +void +exdog_reset() +{ + uint32_t wtcon; + + if (exdog_sc == NULL) + return; + + /* disable watchdog */ + exdog_stop(); + + wtcon = bus_space_read_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTCON); + + wtcon |= WTCON_EN | WTCON_CLKSEL_128; + wtcon &= ~WTCON_INT; + wtcon |= WTCON_RESET; + wtcon |= WTCON_PRESCALER(0xff); + + /* set timeout to 1 */ + bus_space_write_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTDAT, 1); + bus_space_write_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTCNT, 1); + + /* kick off the watchdog */ + bus_space_write_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTCON, wtcon); + + delay(100000); +} diff --git a/sys/arch/armv7/exynos/exehci.c b/sys/arch/armv7/exynos/exehci.c new file mode 100644 index 00000000000..a73f6284fb3 --- /dev/null +++ b/sys/arch/armv7/exynos/exehci.c @@ -0,0 +1,321 @@ +/* $OpenBSD: exehci.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/rwlock.h> +#include <sys/timeout.h> + +#include <machine/intr.h> +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usb_mem.h> + +#include <armv7/armv7/armv7var.h> +#include <armv7/exynos/exsysregvar.h> +#include <armv7/exynos/expowervar.h> +#include <armv7/exynos/exgpiovar.h> + +#include <dev/usb/ehcireg.h> +#include <dev/usb/ehcivar.h> + +/* registers */ +#define USBPHY_CTRL0 0x00 +#define USBPHY_TUNE0 0x04 +#define HSICPHY_CTRL1 0x10 +#define HSICPHY_TUNE1 0x14 +#define HSICPHY_CTRL2 0x20 +#define HSICPHY_TUNE2 0x24 +#define EHCI_CTRL 0x30 +#define OHCI_CTRL 0x34 +#define USBOTG_SYS 0x38 +#define USBOTG_TUNE 0x40 + +/* bits and bytes */ +#define CLK_24MHZ 5 + +#define HOST_CTRL0_PHYSWRSTALL (1U << 31) +#define HOST_CTRL0_COMMONON_N (1 << 9) +#define HOST_CTRL0_SIDDQ (1 << 6) +#define HOST_CTRL0_FORCESLEEP (1 << 5) +#define HOST_CTRL0_FORCESUSPEND (1 << 4) +#define HOST_CTRL0_WORDINTERFACE (1 << 3) +#define HOST_CTRL0_UTMISWRST (1 << 2) +#define HOST_CTRL0_LINKSWRST (1 << 1) +#define HOST_CTRL0_PHYSWRST (1 << 0) + +#define HOST_CTRL0_FSEL_MASK (7 << 16) + +#define EHCI_CTRL_ENAINCRXALIGN (1 << 29) +#define EHCI_CTRL_ENAINCR4 (1 << 28) +#define EHCI_CTRL_ENAINCR8 (1 << 27) +#define EHCI_CTRL_ENAINCR16 (1 << 26) + +int exehci_match(struct device *, void *, void *); +void exehci_attach(struct device *, struct device *, void *); +int exehci_detach(struct device *, int); + +struct exehci_softc { + struct device sc_dev; + struct ehci_softc *sc_ehci; + void *sc_ih; + bus_dma_tag_t sc_dmat; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_size_t sc_size; + bus_space_handle_t ph_ioh; +}; + +struct cfdriver exehci_cd = { + NULL, "exehci", DV_DULL +}; + +struct cfattach exehci_ca = { + sizeof (struct exehci_softc), NULL, exehci_attach, + exehci_detach, NULL +}; +struct cfattach exehci_fdt_ca = { + sizeof (struct exehci_softc), exehci_match, exehci_attach, + exehci_detach, NULL +}; + +void exehci_setup(struct exehci_softc *); + +int +exehci_match(struct device *parent, void *v, void *aux) +{ + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("samsung,exynos4210-ehci", aa->aa_node)) + return 1; + + return 0; +} + +void +exehci_attach(struct device *parent, struct device *self, void *aux) +{ + struct exehci_softc *sc = (struct exehci_softc *)self; + struct ehci_softc *esc; + struct armv7_attach_args *aa = aux; + struct fdt_memory hmem, pmem; + int irq; + usbd_status r; + + sc->sc_iot = aa->aa_iot; + sc->sc_dmat = aa->aa_dmat; + + if (aa->aa_node) { + uint32_t ints[3]; + + if (fdt_get_memory_address(aa->aa_node, 0, &hmem)) + panic("%s: could not extract memory data from FDT", + __func__); + + /* XXX: In a different way, please. */ + void *node = fdt_find_compatible("samsung,exynos5250-usb2-phy"); + if (node == NULL || fdt_get_memory_address(node, 0, &pmem)) + panic("%s: could not extract phy data from FDT", + __func__); + + /* TODO: Add interrupt FDT API. */ + if (fdt_node_property_ints(aa->aa_node, "interrupts", + ints, 3) != 3) + panic("%s: could not extract interrupt data from FDT", + __func__); + + irq = ints[1]; + } else { + hmem.addr = aa->aa_dev->mem[0].addr; + hmem.size = aa->aa_dev->mem[0].size; + pmem.addr = aa->aa_dev->mem[1].addr; + pmem.size = aa->aa_dev->mem[1].size; + irq = aa->aa_dev->irq[0]; + } + + /* Map I/O space */ + sc->sc_size = hmem.size; + if (bus_space_map(sc->sc_iot, hmem.addr, hmem.size, 0, &sc->sc_ioh)) { + printf(": cannot map mem space\n"); + goto out; + } + + if (bus_space_map(sc->sc_iot, pmem.addr, pmem.size, 0, &sc->ph_ioh)) { + printf(": cannot map mem space\n"); + goto pmem; + } + + printf("\n"); + + exehci_setup(sc); + + if ((esc = (struct ehci_softc *)config_found(self, NULL, NULL)) == NULL) + goto hmem; + + sc->sc_ehci = esc; + esc->iot = sc->sc_iot; + esc->ioh = sc->sc_ioh; + esc->sc_bus.dmatag = aa->aa_dmat; + + sc->sc_ih = arm_intr_establish(irq, IPL_USB, + ehci_intr, esc, esc->sc_bus.bdev.dv_xname); + if (sc->sc_ih == NULL) { + printf(": unable to establish interrupt\n"); + goto hmem; + } + + strlcpy(esc->sc_vendor, "Exynos 5", sizeof(esc->sc_vendor)); + r = ehci_init(esc); + if (r != USBD_NORMAL_COMPLETION) { + printf("%s: init failed, error=%d\n", + esc->sc_bus.bdev.dv_xname, r); + goto intr; + } + + printf("\n"); + + config_found((struct device *)esc, &esc->sc_bus, usbctlprint); + + goto out; + +intr: + arm_intr_disestablish(sc->sc_ih); + sc->sc_ih = NULL; +hmem: + bus_space_unmap(sc->sc_iot, sc->ph_ioh, hmem.size); +pmem: + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); + sc->sc_size = 0; +out: + return; +} + +int +exehci_detach(struct device *self, int flags) +{ + struct exehci_softc *sc = (struct exehci_softc *)self; + int rv = 0; + + rv = ehci_detach(self, flags); + if (rv) + return (rv); + + if (sc->sc_ih != NULL) { + arm_intr_disestablish(sc->sc_ih); + sc->sc_ih = NULL; + } + + if (sc->sc_size) { + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); + sc->sc_size = 0; + } + + return 0; +} + +void +exehci_setup(struct exehci_softc *sc) +{ + uint32_t val; + + /* VBUS, GPIO_X11, only on SMDK5250 and Chromebooks */ + exgpio_set_dir(0xa9, EXGPIO_DIR_OUT); + exgpio_set_bit(0xa9); + delay(3000); + + exsysreg_usbhost_mode(1); + expower_usbhost_phy_ctrl(1); + + delay(10000); + + /* Setting up host and device simultaneously */ + val = bus_space_read_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0); + val &= ~(HOST_CTRL0_FSEL_MASK | + HOST_CTRL0_COMMONON_N | + /* HOST Phy setting */ + HOST_CTRL0_PHYSWRST | + HOST_CTRL0_PHYSWRSTALL | + HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESUSPEND | + HOST_CTRL0_FORCESLEEP); + val |= (/* Setting up the ref freq */ + CLK_24MHZ << 16 | + /* HOST Phy setting */ + HOST_CTRL0_LINKSWRST | + HOST_CTRL0_UTMISWRST); + bus_space_write_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0, val); + delay(10000); + bus_space_write_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0, + bus_space_read_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0) & + ~(HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST)); + delay(20000); + + /* EHCI Ctrl setting */ + bus_space_write_4(sc->sc_iot, sc->ph_ioh, EHCI_CTRL, + bus_space_read_4(sc->sc_iot, sc->ph_ioh, EHCI_CTRL) | + EHCI_CTRL_ENAINCRXALIGN | + EHCI_CTRL_ENAINCR4 | + EHCI_CTRL_ENAINCR8 | + EHCI_CTRL_ENAINCR16); + + /* HSIC USB Hub initialization. */ + if (1) { + exgpio_set_dir(0xc8, EXGPIO_DIR_OUT); + exgpio_clear_bit(0xc8); + delay(1000); + exgpio_set_bit(0xc8); + delay(5000); + + val = bus_space_read_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1); + val &= ~(HOST_CTRL0_SIDDQ | + HOST_CTRL0_FORCESLEEP | + HOST_CTRL0_FORCESUSPEND); + bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val); + val |= HOST_CTRL0_PHYSWRST; + bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val); + delay(1000); + val &= ~HOST_CTRL0_PHYSWRST; + bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val); + } + + /* PHY clock and power setup time */ + delay(50000); +} + +int ehci_ex_match(struct device *, void *, void *); +void ehci_ex_attach(struct device *, struct device *, void *); + +struct cfattach ehci_ex_ca = { + sizeof (struct ehci_softc), ehci_ex_match, ehci_ex_attach +}; + +int +ehci_ex_match(struct device *parent, void *v, void *aux) +{ + return 1; +} + +void +ehci_ex_attach(struct device *parent, struct device *self, void *aux) +{ +} diff --git a/sys/arch/armv7/exynos/exesdhc.c b/sys/arch/armv7/exynos/exesdhc.c new file mode 100644 index 00000000000..2a48896cfab --- /dev/null +++ b/sys/arch/armv7/exynos/exesdhc.c @@ -0,0 +1,990 @@ +/* $OpenBSD: exesdhc.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2009 Dale Rahn <drahn@openbsd.org> + * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* i.MX SD/MMC support derived from /sys/dev/sdmmc/sdhc.c */ + + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/sdmmc/sdmmcchip.h> +#include <dev/sdmmc/sdmmcvar.h> + +#include <armv7/armv7/armv7var.h> +#include <armv7/exynos/exclockvar.h> +#include <armv7/exynos/exgpiovar.h> + +/* registers */ +#define SDHC_DS_ADDR 0x00 +#define SDHC_BLK_ATT 0x04 +#define SDHC_CMD_ARG 0x08 +#define SDHC_CMD_XFR_TYP 0x0c +#define SDHC_CMD_RSP0 0x10 +#define SDHC_CMD_RSP1 0x14 +#define SDHC_CMD_RSP2 0x18 +#define SDHC_CMD_RSP3 0x1c +#define SDHC_DATA_BUFF_ACC_PORT 0x20 +#define SDHC_PRES_STATE 0x24 +#define SDHC_PROT_CTRL 0x28 +#define SDHC_SYS_CTRL 0x2c +#define SDHC_INT_STATUS 0x30 +#define SDHC_INT_STATUS_EN 0x34 +#define SDHC_INT_SIGNAL_EN 0x38 +#define SDHC_AUTOCMD12_ERR_STATUS 0x3c +#define SDHC_HOST_CTRL_CAP 0x40 +#define SDHC_WTMK_LVL 0x44 +#define SDHC_MIX_CTRL 0x48 +#define SDHC_FORCE_EVENT 0x50 +#define SDHC_ADMA_ERR_STATUS 0x54 +#define SDHC_ADMA_SYS_ADDR 0x58 +#define SDHC_DLL_CTRL 0x60 +#define SDHC_DLL_STATUS 0x64 +#define SDHC_CLK_TUNE_CTRL_STATUS 0x68 +#define SDHC_VEND_SPEC 0xc0 +#define SDHC_MMC_BOOT 0xc4 +#define SDHC_VEND_SPEC2 0xc8 +#define SDHC_HOST_CTRL_VER 0xfc + +/* bits and bytes */ +#define SDHC_BLK_ATT_BLKCNT_MAX 0xffff +#define SDHC_BLK_ATT_BLKCNT_SHIFT 16 +#define SDHC_BLK_ATT_BLKSIZE_SHIFT 0 +#define SDHC_CMD_XFR_TYP_CMDINDX_SHIFT 24 +#define SDHC_CMD_XFR_TYP_CMDINDX_SHIFT_MASK (0x3f << SDHC_CMD_XFR_TYP_CMDINDX_SHIFT) +#define SDHC_CMD_XFR_TYP_CMDTYP_SHIFT 22 +#define SDHC_CMD_XFR_TYP_DPSEL_SHIFT 21 +#define SDHC_CMD_XFR_TYP_DPSEL (1 << SDHC_CMD_XFR_TYP_DPSEL_SHIFT) +#define SDHC_CMD_XFR_TYP_CICEN_SHIFT 20 +#define SDHC_CMD_XFR_TYP_CICEN (1 << SDHC_CMD_XFR_TYP_CICEN_SHIFT) +#define SDHC_CMD_XFR_TYP_CCCEN_SHIFT 19 +#define SDHC_CMD_XFR_TYP_CCCEN (1 << SDHC_CMD_XFR_TYP_CCCEN_SHIFT) +#define SDHC_CMD_XFR_TYP_RSPTYP_SHIFT 16 +#define SDHC_CMD_XFR_TYP_RSP_NONE (0x0 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT) +#define SDHC_CMD_XFR_TYP_RSP136 (0x1 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT) +#define SDHC_CMD_XFR_TYP_RSP48 (0x2 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT) +#define SDHC_CMD_XFR_TYP_RSP48B (0x3 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT) +#define SDHC_PRES_STATE_WPSPL (1 << 19) +#define SDHC_PRES_STATE_BREN (1 << 11) +#define SDHC_PRES_STATE_BWEN (1 << 10) +#define SDHC_PRES_STATE_SDSTB (1 << 3) +#define SDHC_PRES_STATE_DLA (1 << 2) +#define SDHC_PRES_STATE_CDIHB (1 << 1) +#define SDHC_PRES_STATE_CIHB (1 << 0) +#define SDHC_SYS_CTRL_RSTA (1 << 24) +#define SDHC_SYS_CTRL_RSTC (1 << 25) +#define SDHC_SYS_CTRL_RSTD (1 << 26) +#define SDHC_SYS_CTRL_CLOCK_MASK (0xfff << 4) +#define SDHC_SYS_CTRL_CLOCK_DIV_SHIFT 4 +#define SDHC_SYS_CTRL_CLOCK_PRE_SHIFT 8 +#define SDHC_SYS_CTRL_DTOCV_SHIFT 16 +#define SDHC_INT_STATUS_CC (1 << 0) +#define SDHC_INT_STATUS_TC (1 << 1) +#define SDHC_INT_STATUS_BGE (1 << 2) +#define SDHC_INT_STATUS_DINT (1 << 3) +#define SDHC_INT_STATUS_BWR (1 << 4) +#define SDHC_INT_STATUS_BRR (1 << 5) +#define SDHC_INT_STATUS_CINS (1 << 6) +#define SDHC_INT_STATUS_CRM (1 << 7) +#define SDHC_INT_STATUS_CINT (1 << 8) +#define SDHC_INT_STATUS_CTOE (1 << 16) +#define SDHC_INT_STATUS_CCE (1 << 17) +#define SDHC_INT_STATUS_CEBE (1 << 18) +#define SDHC_INT_STATUS_CIC (1 << 19) +#define SDHC_INT_STATUS_DTOE (1 << 20) +#define SDHC_INT_STATUS_DCE (1 << 21) +#define SDHC_INT_STATUS_DEBE (1 << 22) +#define SDHC_INT_STATUS_DMAE (1 << 28) +#define SDHC_INT_STATUS_CMD_ERR (SDHC_INT_STATUS_CIC | SDHC_INT_STATUS_CEBE | SDHC_INT_STATUS_CCE) +#define SDHC_INT_STATUS_ERR (SDHC_INT_STATUS_CTOE | SDHC_INT_STATUS_CCE | SDHC_INT_STATUS_CEBE | \ + SDHC_INT_STATUS_CIC | SDHC_INT_STATUS_DTOE | SDHC_INT_STATUS_DCE | \ + SDHC_INT_STATUS_DEBE | SDHC_INT_STATUS_DMAE) +#define SDHC_MIX_CTRL_DMAEN (1 << 0) +#define SDHC_MIX_CTRL_BCEN (1 << 1) +#define SDHC_MIX_CTRL_AC12EN (1 << 2) +#define SDHC_MIX_CTRL_DTDSEL (1 << 4) +#define SDHC_MIX_CTRL_MSBSEL (1 << 5) +#define SDHC_PROT_CTRL_DMASEL_SDMA_MASK (0x3 << 8) +#define SDHC_HOST_CTRL_CAP_MBL_SHIFT 16 +#define SDHC_HOST_CTRL_CAP_MBL_MASK 0x7 +#define SDHC_HOST_CTRL_CAP_VS33 (1 << 24) +#define SDHC_HOST_CTRL_CAP_VS30 (1 << 25) +#define SDHC_HOST_CTRL_CAP_VS18 (1 << 26) +#define SDHC_VEND_SPEC_FRC_SDCLK_ON (1 << 8) +#define SDHC_WTMK_LVL_RD_WML_SHIFT 0 +#define SDHC_WTMK_LVL_WR_WML_SHIFT 16 + +#define SDHC_COMMAND_TIMEOUT hz +#define SDHC_BUFFER_TIMEOUT hz +#define SDHC_TRANSFER_TIMEOUT hz + +int exesdhc_match(struct device *parent, void *v, void *aux); +void exesdhc_attach(struct device *parent, struct device *self, void *args); + +#include <machine/bus.h> + +struct exesdhc_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + void *sc_ih; /* Interrupt handler */ + u_int sc_flags; + + int unit; /* unit id */ + struct device *sdmmc; /* generic SD/MMC device */ + int clockbit; /* clock control bit */ + u_int clkbase; /* base clock frequency in KHz */ + int maxblklen; /* maximum block length */ + int flags; /* flags for this host */ + uint32_t ocr; /* OCR value from capabilities */ +// u_int8_t regs[14]; /* host controller state */ + uint32_t intr_status; /* soft interrupt status */ + uint32_t intr_error_status; /* */ +}; + + +/* Host controller functions called by the attachment driver. */ +int exesdhc_host_found(struct exesdhc_softc *, bus_space_tag_t, + bus_space_handle_t, bus_size_t, int); +void exesdhc_power(int, void *); +void exesdhc_shutdown(void *); +int exesdhc_intr(void *); + +/* RESET MODES */ +#define MMC_RESET_DAT 1 +#define MMC_RESET_CMD 2 +#define MMC_RESET_ALL (MMC_RESET_CMD|MMC_RESET_DAT) + +#define HDEVNAME(sc) ((sc)->sc_dev.dv_xname) + +/* flag values */ +#define SHF_USE_DMA 0x0001 + +/* SDHC should only be accessed with 4 byte reads or writes. */ +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +int exesdhc_host_reset(sdmmc_chipset_handle_t); +uint32_t exesdhc_host_ocr(sdmmc_chipset_handle_t); +int exesdhc_host_maxblklen(sdmmc_chipset_handle_t); +int exesdhc_card_detect(sdmmc_chipset_handle_t); +int exesdhc_bus_power(sdmmc_chipset_handle_t, uint32_t); +int exesdhc_bus_clock(sdmmc_chipset_handle_t, int); +void exesdhc_card_intr_mask(sdmmc_chipset_handle_t, int); +void exesdhc_card_intr_ack(sdmmc_chipset_handle_t); +void exesdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); +int exesdhc_start_command(struct exesdhc_softc *, struct sdmmc_command *); +int exesdhc_wait_state(struct exesdhc_softc *, uint32_t, uint32_t); +int exesdhc_soft_reset(struct exesdhc_softc *, int); +int exesdhc_wait_intr(struct exesdhc_softc *, int, int); +void exesdhc_transfer_data(struct exesdhc_softc *, struct sdmmc_command *); +void exesdhc_read_data(struct exesdhc_softc *, u_char *, int); +void exesdhc_write_data(struct exesdhc_softc *, u_char *, int); + +//#define SDHC_DEBUG +#ifdef SDHC_DEBUG +int exesdhcdebug = 20; +#define DPRINTF(n,s) do { if ((n) <= exesdhcdebug) printf s; } while (0) +#else +#define DPRINTF(n,s) do {} while(0) +#endif + +struct sdmmc_chip_functions exesdhc_functions = { + /* host controller reset */ + exesdhc_host_reset, + /* host controller capabilities */ + exesdhc_host_ocr, + exesdhc_host_maxblklen, + /* card detection */ + exesdhc_card_detect, + /* bus power and clock frequency */ + exesdhc_bus_power, + exesdhc_bus_clock, + /* command execution */ + exesdhc_exec_command, + /* card interrupt */ + exesdhc_card_intr_mask, + exesdhc_card_intr_ack +}; + +struct cfdriver exesdhc_cd = { + NULL, "exesdhc", DV_DULL +}; + +struct cfattach exesdhc_ca = { + sizeof(struct exesdhc_softc), NULL, exesdhc_attach +}; +struct cfattach exesdhc_fdt_ca = { + sizeof(struct exesdhc_softc), exesdhc_match, exesdhc_attach +}; + +int +exesdhc_match(struct device *parent, void *v, void *aux) +{ + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("samsung,exynos5250-dw-mshc", aa->aa_node)) + return 1; + + return 0; +} + +void +exesdhc_attach(struct device *parent, struct device *self, void *args) +{ + struct exesdhc_softc *sc = (struct exesdhc_softc *) self; + struct armv7_attach_args *aa = args; + struct fdt_memory mem; + struct sdmmcbus_attach_args saa; + int error = 1, irq; + uint32_t caps; + + sc->sc_iot = aa->aa_iot; + if (aa->aa_node) { + static int unit = 0; + uint32_t ints[3]; + + sc->unit = unit++; + + if (fdt_get_memory_address(aa->aa_node, 0, &mem)) + panic("%s: could not extract memory data from FDT", + __func__); + + /* TODO: Add interrupt FDT API. */ + if (fdt_node_property_ints(aa->aa_node, "interrupts", + ints, 3) != 3) + panic("%s: could not extract interrupt data from FDT", + __func__); + + irq = ints[1]; + } else { + irq = aa->aa_dev->irq[0]; + mem.addr = aa->aa_dev->mem[0].addr; + mem.size = aa->aa_dev->mem[0].size; + } + + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + printf("\n"); + + /* XXX DMA channels? */ + + sc->sc_ih = arm_intr_establish(irq, IPL_SDMMC, + exesdhc_intr, sc, sc->sc_dev.dv_xname); + + /* + * Reset the host controller and enable interrupts. + */ + if (exesdhc_host_reset(sc)) + goto err; + + /* Determine host capabilities. */ + caps = HREAD4(sc, SDHC_HOST_CTRL_CAP); + + /* + * Determine the base clock frequency. (2.2.24) + */ + //sc->clkbase = exccm_get_usdhx(aa->aa_dev->unit + 1); + sc->clkbase = 0; + + /* + * Determine SD bus voltage levels supported by the controller. + */ + if (caps & SDHC_HOST_CTRL_CAP_VS18) + SET(sc->ocr, MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V); + if (caps & SDHC_HOST_CTRL_CAP_VS30) + SET(sc->ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V); + if (caps & SDHC_HOST_CTRL_CAP_VS33) + SET(sc->ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V); + + /* + * Determine max block size. + */ + switch ((caps >> SDHC_HOST_CTRL_CAP_MBL_SHIFT) + & SDHC_HOST_CTRL_CAP_MBL_MASK) { + case 0: + sc->maxblklen = 512; + break; + case 1: + sc->maxblklen = 1024; + break; + case 2: + sc->maxblklen = 2048; + break; + case 3: + sc->maxblklen = 4096; + break; + default: + sc->maxblklen = 512; + printf("invalid capability blocksize in capa %08x," + " trying 512\n", caps); + } + + /* somewhere this blksize might be used instead of the device's */ + sc->maxblklen = 512; + + /* + * Attach the generic SD/MMC bus driver. (The bus driver must + * not invoke any chipset functions before it is attached.) + */ + + bzero(&saa, sizeof(saa)); + saa.saa_busname = "sdmmc"; + saa.sct = &exesdhc_functions; + saa.sch = sc; + + sc->sdmmc = config_found(&sc->sc_dev, &saa, NULL); + if (sc->sdmmc == NULL) { + error = 0; + goto err; + } + + return; + +err: + return; +} + + +/* + * Power hook established by or called from attachment driver. + */ +void +exesdhc_power(int why, void *arg) +{ +} + +/* + * Shutdown hook established by or called from attachment driver. + */ +void +exesdhc_shutdown(void *arg) +{ + struct exesdhc_softc *sc = arg; + + /* XXX chip locks up if we don't disable it before reboot. */ + (void)exesdhc_host_reset(sc); +} + +/* + * Reset the host controller. Called during initialization, when + * cards are removed, upon resume, and during error recovery. + */ +int +exesdhc_host_reset(sdmmc_chipset_handle_t sch) +{ + struct exesdhc_softc *sc = sch; + u_int32_t imask; + int error; + int s; + + s = splsdmmc(); + + /* Disable all interrupts. */ + HWRITE4(sc, SDHC_INT_STATUS_EN, 0); + HWRITE4(sc, SDHC_INT_SIGNAL_EN, 0); + + /* + * Reset the entire host controller and wait up to 100ms for + * the controller to clear the reset bit. + */ + if ((error = exesdhc_soft_reset(sc, SDHC_SYS_CTRL_RSTA)) != 0) { + splx(s); + return (error); + } + + /* Set data timeout counter value to max for now. */ + HSET4(sc, SDHC_SYS_CTRL, 0xe << SDHC_SYS_CTRL_DTOCV_SHIFT); + + /* Enable interrupts. */ + imask = SDHC_INT_STATUS_CC | SDHC_INT_STATUS_TC | + SDHC_INT_STATUS_BGE | +#ifdef SDHC_DMA + SHDC_INT_STATUS_DINT; +#else + SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR; +#endif + + imask |= SDHC_INT_STATUS_CTOE | SDHC_INT_STATUS_CCE | + SDHC_INT_STATUS_CEBE | SDHC_INT_STATUS_CIC | + SDHC_INT_STATUS_DTOE | SDHC_INT_STATUS_DCE | + SDHC_INT_STATUS_DEBE | SDHC_INT_STATUS_DMAE; + + HWRITE4(sc, SDHC_INT_STATUS_EN, imask); + HWRITE4(sc, SDHC_INT_SIGNAL_EN, imask); + + // Use no or simple DMA + HWRITE4(sc, SDHC_PROT_CTRL, + HREAD4(sc, SDHC_PROT_CTRL) & ~SDHC_PROT_CTRL_DMASEL_SDMA_MASK); + + splx(s); + return 0; +} + +uint32_t +exesdhc_host_ocr(sdmmc_chipset_handle_t sch) +{ + struct exesdhc_softc *sc = sch; + return sc->ocr; +} + +int +exesdhc_host_maxblklen(sdmmc_chipset_handle_t sch) +{ + struct exesdhc_softc *sc = sch; + return sc->maxblklen; +} + +/* + * Return non-zero if the card is currently inserted. + */ +int +exesdhc_card_detect(sdmmc_chipset_handle_t sch) +{ + struct exesdhc_softc *sc = sch; + int gpio; + + switch (board_id) + { + case BOARD_ID_EXYNOS5_CHROMEBOOK: + switch (sc->unit) { + case 2: + gpio = 6*32 + 0; + break; + case 3: + gpio = 1*32 + 6; + break; + default: + return 0; + } + return exgpio_get_bit(gpio) ? 0 : 1; + default: + return 1; + } +} + +/* + * Set or change SD bus voltage and enable or disable SD bus power. + * Return zero on success. + */ +int +exesdhc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) +{ + return 0; +} + +/* + * Set or change SDCLK frequency or disable the SD clock. + * Return zero on success. + */ +int +exesdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq) +{ + struct exesdhc_softc *sc = sch; + int div, pre_div, cur_freq, s; + int error = 0; + + s = splsdmmc(); + + if (sc->clkbase / 16 > freq) { + for (pre_div = 2; pre_div < 256; pre_div *= 2) + if ((sc->clkbase / pre_div) <= (freq * 16)) + break; + } else + pre_div = 2; + + if (sc->clkbase == freq) + pre_div = 1; + + for (div = 1; div <= 16; div++) + if ((sc->clkbase / (div * pre_div)) <= freq) + break; + + div -= 1; + pre_div >>= 1; + + cur_freq = sc->clkbase / (pre_div * 2) / (div + 1); + + /* disable force CLK ouput active */ + HCLR4(sc, SDHC_VEND_SPEC, SDHC_VEND_SPEC_FRC_SDCLK_ON); + + /* wait while clock is unstable */ + if ((error = exesdhc_wait_state(sc, SDHC_PRES_STATE_SDSTB, SDHC_PRES_STATE_SDSTB)) != 0) + goto ret; + + HCLR4(sc, SDHC_SYS_CTRL, SDHC_SYS_CTRL_CLOCK_MASK); + HSET4(sc, SDHC_SYS_CTRL, (div << SDHC_SYS_CTRL_CLOCK_DIV_SHIFT) | (pre_div << SDHC_SYS_CTRL_CLOCK_PRE_SHIFT)); + + /* wait while clock is unstable */ + if ((error = exesdhc_wait_state(sc, SDHC_PRES_STATE_SDSTB, SDHC_PRES_STATE_SDSTB)) != 0) + goto ret; + +ret: + splx(s); + return error; +} + +void +exesdhc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable) +{ + printf("exesdhc_card_intr_mask\n"); + /* - this is SDIO card interrupt */ + struct exesdhc_softc *sc = sch; + + if (enable) { + HSET4(sc, SDHC_INT_STATUS_EN, SDHC_INT_STATUS_CINT); + HSET4(sc, SDHC_INT_SIGNAL_EN, SDHC_INT_STATUS_CINT); + } else { + HCLR4(sc, SDHC_INT_STATUS_EN, SDHC_INT_STATUS_CINT); + HCLR4(sc, SDHC_INT_SIGNAL_EN, SDHC_INT_STATUS_CINT); + } +} + +void +exesdhc_card_intr_ack(sdmmc_chipset_handle_t sch) +{ + printf("exesdhc_card_intr_ack\n"); + struct exesdhc_softc *sc = sch; + + HWRITE4(sc, SDHC_INT_STATUS, SDHC_INT_STATUS_CINT); +} + +int +exesdhc_wait_state(struct exesdhc_softc *sc, uint32_t mask, uint32_t value) +{ + uint32_t state; + int timeout; + state = HREAD4(sc, SDHC_PRES_STATE); + DPRINTF(3,("%s: wait_state %x %x %x)\n", HDEVNAME(sc), + mask, value, state)); + for (timeout = 1000; timeout > 0; timeout--) { + if (((state = HREAD4(sc, SDHC_PRES_STATE)) & mask) == value) + return 0; + delay(10); + } + DPRINTF(0,("%s: timeout waiting for %x\n", HDEVNAME(sc), + value, state)); + return ETIMEDOUT; +} + +void +exesdhc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + struct exesdhc_softc *sc = sch; + int error; + + /* + * Start the command, or mark `cmd' as failed and return. + */ + error = exesdhc_start_command(sc, cmd); + if (error != 0) { + cmd->c_error = error; + SET(cmd->c_flags, SCF_ITSDONE); + return; + } + + /* + * Wait until the command phase is done, or until the command + * is marked done for any other reason. + */ + if (!exesdhc_wait_intr(sc, SDHC_INT_STATUS_CC, SDHC_COMMAND_TIMEOUT)) { + cmd->c_error = ETIMEDOUT; + SET(cmd->c_flags, SCF_ITSDONE); + return; + } + + /* + * The host controller removes bits [0:7] from the response + * data (CRC) and we pass the data up unchanged to the bus + * driver (without padding). + */ + if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { + if (ISSET(cmd->c_flags, SCF_RSP_136)) { + cmd->c_resp[0] = HREAD4(sc, SDHC_CMD_RSP0); + cmd->c_resp[1] = HREAD4(sc, SDHC_CMD_RSP1); + cmd->c_resp[2] = HREAD4(sc, SDHC_CMD_RSP2); + cmd->c_resp[3] = HREAD4(sc, SDHC_CMD_RSP3); + +#ifdef SDHC_DEBUG + printf("resp[0] 0x%08x\nresp[1] 0x%08x\nresp[2] 0x%08x\nresp[3] 0x%08x\n", cmd->c_resp[0], cmd->c_resp[1], cmd->c_resp[2], cmd->c_resp[3]); +#endif + } else { + cmd->c_resp[0] = HREAD4(sc, SDHC_CMD_RSP0); +#ifdef SDHC_DEBUG + printf("resp[0] 0x%08x\n", cmd->c_resp[0]); +#endif + } + } + + /* + * If the command has data to transfer in any direction, + * execute the transfer now. + */ + if (cmd->c_error == 0 && cmd->c_data) + exesdhc_transfer_data(sc, cmd); + + DPRINTF(1,("%s: cmd %u done (flags=%#x error=%d)\n", + HDEVNAME(sc), cmd->c_opcode, cmd->c_flags, cmd->c_error)); + SET(cmd->c_flags, SCF_ITSDONE); +} + +int +exesdhc_start_command(struct exesdhc_softc *sc, struct sdmmc_command *cmd) +{ + u_int32_t blksize = 0; + u_int32_t blkcount = 0; + u_int32_t command; + int error; + int s; + + DPRINTF(1,("%s: start cmd %u arg=%#x data=%#x dlen=%d flags=%#x " + "proc=\"%s\"\n", HDEVNAME(sc), cmd->c_opcode, cmd->c_arg, + cmd->c_data, cmd->c_datalen, cmd->c_flags, curproc ? + curproc->p_comm : "")); + + /* + * The maximum block length for commands should be the minimum + * of the host buffer size and the card buffer size. (1.7.2) + */ + + /* Fragment the data into proper blocks. */ + if (cmd->c_datalen > 0) { + blksize = MIN(cmd->c_datalen, cmd->c_blklen); + blkcount = cmd->c_datalen / blksize; + if (cmd->c_datalen % blksize > 0) { + /* XXX: Split this command. (1.7.4) */ + printf("%s: data not a multiple of %d bytes\n", + HDEVNAME(sc), blksize); + return EINVAL; + } + } + + /* Check limit imposed by 9-bit block count. (1.7.2) */ + if (blkcount > SDHC_BLK_ATT_BLKCNT_MAX) { + printf("%s: too much data\n", HDEVNAME(sc)); + return EINVAL; + } + + /* setup for PIO, check for write protection */ + if (!ISSET(cmd->c_flags, SCF_CMD_READ)) { + if (!(HREAD4(sc, SDHC_PRES_STATE) & SDHC_PRES_STATE_WPSPL)) { + printf("%s: card is write protected\n", + HDEVNAME(sc)); + return EINVAL; + } + } + +#ifdef SDHC_DMA + /* set watermark level */ + uint32_t wml = blksize / sizeof(uint32_t); + if (ISSET(cmd->c_flags, SCF_CMD_READ)) { + if (wml > 16) + wml = 16; + HWRITE4(sc, SDHC_WTMK_LVL, wml << SDHC_WTMK_LVL_RD_WML_SHIFT); + } else { + if (wml > 128) + wml = 128; + HWRITE4(sc, SDHC_WTMK_LVL, wml << SDHC_WTMK_LVL_WR_WML_SHIFT); + } +#endif + + /* Prepare transfer mode register value. (2.2.5) */ + command = 0; + + if (ISSET(cmd->c_flags, SCF_CMD_READ)) + command |= SDHC_MIX_CTRL_DTDSEL; + if (blkcount > 0) { + command |= SDHC_MIX_CTRL_BCEN; +#ifdef SDHC_DMA + command |= SDHC_MIX_CTRL_DMAEN; +#endif + if (blkcount > 1) { + command |= SDHC_MIX_CTRL_MSBSEL; + command |= SDHC_MIX_CTRL_AC12EN; + } + } + + command |= (cmd->c_opcode << SDHC_CMD_XFR_TYP_CMDINDX_SHIFT) & + SDHC_CMD_XFR_TYP_CMDINDX_SHIFT_MASK; + + if (ISSET(cmd->c_flags, SCF_RSP_CRC)) + command |= SDHC_CMD_XFR_TYP_CCCEN; + if (ISSET(cmd->c_flags, SCF_RSP_IDX)) + command |= SDHC_CMD_XFR_TYP_CICEN; + if (cmd->c_data != NULL) + command |= SDHC_CMD_XFR_TYP_DPSEL; + + if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT)) + command |= SDHC_CMD_XFR_TYP_RSP_NONE; + else if (ISSET(cmd->c_flags, SCF_RSP_136)) + command |= SDHC_CMD_XFR_TYP_RSP136; + else if (ISSET(cmd->c_flags, SCF_RSP_BSY)) + command |= SDHC_CMD_XFR_TYP_RSP48B; + else + command |= SDHC_CMD_XFR_TYP_RSP48; + + /* Wait until command and data inhibit bits are clear. (1.5) */ + if ((error = exesdhc_wait_state(sc, SDHC_PRES_STATE_CIHB, 0)) != 0) + return error; + + s = splsdmmc(); + + /* + * Start a CPU data transfer. Writing to the high order byte + * of the SDHC_COMMAND register triggers the SD command. (1.5) + */ +#ifdef SDHC_DMA + if (cmd->c_data) + HWRITE4(sc, SDHC_DS_ADDR, (uint32_t)cmd->c_data); +#endif + HWRITE4(sc, SDHC_BLK_ATT, blkcount << SDHC_BLK_ATT_BLKCNT_SHIFT | + blksize << SDHC_BLK_ATT_BLKSIZE_SHIFT); + HWRITE4(sc, SDHC_CMD_ARG, cmd->c_arg); + HWRITE4(sc, SDHC_MIX_CTRL, + (HREAD4(sc, SDHC_MIX_CTRL) & (0xf << 22)) | (command & 0xffff)); + HWRITE4(sc, SDHC_CMD_XFR_TYP, command); + + splx(s); + return 0; +} + +void +exesdhc_transfer_data(struct exesdhc_softc *sc, struct sdmmc_command *cmd) +{ +#ifndef SDHC_DMA + u_char *datap = cmd->c_data; + int i; +#endif + int datalen; + int mask; + int error; + + mask = ISSET(cmd->c_flags, SCF_CMD_READ) ? + SDHC_PRES_STATE_BREN : SDHC_PRES_STATE_BWEN; + error = 0; + datalen = cmd->c_datalen; + + DPRINTF(1,("%s: resp=%#x datalen=%d\n", HDEVNAME(sc), + MMC_R1(cmd->c_resp), datalen)); + +#ifndef SDHC_DMA + while (datalen > 0) { + if (!exesdhc_wait_intr(sc, SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR, + SDHC_BUFFER_TIMEOUT)) { + error = ETIMEDOUT; + break; + } + + if ((error = exesdhc_wait_state(sc, mask, mask)) != 0) + break; + + /* FIXME: wait a bit, else it fails */ + delay(100); + i = MIN(datalen, cmd->c_blklen); + if (ISSET(cmd->c_flags, SCF_CMD_READ)) + exesdhc_read_data(sc, datap, i); + else + exesdhc_write_data(sc, datap, i); + + datap += i; + datalen -= i; + } +#endif + + if (error == 0 && !exesdhc_wait_intr(sc, SDHC_INT_STATUS_TC, + SDHC_TRANSFER_TIMEOUT)) + error = ETIMEDOUT; + + if (error != 0) + cmd->c_error = error; + SET(cmd->c_flags, SCF_ITSDONE); + + DPRINTF(1,("%s: data transfer done (error=%d)\n", + HDEVNAME(sc), cmd->c_error)); +} + +void +exesdhc_read_data(struct exesdhc_softc *sc, u_char *datap, int datalen) +{ + while (datalen > 3) { + *(uint32_t *)datap = HREAD4(sc, SDHC_DATA_BUFF_ACC_PORT); + datap += 4; + datalen -= 4; + } + if (datalen > 0) { + uint32_t rv = HREAD4(sc, SDHC_DATA_BUFF_ACC_PORT); + do { + *datap++ = rv & 0xff; + rv = rv >> 8; + } while (--datalen > 0); + } +} + +void +exesdhc_write_data(struct exesdhc_softc *sc, u_char *datap, int datalen) +{ + while (datalen > 3) { + DPRINTF(3,("%08x\n", *(uint32_t *)datap)); + HWRITE4(sc, SDHC_DATA_BUFF_ACC_PORT, *((uint32_t *)datap)); + datap += 4; + datalen -= 4; + } + if (datalen > 0) { + uint32_t rv = *datap++; + if (datalen > 1) + rv |= *datap++ << 8; + if (datalen > 2) + rv |= *datap++ << 16; + DPRINTF(3,("rv %08x\n", rv)); + HWRITE4(sc, SDHC_DATA_BUFF_ACC_PORT, rv); + } +} + +/* Prepare for another command. */ +int +exesdhc_soft_reset(struct exesdhc_softc *sc, int mask) +{ + int timo; + + DPRINTF(1,("%s: software reset reg=%#x\n", HDEVNAME(sc), mask)); + + /* disable force CLK ouput active */ + HCLR4(sc, SDHC_VEND_SPEC, SDHC_VEND_SPEC_FRC_SDCLK_ON); + + /* reset */ + HSET4(sc, SDHC_SYS_CTRL, mask); + delay(10); + + for (timo = 1000; timo > 0; timo--) { + if (!ISSET(HREAD4(sc, SDHC_SYS_CTRL), mask)) + break; + delay(10); + } + if (timo == 0) { + DPRINTF(1,("%s: timeout reg=%#x\n", HDEVNAME(sc), + HREAD4(sc, SDHC_SYS_CTRL))); + return ETIMEDOUT; + } + + return 0; +} + +int +exesdhc_wait_intr(struct exesdhc_softc *sc, int mask, int timo) +{ + int status; + int s; + + mask |= SDHC_INT_STATUS_ERR; + s = splsdmmc(); + + /* enable interrupts for brr and bwr */ + if (mask & (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR)) + HSET4(sc, SDHC_INT_SIGNAL_EN, (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR)); + + status = sc->intr_status & mask; + while (status == 0) { + if (tsleep(&sc->intr_status, PWAIT, "hcintr", timo) + == EWOULDBLOCK) { + status |= SDHC_INT_STATUS_ERR; + break; + } + status = sc->intr_status & mask; + } + sc->intr_status &= ~status; + DPRINTF(2,("%s: intr status %#x error %#x\n", HDEVNAME(sc), status, + sc->intr_error_status)); + + /* Command timeout has higher priority than command complete. */ + if (ISSET(status, SDHC_INT_STATUS_ERR)) { + sc->intr_error_status = 0; + (void)exesdhc_soft_reset(sc, SDHC_SYS_CTRL_RSTC | SDHC_SYS_CTRL_RSTD); + status = 0; + } + + splx(s); + return status; +} + +/* + * Established by attachment driver at interrupt priority IPL_SDMMC. + */ +int +exesdhc_intr(void *arg) +{ + struct exesdhc_softc *sc = arg; + + u_int32_t status; + + /* Find out which interrupts are pending. */ + status = HREAD4(sc, SDHC_INT_STATUS); + +#ifndef SDHC_DMA + /* disable interrupts for brr and bwr, else we get flooded */ + if (status & (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR)) + HCLR4(sc, SDHC_INT_SIGNAL_EN, (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR)); +#endif + + /* Acknowledge the interrupts we are about to handle. */ + HWRITE4(sc, SDHC_INT_STATUS, status); + DPRINTF(2,("%s: interrupt status=0x%08x\n", HDEVNAME(sc), + status, status)); + + /* + * Service error interrupts. + */ + if (ISSET(status, SDHC_INT_STATUS_CMD_ERR | + SDHC_INT_STATUS_CTOE | SDHC_INT_STATUS_DTOE)) { + sc->intr_status |= status; + sc->intr_error_status |= status & 0xffff0000; + wakeup(&sc->intr_status); + } + + /* + * Wake up the blocking process to service command + * related interrupt(s). + */ + if (ISSET(status, SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR | + SDHC_INT_STATUS_TC | SDHC_INT_STATUS_CC)) { + sc->intr_status |= status; + wakeup(&sc->intr_status); + } + + /* + * Service SD card interrupts. + */ + if (ISSET(status, SDHC_INT_STATUS_CINT)) { + DPRINTF(0,("%s: card interrupt\n", HDEVNAME(sc))); + HCLR4(sc, SDHC_INT_STATUS, SDHC_INT_STATUS_CINT); + sdmmc_card_intr(sc->sdmmc); + } + return 1; +} diff --git a/sys/arch/armv7/exynos/exgpio.c b/sys/arch/armv7/exynos/exgpio.c new file mode 100644 index 00000000000..fa8bbf377b3 --- /dev/null +++ b/sys/arch/armv7/exynos/exgpio.c @@ -0,0 +1,283 @@ +/* $OpenBSD: exgpio.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org> + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/evcount.h> + +#include <arm/cpufunc.h> + +#include <machine/bus.h> +#include <machine/fdt.h> +#include <machine/intr.h> + +#include <armv7/armv7/armv7var.h> +#include <armv7/exynos/exgpiovar.h> + +/* Exynos5 registers */ +#define GPIO_BANK_SIZE 0x20 +#define GPIO_BANK(x) (GPIO_BANK_SIZE * ((x) / 8)) +#define GPIO_CON(x) (GPIO_BANK(x) + 0x00) +#define GPIO_DAT(x) (GPIO_BANK(x) + 0x04) +#define GPIO_PULL(x) (GPIO_BANK(x) + 0x08) +#define GPIO_DRV(x) (GPIO_BANK(x) + 0x0c) +#define GPIO_PDN_CON(x) (GPIO_BANK(x) + 0x10) +#define GPIO_PDN_PULL(x) (GPIO_BANK(x) + 0x14) + +/* bits and bytes */ +#define GPIO_PIN(x) ((x) % 8) +#define GPIO_CON_INPUT(x) (0x0 << (GPIO_PIN(x) << 2)) +#define GPIO_CON_OUTPUT(x) (0x1 << (GPIO_PIN(x) << 2)) +#define GPIO_CON_IRQ(x) (0xf << (GPIO_PIN(x) << 2)) +#define GPIO_CON_MASK(x) (0xf << (GPIO_PIN(x) << 2)) +#define GPIO_DAT_SET(x) (0x1 << (GPIO_PIN(x) << 0)) +#define GPIO_DAT_MASK(x) (0x1 << (GPIO_PIN(x) << 0)) +#define GPIO_PULL_NONE(x) (0x0 << (GPIO_PIN(x) << 1)) +#define GPIO_PULL_DOWN(x) (0x1 << (GPIO_PIN(x) << 1)) +#define GPIO_PULL_UP(x) (0x3 << (GPIO_PIN(x) << 1)) +#define GPIO_PULL_MASK(x) (0x3 << (GPIO_PIN(x) << 1)) +#define GPIO_DRV_1X(x) (0x0 << (GPIO_PIN(x) << 1)) +#define GPIO_DRV_2X(x) (0x1 << (GPIO_PIN(x) << 1)) +#define GPIO_DRV_3X(x) (0x2 << (GPIO_PIN(x) << 1)) +#define GPIO_DRV_4X(x) (0x3 << (GPIO_PIN(x) << 1)) +#define GPIO_DRV_MASK(x) (0x3 << (GPIO_PIN(x) << 1)) + +#define GPIO_PINS_PER_BANK 8 + +struct exgpio_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + int sc_ngpio; + unsigned int (*sc_get_bit)(struct exgpio_softc *sc, + unsigned int gpio); + void (*sc_set_bit)(struct exgpio_softc *sc, + unsigned int gpio); + void (*sc_clear_bit)(struct exgpio_softc *sc, + unsigned int gpio); + void (*sc_set_dir)(struct exgpio_softc *sc, + unsigned int gpio, unsigned int dir); +}; + +int exgpio_match(struct device *parent, void *v, void *aux); +void exgpio_attach(struct device *parent, struct device *self, void *args); + +struct exgpio_softc *exgpio_pin_to_inst(unsigned int); +unsigned int exgpio_pin_to_offset(unsigned int); +unsigned int exgpio_v5_get_bit(struct exgpio_softc *, unsigned int); +void exgpio_v5_set_bit(struct exgpio_softc *, unsigned int); +void exgpio_v5_clear_bit(struct exgpio_softc *, unsigned int); +void exgpio_v5_set_dir(struct exgpio_softc *, unsigned int, unsigned int); +unsigned int exgpio_v5_get_dir(struct exgpio_softc *, unsigned int); + + +struct cfattach exgpio_ca = { + sizeof (struct exgpio_softc), NULL, exgpio_attach +}; +struct cfattach exgpio_fdt_ca = { + sizeof (struct exgpio_softc), exgpio_match, exgpio_attach +}; + +struct cfdriver exgpio_cd = { + NULL, "exgpio", DV_DULL +}; + +int +exgpio_match(struct device *parent, void *v, void *aux) +{ + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("samsung,exynos5250-pinctrl", aa->aa_node)) + return 1; + + return 0; +} + +void +exgpio_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct exgpio_softc *sc = (struct exgpio_softc *) self; + struct fdt_memory mem; + + sc->sc_iot = aa->aa_iot; + if (aa->aa_node) { + if (fdt_get_memory_address(aa->aa_node, 0, &mem)) + panic("%s: could not extract memory data from FDT", + __func__); + } else { + mem.addr = aa->aa_dev->mem[0].addr; + mem.size = aa->aa_dev->mem[0].size; + } + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + sc->sc_ngpio = (mem.size / GPIO_BANK_SIZE) * GPIO_PINS_PER_BANK; + + sc->sc_get_bit = exgpio_v5_get_bit; + sc->sc_set_bit = exgpio_v5_set_bit; + sc->sc_clear_bit = exgpio_v5_clear_bit; + sc->sc_set_dir = exgpio_v5_set_dir; + + printf("\n"); + + /* XXX - IRQ */ + /* XXX - SYSCONFIG */ + /* XXX - CTRL */ + /* XXX - DEBOUNCE */ +} + +struct exgpio_softc * +exgpio_pin_to_inst(unsigned int gpio) +{ + int i; + + for (i = 0; exgpio_cd.cd_devs[i] != NULL; i++) + { + struct exgpio_softc *sc = exgpio_cd.cd_devs[i]; + if (gpio < sc->sc_ngpio) + return sc; + else + gpio -= sc->sc_ngpio; + } + + return NULL; +} + +unsigned int +exgpio_pin_to_offset(unsigned int gpio) +{ + int i; + + for (i = 0; exgpio_cd.cd_devs[i] != NULL; i++) + { + struct exgpio_softc *sc = exgpio_cd.cd_devs[i]; + if (gpio < sc->sc_ngpio) + return gpio; + else + gpio -= sc->sc_ngpio; + } + + return 0; +} + +unsigned int +exgpio_get_bit(unsigned int gpio) +{ + struct exgpio_softc *sc = exgpio_pin_to_inst(gpio); + + return sc->sc_get_bit(sc, gpio); +} + +void +exgpio_set_bit(unsigned int gpio) +{ + struct exgpio_softc *sc = exgpio_pin_to_inst(gpio); + + sc->sc_set_bit(sc, gpio); +} + +void +exgpio_clear_bit(unsigned int gpio) +{ + struct exgpio_softc *sc = exgpio_pin_to_inst(gpio); + + sc->sc_clear_bit(sc, gpio); +} +void +exgpio_set_dir(unsigned int gpio, unsigned int dir) +{ + struct exgpio_softc *sc = exgpio_pin_to_inst(gpio); + + sc->sc_set_dir(sc, gpio, dir); +} + +unsigned int +exgpio_v5_get_bit(struct exgpio_softc *sc, unsigned int gpio) +{ + u_int32_t val; + + gpio = exgpio_pin_to_offset(gpio); + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_DAT(gpio)); + + return !!(val & GPIO_DAT_SET(gpio)); +} + +void +exgpio_v5_set_bit(struct exgpio_softc *sc, unsigned int gpio) +{ + u_int32_t val; + + gpio = exgpio_pin_to_offset(gpio); + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_DAT(gpio)); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_DAT(gpio), + val | GPIO_DAT_SET(gpio)); +} + +void +exgpio_v5_clear_bit(struct exgpio_softc *sc, unsigned int gpio) +{ + u_int32_t val; + + gpio = exgpio_pin_to_offset(gpio); + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_DAT(gpio)); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_DAT(gpio), + val & ~GPIO_DAT_MASK(gpio)); +} + +void +exgpio_v5_set_dir(struct exgpio_softc *sc, unsigned int gpio, unsigned int dir) +{ + int s; + u_int32_t val; + + gpio = exgpio_pin_to_offset(gpio); + s = splhigh(); + + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_CON(gpio)); + val &= ~GPIO_CON_OUTPUT(gpio); + if (dir == EXGPIO_DIR_OUT) + val |= GPIO_CON_OUTPUT(gpio); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_CON(gpio), val); + + splx(s); +} + +unsigned int +exgpio_v5_get_dir(struct exgpio_softc *sc, unsigned int gpio) +{ + int s; + u_int32_t val; + + gpio = exgpio_pin_to_offset(gpio); + s = splhigh(); + + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_CON(gpio)); + if (val & GPIO_CON_OUTPUT(gpio)) + val = EXGPIO_DIR_OUT; + else + val = EXGPIO_DIR_IN; + + splx(s); + return val; +} diff --git a/sys/arch/armv7/exynos/exgpiovar.h b/sys/arch/armv7/exynos/exgpiovar.h new file mode 100644 index 00000000000..a53b9fedd3d --- /dev/null +++ b/sys/arch/armv7/exynos/exgpiovar.h @@ -0,0 +1,41 @@ +/* $OpenBSD: exgpiovar.h,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org> + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef EXGPIOVAR_H +#define EXGPIOVAR_H + +#define EXGPIO_DIR_IN 0 +#define EXGPIO_DIR_OUT 1 + +unsigned int exgpio_get_function(unsigned int gpio, unsigned int fn); +void exgpio_set_function(unsigned int gpio, unsigned int fn); +unsigned int exgpio_get_bit(unsigned int gpio); +void exgpio_set_bit(unsigned int gpio); +void exgpio_clear_bit(unsigned int gpio); +void exgpio_set_dir(unsigned int gpio, unsigned int dir); + +/* interrupts */ +void exgpio_clear_intr(unsigned int gpio); +void exgpio_intr_mask(unsigned int gpio); +void exgpio_intr_unmask(unsigned int gpio); +void exgpio_intr_level(unsigned int gpio, unsigned int level); +void *exgpio_intr_establish(unsigned int gpio, int level, int spl, + int (*func)(void *), void *arg, char *name); +void exgpio_intr_disestablish(void *cookie); + +#endif /* EXGPIOVAR_H */ diff --git a/sys/arch/armv7/exynos/exiic.c b/sys/arch/armv7/exynos/exiic.c new file mode 100644 index 00000000000..e47f156725e --- /dev/null +++ b/sys/arch/armv7/exynos/exiic.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <armv7/armv7/armv7var.h> +#include <armv7/exynos/exgpiovar.h> +#include <armv7/exynos/exiicvar.h> +#include <armv7/exynos/exclockvar.h> + +/* registers */ +#define I2C_CON 0x00 /* control register */ +#define I2C_STAT 0x04 /* control/status register */ +#define I2C_ADD 0x08 /* address register */ +#define I2C_DS 0x0C /* transmit/receive data shift register */ +#define I2C_LC 0x10 /* multi-master line control register */ + +/* bits and bytes */ +#define I2C_CON_TXCLKVAL_MASK (0xf << 0) /* tx clock = i2cclk / (i2ccon[3:0] + 1) */ +#define I2C_CON_INTPENDING (0x1 << 4) /* 0 = no interrupt pending/clear, 1 = pending */ +#define I2C_CON_TXRX_INT (0x1 << 5) /* enable/disable */ +#define I2C_CON_TXCLKSRC_16 (0x0 << 6) /* i2clk = fpclk/16 */ +#define I2C_CON_TXCLKSRC_512 (0x1 << 6) /* i2clk = fpclk/512 */ +#define I2C_CON_ACK (0x1 << 7) +#define I2C_STAT_LAST_RVCD_BIT (0x1 << 0) /* last received bit 0 => ack, 1 => no ack */ +#define I2C_STAT_ADDR_ZERO_FLAG (0x1 << 1) /* 0 => start/stop cond. detected, 1 => received slave addr 0xb */ +#define I2C_STAT_ADDR_SLAVE_ZERO_FLAG (0x1 << 2) /* 0 => start/stop cond. detected, 1 => received slave addr matches i2cadd */ +#define I2C_STAT_ARBITRATION (0x1 << 3) /* 0 => successul, 1 => failed */ +#define I2C_STAT_SERIAL_OUTPUT (0x1 << 4) /* 0 => disable tx/rx, 1 => enable tx/rx */ +#define I2C_STAT_BUSY_SIGNAL (0x1 << 5) /* 0 => not busy / stop signal generation, 1 => busy / start signal generation */ +#define I2C_STAT_MODE_SEL_SLAVE_RX (0x0 << 6) /* slave receive mode */ +#define I2C_STAT_MODE_SEL_SLAVE_TX (0x1 << 6) /* slave transmit mode */ +#define I2C_STAT_MODE_SEL_MASTER_RX (0x2 << 6) /* master receive mode */ +#define I2C_STAT_MODE_SEL_MASTER_TX (0x3 << 6) /* master transmit */ +#define I2C_ADD_SLAVE_ADDR(x) (((x) & 0x7f) << 1) +#define I2C_DS_DATA_SHIFT(x) (((x) & 0xff) << 0) + +#define I2C_ACK 0 +#define I2C_NACK 1 +#define I2C_TIMEOUT 2 + +struct exiic_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_size_t sc_ios; + void *sc_ih; + int unit; + + struct rwlock sc_buslock; + struct i2c_controller i2c_tag; + + uint16_t frequency; + uint16_t intr_status; +}; + +int exiic_match(struct device *parent, void *v, void *aux); +void exiic_attach(struct device *, struct device *, void *); +int exiic_detach(struct device *, int); +void exiic_bus_scan(struct device *, struct i2cbus_attach_args *, void *); +void exiic_setspeed(struct exiic_softc *, int); +int exiic_intr(void *); +int exiic_wait_intr(struct exiic_softc *, int, int); +int exiic_wait_state(struct exiic_softc *, uint32_t, uint32_t, uint32_t); +int exiic_start(struct exiic_softc *, int, int, void *, int); + +void exiic_xfer_start(struct exiic_softc *); +int exiic_xfer_wait(struct exiic_softc *); +int exiic_i2c_acquire_bus(void *, int); +void exiic_i2c_release_bus(void *, int); +int exiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, + void *, size_t, int); + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + + +struct cfattach exiic_ca = { + sizeof(struct exiic_softc), NULL, exiic_attach, exiic_detach +}; +struct cfattach exiic_fdt_ca = { + sizeof(struct exiic_softc), exiic_match, exiic_attach, exiic_detach +}; + +struct cfdriver exiic_cd = { + NULL, "exiic", DV_DULL +}; + +int +exiic_match(struct device *parent, void *v, void *aux) +{ + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("samsung,s3c2440-i2c", aa->aa_node)) + return 1; + + return 0; +} + +void +exiic_attach(struct device *parent, struct device *self, void *args) +{ + struct exiic_softc *sc = (struct exiic_softc *)self; + struct armv7_attach_args *aa = args; + struct fdt_memory mem; + + sc->sc_iot = aa->aa_iot; + if (aa->aa_node) { + static int unit = 0; + + sc->unit = unit++; + if (fdt_get_memory_address(aa->aa_node, 0, &mem)) + panic("%s: could not extract memory data from FDT", + __func__); + } else { + mem.addr = aa->aa_dev->mem[0].addr; + mem.size = aa->aa_dev->mem[0].size; + sc->unit = aa->aa_dev->unit; + } + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + sc->sc_ios = mem.size; + +#if 0 + sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_BIO, + exiic_intr, sc, sc->sc_dev.dv_xname); +#endif + + printf("\n"); + + rw_init(&sc->sc_buslock, sc->sc_dev.dv_xname); + + struct i2cbus_attach_args iba; + + sc->i2c_tag.ic_cookie = sc; + sc->i2c_tag.ic_acquire_bus = exiic_i2c_acquire_bus; + sc->i2c_tag.ic_release_bus = exiic_i2c_release_bus; + sc->i2c_tag.ic_exec = exiic_i2c_exec; + + bzero(&iba, sizeof iba); + iba.iba_name = "iic"; + iba.iba_tag = &sc->i2c_tag; + iba.iba_bus_scan = exiic_bus_scan; + iba.iba_bus_scan_arg = sc; + config_found(&sc->sc_dev, &iba, NULL); +} + +void +exiic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg) +{ + struct exiic_softc *sc = (struct exiic_softc *)arg; + struct i2c_attach_args ia; + + /* XXX: We currently only attach cros-ec on I2C4. We'll use FDT later. */ + if (sc->unit != 4) + return; + + char *name = "crosec"; + int addr = 0x1e; + + memset(&ia, 0, sizeof(ia)); + ia.ia_tag = iba->iba_tag; + ia.ia_addr = addr; + ia.ia_size = 1; + ia.ia_name = name; + config_found(self, &ia, iicbus_print); + + name = "tps65090"; + addr = 0x48; + + memset(&ia, 0, sizeof(ia)); + ia.ia_tag = iba->iba_tag; + ia.ia_addr = addr; + ia.ia_size = 1; + ia.ia_name = name; + config_found(self, &ia, iicbus_print); +} + +void +exiic_setspeed(struct exiic_softc *sc, int speed) +{ + if (!sc->frequency) { + uint32_t freq, div = 0, pres = 16; + freq = exclock_get_i2cclk(); + + /* calculate prescaler and divisor values */ + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512; + + while ((freq / pres / (div + 1)) > speed) + div++; + + /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ + sc->frequency = (div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0); + } + + HWRITE4(sc, I2C_CON, sc->frequency); +} + +#if 0 +int +exiic_intr(void *arg) +{ + struct exiic_softc *sc = arg; + u_int16_t status; + int rc = 0; + + status = HREAD4(sc, I2C_CON); + + if (ISSET(status, I2C_CON_INTPENDING)) { + /* we do not acknowledge the interrupt here */ + rc = 1; + + sc->intr_status |= status; + wakeup(&sc->intr_status); + } + + return (rc); +} + +int +exiic_wait_intr(struct exiic_softc *sc, int mask, int timo) +{ + int status; + int s; + + s = splbio(); + + status = sc->intr_status & mask; + while (status == 0) { + if (tsleep(&sc->intr_status, PWAIT, "hcintr", timo) + == EWOULDBLOCK) { + break; + } + status = sc->intr_status & mask; + } + status = sc->intr_status & mask; + sc->intr_status &= ~status; + + splx(s); + return status; +} +#endif + +int +exiic_wait_state(struct exiic_softc *sc, uint32_t reg, uint32_t mask, uint32_t value) +{ + uint32_t state; + int timeout; + state = HREAD4(sc, reg); + for (timeout = 1000; timeout > 0; timeout--) { + if (((state = HREAD4(sc, reg)) & mask) == value) + return 0; + delay(1000); + } + return ETIMEDOUT; +} + +int +exiic_i2c_acquire_bus(void *cookie, int flags) +{ + struct exiic_softc *sc = cookie; + int ret = rw_enter(&sc->sc_buslock, RW_WRITE); + + if (!ret) { + /* set speed to 100 Kbps */ + exiic_setspeed(sc, 100); + + /* STOP */ + HWRITE4(sc, I2C_STAT, 0); + HWRITE4(sc, I2C_ADD, 0); + HWRITE4(sc, I2C_STAT, I2C_STAT_MODE_SEL_MASTER_TX + | I2C_STAT_SERIAL_OUTPUT); + } + + return ret; +} + +void +exiic_i2c_release_bus(void *cookie, int flags) +{ + struct exiic_softc *sc = cookie; + + (void) rw_exit(&sc->sc_buslock); +} + +int +exiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t _addr, + const void *cmdbuf, size_t cmdlen, void *databuf, size_t datalen, int flags) +{ + struct exiic_softc *sc = cookie; + uint32_t ret = 0; + u_int8_t addr = 0; + int i = 0; + + addr = (_addr & 0x7f) << 1; + + /* clock gating */ + //exccm_enable_i2c(sc->unit); + + if (exiic_wait_state(sc, I2C_STAT, I2C_STAT_BUSY_SIGNAL, 0)) { + printf("%s: busy\n", __func__); + return (EIO); + } + + /* acknowledge generation */ + HSET4(sc, I2C_CON, I2C_CON_ACK); + + /* Send the slave-address */ + HWRITE4(sc, I2C_DS, addr); + if (!I2C_OP_READ_P(op) || (cmdbuf && cmdlen)) + HWRITE4(sc, I2C_STAT, I2C_STAT_MODE_SEL_MASTER_TX + | I2C_STAT_SERIAL_OUTPUT + | I2C_STAT_BUSY_SIGNAL); + else + HWRITE4(sc, I2C_STAT, I2C_STAT_MODE_SEL_MASTER_RX + | I2C_STAT_SERIAL_OUTPUT + | I2C_STAT_BUSY_SIGNAL); + + ret = exiic_xfer_wait(sc); + if (ret != I2C_ACK) + goto fail; + + /* transmit commands */ + if (cmdbuf && cmdlen) { + for (i = 0; i < cmdlen; i++) { + HWRITE4(sc, I2C_DS, ((uint8_t *)cmdbuf)[i]); + exiic_xfer_start(sc); + ret = exiic_xfer_wait(sc); + if (ret != I2C_ACK) + goto fail; + } + } + + if (I2C_OP_READ_P(op)) { + if (cmdbuf && cmdlen) { + /* write slave chip address again for actual read */ + HWRITE4(sc, I2C_DS, addr); + + /* restart */ + HWRITE4(sc, I2C_STAT, I2C_STAT_MODE_SEL_MASTER_RX + | I2C_STAT_SERIAL_OUTPUT + | I2C_STAT_BUSY_SIGNAL); + exiic_xfer_start(sc); + ret = exiic_xfer_wait(sc); + if (ret != I2C_ACK) + goto fail; + } + + for (i = 0; i < datalen && ret == I2C_ACK; i++) { + /* disable ACK for final read */ + if (i == datalen - 1) + HCLR4(sc, I2C_CON, I2C_CON_ACK); + exiic_xfer_start(sc); + ret = exiic_xfer_wait(sc); + ((uint8_t *)databuf)[i] = HREAD4(sc, I2C_DS); + } + if (ret == I2C_NACK) + ret = I2C_ACK; /* Normal terminated read. */ + } else { + for (i = 0; i < datalen && ret == I2C_ACK; i++) { + HWRITE4(sc, I2C_DS, ((uint8_t *)databuf)[i]); + exiic_xfer_start(sc); + ret = exiic_xfer_wait(sc); + } + } + +fail: + /* send STOP */ + if (op & I2C_OP_READ_WITH_STOP) { + HWRITE4(sc, I2C_STAT, I2C_STAT_MODE_SEL_MASTER_RX + | I2C_STAT_SERIAL_OUTPUT); + exiic_xfer_start(sc); + } + + return ret; +} + +void +exiic_xfer_start(struct exiic_softc *sc) +{ + HCLR4(sc, I2C_CON, I2C_CON_INTPENDING); +} + +int +exiic_xfer_wait(struct exiic_softc *sc) +{ + if (!exiic_wait_state(sc, I2C_CON, I2C_CON_INTPENDING, + I2C_CON_INTPENDING)) + return (HREAD4(sc, I2C_STAT) & I2C_STAT_LAST_RVCD_BIT) ? + I2C_NACK : I2C_ACK; + else + return I2C_TIMEOUT; +} + +int +exiic_detach(struct device *self, int flags) +{ + struct exiic_softc *sc = (struct exiic_softc *)self; + + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); + return 0; +} diff --git a/sys/arch/armv7/exynos/exiicvar.h b/sys/arch/armv7/exynos/exiicvar.h new file mode 100644 index 00000000000..8a70257be22 --- /dev/null +++ b/sys/arch/armv7/exynos/exiicvar.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef EXIICVAR_H +#define EXIICVAR_H + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> +#include <sys/rwlock.h> + +#include <dev/i2c/i2cvar.h> + +#endif diff --git a/sys/arch/armv7/exynos/exmct.c b/sys/arch/armv7/exynos/exmct.c new file mode 100644 index 00000000000..37c71806fdc --- /dev/null +++ b/sys/arch/armv7/exynos/exmct.c @@ -0,0 +1,117 @@ +/* $OpenBSD: exmct.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> +#include <arm/cpufunc.h> +#include <machine/intr.h> +#include <machine/bus.h> +#include <machine/fdt.h> +#include <armv7/armv7/armv7var.h> + +/* registers */ +#define MCT_CTRL 0x240 +#define MCT_WRITE_STAT 0x24C + +/* bits and bytes */ +#define MCT_CTRL_START (1 << 8) + +struct exmct_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +struct exmct_softc *exmct_sc; + +int exmct_match(struct device *parent, void *v, void *aux); +void exmct_attach(struct device *parent, struct device *self, void *args); +void exmct_stop(void); +void exmct_reset(void); + +struct cfattach exmct_ca = { + sizeof (struct exmct_softc), NULL, exmct_attach +}; +struct cfattach exmct_fdt_ca = { + sizeof (struct exmct_softc), exmct_match, exmct_attach +}; + +struct cfdriver exmct_cd = { + NULL, "exmct", DV_DULL +}; + +int +exmct_match(struct device *parent, void *v, void *aux) +{ + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("samsung,exynos4210-mct", aa->aa_node)) + return 1; + + return 0; +} + +void +exmct_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct exmct_softc *sc = (struct exmct_softc *) self; + struct fdt_memory mem; + uint32_t i, mask, reg; + + sc->sc_iot = aa->aa_iot; + if (aa->aa_node) { + if (fdt_get_memory_address(aa->aa_node, 0, &mem)) + panic("%s: could not extract memory data from FDT", + __func__); + } else { + mem.addr = aa->aa_dev->mem[0].addr; + mem.size = aa->aa_dev->mem[0].size; + } + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + printf("\n"); + + exmct_sc = sc; + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MCT_CTRL, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, MCT_CTRL) | MCT_CTRL_START); + + mask = (1 << 16); + + /* Wait 10 times until written value is applied */ + for (i = 0; i < 10; i++) { + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MCT_WRITE_STAT); + if (reg & mask) { + bus_space_write_4(sc->sc_iot, sc->sc_ioh, + MCT_WRITE_STAT, mask); + return; + } + cpufunc_nullop(); + } + + /* NOTREACHED */ + + panic("%s: Can't enable timer!", __func__); +} diff --git a/sys/arch/armv7/exynos/expower.c b/sys/arch/armv7/exynos/expower.c new file mode 100644 index 00000000000..4bb4a2b7fd8 --- /dev/null +++ b/sys/arch/armv7/exynos/expower.c @@ -0,0 +1,114 @@ +/* $OpenBSD: expower.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/sysctl.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> +#include <machine/intr.h> +#include <machine/bus.h> +#include <machine/fdt.h> +#include <armv7/armv7/armv7var.h> +#include <armv7/exynos/expowervar.h> + +/* registers */ +#define POWER_PHY_CTRL 0x708 + +/* bits and bytes */ +#define POWER_PHY_CTRL_USB_HOST_EN (1 << 0) + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +struct expower_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +struct expower_softc *expower_sc; + +int expower_match(struct device *parent, void *v, void *aux); +void expower_attach(struct device *parent, struct device *self, void *args); + +struct cfattach expower_ca = { + sizeof (struct expower_softc), NULL, expower_attach +}; +struct cfattach expower_fdt_ca = { + sizeof (struct expower_softc), expower_match, expower_attach +}; + +struct cfdriver expower_cd = { + NULL, "expower", DV_DULL +}; + +int +expower_match(struct device *parent, void *v, void *aux) +{ + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("samsung,exynos5250-pmu", aa->aa_node)) + return 1; + + return 0; +} + +void +expower_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct expower_softc *sc = (struct expower_softc *) self; + struct fdt_memory mem; + + sc->sc_iot = aa->aa_iot; + if (aa->aa_node) { + if (fdt_get_memory_address(aa->aa_node, 0, &mem)) + panic("%s: could not extract memory data from FDT", + __func__); + } else { + mem.addr = aa->aa_dev->mem[0].addr; + mem.size = aa->aa_dev->mem[0].size; + } + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + printf("\n"); + + expower_sc = sc; +} + +void +expower_usbhost_phy_ctrl(int on) +{ + struct expower_softc *sc = expower_sc; + + if (on) + HSET4(sc, POWER_PHY_CTRL, POWER_PHY_CTRL_USB_HOST_EN); + else + HCLR4(sc, POWER_PHY_CTRL, POWER_PHY_CTRL_USB_HOST_EN); +} diff --git a/sys/arch/armv7/exynos/expowervar.h b/sys/arch/armv7/exynos/expowervar.h new file mode 100644 index 00000000000..14e41bc3d0d --- /dev/null +++ b/sys/arch/armv7/exynos/expowervar.h @@ -0,0 +1,23 @@ +/* $OpenBSD: expowervar.h,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef EXPOWERVAR_H +#define EXPOWERVAR_H + +void expower_usbhost_phy_ctrl(int); + +#endif /* EXPOWERVAR_H */ diff --git a/sys/arch/armv7/exynos/exsysreg.c b/sys/arch/armv7/exynos/exsysreg.c new file mode 100644 index 00000000000..169ecc0d69f --- /dev/null +++ b/sys/arch/armv7/exynos/exsysreg.c @@ -0,0 +1,114 @@ +/* $OpenBSD: exsysreg.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/sysctl.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> +#include <machine/intr.h> +#include <machine/bus.h> +#include <machine/fdt.h> +#include <armv7/armv7/armv7var.h> +#include <armv7/exynos/exsysregvar.h> + +/* registers */ +#define SYSREG_USB20PHY_CFG 0x230 + +/* bits and bytes */ +#define SYSREG_USB20PHY_CFG_HOST_LINK_EN (1 << 0) + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +struct exsysreg_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +struct exsysreg_softc *exsysreg_sc; + +int exsysreg_match(struct device *parent, void *v, void *aux); +void exsysreg_attach(struct device *parent, struct device *self, void *args); + +struct cfattach exsysreg_ca = { + sizeof (struct exsysreg_softc), NULL, exsysreg_attach +}; +struct cfattach exsysreg_fdt_ca = { + sizeof (struct exsysreg_softc), exsysreg_match, exsysreg_attach +}; + +struct cfdriver exsysreg_cd = { + NULL, "exsysreg", DV_DULL +}; + +int +exsysreg_match(struct device *parent, void *v, void *aux) +{ + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("samsung,exynos5-sysreg", aa->aa_node)) + return 1; + + return 0; +} + +void +exsysreg_attach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct exsysreg_softc *sc = (struct exsysreg_softc *) self; + struct fdt_memory mem; + + sc->sc_iot = aa->aa_iot; + if (aa->aa_node) { + if (fdt_get_memory_address(aa->aa_node, 0, &mem)) + panic("%s: could not extract memory data from FDT", + __func__); + } else { + mem.addr = aa->aa_dev->mem[0].addr; + mem.size = aa->aa_dev->mem[0].size; + } + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + printf("\n"); + + exsysreg_sc = sc; +} + +void +exsysreg_usbhost_mode(int on) +{ + struct exsysreg_softc *sc = exsysreg_sc; + + if (on) + HSET4(sc, SYSREG_USB20PHY_CFG, SYSREG_USB20PHY_CFG_HOST_LINK_EN); + else + HCLR4(sc, SYSREG_USB20PHY_CFG, SYSREG_USB20PHY_CFG_HOST_LINK_EN); +} diff --git a/sys/arch/armv7/exynos/exsysregvar.h b/sys/arch/armv7/exynos/exsysregvar.h new file mode 100644 index 00000000000..de4bbb0463e --- /dev/null +++ b/sys/arch/armv7/exynos/exsysregvar.h @@ -0,0 +1,23 @@ +/* $OpenBSD: exsysregvar.h,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef EXSYSREGVAR_H +#define EXSYSREGVAR_H + +void exsysreg_usbhost_mode(int); + +#endif /* EXSYSREGVAR_H */ diff --git a/sys/arch/armv7/exynos/exuart.c b/sys/arch/armv7/exynos/exuart.c new file mode 100644 index 00000000000..9e2c1149337 --- /dev/null +++ b/sys/arch/armv7/exynos/exuart.c @@ -0,0 +1,888 @@ +/* $OpenBSD: exuart.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2005 Dale Rahn <drahn@motorola.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/device.h> +#include <sys/syslog.h> +#include <sys/conf.h> +#include <sys/fcntl.h> +#include <sys/select.h> +#include <sys/kernel.h> + +#include <dev/cons.h> + + +#ifdef DDB +#include <ddb/db_var.h> +#endif + +#include <machine/bus.h> +#include <machine/fdt.h> +#include <armv7/exynos/exuartreg.h> +#include <armv7/exynos/exuartvar.h> +#include <armv7/armv7/armv7var.h> +#include <armv7/exynos/exclockvar.h> + +#define DEVUNIT(x) (minor(x) & 0x7f) +#define DEVCUA(x) (minor(x) & 0x80) + +struct exuart_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + struct soft_intrhand *sc_si; + void *sc_irq; + struct tty *sc_tty; + struct timeout sc_diag_tmo; + struct timeout sc_dtr_tmo; + int sc_fifo; + int sc_overflows; + int sc_floods; + int sc_errors; + int sc_halt; + u_int32_t sc_ulcon; + u_int32_t sc_ucon; + u_int32_t sc_ufcon; + u_int32_t sc_umcon; + u_int8_t sc_hwflags; +#define COM_HW_NOIEN 0x01 +#define COM_HW_FIFO 0x02 +#define COM_HW_SIR 0x20 +#define COM_HW_CONSOLE 0x40 +#define COM_HW_KGDB 0x80 + u_int8_t sc_swflags; +#define COM_SW_SOFTCAR 0x01 +#define COM_SW_CLOCAL 0x02 +#define COM_SW_CRTSCTS 0x04 +#define COM_SW_MDMBUF 0x08 +#define COM_SW_PPS 0x10 + + u_int8_t sc_initialize; + u_int8_t sc_cua; + u_int16_t *sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend; +#define EXUART_IBUFSIZE 128 +#define EXUART_IHIGHWATER 100 + u_int16_t sc_ibufs[2][EXUART_IBUFSIZE]; +}; + + +int exuartprobe(struct device *parent, void *self, void *aux); +int exuartprobe_fdt(struct device *parent, void *self, void *aux); +void exuartattach(struct device *parent, struct device *self, void *aux); + +void exuartcnprobe(struct consdev *cp); +void exuartcnprobe(struct consdev *cp); +void exuartcninit(struct consdev *cp); +int exuartcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, + tcflag_t cflag); +int exuartcngetc(dev_t dev); +void exuartcnputc(dev_t dev, int c); +void exuartcnpollc(dev_t dev, int on); +int exuart_param(struct tty *tp, struct termios *t); +void exuart_start(struct tty *); +void exuart_pwroff(struct exuart_softc *sc); +void exuart_diag(void *arg); +void exuart_raisedtr(void *arg); +void exuart_softint(void *arg); +struct exuart_softc *exuart_sc(dev_t dev); + +int exuart_intr(void *); + + +/* XXX - we imitate 'com' serial ports and take over their entry points */ +/* XXX: These belong elsewhere */ +cdev_decl(exuart); + +struct cfdriver exuart_cd = { + NULL, "exuart", DV_TTY +}; + +struct cfattach exuart_ca = { + sizeof(struct exuart_softc), exuartprobe, exuartattach +}; +struct cfattach exuart_fdt_ca = { + sizeof(struct exuart_softc), exuartprobe_fdt, exuartattach +}; + +bus_space_tag_t exuartconsiot; +bus_space_handle_t exuartconsioh; +bus_addr_t exuartconsaddr; +tcflag_t exuartconscflag = TTYDEF_CFLAG; +int exuartdefaultrate = B115200; + +int +exuartprobe(struct device *parent, void *self, void *aux) +{ + return 1; +} + +int +exuartprobe_fdt(struct device *parent, void *self, void *aux) +{ + struct armv7_attach_args *aa = aux; + + if (fdt_node_compatible("samsung,exynos4210-uart", aa->aa_node)) + return 1; + + return 0; +} + +struct cdevsw exuartdev = + cdev_tty_init(3/*XXX NEXUART */ ,exuart); /* 12: serial port */ + +void +exuartattach(struct device *parent, struct device *self, void *args) +{ + struct armv7_attach_args *aa = args; + struct exuart_softc *sc = (struct exuart_softc *) self; + struct fdt_memory mem; + int irq; + + sc->sc_iot = aa->aa_iot; + if (aa->aa_node) { + uint32_t ints[3]; + + if (fdt_get_memory_address(aa->aa_node, 0, &mem)) + panic("%s: could not extract memory data from FDT", + __func__); + + /* TODO: Add interrupt FDT API. */ + if (fdt_node_property_ints(aa->aa_node, "interrupts", + ints, 3) != 3) + panic("%s: could not extract interrupt data from FDT", + __func__); + + irq = ints[1]; + } else { + irq = aa->aa_dev->irq[0]; + mem.addr = aa->aa_dev->mem[0].addr; + mem.size = aa->aa_dev->mem[0].size; + } + + sc->sc_irq = arm_intr_establish(irq, IPL_TTY, + exuart_intr, sc, sc->sc_dev.dv_xname); + if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh)) + panic("%s: bus_space_map failed!", __func__); + + if (mem.addr == exuartconsaddr) + printf(" console"); + + timeout_set(&sc->sc_diag_tmo, exuart_diag, sc); + timeout_set(&sc->sc_dtr_tmo, exuart_raisedtr, sc); + sc->sc_si = softintr_establish(IPL_TTY, exuart_softint, sc); + + if(sc->sc_si == NULL) + panic("%s: can't establish soft interrupt.", + sc->sc_dev.dv_xname); + + printf("\n"); +} + +int +exuart_intr(void *arg) +{ +#if 0 + struct exuart_softc *sc = arg; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + struct tty *tp = sc->sc_tty; + u_int32_t reg; + u_int16_t *p; + u_int16_t c; + + sr1 = bus_space_read_2(iot, ioh, EXUART_USR1); + if (ISSET(sr1, EXUART_SR1_TRDY) && ISSET(tp->t_state, TS_BUSY)) { + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + if (sc->sc_halt > 0) + wakeup(&tp->t_outq); + (*linesw[tp->t_line].l_start)(tp); + } + + if (sc->sc_tty == NULL) + return(0); + + if(!ISSET(bus_space_read_2(iot, ioh, EXUART_USR2), EXUART_SR2_RDR)) + return 0; + + p = sc->sc_ibufp; + + while(ISSET(bus_space_read_2(iot, ioh, EXUART_USR2), EXUART_SR2_RDR)) { + c = bus_space_read_1(iot, ioh, EXUART_URXH); + if (p >= sc->sc_ibufend) { + sc->sc_floods++; + if (sc->sc_errors++ == 0) + timeout_add(&sc->sc_diag_tmo, 60 * hz); + } else { + *p++ = c; + if (p == sc->sc_ibufhigh && ISSET(tp->t_cflag, CRTSCTS)) + /* XXX */ + //CLR(sc->sc_ucr3, EXUART_CR3_DSR); + //bus_space_write_2(iot, ioh, EXUART_UCR3, + // sc->sc_ucr3); + + } + /* XXX - msr stuff ? */ + } + sc->sc_ibufp = p; + + softintr_schedule(sc->sc_si); +#endif + + return 1; +} + +int +exuart_param(struct tty *tp, struct termios *t) +{ + struct exuart_softc *sc = exuart_cd.cd_devs[DEVUNIT(tp->t_dev)]; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int ospeed = t->c_ospeed; + int error; + tcflag_t oldcflag; + + + if (t->c_ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) + return EINVAL; + + switch (ISSET(t->c_cflag, CSIZE)) { + case CS5: + CLR(sc->sc_ulcon, EXUART_ULCON_WORD_MASK); + SET(sc->sc_ulcon, EXUART_ULCON_WORD_FIVE); + break; + case CS6: + CLR(sc->sc_ulcon, EXUART_ULCON_WORD_MASK); + SET(sc->sc_ulcon, EXUART_ULCON_WORD_SIX); + break; + case CS7: + CLR(sc->sc_ulcon, EXUART_ULCON_WORD_MASK); + SET(sc->sc_ulcon, EXUART_ULCON_WORD_SEVEN); + break; + case CS8: + CLR(sc->sc_ulcon, EXUART_ULCON_WORD_MASK); + SET(sc->sc_ulcon, EXUART_ULCON_WORD_EIGHT); + break; + } + + CLR(sc->sc_ulcon, EXUART_ULCON_PARITY_MASK); + if (ISSET(t->c_cflag, PARENB)) { + if (ISSET(t->c_cflag, PARODD)) + SET(sc->sc_ulcon, EXUART_ULCON_PARITY_ODD); + else + SET(sc->sc_ulcon, EXUART_ULCON_PARITY_EVEN); + } + + if (ISSET(t->c_cflag, CSTOPB)) + SET(sc->sc_ulcon, EXUART_ULCON_STOP_TWO); + else + CLR(sc->sc_ulcon, EXUART_ULCON_STOP_ONE); + + bus_space_write_4(iot, ioh, EXUART_ULCON, sc->sc_ulcon); + + if (ospeed == 0) { + /* lower dtr */ + } + + if (ospeed != 0) { + while (ISSET(tp->t_state, TS_BUSY)) { + ++sc->sc_halt; + error = ttysleep(tp, &tp->t_outq, + TTOPRI | PCATCH, "exuartprm", 0); + --sc->sc_halt; + if (error) { + exuart_start(tp); + return (error); + } + } + /* set speed */ + } + + /* setup fifo */ + + /* When not using CRTSCTS, RTS follows DTR. */ + /* sc->sc_dtr = MCR_DTR; */ + + + /* and copy to tty */ + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + oldcflag = tp->t_cflag; + tp->t_cflag = t->c_cflag; + + /* + * If DCD is off and MDMBUF is changed, ask the tty layer if we should + * stop the device. + */ + /* XXX */ + + exuart_start(tp); + + return 0; +} + +void +exuart_start(struct tty *tp) +{ + struct exuart_softc *sc = exuart_cd.cd_devs[DEVUNIT(tp->t_dev)]; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + int s; + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY)) { + splx(s); + return; + } + if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP)) + goto stopped; +#ifdef DAMNFUCKSHIT + /* clear to send (IE the RTS pin on this shit) is not directly \ + * readable - skip check for now + */ + if (ISSET(tp->t_cflag, CRTSCTS) && !ISSET(sc->sc_msr, EXUART_CTS)) + goto stopped; +#endif + if (tp->t_outq.c_cc <= tp->t_lowat) { + if (ISSET(tp->t_state, TS_ASLEEP)) { + CLR(tp->t_state, TS_ASLEEP); + wakeup(&tp->t_outq); + } + if (tp->t_outq.c_cc == 0) + goto stopped; + selwakeup(&tp->t_wsel); + } + SET(tp->t_state, TS_BUSY); + +#if 0 + if (!ISSET(sc->sc_ucr1, EXUART_CR1_TXMPTYEN)) { + SET(sc->sc_ucr1, EXUART_CR1_TXMPTYEN); + bus_space_write_2(iot, ioh, EXUART_UCR1, sc->sc_ucr1); + } +#endif + + { + u_char buf[32]; + int n = q_to_b(&tp->t_outq, buf, 32/*XXX*/); + int i; + for (i = 0; i < n; i++) + bus_space_write_1(iot, ioh, EXUART_UTXH, buf[i]); + } + splx(s); + return; +stopped: +#if 0 + if (ISSET(sc->sc_ucr1, )) { + CLR(sc->sc_ucr1, EXUART_CR1_TXMPTYEN); + bus_space_write_2(iot, ioh, EXUART_UCR1, sc->sc_ucr1); + } +#endif + splx(s); +} + +void +exuart_pwroff(struct exuart_softc *sc) +{ +} + +void +exuart_diag(void *arg) +{ + struct exuart_softc *sc = arg; + int overflows, floods; + int s; + + s = spltty(); + sc->sc_errors = 0; + overflows = sc->sc_overflows; + sc->sc_overflows = 0; + floods = sc->sc_floods; + sc->sc_floods = 0; + splx(s); + log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf overflow%s\n", + sc->sc_dev.dv_xname, + overflows, overflows == 1 ? "" : "s", + floods, floods == 1 ? "" : "s"); +} + +void +exuart_raisedtr(void *arg) +{ + //struct exuart_softc *sc = arg; + + //SET(sc->sc_ucr3, EXUART_CR3_DSR); /* XXX */ + //bus_space_write_2(sc->sc_iot, sc->sc_ioh, EXUART_UCR3, sc->sc_ucr3); +} + +void +exuart_softint(void *arg) +{ + struct exuart_softc *sc = arg; + struct tty *tp; + u_int16_t *ibufp; + u_int16_t *ibufend; + int c; + int err; + int s; + + if (sc == NULL || sc->sc_ibufp == sc->sc_ibuf) + return; + + tp = sc->sc_tty; + s = spltty(); + + ibufp = sc->sc_ibuf; + ibufend = sc->sc_ibufp; + + if (ibufp == ibufend || tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) { + splx(s); + return; + } + + sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ? + sc->sc_ibufs[1] : sc->sc_ibufs[0]; + sc->sc_ibufhigh = sc->sc_ibuf + EXUART_IHIGHWATER; + sc->sc_ibufend = sc->sc_ibuf + EXUART_IBUFSIZE; + +#if 0 + if (ISSET(tp->t_cflag, CRTSCTS) && + !ISSET(sc->sc_ucr3, EXUART_CR3_DSR)) { + /* XXX */ + SET(sc->sc_ucr3, EXUART_CR3_DSR); + bus_space_write_2(sc->sc_iot, sc->sc_ioh, EXUART_UCR3, + sc->sc_ucr3); + } +#endif + + splx(s); + + while (ibufp < ibufend) { + c = *ibufp++; + if (ISSET(c, EXUART_UERSTAT_OVERRUN)) { + sc->sc_overflows++; + if (sc->sc_errors++ == 0) + timeout_add(&sc->sc_diag_tmo, 60 * hz); + } + /* This is ugly, but fast. */ + + err = 0; + if (ISSET(c, EXUART_UERSTAT_PARITY)) + err |= TTY_PE; + if (ISSET(c, EXUART_UERSTAT_FRAME)) + err |= TTY_FE; + c = (c & 0xff) | err; + (*linesw[tp->t_line].l_rint)(c, tp); + } +} + +int +exuartopen(dev_t dev, int flag, int mode, struct proc *p) +{ + int unit = DEVUNIT(dev); + struct exuart_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; + struct tty *tp; + int s; + int error = 0; + + if (unit >= exuart_cd.cd_ndevs) + return ENXIO; + sc = exuart_cd.cd_devs[unit]; + if (sc == NULL) + return ENXIO; + + s = spltty(); + if (sc->sc_tty == NULL) + tp = sc->sc_tty = ttymalloc(0); + else + tp = sc->sc_tty; + + splx(s); + + tp->t_oproc = exuart_start; + tp->t_param = exuart_param; + tp->t_dev = dev; + + if (!ISSET(tp->t_state, TS_ISOPEN)) { + SET(tp->t_state, TS_WOPEN); + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + tp->t_cflag = exuartconscflag; + else + tp->t_cflag = TTYDEF_CFLAG; + if (ISSET(sc->sc_swflags, COM_SW_CLOCAL)) + SET(tp->t_cflag, CLOCAL); + if (ISSET(sc->sc_swflags, COM_SW_CRTSCTS)) + SET(tp->t_cflag, CRTSCTS); + if (ISSET(sc->sc_swflags, COM_SW_MDMBUF)) + SET(tp->t_cflag, MDMBUF); + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = exuartdefaultrate; + + s = spltty(); + + sc->sc_initialize = 1; + exuart_param(tp, &tp->t_termios); + ttsetwater(tp); + sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0]; + sc->sc_ibufhigh = sc->sc_ibuf + EXUART_IHIGHWATER; + sc->sc_ibufend = sc->sc_ibuf + EXUART_IBUFSIZE; + + iot = sc->sc_iot; + ioh = sc->sc_ioh; + + sc->sc_ulcon = bus_space_read_4(iot, ioh, EXUART_ULCON); + sc->sc_ucon = bus_space_read_4(iot, ioh, EXUART_UCON); + sc->sc_ufcon = bus_space_read_4(iot, ioh, EXUART_UFCON); + sc->sc_umcon = bus_space_read_4(iot, ioh, EXUART_UMCON); + +#if 0 + /* interrupt after one char on tx/rx */ + /* reference frequency divider: 1 */ + bus_space_write_2(iot, ioh, EXUART_UFCR, + 1 << EXUART_FCR_TXTL_SH | + 5 << EXUART_FCR_RFDIV_SH | + 1 << EXUART_FCR_RXTL_SH); + + bus_space_write_2(iot, ioh, EXUART_UBIR, + (exuartdefaultrate / 100) - 1); + + /* formula: clk / (rfdiv * 1600) */ + bus_space_write_2(iot, ioh, EXUART_UBMR, + (exccm_get_uartclk() * 1000) / 1600); + + SET(sc->sc_ucr1, EXUART_CR1_EN|EXUART_CR1_RRDYEN); + SET(sc->sc_ucr2, EXUART_CR2_TXEN|EXUART_CR2_RXEN); + bus_space_write_2(iot, ioh, EXUART_UCR1, sc->sc_ucr1); + bus_space_write_2(iot, ioh, EXUART_UCR2, sc->sc_ucr2); + + /* sc->sc_mcr = MCR_DTR | MCR_RTS; XXX */ + SET(sc->sc_ucr3, EXUART_CR3_DSR); /* XXX */ + bus_space_write_2(iot, ioh, EXUART_UCR3, sc->sc_ucr3); +#endif + + SET(tp->t_state, TS_CARR_ON); /* XXX */ + + + } else if (ISSET(tp->t_state, TS_XCLUDE) && p->p_ucred->cr_uid != 0) + return EBUSY; + else + s = spltty(); + + if (DEVCUA(dev)) { + if (ISSET(tp->t_state, TS_ISOPEN)) { + splx(s); + return EBUSY; + } + sc->sc_cua = 1; + } else { + /* tty (not cua) device; wait for carrier if necessary */ + if (ISSET(flag, O_NONBLOCK)) { + if (sc->sc_cua) { + /* Opening TTY non-blocking... but the CUA is busy */ + splx(s); + return EBUSY; + } + } else { + while (sc->sc_cua || + (!ISSET(tp->t_cflag, CLOCAL) && + !ISSET(tp->t_state, TS_CARR_ON))) { + SET(tp->t_state, TS_WOPEN); + error = ttysleep(tp, &tp->t_rawq, + TTIPRI | PCATCH, ttopen, 0); + /* + * If TS_WOPEN has been reset, that means the + * cua device has been closed. We don't want + * to fail in that case, + * so just go around again. + */ + if (error && ISSET(tp->t_state, TS_WOPEN)) { + CLR(tp->t_state, TS_WOPEN); + if (!sc->sc_cua && !ISSET(tp->t_state, + TS_ISOPEN)) + exuart_pwroff(sc); + splx(s); + return error; + } + } + } + } + splx(s); + return (*linesw[tp->t_line].l_open)(dev,tp,p); +} + +int +exuartclose(dev_t dev, int flag, int mode, struct proc *p) +{ + int unit = DEVUNIT(dev); + struct exuart_softc *sc = exuart_cd.cd_devs[unit]; + //bus_space_tag_t iot = sc->sc_iot; + //bus_space_handle_t ioh = sc->sc_ioh; + struct tty *tp = sc->sc_tty; + int s; + + /* XXX This is for cons.c. */ + if (!ISSET(tp->t_state, TS_ISOPEN)) + return 0; + + (*linesw[tp->t_line].l_close)(tp, flag, p); + s = spltty(); + if (ISSET(tp->t_state, TS_WOPEN)) { + /* tty device is waiting for carrier; drop dtr then re-raise */ + //CLR(sc->sc_ucr3, EXUART_CR3_DSR); + //bus_space_write_2(iot, ioh, EXUART_UCR3, sc->sc_ucr3); + timeout_add(&sc->sc_dtr_tmo, hz * 2); + } else { + /* no one else waiting; turn off the uart */ + exuart_pwroff(sc); + } + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + + sc->sc_cua = 0; + splx(s); + ttyclose(tp); + + return 0; +} + +int +exuartread(dev_t dev, struct uio *uio, int flag) +{ + struct tty *tty; + + tty = exuarttty(dev); + if (tty == NULL) + return ENODEV; + + return((*linesw[tty->t_line].l_read)(tty, uio, flag)); +} + +int +exuartwrite(dev_t dev, struct uio *uio, int flag) +{ + struct tty *tty; + + tty = exuarttty(dev); + if (tty == NULL) + return ENODEV; + + return((*linesw[tty->t_line].l_write)(tty, uio, flag)); +} + +int +exuartioctl( dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct exuart_softc *sc; + struct tty *tp; + int error; + + sc = exuart_sc(dev); + if (sc == NULL) + return (ENODEV); + + tp = sc->sc_tty; + if (tp == NULL) + return (ENXIO); + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + switch(cmd) { + case TIOCSBRK: + /* */ + break; + + case TIOCCBRK: + /* */ + break; + + case TIOCSDTR: +#if 0 + (void) clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIS); +#endif + break; + + case TIOCCDTR: +#if 0 + (void) clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIC); +#endif + break; + + case TIOCMSET: +#if 0 + (void) clmctl(dev, *(int *) data, DMSET); +#endif + break; + + case TIOCMBIS: +#if 0 + (void) clmctl(dev, *(int *) data, DMBIS); +#endif + break; + + case TIOCMBIC: +#if 0 + (void) clmctl(dev, *(int *) data, DMBIC); +#endif + break; + + case TIOCMGET: +#if 0 + *(int *)data = clmctl(dev, 0, DMGET); +#endif + break; + + case TIOCGFLAGS: +#if 0 + *(int *)data = cl->cl_swflags; +#endif + break; + + case TIOCSFLAGS: + error = suser(p, 0); + if (error != 0) + return(EPERM); + +#if 0 + cl->cl_swflags = *(int *)data; + cl->cl_swflags &= /* only allow valid flags */ + (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS); +#endif + break; + default: + return (ENOTTY); + } + + return 0; +} + +int +exuartstop(struct tty *tp, int flag) +{ + return 0; +} + +struct tty * +exuarttty(dev_t dev) +{ + int unit; + struct exuart_softc *sc; + unit = DEVUNIT(dev); + if (unit >= exuart_cd.cd_ndevs) + return NULL; + sc = (struct exuart_softc *)exuart_cd.cd_devs[unit]; + if (sc == NULL) + return NULL; + return sc->sc_tty; +} + +struct exuart_softc * +exuart_sc(dev_t dev) +{ + int unit; + struct exuart_softc *sc; + unit = DEVUNIT(dev); + if (unit >= exuart_cd.cd_ndevs) + return NULL; + sc = (struct exuart_softc *)exuart_cd.cd_devs[unit]; + return sc; +} + + +/* serial console */ +void +exuartcnprobe(struct consdev *cp) +{ + cp->cn_dev = makedev(12 /* XXX */, 0); + cp->cn_pri = CN_MIDPRI; +} + +void +exuartcninit(struct consdev *cp) +{ +} + +int +exuartcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, tcflag_t cflag) +{ + static struct consdev exuartcons = { + NULL, NULL, exuartcngetc, exuartcnputc, exuartcnpollc, NULL, + NODEV, CN_MIDPRI + }; + + if (bus_space_map(iot, iobase, 0x100, 0, &exuartconsioh)) + return ENOMEM; + + cn_tab = &exuartcons; + cn_tab->cn_dev = makedev(12 /* XXX */, 0); + cdevsw[12] = exuartdev; /* KLUDGE */ + + exuartconsiot = iot; + exuartconsaddr = iobase; + exuartconscflag = cflag; + + return 0; +} + +int +exuartcngetc(dev_t dev) +{ + int c; + int s; + s = splhigh(); + while((bus_space_read_4(exuartconsiot, exuartconsioh, EXUART_UTRSTAT) & + EXUART_UTRSTAT_RXBREADY) == 0 && + (bus_space_read_4(exuartconsiot, exuartconsioh, EXUART_UFSTAT) & + (EXUART_UFSTAT_RX_FIFO_CNT_MASK|EXUART_UFSTAT_RX_FIFO_FULL)) == 0) + ; + c = bus_space_read_1(exuartconsiot, exuartconsioh, EXUART_URXH); + splx(s); + return c; +} + +void +exuartcnputc(dev_t dev, int c) +{ + int s; + s = splhigh(); + bus_space_write_1(exuartconsiot, exuartconsioh, EXUART_UTXH, (uint8_t)c); + while((bus_space_read_2(exuartconsiot, exuartconsioh, EXUART_UTRSTAT) & + EXUART_UTRSTAT_TXBEMPTY) != 0 && + (bus_space_read_2(exuartconsiot, exuartconsioh, EXUART_UFSTAT) & + (EXUART_UFSTAT_TX_FIFO_CNT_MASK|EXUART_UFSTAT_TX_FIFO_FULL)) != 0) + ; + splx(s); +} + +void +exuartcnpollc(dev_t dev, int on) +{ +} diff --git a/sys/arch/armv7/exynos/exuartreg.h b/sys/arch/armv7/exynos/exuartreg.h new file mode 100644 index 00000000000..37948af47db --- /dev/null +++ b/sys/arch/armv7/exynos/exuartreg.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define EXUART_ULCON 0x00 +#define EXUART_ULCON_WORD_FIVE (0x0 << 0) +#define EXUART_ULCON_WORD_SIX (0x1 << 0) +#define EXUART_ULCON_WORD_SEVEN (0x2 << 0) +#define EXUART_ULCON_WORD_EIGHT (0x3 << 0) +#define EXUART_ULCON_WORD_MASK (0x3 << 0) +#define EXUART_ULCON_STOP_ONE (0x0 << 2) +#define EXUART_ULCON_STOP_TWO (0x1 << 2) +#define EXUART_ULCON_PARITY_NONE (0x0 << 3) +#define EXUART_ULCON_PARITY_ODD (0x4 << 3) +#define EXUART_ULCON_PARITY_EVEN (0x5 << 3) +#define EXUART_ULCON_PARITY_FORCED1 (0x6 << 3) +#define EXUART_ULCON_PARITY_FORCED0 (0x7 << 3) +#define EXUART_ULCON_PARITY_MASK (0x7 << 3) +#define EXUART_ULCON_INFRARED (0x1 << 6) +#define EXUART_UCON 0x04 +#define EXUART_UCON_RX_IRQORPOLL (0x1 << 0) +#define EXUART_UCON_RX_DMA (0x2 << 0) +#define EXUART_UCON_TX_IRQORPOLL (0x1 << 2) +#define EXUART_UCON_TX_DMA (0x2 << 2) +#define EXUART_UCON_SEND_BREAK (0x1 << 4) +#define EXUART_UCON_LOOPBACK_MODE (0x1 << 5) +#define EXUART_UCON_RX_ERR_STS_INT (0x1 << 6) +#define EXUART_UCON_RX_TIMEOUT (0x1 << 7) +#define EXUART_UCON_RX_INT_TYPE_PULSE (0x0 << 8) +#define EXUART_UCON_RX_INT_TYPE_LEVEL (0x1 << 8) +#define EXUART_UCON_TX_INT_TYPE_PULSE (0x0 << 9) +#define EXUART_UCON_TX_INT_TYPE_LEVEL (0x1 << 9) +#define EXUART_UCON_RX_TIMEOUT_DMA_SUSPEND (0x1 << 10) +#define EXUART_UCON_RX_TIMEOUT_EMPTY_FIFO (0x1 << 11) +#define EXUART_UCON_RX_TIMEOUT_INTERVAL(x) (((x) & 0xf) << 12) /* 8 * (x + a) frame time */ +#define EXUART_UCON_RX_DMA_BURST_1B (0x0 << 16) +#define EXUART_UCON_RX_DMA_BURST_4B (0x1 << 16) +#define EXUART_UCON_RX_DMA_BURST_8B (0x2 << 16) +#define EXUART_UCON_RX_DMA_BURST_16B (0x3 << 16) +#define EXUART_UCON_TX_DMA_BURST_1B (0x0 << 20) +#define EXUART_UCON_TX_DMA_BURST_4B (0x1 << 20) +#define EXUART_UCON_TX_DMA_BURST_8B (0x2 << 20) +#define EXUART_UCON_TX_DMA_BURST_16B (0x3 << 20) +#define EXUART_UFCON 0x08 +#define EXUART_UFCON_FIFO_ENABLE (0x1 << 0) +#define EXUART_UFCON_RX_FIFO_RESET (0x1 << 1) +#define EXUART_UFCON_TX_FIFO_RESET (0x1 << 2) +#define EXUART_UFCON_RX_FIFO_TRIGGER_LEVEL (((x) & 0x7) << 4) +#define EXUART_UFCON_TX_FIFO_TRIGGER_LEVEL (((x) & 0x7) << 8) +#define EXUART_UMCON 0x0c +#define EXUART_UMCON_RTS (0x1 << 0) +#define EXUART_UMCON_MODEM_INT_EN (0x1 << 3) +#define EXUART_UMCON_AUTO_FLOW_CONTROL (0x1 << 4) +#define EXUART_UMCON_RTS_TRIGGER_LEVEL (((x) & 0x7) << 5) +#define EXUART_UTRSTAT 0x10 +#define EXUART_UTRSTAT_RXBREADY (0x1 << 0) +#define EXUART_UTRSTAT_TXBEMPTY (0x1 << 1) +#define EXUART_UTRSTAT_TXEMPTY (0x1 << 2) +#define EXUART_UTRSTAT_RX_TIMEOUT_STSCLR (0x1 << 3) +#define EXUART_UTRSTAT_RX_DMA_FSM_STS(x) (((x) >> 8) & 0xf) +#define EXUART_UTRSTAT_TX_DMA_FSM_STS(x) (((x) >> 12) & 0xf) +#define EXUART_UTRSTAT_RX_FIFO_CNT_TIMEOUT(x) (((x) >> 16) & 0xff) +#define EXUART_UERSTAT 0x14 +#define EXUART_UERSTAT_OVERRUN (0x1 << 0) +#define EXUART_UERSTAT_PARITY (0x1 << 1) +#define EXUART_UERSTAT_FRAME (0x1 << 2) +#define EXUART_UERSTAT_BREAK (0x1 << 3) +#define EXUART_UFSTAT 0x18 +#define EXUART_UFSTAT_RX_FIFO_CNT(x) (((x) >> 0) & 0xff) /* 0 when full */ +#define EXUART_UFSTAT_RX_FIFO_CNT_MASK (0xff << 0) /* 0 when full */ +#define EXUART_UFSTAT_RX_FIFO_FULL (0x1 << 8) +#define EXUART_UFSTAT_RX_FIFO_ERROR (0x1 << 9) +#define EXUART_UFSTAT_TX_FIFO_CNT(x) (((x) >> 16) & 0xff) /* 0 when full */ +#define EXUART_UFSTAT_TX_FIFO_CNT_MASK (0xff << 16) /* 0 when full */ +#define EXUART_UFSTAT_TX_FIFO_FULL (0x1 << 24) +#define EXUART_UMSTAT 0x1c +#define EXUART_UMSTAT_CTS (0x1 << 0) +#define EXUART_UMSTAT_DELTA_CTS (0x1 << 4) +#define EXUART_UTXH 0x20 +#define EXUART_URXH 0x24 +#define EXUART_UBRDIV 0x28 +#define EXUART_UBRDIV_SEL(x) (((x) & 0xffff) << 0) +#define EXUART_UFRACVAL 0x2c +#define EXUART_UFRACVAL_SEL(x) (((x) & 0xf) << 0) +#define EXUART_UINTP 0x30 +#define EXUART_UINTP_RXD (0x1 << 0) +#define EXUART_UINTP_ERROR (0x1 << 1) +#define EXUART_UINTP_TXD (0x1 << 2) +#define EXUART_UINTP_MODEM (0x1 << 3) +#define EXUART_UINTS 0x34 +#define EXUART_UINTS_RXD (0x1 << 0) +#define EXUART_UINTS_ERROR (0x1 << 1) +#define EXUART_UINTS_TXD (0x1 << 2) +#define EXUART_UINTS_MODEM (0x1 << 3) +#define EXUART_UINTM 0x38 +#define EXUART_UINTM_RXD (0x1 << 0) +#define EXUART_UINTM_ERROR (0x1 << 1) +#define EXUART_UINTM_TXD (0x1 << 2) +#define EXUART_UINTM_MODEM (0x1 << 3) diff --git a/sys/arch/armv7/exynos/exuartvar.h b/sys/arch/armv7/exynos/exuartvar.h new file mode 100644 index 00000000000..f331f6f4802 --- /dev/null +++ b/sys/arch/armv7/exynos/exuartvar.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2005 Dale Rahn <drahn@motorola.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +int exuartcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, + tcflag_t cflag); diff --git a/sys/arch/armv7/exynos/exynos.c b/sys/arch/armv7/exynos/exynos.c new file mode 100644 index 00000000000..dc370633cc8 --- /dev/null +++ b/sys/arch/armv7/exynos/exynos.c @@ -0,0 +1,71 @@ +/* $OpenBSD: exynos.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2005,2008 Dale Rahn <drahn@openbsd.com> + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> + +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <armv7/armv7/armv7var.h> + +static int +exynos_match(struct device *parent, void *cfdata, void *aux) +{ + /* If we're running with fdt, do not attach. */ + /* XXX: Find a better way. */ + if (fdt_next_node(0)) + return (0); + + switch (board_id) + { + case BOARD_ID_EXYNOS5_CHROMEBOOK: + return (1); + } + + return (0); +} + +struct cfattach exynos_ca = { + sizeof(struct armv7_softc), exynos_match, armv7_attach, NULL, + config_activate_children +}; + +struct cfdriver exynos_cd = { + NULL, "exynos", DV_DULL +}; + +struct board_dev chromebook_devs[] = { + { "exmct", 0 }, + { "exdog", 0 }, + { "exclock", 0 }, + { "expower", 0 }, + { "exsysreg", 0 }, +// { "exuart", 1 }, + { "exgpio", 0 }, + { "exgpio", 1 }, + { "exgpio", 2 }, + { "exgpio", 3 }, + { "exgpio", 4 }, + { "exgpio", 5 }, + { "exehci", 0 }, + { "exiic", 4 }, +// { "exesdhc", 2 }, +// { "exesdhc", 3 }, + { NULL, 0 } +}; diff --git a/sys/arch/armv7/exynos/exynos5.c b/sys/arch/armv7/exynos/exynos5.c new file mode 100644 index 00000000000..261b33bec5b --- /dev/null +++ b/sys/arch/armv7/exynos/exynos5.c @@ -0,0 +1,324 @@ +/* $OpenBSD: exynos5.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2011 Uwe Stuehler <uwe@openbsd.org> + * Copyright (c) 2012 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <machine/bus.h> +#include <arch/arm/armv7/armv7var.h> + +#include <armv7/armv7/armv7var.h> + +/* XXX ??? */ +/* IRQs are defined without the 32 cpu IRQs */ + +#define MCT_ADDR 0x101c0000 +#define MCT_SIZE 0x1000 + +#define WD_ADDR 0x101d0000 +#define WD_SIZE 0x400 + +#define CLOCK_ADDR 0x10010000 +#define CLOCK_SIZE 0x5000 + +#define POWER_ADDR 0x10040000 +#define POWER_SIZE 0x1000 + +#define SYSREG_ADDR 0x10050000 +#define SYSREG_SIZE 0x1000 + +#define UARTx_SIZE 0x100 +#define UART1_ADDR 0x12c00000 +#define UART2_ADDR 0x12c10000 +#define UART3_ADDR 0x12c20000 +#define UART4_ADDR 0x12c30000 + +#define UART1_IRQ 83 +#define UART2_IRQ 84 +#define UART3_IRQ 85 +#define UART4_IRQ 86 + +#define USB_EHCI_ADDR 0x12110000 +#define USB_PHY_ADDR 0x12130000 +#define USBx_SIZE 0x1000 + +#define USB_IRQ 71 + +#define GPIO1_ADDR 0x11400000 +#define GPIO1_SIZE 0x280 +#define GPIO2_ADDR 0x11400C00 +#define GPIO2_SIZE 0x80 +#define GPIO3_ADDR 0x13400000 +#define GPIO3_SIZE 0x120 +#define GPIO4_ADDR 0x10D10000 +#define GPIO4_SIZE 0x80 +#define GPIO5_ADDR 0x10D100C0 +#define GPIO5_SIZE 0x20 +#define GPIO6_ADDR 0x03860000 +#define GPIO6_SIZE 0x20 + +#define I2Cx_SIZE 0x100 +#define I2C1_ADDR 0x12c60000 +#define I2C2_ADDR 0x12c70000 +#define I2C3_ADDR 0x12c80000 +#define I2C4_ADDR 0x12c90000 +#define I2C5_ADDR 0x12ca0000 +#define I2C6_ADDR 0x12cb0000 +#define I2C7_ADDR 0x12cc0000 +#define I2C8_ADDR 0x12cd0000 + +#define I2C1_IRQ 56 +#define I2C2_IRQ 57 +#define I2C3_IRQ 58 +#define I2C4_IRQ 59 +#define I2C5_IRQ 60 +#define I2C6_IRQ 61 +#define I2C7_IRQ 62 +#define I2C8_IRQ 63 + +#define ESDHCx_SIZE 0x1000 +#define ESDHC1_ADDR 0x12200000 +#define ESDHC2_ADDR 0x12210000 +#define ESDHC3_ADDR 0x12220000 +#define ESDHC4_ADDR 0x12230000 + +#define ESDHC1_IRQ 75 +#define ESDHC2_IRQ 76 +#define ESDHC3_IRQ 77 +#define ESDHC4_IRQ 78 + +#define PCIE_REG_ADDR 0x01ffc000 +#define PCIE_REG_SIZE 0x4000 +#define PCIE_MAP_ADDR 0x01000000 +#define PCIE_MAP_SIZE 0xffc000 + +#define PCIE_IRQ0 120 +#define PCIE_IRQ1 121 +#define PCIE_IRQ2 122 +#define PCIE_IRQ3 123 + +struct armv7_dev exynos5_devs[] = { + + /* + * Multi-Core Timer + */ + { .name = "exmct", + .unit = 0, + .mem = { + { MCT_ADDR, MCT_SIZE }, + }, + }, + + /* + * Watchdog Timer + */ + { .name = "exdog", + .unit = 0, + .mem = { + { WD_ADDR, WD_SIZE }, + }, + }, + + /* + * Clock + */ + { .name = "exclock", + .unit = 0, + .mem = { + { CLOCK_ADDR, CLOCK_SIZE }, + }, + }, + + /* + * Power + */ + { .name = "expower", + .unit = 0, + .mem = { + { POWER_ADDR, POWER_SIZE }, + }, + }, + + /* + * Sysreg + */ + { .name = "exsysreg", + .unit = 0, + .mem = { + { SYSREG_ADDR, SYSREG_SIZE }, + }, + }, + + /* + * UART + */ + { .name = "exuart", + .unit = 0, + .mem = { { UART1_ADDR, UARTx_SIZE } }, + .irq = { UART1_IRQ } + }, + { .name = "exuart", + .unit = 1, + .mem = { { UART2_ADDR, UARTx_SIZE } }, + .irq = { UART2_IRQ } + }, + { .name = "exuart", + .unit = 2, + .mem = { { UART3_ADDR, UARTx_SIZE } }, + .irq = { UART3_IRQ } + }, + { .name = "exuart", + .unit = 3, + .mem = { { UART4_ADDR, UARTx_SIZE } }, + .irq = { UART4_IRQ } + }, + + /* + * GPIO + */ + { .name = "exgpio", + .unit = 0, + .mem = { { GPIO1_ADDR, GPIO1_SIZE } }, + }, + + { .name = "exgpio", + .unit = 1, + .mem = { { GPIO2_ADDR, GPIO2_SIZE } }, + }, + + { .name = "exgpio", + .unit = 2, + .mem = { { GPIO3_ADDR, GPIO3_SIZE } }, + }, + + { .name = "exgpio", + .unit = 3, + .mem = { { GPIO4_ADDR, GPIO4_SIZE } }, + }, + + { .name = "exgpio", + .unit = 4, + .mem = { { GPIO5_ADDR, GPIO5_SIZE } }, + }, + + { .name = "exgpio", + .unit = 5, + .mem = { { GPIO6_ADDR, GPIO6_SIZE } }, + }, + + /* + * I2C + */ + { .name = "exiic", + .unit = 0, + .mem = { { I2C1_ADDR, I2Cx_SIZE } }, + .irq = { I2C1_IRQ }, + }, + + { .name = "exiic", + .unit = 1, + .mem = { { I2C2_ADDR, I2Cx_SIZE } }, + .irq = { I2C2_IRQ }, + }, + + { .name = "exiic", + .unit = 2, + .mem = { { I2C3_ADDR, I2Cx_SIZE } }, + .irq = { I2C3_IRQ }, + }, + + { .name = "exiic", + .unit = 3, + .mem = { { I2C4_ADDR, I2Cx_SIZE } }, + .irq = { I2C4_IRQ }, + }, + + { .name = "exiic", + .unit = 4, + .mem = { { I2C5_ADDR, I2Cx_SIZE } }, + .irq = { I2C5_IRQ }, + }, + + { .name = "exiic", + .unit = 5, + .mem = { { I2C6_ADDR, I2Cx_SIZE } }, + .irq = { I2C6_IRQ }, + }, + + { .name = "exiic", + .unit = 6, + .mem = { { I2C7_ADDR, I2Cx_SIZE } }, + .irq = { I2C7_IRQ }, + }, + + { .name = "exiic", + .unit = 7, + .mem = { { I2C8_ADDR, I2Cx_SIZE } }, + .irq = { I2C8_IRQ }, + }, + + /* + * ESDHC + */ + { .name = "exesdhc", + .unit = 0, + .mem = { { ESDHC1_ADDR, ESDHCx_SIZE } }, + .irq = { ESDHC1_IRQ }, + }, + + { .name = "exesdhc", + .unit = 1, + .mem = { { ESDHC2_ADDR, ESDHCx_SIZE } }, + .irq = { ESDHC2_IRQ }, + }, + + { .name = "exesdhc", + .unit = 2, + .mem = { { ESDHC3_ADDR, ESDHCx_SIZE } }, + .irq = { ESDHC3_IRQ }, + }, + + { .name = "exesdhc", + .unit = 3, + .mem = { { ESDHC4_ADDR, ESDHCx_SIZE } }, + .irq = { ESDHC4_IRQ }, + }, + + /* + * USB + */ + { .name = "exehci", + .unit = 0, + .mem = { + { USB_EHCI_ADDR, USBx_SIZE }, + { USB_PHY_ADDR, USBx_SIZE }, + }, + .irq = { USB_IRQ } + }, + + /* Terminator */ + { .name = NULL, + .unit = 0 + } +}; + +void +exynos5_init(void) +{ + armv7_set_devs(exynos5_devs); +} diff --git a/sys/arch/armv7/exynos/exynos_machdep.c b/sys/arch/armv7/exynos/exynos_machdep.c new file mode 100644 index 00000000000..aff9bf38c13 --- /dev/null +++ b/sys/arch/armv7/exynos/exynos_machdep.c @@ -0,0 +1,109 @@ +/* $OpenBSD */ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/systm.h> +#include <sys/termios.h> + +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <arm/cortex/smc.h> +#include <arm/armv7/armv7var.h> +#include <armv7/armv7/armv7var.h> +#include <armv7/exynos/exdisplayvar.h> +#include <armv7/armv7/armv7_machdep.h> + +extern void exdog_reset(void); +extern int32_t agtimer_frequency; +extern int comcnspeed; +extern int comcnmode; + +static void +exynos_platform_smc_write(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off, + uint32_t op, uint32_t val) +{ + bus_space_write_4(iot, ioh, off, val); +} + +static void +exynos_platform_init_cons(void) +{ + paddr_t paddr; + size_t size; + void *node; + + switch (board_id) { + case BOARD_ID_EXYNOS5_CHROMEBOOK: + node = fdt_find_node("/framebuffer"); + if (node != NULL) { + uint32_t *mem; + if (fdt_node_property(node, "reg", (char **)&mem) >= 2*sizeof(uint32_t)) { + paddr = betoh32(*mem++); + size = betoh32(*mem); + } + } + exdisplay_cnattach(&armv7_bs_tag, paddr, size); + break; + default: + printf("board type %x unknown", board_id); + return; + /* XXX - HELP */ + } +} + +static void +exynos_platform_watchdog_reset(void) +{ + exdog_reset(); +} + +static void +exynos_platform_powerdown(void) +{ + +} + +static void +exynos_platform_print_board_type(void) +{ + switch (board_id) { + case BOARD_ID_EXYNOS5_CHROMEBOOK: + agtimer_frequency = 24 * 1000 * 1000; + printf("board type: Exynos 5 Chromebook\n"); + break; + default: + printf("board type %x unknown\n", board_id); + } +} + +static void +exynos_platform_disable_l2_if_needed(void) +{ + +} + +struct armv7_platform exynos_platform = { + .boot_name = "OpenBSD/exynos", + .smc_write = exynos_platform_smc_write, + .init_cons = exynos_platform_init_cons, + .watchdog_reset = exynos_platform_watchdog_reset, + .powerdown = exynos_platform_powerdown, + .print_board_type = exynos_platform_print_board_type, + .disable_l2_if_needed = exynos_platform_disable_l2_if_needed, +}; diff --git a/sys/arch/armv7/exynos/files.exynos b/sys/arch/armv7/exynos/files.exynos new file mode 100644 index 00000000000..48f571dcef8 --- /dev/null +++ b/sys/arch/armv7/exynos/files.exynos @@ -0,0 +1,74 @@ +# $OpenBSD: files.exynos,v 1.1 2015/01/26 02:48:24 bmercer Exp $ + +define exynos {} +device exynos: exynos +attach exynos at mainbus +file arch/armv7/exynos/exynos_machdep.c exynos needs-flag +file arch/armv7/exynos/exynos.c exynos +file arch/armv7/exynos/exynos5.c exynos + +# serial ports +device exuart +attach exuart at exynos +attach exuart at fdt with exuart_fdt +file arch/armv7/exynos/exuart.c exuart | exuart_fdt + +device exdisplay: wsemuldisplaydev, rasops16 +attach exdisplay at exynos +attach exdisplay at fdt with exdisplay_fdt +file arch/armv7/exynos/exdisplay.c exdisplay | exdisplay_fdt + +device exclock +attach exclock at exynos +attach exclock at fdt with exclock_fdt +file arch/armv7/exynos/exclock.c exclock | exclock_fdt + +device expower +attach expower at exynos +attach expower at fdt with expower_fdt +file arch/armv7/exynos/expower.c expower | expower_fdt + +device exsysreg +attach exsysreg at exynos +attach exsysreg at fdt with exsysreg_fdt +file arch/armv7/exynos/exsysreg.c exsysreg | exsysreg_fdt + +device exmct +attach exmct at exynos +attach exmct at fdt with exmct_fdt +file arch/armv7/exynos/exmct.c exmct | exiomuxc_fdt + +device exdog +attach exdog at exynos +attach exdog at fdt with exdog_fdt +file arch/armv7/exynos/exdog.c exdog | exdog_fdt + +device exgpio +attach exgpio at exynos +attach exgpio at fdt with exgpio_fdt +file arch/armv7/exynos/exgpio.c exgpio | exgpio_fdt + +device exiic: i2cbus +attach exiic at exynos +attach exiic at fdt with exiic_fdt +file arch/armv7/exynos/exiic.c exiic | exiic_fdt + +device exehci {} +attach exehci at exynos with exehci +attach exehci at fdt with exehci_fdt +attach ehci at exehci with ehci_ex +file arch/armv7/exynos/exehci.c exehci | exehci_fdt + +device exesdhc: sdmmcbus +attach exesdhc at exynos +attach exesdhc at fdt with exesdhc_fdt +file arch/armv7/exynos/exesdhc.c exesdhc | exesdhc_fdt + +device crosec: wskbddev +attach crosec at i2c +file arch/armv7/exynos/crosec.c crosec +file arch/armv7/exynos/crosec_kbd.c crosec + +device tpspmic +attach tpspmic at i2c +file arch/armv7/exynos/tps65090.c tpspmic diff --git a/sys/arch/armv7/exynos/tps65090.c b/sys/arch/armv7/exynos/tps65090.c new file mode 100644 index 00000000000..23e4fc9b228 --- /dev/null +++ b/sys/arch/armv7/exynos/tps65090.c @@ -0,0 +1,225 @@ +/* $OpenBSD: tps65090.c,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/sensors.h> +#include <sys/malloc.h> + +#include <dev/i2c/i2cvar.h> + +#define REG_IRQ1 0x00 +#define REG_IRQ2 0x01 +#define REG_IRQ1MASK 0x02 +#define REG_IRQ2MASK 0x03 +#define REG_CG_CTRL0 0x04 +#define REG_CG_CTRL0_ENC_MASK (1 << 0) +#define REG_CG_CTRL1 0x05 +#define REG_CG_CTRL2 0x06 +#define REG_CG_CTRL3 0x07 +#define REG_CG_CTRL4 0x08 +#define REG_CG_CTRL5 0x09 +#define REG_CG_STATUS1 0x0a +#define REG_CG_STATUS2 0x0b +#define REG_DCDC1_CTRL 0x0c +#define REG_DCDC2_CTRL 0x0d +#define REG_DCDC3_CTRL 0x0e +#define REG_FET1_CTRL 0x0f +#define REG_FET2_CTRL 0x10 +#define REG_FET3_CTRL 0x11 +#define REG_FET4_CTRL 0x12 +#define REG_FET5_CTRL 0x13 +#define REG_FET6_CTRL 0x14 +#define REG_FET7_CTRL 0x15 +#define REG_FETx_CTRL(x) (0x0e + (x)) +#define REG_FETx_CTRL_ENFET (1 << 0) /* Enable FET */ +#define REG_FETx_CTRL_ADENFET (1 << 1) /* Enable output auto discharge */ +#define REG_FETx_CTRL_WAIT (3 << 2) /* Overcurrent timeout max */ +#define REG_FETx_CTRL_PGFET (1 << 4) /* Power good for FET status */ +#define REG_FETx_CTRL_TOFET (1 << 7) /* Timeout, startup, overload */ +#define REG_AD_CTRL 0x16 +#define REG_AD_OUT1 0x17 +#define REG_AD_OUT2 0x18 + +#define NFET 7 + +#ifdef DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +struct tps65090_softc { + struct device sc_dev; + i2c_tag_t sc_tag; + i2c_addr_t sc_addr; +}; + +struct tps65090_softc *tps65090_sc; + +int tps65090_match(struct device *, void *, void *); +void tps65090_attach(struct device *, struct device *, void *); + +int tps65090_read_reg(struct tps65090_softc *, uint8_t); +void tps65090_write_reg(struct tps65090_softc *, uint8_t, uint8_t); +int tps65090_fet_set(int, int); +int tps65090_fet_get(int); +void tps65090_fet_enable(int); +void tps65090_fet_disable(int); +int tps65090_get_charging(void); +void tps65090_set_charging(int); + +struct cfattach tpspmic_ca = { + sizeof(struct tps65090_softc), tps65090_match, tps65090_attach +}; + +struct cfdriver tpspmic_cd = { + NULL, "tps65090", DV_DULL +}; + +int +tps65090_match(struct device *parent, void *match, void *aux) +{ + struct i2c_attach_args *ia = aux; + + if (strcmp(ia->ia_name, "tps65090") == 0) + return (1); + return (0); +} + +void +tps65090_attach(struct device *parent, struct device *self, void *aux) +{ + struct tps65090_softc *sc = (struct tps65090_softc *)self; + struct i2c_attach_args *ia = aux; + + sc->sc_tag = ia->ia_tag; + sc->sc_addr = ia->ia_addr; + tps65090_sc = sc; + + printf("\n"); +} + +/* + * FET1: Backlight on the Chromebook + * FET6: LCD panel on the Chromebook + */ +void +tps65090_fet_enable(int fet) +{ + int i; + + if (fet < 1 || fet > NFET) + return; + + for (i = 0; i < 10; i++) { + if (!tps65090_fet_set(fet, 1)) + break; + + if (i != 9) + tps65090_fet_set(fet, 0); + } +} + +void +tps65090_fet_disable(int fet) +{ + if (fet < 1 || fet > NFET) + return; + + tps65090_fet_set(fet, 0); +} + +int +tps65090_fet_set(int fet, int set) +{ + struct tps65090_softc *sc = tps65090_sc; + int i; + uint8_t val, check; + + val = REG_FETx_CTRL_ADENFET | REG_FETx_CTRL_WAIT; + if (set) + val |= REG_FETx_CTRL_ENFET; + + tps65090_write_reg(sc, REG_FETx_CTRL(fet), val); + for (i = 0; i < 5; i++) { + check = tps65090_read_reg(sc, REG_FETx_CTRL(fet)); + + /* FET state correct? */ + if (!!(check & REG_FETx_CTRL_PGFET) == set) + return 0; + + /* Timeout, don't need to try again. */ + if (check & REG_FETx_CTRL_TOFET) + break; + + delay(1000); + } + + return -1; +} + +int +tps65090_fet_get(int fet) +{ + struct tps65090_softc *sc = tps65090_sc; + uint8_t val = tps65090_read_reg(sc, REG_FETx_CTRL(fet)); + return val & REG_FETx_CTRL_ENFET; +} + +int +tps65090_get_charging() +{ + struct tps65090_softc *sc = tps65090_sc; + uint8_t val = tps65090_read_reg(sc, REG_CG_CTRL0); + return val & REG_CG_CTRL0_ENC_MASK; +} + +void +tps65090_set_charging(int set) +{ + struct tps65090_softc *sc = tps65090_sc; + uint8_t val = tps65090_read_reg(sc, REG_CG_CTRL0); + if (set) + val |= REG_CG_CTRL0_ENC_MASK; + else + val &= REG_CG_CTRL0_ENC_MASK; + tps65090_write_reg(sc, REG_CG_CTRL0, val); +} + +int +tps65090_read_reg(struct tps65090_softc *sc, uint8_t cmd) +{ + uint8_t val; + + iic_acquire_bus(sc->sc_tag, 0); + iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_addr, &cmd, 1, &val, 1, 0); + iic_release_bus(sc->sc_tag, 0); + + return val; +} + +void +tps65090_write_reg(struct tps65090_softc *sc, uint8_t cmd, uint8_t val) +{ + iic_acquire_bus(sc->sc_tag, 0); + iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, + sc->sc_addr, &cmd, 1, &val, 1, 0); + iic_release_bus(sc->sc_tag, 0); +} |