From f484e00cba11cc9a0349663251cca555828fdfe7 Mon Sep 17 00:00:00 2001 From: br Date: Fri, 1 Aug 2014 06:20:25 +0000 Subject: Add support for Chromebook2 -- next-generation 8-core (4 in operation), 4GB ram (3.5 usable) ARM machine. Support covers device drivers for: - Serial Peripheral Interface (SPI) - Chrome Embedded Controller (EC) - SPI-based version - XHCI and USB 3.0 dual-role device PHY Also: - Add support for Exynos5420 in Pad module - Move power-related functions to separate driver -- Power Management Unit (PMU) - Enable XHCI for Chromebook1 Special thanks to grehan@ for hardware, and to hselasky@ for r269139. --- sys/arm/samsung/exynos/chrome_ec.c | 6 +- sys/arm/samsung/exynos/chrome_ec.h | 2 + sys/arm/samsung/exynos/chrome_ec_spi.c | 230 ++++++++++++++++++++ sys/arm/samsung/exynos/exynos5_combiner.c | 3 + sys/arm/samsung/exynos/exynos5_ehci.c | 42 ++-- sys/arm/samsung/exynos/exynos5_pad.c | 311 +++++++++++++++++++-------- sys/arm/samsung/exynos/exynos5_pmu.c | 181 ++++++++++++++++ sys/arm/samsung/exynos/exynos5_pmu.h | 30 +++ sys/arm/samsung/exynos/exynos5_spi.c | 236 +++++++++++++++++++++ sys/arm/samsung/exynos/exynos5_usb_phy.c | 273 ++++++++++++++++++++++++ sys/arm/samsung/exynos/exynos5_xhci.c | 341 ++++++++++++++++++++++++++++++ sys/arm/samsung/exynos/files.exynos5 | 7 +- 12 files changed, 1537 insertions(+), 125 deletions(-) create mode 100644 sys/arm/samsung/exynos/chrome_ec_spi.c create mode 100644 sys/arm/samsung/exynos/exynos5_pmu.c create mode 100644 sys/arm/samsung/exynos/exynos5_pmu.h create mode 100644 sys/arm/samsung/exynos/exynos5_spi.c create mode 100644 sys/arm/samsung/exynos/exynos5_usb_phy.c create mode 100644 sys/arm/samsung/exynos/exynos5_xhci.c (limited to 'sys/arm/samsung') diff --git a/sys/arm/samsung/exynos/chrome_ec.c b/sys/arm/samsung/exynos/chrome_ec.c index ef64c1a..1718f6c 100644 --- a/sys/arm/samsung/exynos/chrome_ec.c +++ b/sys/arm/samsung/exynos/chrome_ec.c @@ -95,7 +95,7 @@ bus_claim(struct ec_softc *sc) /* Say we want the bus */ GPIO_PIN_SET(gpio_dev, sc->our_gpio, GPIO_PIN_LOW); - /* TODO(imax): insert a delay to allow EC to react. */ + /* TODO: insert a delay to allow EC to react. */ /* Check EC decision */ GPIO_PIN_GET(gpio_dev, sc->ec_gpio, &status); @@ -214,7 +214,7 @@ int ec_hello(void) data_in[2] = 0x20; data_in[3] = 0x10; - ec_command(EC_CMD_MKBP_STATE, data_in, 4, + ec_command(EC_CMD_HELLO, data_in, 4, data_out, 4); return (0); @@ -225,7 +225,7 @@ configure_i2c_arbitrator(struct ec_softc *sc) { phandle_t arbitrator; - /* TODO(imax): look for compatible entry instead of hard-coded path */ + /* TODO: look for compatible entry instead of hard-coded path */ arbitrator = OF_finddevice("/i2c-arbitrator"); if (arbitrator > 0 && OF_hasprop(arbitrator, "freebsd,our-gpio") && diff --git a/sys/arm/samsung/exynos/chrome_ec.h b/sys/arm/samsung/exynos/chrome_ec.h index 9d3f521..9879c16 100644 --- a/sys/arm/samsung/exynos/chrome_ec.h +++ b/sys/arm/samsung/exynos/chrome_ec.h @@ -30,6 +30,8 @@ #define EC_CMD_GET_VERSION 0x02 #define EC_CMD_MKBP_STATE 0x60 #define EC_CMD_VERSION0 0xdc +#define EC_CMD_RESEND_RESPONSE 0xdb +#define EC_CMD_GET_COMMS_STATUS 0x09 int ec_command(uint8_t cmd, uint8_t *dout, uint8_t dout_len, uint8_t *dinp, uint8_t dinp_len); diff --git a/sys/arm/samsung/exynos/chrome_ec_spi.c b/sys/arm/samsung/exynos/chrome_ec_spi.c new file mode 100644 index 0000000..3a43a1a --- /dev/null +++ b/sys/arm/samsung/exynos/chrome_ec_spi.c @@ -0,0 +1,230 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPREC OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNEC FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINEC INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Samsung Chromebook Embedded Controller (EC) + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "spibus_if.h" +#include "gpio_if.h" + +#include + +struct ec_softc { + device_t dev; + device_t dev_gpio; +}; + +struct ec_softc *ec_sc; + +#define EC_SPI_CS 200 + +static int +assert_cs(struct ec_softc *sc, int enable) +{ + /* Get the GPIO device */ + sc->dev_gpio = devclass_get_device(devclass_find("gpio"), 0); + if (sc->dev_gpio == NULL) { + device_printf(sc->dev, "Error: failed to get the GPIO dev\n"); + return (1); + } + + GPIO_PIN_SETFLAGS(sc->dev_gpio, EC_SPI_CS, GPIO_PIN_OUTPUT); + + if (enable) { + GPIO_PIN_SET(sc->dev_gpio, EC_SPI_CS, GPIO_PIN_LOW); + } else { + GPIO_PIN_SET(sc->dev_gpio, EC_SPI_CS, GPIO_PIN_HIGH); + } + + return (0); +} + +static int +ec_probe(device_t dev) +{ + + device_set_desc(dev, "Chromebook Embedded Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +fill_checksum(uint8_t *data_out, int len) +{ + int res; + int i; + + res = 0; + for (i = 0; i < len; i++) { + res += data_out[i]; + } + + data_out[len] = (res & 0xff); + + return (0); +} + +int +ec_command(uint8_t cmd, uint8_t *dout, uint8_t dout_len, + uint8_t *dinp, uint8_t dinp_len) +{ + struct spi_command spi_cmd; + struct ec_softc *sc; + uint8_t *msg_dout; + uint8_t *msg_dinp; + int ret; + int i; + + memset(&spi_cmd, 0, sizeof(spi_cmd)); + + msg_dout = malloc(dout_len + 4, M_DEVBUF, M_NOWAIT | M_ZERO); + msg_dinp = malloc(dinp_len + 4, M_DEVBUF, M_NOWAIT | M_ZERO); + + spi_cmd.tx_cmd = msg_dout; + spi_cmd.rx_cmd = msg_dinp; + + if (ec_sc == NULL) + return (-1); + + sc = ec_sc; + + msg_dout[0] = EC_CMD_VERSION0; + msg_dout[1] = cmd; + msg_dout[2] = dout_len; + + for (i = 0; i < dout_len; i++) { + msg_dout[i + 3] = dout[i]; + }; + + fill_checksum(msg_dout, dout_len + 3); + + assert_cs(sc, 1); + spi_cmd.rx_cmd_sz = spi_cmd.tx_cmd_sz = dout_len + 4; + ret = SPIBUS_TRANSFER(device_get_parent(sc->dev), sc->dev, &spi_cmd); + + /* Wait 0xec */ + for (i = 0; i < 1000; i++) { + DELAY(10); + msg_dout[0] = 0xff; + spi_cmd.rx_cmd_sz = spi_cmd.tx_cmd_sz = 1; + SPIBUS_TRANSFER(device_get_parent(sc->dev), sc->dev, &spi_cmd); + if (msg_dinp[0] == 0xec) + break; + } + + /* Get the rest */ + for (i = 0; i < (dout_len + 4); i++) + msg_dout[i] = 0xff; + spi_cmd.rx_cmd_sz = spi_cmd.tx_cmd_sz = dout_len + 4 - 1; + ret = SPIBUS_TRANSFER(device_get_parent(sc->dev), sc->dev, &spi_cmd); + assert_cs(sc, 0); + + if (ret != 0) { + device_printf(sc->dev, "spibus_transfer returned %d\n", ret); + free(msg_dout, M_DEVBUF); + free(msg_dinp, M_DEVBUF); + return (-1); + } + + for (i = 0; i < dinp_len; i++) { + dinp[i] = msg_dinp[i + 2]; + }; + + free(msg_dout, M_DEVBUF); + free(msg_dinp, M_DEVBUF); + + return (0); +} + +static int +ec_attach(device_t dev) +{ + struct ec_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + ec_sc = sc; + + return (0); +} + +static int +ec_detach(device_t dev) +{ + struct ec_softc *sc; + + sc = device_get_softc(dev); + + return (0); +} + +static device_method_t ec_methods[] = { + DEVMETHOD(device_probe, ec_probe), + DEVMETHOD(device_attach, ec_attach), + DEVMETHOD(device_detach, ec_detach), + { 0, 0 } +}; + +static driver_t ec_driver = { + "chrome_ec", + ec_methods, + sizeof(struct ec_softc), +}; + +static devclass_t ec_devclass; + +DRIVER_MODULE(chrome_ec, spibus, ec_driver, ec_devclass, 0, 0); +MODULE_VERSION(chrome_ec, 1); +MODULE_DEPEND(chrome_ec, spibus, 1, 1, 1); diff --git a/sys/arm/samsung/exynos/exynos5_combiner.c b/sys/arm/samsung/exynos/exynos5_combiner.c index bb18a1b..eb68436 100644 --- a/sys/arm/samsung/exynos/exynos5_combiner.c +++ b/sys/arm/samsung/exynos/exynos5_combiner.c @@ -353,6 +353,9 @@ static int combiner_probe(device_t dev) { + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (!ofw_bus_is_compatible(dev, "exynos,combiner")) return (ENXIO); diff --git a/sys/arm/samsung/exynos/exynos5_ehci.c b/sys/arm/samsung/exynos/exynos5_ehci.c index 84e4c5f..2f61824 100644 --- a/sys/arm/samsung/exynos/exynos5_ehci.c +++ b/sys/arm/samsung/exynos/exynos5_ehci.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013 Ruslan Bukin + * Copyright (c) 2013-2014 Ruslan Bukin * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,6 +55,9 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include + #include "gpio_if.h" #include "opt_platform.h" @@ -64,11 +67,6 @@ __FBSDID("$FreeBSD$"); #define GPIO_INPUT 0 #define PIN_USB 161 -/* PWR control */ -#define EXYNOS5_PWR_USBHOST_PHY 0x708 -#define PHY_POWER_ON 1 -#define PHY_POWER_OFF 0 - /* SYSREG */ #define EXYNOS5_SYSREG_USB2_PHY 0x0 #define USB2_MODE_HOST 0x1 @@ -91,12 +89,10 @@ static int exynos_ehci_probe(device_t dev); struct exynos_ehci_softc { device_t dev; ehci_softc_t base; - struct resource *res[5]; + struct resource *res[4]; bus_space_tag_t host_bst; - bus_space_tag_t pwr_bst; bus_space_tag_t sysreg_bst; bus_space_handle_t host_bsh; - bus_space_handle_t pwr_bsh; bus_space_handle_t sysreg_bsh; }; @@ -105,7 +101,6 @@ static struct resource_spec exynos_ehci_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE }, - { SYS_RES_MEMORY, 3, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; @@ -185,10 +180,8 @@ reset_hsic_hub(struct exynos_ehci_softc *esc, phandle_t hub) device_t gpio_dev; pcell_t pin; - /* TODO(imax): check that hub is compatible with "smsc,usb3503" */ + /* TODO: check that hub is compatible with "smsc,usb3503" */ if (!OF_hasprop(hub, "freebsd,reset-gpio")) { - device_printf(esc->dev, - "cannot detect reset GPIO pin for HSIC hub\n"); return (1); } @@ -201,7 +194,7 @@ reset_hsic_hub(struct exynos_ehci_softc *esc, phandle_t hub) /* Get the GPIO device, we need this to give power to USB */ gpio_dev = devclass_get_device(devclass_find("gpio"), 0); if (gpio_dev == NULL) { - device_printf(esc->dev, "cant find gpio_dev\n"); + device_printf(esc->dev, "Cant find gpio device\n"); return (1); } @@ -225,8 +218,7 @@ phy_init(struct exynos_ehci_softc *esc) EXYNOS5_SYSREG_USB2_PHY, USB2_MODE_HOST); /* Power ON phy */ - bus_space_write_4(esc->pwr_bst, esc->pwr_bsh, - EXYNOS5_PWR_USBHOST_PHY, PHY_POWER_ON); + usb2_phy_power_on(); reg = bus_space_read_4(esc->host_bst, esc->host_bsh, 0x0); reg &= ~(HOST_CTRL_CLK_MASK | @@ -284,13 +276,9 @@ exynos_ehci_attach(device_t dev) esc->host_bst = rman_get_bustag(esc->res[1]); esc->host_bsh = rman_get_bushandle(esc->res[1]); - /* PWR registers */ - esc->pwr_bst = rman_get_bustag(esc->res[2]); - esc->pwr_bsh = rman_get_bushandle(esc->res[2]); - /* SYSREG */ - esc->sysreg_bst = rman_get_bustag(esc->res[3]); - esc->sysreg_bsh = rman_get_bushandle(esc->res[3]); + esc->sysreg_bst = rman_get_bustag(esc->res[2]); + esc->sysreg_bsh = rman_get_bushandle(esc->res[2]); /* get all DMA memory */ if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(dev), @@ -309,7 +297,7 @@ exynos_ehci_attach(device_t dev) phy_init(esc); /* Setup interrupt handler */ - err = bus_setup_intr(dev, esc->res[4], INTR_TYPE_BIO | INTR_MPSAFE, + err = bus_setup_intr(dev, esc->res[3], INTR_TYPE_BIO | INTR_MPSAFE, NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); if (err) { @@ -322,7 +310,7 @@ exynos_ehci_attach(device_t dev) sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); if (!sc->sc_bus.bdev) { device_printf(dev, "Could not add USB device\n"); - err = bus_teardown_intr(dev, esc->res[4], + err = bus_teardown_intr(dev, esc->res[3], sc->sc_intr_hdl); if (err) device_printf(dev, "Could not tear down irq," @@ -343,7 +331,7 @@ exynos_ehci_attach(device_t dev) device_delete_child(dev, sc->sc_bus.bdev); sc->sc_bus.bdev = NULL; - err = bus_teardown_intr(dev, esc->res[4], + err = bus_teardown_intr(dev, esc->res[3], sc->sc_intr_hdl); if (err) device_printf(dev, "Could not tear down irq," @@ -382,8 +370,8 @@ exynos_ehci_detach(device_t dev) bus_space_write_4(sc->sc_io_tag, sc->sc_io_hdl, EHCI_USBINTR, 0); - if (esc->res[4] && sc->sc_intr_hdl) { - err = bus_teardown_intr(dev, esc->res[4], + if (esc->res[3] && sc->sc_intr_hdl) { + err = bus_teardown_intr(dev, esc->res[3], sc->sc_intr_hdl); if (err) { device_printf(dev, "Could not tear down irq," diff --git a/sys/arm/samsung/exynos/exynos5_pad.c b/sys/arm/samsung/exynos/exynos5_pad.c index 10e1a25..6a2c401 100644 --- a/sys/arm/samsung/exynos/exynos5_pad.c +++ b/sys/arm/samsung/exynos/exynos5_pad.c @@ -65,10 +65,13 @@ __FBSDID("$FreeBSD$"); #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) -#define NPORTS 4 -#define NGRP 40 -#define NGPIO 253 -#define NINTS 16 +#define MAX_PORTS 5 +#define MAX_NGPIO 253 + +#define N_EXT_INTS 16 + +#define EXYNOS5250 1 +#define EXYNOS5420 2 #define PIN_IN 0 #define PIN_OUT 1 @@ -90,45 +93,81 @@ static int pad_pin_set(device_t, uint32_t, unsigned int); static int pad_pin_get(device_t, uint32_t, unsigned int *); static int pad_pin_toggle(device_t, uint32_t pin); +struct gpio_bank { + char *name; + uint32_t port; + uint32_t con; + uint32_t ngpio; + uint32_t ext_con; + uint32_t ext_flt_con; + uint32_t mask; + uint32_t pend; +}; + struct pad_softc { - struct resource *res[NPORTS+4]; - bus_space_tag_t bst[NPORTS]; - bus_space_handle_t bsh[NPORTS]; + struct resource *res[MAX_PORTS * 2]; + bus_space_tag_t bst[MAX_PORTS]; + bus_space_handle_t bsh[MAX_PORTS]; struct mtx sc_mtx; int gpio_npins; - struct gpio_pin gpio_pins[NGPIO]; - void *gpio_ih[NPORTS+4]; + struct gpio_pin gpio_pins[MAX_NGPIO]; + void *gpio_ih[MAX_PORTS]; device_t dev; + int model; + struct resource_spec *pad_spec; + struct gpio_bank *gpio_map; + struct interrupt_entry *interrupt_table; + int nports; }; struct pad_softc *gpio_sc; -static struct resource_spec pad_spec[] = { +static struct resource_spec pad_spec_5250[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { SYS_RES_MEMORY, 2, RF_ACTIVE }, + { SYS_RES_MEMORY, 3, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 2, RF_ACTIVE }, + { SYS_RES_IRQ, 3, RF_ACTIVE }, + { -1, 0 } +}; + +static struct resource_spec pad_spec_5420[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE }, { SYS_RES_MEMORY, 3, RF_ACTIVE }, + { SYS_RES_MEMORY, 4, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, + { SYS_RES_IRQ, 4, RF_ACTIVE }, { -1, 0 } }; +static struct ofw_compat_data compat_data[] = { + {"samsung,exynos5420-padctrl", EXYNOS5420}, + {"samsung,exynos5250-padctrl", EXYNOS5250}, + {NULL, 0} +}; + struct pad_intr { uint32_t enabled; void (*ih) (void *); void *ih_user; }; -static struct pad_intr intr_map[NGPIO]; +static struct pad_intr intr_map[MAX_NGPIO]; struct interrupt_entry { int gpio_number; char *combiner_source_name; }; -struct interrupt_entry interrupt_table[NINTS] = { +struct interrupt_entry interrupt_table_5250[N_EXT_INTS] = { { 147, "EINT[15]" }, { 146, "EINT[14]" }, { 145, "EINT[13]" }, @@ -147,84 +186,144 @@ struct interrupt_entry interrupt_table[NINTS] = { { 132, "EINT[0]" }, }; -struct gpio_bank { - char *name; - uint32_t port; - uint32_t con; - uint32_t ngpio; - uint32_t ext_int_grp; - uint32_t ext_con; - uint32_t ext_flt_con; - uint32_t mask; - uint32_t pend; +struct interrupt_entry interrupt_table_5420[N_EXT_INTS] = { + { 23, "EINT[15]" }, + { 22, "EINT[14]" }, + { 21, "EINT[13]" }, + { 20, "EINT[12]" }, + { 19, "EINT[11]" }, + { 18, "EINT[10]" }, + { 17, "EINT[9]" }, + { 16, "EINT[8]" }, + { 15, "EINT[7]" }, + { 14, "EINT[6]" }, + { 13, "EINT[5]" }, + { 12, "EINT[4]" }, + { 11, "EINT[3]" }, + { 10, "EINT[2]" }, + { 9, "EINT[1]" }, + { 8, "EINT[0]" }, }; /* * 253 multi-functional input/output ports */ -static struct gpio_bank gpio_map[] = { +static struct gpio_bank gpio_map_5250[] = { /* first 132 gpio */ - { "gpa0", 0, 0x000, 8, 1, 0x700, 0x800, 0x900, 0xA00 }, - { "gpa1", 0, 0x020, 6, 2, 0x704, 0x808, 0x904, 0xA04 }, - { "gpa2", 0, 0x040, 8, 3, 0x708, 0x810, 0x908, 0xA08 }, - { "gpb0", 0, 0x060, 5, 4, 0x70C, 0x818, 0x90C, 0xA0C }, - { "gpb1", 0, 0x080, 5, 5, 0x710, 0x820, 0x910, 0xA10 }, - { "gpb2", 0, 0x0A0, 4, 6, 0x714, 0x828, 0x914, 0xA14 }, - { "gpb3", 0, 0x0C0, 4, 7, 0x718, 0x830, 0x918, 0xA18 }, - { "gpc0", 0, 0x0E0, 7, 8, 0x71C, 0x838, 0x91C, 0xA1C }, - { "gpc1", 0, 0x100, 4, 9, 0x720, 0x840, 0x920, 0xA20 }, - { "gpc2", 0, 0x120, 7, 10, 0x724, 0x848, 0x924, 0xA24 }, - { "gpc3", 0, 0x140, 7, 11, 0x728, 0x850, 0x928, 0xA28 }, - { "gpd0", 0, 0x160, 4, 12, 0x72C, 0x858, 0x92C, 0xA2C }, - { "gpd1", 0, 0x180, 8, 13, 0x730, 0x860, 0x930, 0xA30 }, - { "gpy0", 0, 0x1A0, 6, 0, 0, 0, 0, 0 }, - { "gpy1", 0, 0x1C0, 4, 0, 0, 0, 0, 0 }, - { "gpy2", 0, 0x1E0, 6, 0, 0, 0, 0, 0 }, - { "gpy3", 0, 0x200, 8, 0, 0, 0, 0, 0 }, - { "gpy4", 0, 0x220, 8, 0, 0, 0, 0, 0 }, - { "gpy5", 0, 0x240, 8, 0, 0, 0, 0, 0 }, - { "gpy6", 0, 0x260, 8, 0, 0, 0, 0, 0 }, - { "gpc4", 0, 0x2E0, 7, 30, 0x734, 0x868, 0x934, 0xA34 }, + { "gpa0", 0, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpa1", 0, 0x020, 6, 0x704, 0x808, 0x904, 0xA04 }, + { "gpa2", 0, 0x040, 8, 0x708, 0x810, 0x908, 0xA08 }, + { "gpb0", 0, 0x060, 5, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpb1", 0, 0x080, 5, 0x710, 0x820, 0x910, 0xA10 }, + { "gpb2", 0, 0x0A0, 4, 0x714, 0x828, 0x914, 0xA14 }, + { "gpb3", 0, 0x0C0, 4, 0x718, 0x830, 0x918, 0xA18 }, + { "gpc0", 0, 0x0E0, 7, 0x71C, 0x838, 0x91C, 0xA1C }, + { "gpc1", 0, 0x100, 4, 0x720, 0x840, 0x920, 0xA20 }, + { "gpc2", 0, 0x120, 7, 0x724, 0x848, 0x924, 0xA24 }, + { "gpc3", 0, 0x140, 7, 0x728, 0x850, 0x928, 0xA28 }, + { "gpd0", 0, 0x160, 4, 0x72C, 0x858, 0x92C, 0xA2C }, + { "gpd1", 0, 0x180, 8, 0x730, 0x860, 0x930, 0xA30 }, + { "gpy0", 0, 0x1A0, 6, 0, 0, 0, 0 }, + { "gpy1", 0, 0x1C0, 4, 0, 0, 0, 0 }, + { "gpy2", 0, 0x1E0, 6, 0, 0, 0, 0 }, + { "gpy3", 0, 0x200, 8, 0, 0, 0, 0 }, + { "gpy4", 0, 0x220, 8, 0, 0, 0, 0 }, + { "gpy5", 0, 0x240, 8, 0, 0, 0, 0 }, + { "gpy6", 0, 0x260, 8, 0, 0, 0, 0 }, + { "gpc4", 0, 0x2E0, 7, 0x734, 0x868, 0x934, 0xA34 }, /* next 32 */ - { "gpx0", 0, 0xC00, 8, 40, 0xE00, 0xE80, 0xF00, 0xF40 }, - { "gpx1", 0, 0xC20, 8, 41, 0xE04, 0xE88, 0xF04, 0xF44 }, - { "gpx2", 0, 0xC40, 8, 42, 0xE08, 0xE90, 0xF08, 0xF48 }, - { "gpx3", 0, 0xC60, 8, 43, 0xE0C, 0xE98, 0xF0C, 0xF4C }, - - { "gpe0", 1, 0x000, 8, 14, 0x700, 0x800, 0x900, 0xA00 }, - { "gpe1", 1, 0x020, 2, 15, 0x704, 0x808, 0x904, 0xA04 }, - { "gpf0", 1, 0x040, 4, 16, 0x708, 0x810, 0x908, 0xA08 }, - { "gpf1", 1, 0x060, 4, 17, 0x70C, 0x818, 0x90C, 0xA0C }, - { "gpg0", 1, 0x080, 8, 18, 0x710, 0x820, 0x910, 0xA10 }, - { "gpg1", 1, 0x0A0, 8, 19, 0x714, 0x828, 0x914, 0xA14 }, - { "gpg2", 1, 0x0C0, 2, 20, 0x718, 0x830, 0x918, 0xA18 }, - { "gph0", 1, 0x0E0, 4, 21, 0x71C, 0x838, 0x91C, 0xA1C }, - { "gph1", 1, 0x100, 8, 22, 0x720, 0x840, 0x920, 0xA20 }, - - { "gpv0", 2, 0x000, 8, 60, 0x700, 0x800, 0x900, 0xA00 }, - { "gpv1", 2, 0x020, 8, 61, 0x704, 0x808, 0x904, 0xA04 }, - { "gpv2", 2, 0x060, 8, 62, 0x708, 0x810, 0x908, 0xA08 }, - { "gpv3", 2, 0x080, 8, 63, 0x70C, 0x818, 0x90C, 0xA0C }, - { "gpv4", 2, 0x0C0, 2, 64, 0x710, 0x820, 0x910, 0xA10 }, - - { "gpz", 3, 0x000, 7, 50, 0x700, 0x800, 0x900, 0xA00 }, + { "gpx0", 0, 0xC00, 8, 0xE00, 0xE80, 0xF00, 0xF40 }, + { "gpx1", 0, 0xC20, 8, 0xE04, 0xE88, 0xF04, 0xF44 }, + { "gpx2", 0, 0xC40, 8, 0xE08, 0xE90, 0xF08, 0xF48 }, + { "gpx3", 0, 0xC60, 8, 0xE0C, 0xE98, 0xF0C, 0xF4C }, + + { "gpe0", 1, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpe1", 1, 0x020, 2, 0x704, 0x808, 0x904, 0xA04 }, + { "gpf0", 1, 0x040, 4, 0x708, 0x810, 0x908, 0xA08 }, + { "gpf1", 1, 0x060, 4, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpg0", 1, 0x080, 8, 0x710, 0x820, 0x910, 0xA10 }, + { "gpg1", 1, 0x0A0, 8, 0x714, 0x828, 0x914, 0xA14 }, + { "gpg2", 1, 0x0C0, 2, 0x718, 0x830, 0x918, 0xA18 }, + { "gph0", 1, 0x0E0, 4, 0x71C, 0x838, 0x91C, 0xA1C }, + { "gph1", 1, 0x100, 8, 0x720, 0x840, 0x920, 0xA20 }, + + { "gpv0", 2, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpv1", 2, 0x020, 8, 0x704, 0x808, 0x904, 0xA04 }, + { "gpv2", 2, 0x060, 8, 0x708, 0x810, 0x908, 0xA08 }, + { "gpv3", 2, 0x080, 8, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpv4", 2, 0x0C0, 2, 0x710, 0x820, 0x910, 0xA10 }, + + { "gpz", 3, 0x000, 7, 0x700, 0x800, 0x900, 0xA00 }, + + { NULL, -1, -1, -1, -1, -1, -1, -1 }, +}; + +static struct gpio_bank gpio_map_5420[] = { + /* First 40 */ + { "gpy7", 0, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpx0", 0, 0xC00, 8, 0x704, 0xE80, 0xF00, 0xF40 }, + { "gpx1", 0, 0xC20, 8, 0x708, 0xE88, 0xF04, 0xF44 }, + { "gpx2", 0, 0xC40, 8, 0x70C, 0xE90, 0xF08, 0xF48 }, + { "gpx3", 0, 0xC60, 8, 0x710, 0xE98, 0xF0C, 0xF4C }, + + /* Next 85 */ + { "gpc0", 1, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpc1", 1, 0x020, 8, 0x704, 0x808, 0x904, 0xA04 }, + { "gpc2", 1, 0x040, 7, 0x708, 0x810, 0x908, 0xA08 }, + { "gpc3", 1, 0x060, 4, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpc4", 1, 0x080, 2, 0x710, 0x820, 0x910, 0xA10 }, + { "gpd1", 1, 0x0A0, 8, 0x714, 0x828, 0x914, 0xA14 }, + { "gpy0", 1, 0x0C0, 6, 0x718, 0x830, 0x918, 0xA18 }, + { "gpy1", 1, 0x0E0, 4, 0x71C, 0x838, 0x91C, 0xA1C }, + { "gpy2", 1, 0x100, 6, 0x720, 0x840, 0x920, 0xA20 }, + { "gpy3", 1, 0x120, 8, 0x724, 0x848, 0x924, 0xA24 }, + { "gpy4", 1, 0x140, 8, 0x728, 0x850, 0x928, 0xA28 }, + { "gpy5", 1, 0x160, 8, 0x72C, 0x858, 0x92C, 0xA2C }, + { "gpy6", 1, 0x180, 8, 0x730, 0x860, 0x930, 0xA30 }, + + /* Next 46 */ + { "gpe0", 2, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpe1", 2, 0x020, 2, 0x704, 0x808, 0x904, 0xA04 }, + { "gpf0", 2, 0x040, 6, 0x708, 0x810, 0x908, 0xA08 }, + { "gpf1", 2, 0x060, 8, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpg0", 2, 0x080, 8, 0x710, 0x820, 0x910, 0xA10 }, + { "gpg1", 2, 0x0A0, 8, 0x714, 0x828, 0x914, 0xA14 }, + { "gpg2", 2, 0x0C0, 2, 0x718, 0x830, 0x918, 0xA18 }, + { "gpj4", 2, 0x0E0, 4, 0x71C, 0x838, 0x91C, 0xA1C }, + + /* Next 54 */ + { "gpa0", 3, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, + { "gpa1", 3, 0x020, 6, 0x704, 0x808, 0x904, 0xA04 }, + { "gpa2", 3, 0x040, 8, 0x708, 0x810, 0x908, 0xA08 }, + { "gpb0", 3, 0x060, 5, 0x70C, 0x818, 0x90C, 0xA0C }, + { "gpb1", 3, 0x080, 5, 0x710, 0x820, 0x910, 0xA10 }, + { "gpb2", 3, 0x0A0, 4, 0x714, 0x828, 0x914, 0xA14 }, + { "gpb3", 3, 0x0C0, 8, 0x718, 0x830, 0x918, 0xA18 }, + { "gpb4", 3, 0x0E0, 2, 0x71C, 0x838, 0x91C, 0xA1C }, + { "gph0", 3, 0x100, 8, 0x720, 0x840, 0x920, 0xA20 }, + + /* Last 7 */ + { "gpz", 4, 0x000, 7, 0x700, 0x800, 0x900, 0xA00 }, + + { NULL, -1, -1, -1, -1, -1, -1, -1 }, }; static int -get_bank(int gpio_number, struct gpio_bank *bank, int *pin_shift) +get_bank(struct pad_softc *sc, int gpio_number, + struct gpio_bank *bank, int *pin_shift) { int ngpio; int i; int n; n = 0; - for (i = 0; i < NGRP; i++) { - ngpio = gpio_map[i].ngpio; + for (i = 0; sc->gpio_map[i].ngpio != -1; i++) { + ngpio = sc->gpio_map[i].ngpio; if ((n + ngpio) > gpio_number) { - *bank = gpio_map[i]; + *bank = sc->gpio_map[i]; *pin_shift = (gpio_number - n); return (0); }; @@ -260,16 +359,16 @@ ext_intr(void *arg) sc = arg; n = 0; - for (i = 0; i < NGRP; i++) { + for (i = 0; sc->gpio_map[i].ngpio != -1; i++) { found = 0; - ngpio = gpio_map[i].ngpio; + ngpio = sc->gpio_map[i].ngpio; - if (gpio_map[i].pend == 0) { + if (sc->gpio_map[i].pend == 0) { n += ngpio; continue; } - reg = READ4(sc, gpio_map[i].port, gpio_map[i].pend); + reg = READ4(sc, sc->gpio_map[i].port, sc->gpio_map[i].pend); for (j = 0; j < ngpio; j++) { if (reg & (1 << j)) { @@ -286,7 +385,7 @@ ext_intr(void *arg) if (found) { /* ACK */ - WRITE4(sc, gpio_map[i].port, gpio_map[i].pend, reg); + WRITE4(sc, sc->gpio_map[i].port, sc->gpio_map[i].pend, reg); } n += ngpio; @@ -311,13 +410,13 @@ pad_setup_intr(int gpio_number, void (*ih)(void *), void *ih_user) return (-1); } - if (get_bank(gpio_number, &bank, &pin_shift) != 0) + if (get_bank(sc, gpio_number, &bank, &pin_shift) != 0) return (-1); entry = NULL; - for (i = 0; i < NINTS; i++) - if (interrupt_table[i].gpio_number == gpio_number) - entry = &interrupt_table[i]; + for (i = 0; i < N_EXT_INTS; i++) + if (sc->interrupt_table[i].gpio_number == gpio_number) + entry = &(sc->interrupt_table[i]); if (entry == NULL) { device_printf(sc->dev, "Cant find interrupt source for %d\n", @@ -374,11 +473,12 @@ pad_probe(device_t dev) if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "exynos,pad")) - return (ENXIO); + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Exynos Pad Control"); + return (BUS_PROBE_DEFAULT); + } - device_set_desc(dev, "Exynos Pad Control"); - return (BUS_PROBE_DEFAULT); + return (ENXIO); } static int @@ -391,27 +491,47 @@ pad_attach(device_t dev) int i; sc = device_get_softc(dev); + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); - if (bus_alloc_resources(dev, pad_spec, sc->res)) { + sc->model = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + switch (sc->model) { + case EXYNOS5250: + sc->pad_spec = pad_spec_5250; + sc->gpio_map = gpio_map_5250; + sc->interrupt_table = interrupt_table_5250; + sc->gpio_npins = 253; + sc->nports = 4; + break; + case EXYNOS5420: + sc->pad_spec = pad_spec_5420; + sc->gpio_map = gpio_map_5420; + sc->interrupt_table = interrupt_table_5420; + sc->gpio_npins = 232; + sc->nports = 5; + break; + default: + return (-1); + }; + + if (bus_alloc_resources(dev, sc->pad_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ - for (i = 0; i < NPORTS; i++) { + for (i = 0; i < sc->nports; i++) { sc->bst[i] = rman_get_bustag(sc->res[i]); sc->bsh[i] = rman_get_bushandle(sc->res[i]); }; sc->dev = dev; - sc->gpio_npins = NGPIO; gpio_sc = sc; - for (i = 0; i < NPORTS; i++) { - if ((bus_setup_intr(dev, sc->res[NPORTS + i], + for (i = 0; i < sc->nports; i++) { + if ((bus_setup_intr(dev, sc->res[sc->nports + i], INTR_TYPE_BIO | INTR_MPSAFE, port_intr, NULL, sc, &sc->gpio_ih[i]))) { device_printf(dev, @@ -424,7 +544,7 @@ pad_attach(device_t dev) sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; - if (get_bank(i, &bank, &pin_shift) != 0) + if (get_bank(sc, i, &bank, &pin_shift) != 0) continue; pin_shift *= 4; @@ -450,8 +570,11 @@ pad_attach(device_t dev) static int pad_pin_max(device_t dev, int *maxpin) { + struct pad_softc *sc; + + sc = device_get_softc(dev); - *maxpin = NGPIO - 1; + *maxpin = sc->gpio_npins - 1; return (0); } @@ -538,7 +661,7 @@ pad_pin_get(device_t dev, uint32_t pin, unsigned int *val) if (i >= sc->gpio_npins) return (EINVAL); - if (get_bank(pin, &bank, &pin_shift) != 0) + if (get_bank(sc, pin, &bank, &pin_shift) != 0) return (EINVAL); GPIO_LOCK(sc); @@ -569,7 +692,7 @@ pad_pin_toggle(device_t dev, uint32_t pin) if (i >= sc->gpio_npins) return (EINVAL); - if (get_bank(pin, &bank, &pin_shift) != 0) + if (get_bank(sc, pin, &bank, &pin_shift) != 0) return (EINVAL); GPIO_LOCK(sc); @@ -601,7 +724,7 @@ pad_pin_configure(struct pad_softc *sc, struct gpio_pin *pin, if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); - if (get_bank(pin->gp_pin, &bank, &pin_shift) != 0) + if (get_bank(sc, pin->gp_pin, &bank, &pin_shift) != 0) return; pin_shift *= 4; @@ -675,7 +798,7 @@ pad_pin_set(device_t dev, uint32_t pin, unsigned int value) if (i >= sc->gpio_npins) return (EINVAL); - if (get_bank(pin, &bank, &pin_shift) != 0) + if (get_bank(sc, pin, &bank, &pin_shift) != 0) return (EINVAL); GPIO_LOCK(sc); diff --git a/sys/arm/samsung/exynos/exynos5_pmu.c b/sys/arm/samsung/exynos/exynos5_pmu.c new file mode 100644 index 0000000..a6c8160 --- /dev/null +++ b/sys/arm/samsung/exynos/exynos5_pmu.c @@ -0,0 +1,181 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Exynos 5 Power Management Unit (PMU) + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define EXYNOS5250 1 +#define EXYNOS5420 2 + +/* PWR control */ +#define EXYNOS5_PWR_USBHOST_PHY 0x708 +#define EXYNOS5_USBDRD_PHY_CTRL 0x704 +#define EXYNOS5420_USBDRD1_PHY_CTRL 0x708 + +#define PHY_POWER_ON 1 +#define PHY_POWER_OFF 0 + +struct pmu_softc { + struct resource *res[1]; + bus_space_tag_t bst; + bus_space_handle_t bsh; + device_t dev; + int model; +}; + +struct pmu_softc *pmu_sc; + +static struct ofw_compat_data compat_data[] = { + {"samsung,exynos5420-pmu", EXYNOS5420}, + {"samsung,exynos5250-pmu", EXYNOS5250}, + {NULL, 0} +}; + +static struct resource_spec pmu_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int +pmu_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { + device_set_desc(dev, "Samsung Exynos 5 Power Management Unit"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +int +usb2_phy_power_on(void) +{ + struct pmu_softc *sc; + + sc = pmu_sc; + if (sc == NULL) + return (-1); + + /* EHCI */ + WRITE4(sc, EXYNOS5_PWR_USBHOST_PHY, PHY_POWER_ON); + + return (0); +} + +int +usbdrd_phy_power_on(void) +{ + struct pmu_softc *sc; + + sc = pmu_sc; + if (sc == NULL) + return (-1); + + /* + * First XHCI controller (left-side USB port on chromebook2) + */ + WRITE4(sc, EXYNOS5_USBDRD_PHY_CTRL, PHY_POWER_ON); + + /* + * Second XHCI controller (right-side USB port on chrombook2) + * Only available on 5420. + */ + if (sc->model == EXYNOS5420) + WRITE4(sc, EXYNOS5420_USBDRD1_PHY_CTRL, PHY_POWER_ON); + + return (0); +} + +static int +pmu_attach(device_t dev) +{ + struct pmu_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->model = ofw_bus_search_compatible(dev, compat_data)->ocd_data; + + if (bus_alloc_resources(dev, pmu_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + /* Memory interface */ + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + pmu_sc = sc; + + return (0); +} + +static device_method_t pmu_methods[] = { + DEVMETHOD(device_probe, pmu_probe), + DEVMETHOD(device_attach, pmu_attach), + { 0, 0 } +}; + +static driver_t pmu_driver = { + "pmu", + pmu_methods, + sizeof(struct pmu_softc), +}; + +static devclass_t pmu_devclass; + +DRIVER_MODULE(pmu, simplebus, pmu_driver, pmu_devclass, 0, 0); diff --git a/sys/arm/samsung/exynos/exynos5_pmu.h b/sys/arm/samsung/exynos/exynos5_pmu.h new file mode 100644 index 0000000..73c6b85 --- /dev/null +++ b/sys/arm/samsung/exynos/exynos5_pmu.h @@ -0,0 +1,30 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +int usb2_phy_power_on(void); +int usbdrd_phy_power_on(void); diff --git a/sys/arm/samsung/exynos/exynos5_spi.c b/sys/arm/samsung/exynos/exynos5_spi.c new file mode 100644 index 0000000..9cbdc4b --- /dev/null +++ b/sys/arm/samsung/exynos/exynos5_spi.c @@ -0,0 +1,236 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Exynos 5 Serial Peripheral Interface (SPI) + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "spibus_if.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define CH_CFG 0x00 /* SPI configuration */ +#define SW_RST (1 << 5) /* Reset */ +#define RX_CH_ON (1 << 1) /* SPI Rx Channel On */ +#define TX_CH_ON (1 << 0) /* SPI Tx Channel On */ +#define MODE_CFG 0x08 /* FIFO control */ +#define CS_REG 0x0C /* slave selection control */ +#define NSSOUT (1 << 0) +#define SPI_INT_EN 0x10 /* interrupt enable */ +#define SPI_STATUS 0x14 /* SPI status */ +#define TX_FIFO_LVL_S 6 +#define TX_FIFO_LVL_M 0x1ff +#define RX_FIFO_LVL_S 15 +#define RX_FIFO_LVL_M 0x1ff +#define SPI_TX_DATA 0x18 /* Tx data */ +#define SPI_RX_DATA 0x1C /* Rx data */ +#define PACKET_CNT_REG 0x20 /* packet count */ +#define PENDING_CLR_REG 0x24 /* interrupt pending clear */ +#define SWAP_CFG 0x28 /* swap configuration */ +#define FB_CLK_SEL 0x2C /* feedback clock selection */ +#define FB_CLK_180 0x2 /* 180 degree phase lagging */ + +struct spi_softc { + struct resource *res[2]; + bus_space_tag_t bst; + bus_space_handle_t bsh; + device_t dev; +}; + +struct spi_softc *spi_sc; + +static struct resource_spec spi_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int +spi_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "samsung,exynos5-spi")) + return (ENXIO); + + device_set_desc(dev, "Exynos 5 Serial Peripheral Interface (SPI)"); + return (BUS_PROBE_DEFAULT); +} + +static int +spi_attach(device_t dev) +{ + struct spi_softc *sc; + int reg; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, spi_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + /* Memory interface */ + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + spi_sc = sc; + + WRITE4(sc, FB_CLK_SEL, FB_CLK_180); + + reg = READ4(sc, CH_CFG); + reg |= (RX_CH_ON | TX_CH_ON); + WRITE4(sc, CH_CFG, reg); + + device_add_child(dev, "spibus", 0); + return (bus_generic_attach(dev)); +} + +static int +spi_txrx(struct spi_softc *sc, uint8_t *out_buf, + uint8_t *in_buf, int bufsz, int cs) +{ + uint32_t reg; + uint32_t i; + + if (bufsz == 0) { + /* Nothing to transfer */ + return (0); + } + + /* Reset registers */ + reg = READ4(sc, CH_CFG); + reg |= SW_RST; + WRITE4(sc, CH_CFG, reg); + reg &= ~SW_RST; + WRITE4(sc, CH_CFG, reg); + + /* Assert CS */ + reg = READ4(sc, CS_REG); + reg &= ~NSSOUT; + WRITE4(sc, CS_REG, reg); + + for (i = 0; i < bufsz; i++) { + + /* TODO: Implement FIFO operation */ + + /* Wait all the data shifted out */ + while (READ4(sc, SPI_STATUS) & \ + (TX_FIFO_LVL_M << TX_FIFO_LVL_S)) + continue; + + WRITE1(sc, SPI_TX_DATA, out_buf[i]); + + /* Wait until no data available */ + while ((READ4(sc, SPI_STATUS) & \ + (RX_FIFO_LVL_M << RX_FIFO_LVL_S)) == 0) + continue; + + in_buf[i] = READ1(sc, SPI_RX_DATA); + } + + /* Deassert CS */ + reg = READ4(sc, CS_REG); + reg |= NSSOUT; + WRITE4(sc, CS_REG, reg); + + return (0); +} + +static int +spi_transfer(device_t dev, device_t child, struct spi_command *cmd) +{ + struct spi_softc *sc; + uint32_t cs; + + sc = device_get_softc(dev); + + KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, + ("%s: TX/RX command sizes should be equal", __func__)); + KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, + ("%s: TX/RX data sizes should be equal", __func__)); + + /* get the proper chip select */ + spibus_get_cs(child, &cs); + + /* Command */ + spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs); + + /* Data */ + spi_txrx(sc, cmd->tx_data, cmd->rx_data, cmd->tx_data_sz, cs); + + return (0); +} + +static device_method_t spi_methods[] = { + DEVMETHOD(device_probe, spi_probe), + DEVMETHOD(device_attach, spi_attach), + + /* SPI interface */ + DEVMETHOD(spibus_transfer, spi_transfer), + + { 0, 0 } +}; + +static driver_t spi_driver = { + "spi", + spi_methods, + sizeof(struct spi_softc), +}; + +static devclass_t spi_devclass; + +DRIVER_MODULE(spi, simplebus, spi_driver, spi_devclass, 0, 0); diff --git a/sys/arm/samsung/exynos/exynos5_usb_phy.c b/sys/arm/samsung/exynos/exynos5_usb_phy.c new file mode 100644 index 0000000..649cb34 --- /dev/null +++ b/sys/arm/samsung/exynos/exynos5_usb_phy.c @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * DWC3 USB 3.0 DRD (dual role device) PHY + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "gpio_if.h" + +#define USB_DRD_LINKSYSTEM 0x04 +#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) +#define LINKSYSTEM_FLADJ(x) ((x) << 1) +#define LINKSYSTEM_XHCI_VERSION_CTRL (1 << 27) +#define USB_DRD_PHYUTMI 0x08 +#define PHYUTMI_OTGDISABLE (1 << 6) +#define PHYUTMI_FORCESUSPEND (1 << 1) +#define PHYUTMI_FORCESLEEP (1 << 0) +#define USB_DRD_PHYPIPE 0x0c +#define USB_DRD_PHYCLKRST 0x10 +#define PHYCLKRST_PORTRESET (1 << 1) +#define PHYCLKRST_COMMONONN (1 << 0) +#define PHYCLKRST_EN_UTMISUSPEND (1 << 31) +#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) +#define PHYCLKRST_SSC_REFCLKSEL(x) ((x) << 23) +#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) +#define PHYCLKRST_SSC_RANGE(x) ((x) << 21) +#define PHYCLKRST_SSC_EN (1 << 20) +#define PHYCLKRST_REF_SSP_EN (1 << 19) +#define PHYCLKRST_REF_CLKDIV2 (1 << 18) +#define PHYCLKRST_MPLL_MLTPR_MASK (0x7f << 11) +#define PHYCLKRST_MPLL_MLTPR_100MHZ (0x19 << 11) +#define PHYCLKRST_MPLL_MLTPR_50M (0x32 << 11) +#define PHYCLKRST_MPLL_MLTPR_24MHZ (0x68 << 11) +#define PHYCLKRST_MPLL_MLTPR_20MHZ (0x7d << 11) +#define PHYCLKRST_MPLL_MLTPR_19200KHZ (0x02 << 11) +#define PHYCLKRST_FSEL_UTMI_MASK (0x7 << 5) +#define PHYCLKRST_FSEL_PIPE_MASK (0x7 << 8) +#define PHYCLKRST_FSEL(x) ((x) << 5) +#define PHYCLKRST_FSEL_9MHZ6 0x0 +#define PHYCLKRST_FSEL_10MHZ 0x1 +#define PHYCLKRST_FSEL_12MHZ 0x2 +#define PHYCLKRST_FSEL_19MHZ2 0x3 +#define PHYCLKRST_FSEL_20MHZ 0x4 +#define PHYCLKRST_FSEL_24MHZ 0x5 +#define PHYCLKRST_FSEL_50MHZ 0x7 +#define PHYCLKRST_RETENABLEN (1 << 4) +#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) +#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) +#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) +#define USB_DRD_PHYREG0 0x14 +#define USB_DRD_PHYREG1 0x18 +#define USB_DRD_PHYPARAM0 0x1c +#define PHYPARAM0_REF_USE_PAD (1 << 31) +#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) +#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) +#define USB_DRD_PHYPARAM1 0x20 +#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) +#define PHYPARAM1_PCS_TXDEEMPH (0x1c) +#define USB_DRD_PHYTERM 0x24 +#define USB_DRD_PHYTEST 0x28 +#define PHYTEST_POWERDOWN_SSP (1 << 3) +#define PHYTEST_POWERDOWN_HSP (1 << 2) +#define USB_DRD_PHYADP 0x2c +#define USB_DRD_PHYUTMICLKSEL 0x30 +#define PHYUTMICLKSEL_UTMI_CLKSEL (1 << 2) +#define USB_DRD_PHYRESUME 0x34 +#define USB_DRD_LINKPORT 0x44 + +struct usb_phy_softc { + struct resource *res[1]; + bus_space_tag_t bst; + bus_space_handle_t bsh; + device_t dev; +}; + +static struct resource_spec usb_phy_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int +usb_phy_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "samsung,exynos5420-usbdrd-phy")) + return (ENXIO); + + device_set_desc(dev, "Samsung Exynos 5 USB PHY"); + return (BUS_PROBE_DEFAULT); +} + +static int +vbus_on(struct usb_phy_softc *sc) +{ + pcell_t dts_value[3]; + device_t gpio_dev; + phandle_t node; + pcell_t pin; + int len; + + if ((node = ofw_bus_get_node(sc->dev)) == -1) + return (-1); + + /* Power pin */ + if ((len = OF_getproplen(node, "vbus-supply")) <= 0) + return (-1); + OF_getprop(node, "vbus-supply", &dts_value, len); + pin = fdt32_to_cpu(dts_value[0]); + + gpio_dev = devclass_get_device(devclass_find("gpio"), 0); + if (gpio_dev == NULL) { + device_printf(sc->dev, "cant find gpio_dev\n"); + return (1); + } + + GPIO_PIN_SETFLAGS(gpio_dev, pin, GPIO_PIN_OUTPUT); + GPIO_PIN_SET(gpio_dev, pin, GPIO_PIN_HIGH); + + return (0); +} + +static int +usb3_phy_init(struct usb_phy_softc *sc) +{ + int reg; + + /* Reset USB 3.0 PHY */ + WRITE4(sc, USB_DRD_PHYREG0, 0); + + reg = READ4(sc, USB_DRD_PHYPARAM0); + /* PHY CLK src */ + reg &= ~(PHYPARAM0_REF_USE_PAD); + reg &= ~(PHYPARAM0_REF_LOSLEVEL_MASK); + reg |= (PHYPARAM0_REF_LOSLEVEL); + WRITE4(sc, USB_DRD_PHYPARAM0, reg); + WRITE4(sc, USB_DRD_PHYRESUME, 0); + + reg = (LINKSYSTEM_XHCI_VERSION_CTRL | + LINKSYSTEM_FLADJ(0x20)); + WRITE4(sc, USB_DRD_LINKSYSTEM, reg); + + reg = READ4(sc, USB_DRD_PHYPARAM1); + reg &= ~(PHYPARAM1_PCS_TXDEEMPH_MASK); + reg |= (PHYPARAM1_PCS_TXDEEMPH); + WRITE4(sc, USB_DRD_PHYPARAM1, reg); + + reg = READ4(sc, USB_DRD_PHYUTMICLKSEL); + reg |= (PHYUTMICLKSEL_UTMI_CLKSEL); + WRITE4(sc, USB_DRD_PHYUTMICLKSEL, reg); + + reg = READ4(sc, USB_DRD_PHYTEST); + reg &= ~(PHYTEST_POWERDOWN_HSP); + reg &= ~(PHYTEST_POWERDOWN_SSP); + WRITE4(sc, USB_DRD_PHYTEST, reg); + + WRITE4(sc, USB_DRD_PHYUTMI, PHYUTMI_OTGDISABLE); + + /* Clock */ + reg = (PHYCLKRST_REFCLKSEL_EXT_REFCLK); + reg |= (PHYCLKRST_FSEL(PHYCLKRST_FSEL_24MHZ)); + reg |= (PHYCLKRST_MPLL_MLTPR_24MHZ); + reg |= (PHYCLKRST_SSC_REFCLKSEL(0x88)); + reg |= (PHYCLKRST_RETENABLEN | + PHYCLKRST_REF_SSP_EN | /* Super speed */ + PHYCLKRST_SSC_EN | /* Spread spectrum */ + PHYCLKRST_COMMONONN | + PHYCLKRST_PORTRESET); + + WRITE4(sc, USB_DRD_PHYCLKRST, reg); + DELAY(50000); + reg &= ~PHYCLKRST_PORTRESET; + WRITE4(sc, USB_DRD_PHYCLKRST, reg); + + return (0); +} + +static int +usb_phy_attach(device_t dev) +{ + struct usb_phy_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, usb_phy_spec, sc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + /* Memory interface */ + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + vbus_on(sc); + + usbdrd_phy_power_on(); + + DELAY(100); + + usb3_phy_init(sc); + + return (0); +} + +static device_method_t usb_phy_methods[] = { + DEVMETHOD(device_probe, usb_phy_probe), + DEVMETHOD(device_attach, usb_phy_attach), + { 0, 0 } +}; + +static driver_t usb_phy_driver = { + "usb_phy", + usb_phy_methods, + sizeof(struct usb_phy_softc), +}; + +static devclass_t usb_phy_devclass; + +DRIVER_MODULE(usb_phy, simplebus, usb_phy_driver, usb_phy_devclass, 0, 0); diff --git a/sys/arm/samsung/exynos/exynos5_xhci.c b/sys/arm/samsung/exynos/exynos5_xhci.c new file mode 100644 index 0000000..393f442 --- /dev/null +++ b/sys/arm/samsung/exynos/exynos5_xhci.c @@ -0,0 +1,341 @@ +/*- + * Copyright (c) 2014 Ruslan Bukin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_bus.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "opt_platform.h" + +#define GSNPSID 0x20 +#define GSNPSID_MASK 0xffff0000 +#define REVISION_MASK 0xffff +#define GCTL 0x10 +#define GCTL_PWRDNSCALE(n) ((n) << 19) +#define GCTL_U2RSTECN (1 << 16) +#define GCTL_CLK_BUS (0) +#define GCTL_CLK_PIPE (1) +#define GCTL_CLK_PIPEHALF (2) +#define GCTL_CLK_M (3) +#define GCTL_CLK_S (6) +#define GCTL_PRTCAP(n) (((n) & (3 << 12)) >> 12) +#define GCTL_PRTCAPDIR(n) ((n) << 12) +#define GCTL_PRTCAP_HOST 1 +#define GCTL_PRTCAP_DEVICE 2 +#define GCTL_PRTCAP_OTG 3 +#define GCTL_CORESOFTRESET (1 << 11) +#define GCTL_SCALEDOWN_MASK 3 +#define GCTL_SCALEDOWN_SHIFT 4 +#define GCTL_DISSCRAMBLE (1 << 3) +#define GCTL_DSBLCLKGTNG (1 << 0) +#define GHWPARAMS1 0x3c +#define GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) +#define GHWPARAMS1_EN_PWROPT_NO 0 +#define GHWPARAMS1_EN_PWROPT_CLK 1 +#define GUSB2PHYCFG(n) (0x100 + (n * 0x04)) +#define GUSB2PHYCFG_PHYSOFTRST (1 << 31) +#define GUSB2PHYCFG_SUSPHY (1 << 6) +#define GUSB3PIPECTL(n) (0x1c0 + (n * 0x04)) +#define GUSB3PIPECTL_PHYSOFTRST (1 << 31) +#define GUSB3PIPECTL_SUSPHY (1 << 17) + +/* Forward declarations */ +static int exynos_xhci_attach(device_t dev); +static int exynos_xhci_detach(device_t dev); +static int exynos_xhci_probe(device_t dev); + +struct exynos_xhci_softc { + device_t dev; + struct xhci_softc base; + struct resource *res[3]; + bus_space_tag_t bst; + bus_space_handle_t bsh; +}; + +static struct resource_spec exynos_xhci_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_MEMORY, 1, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static device_method_t xhci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, exynos_xhci_probe), + DEVMETHOD(device_attach, exynos_xhci_attach), + DEVMETHOD(device_detach, exynos_xhci_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +/* kobj_class definition */ +static driver_t xhci_driver = { + "xhci", + xhci_methods, + sizeof(struct xhci_softc) +}; + +static devclass_t xhci_devclass; + +DRIVER_MODULE(xhci, simplebus, xhci_driver, xhci_devclass, 0, 0); +MODULE_DEPEND(xhci, usb, 1, 1, 1); + +/* + * Public methods + */ +static int +exynos_xhci_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_is_compatible(dev, "samsung,exynos5250-dwusb3") == 0) + return (ENXIO); + + device_set_desc(dev, "Exynos USB 3.0 controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +dwc3_init(struct exynos_xhci_softc *esc) +{ + struct xhci_softc *sc; + int hwparams1; + int rev; + int reg; + + sc = &esc->base; + + rev = READ4(esc, GSNPSID); + if ((rev & GSNPSID_MASK) != 0x55330000) { + printf("It is not DWC3 controller\n"); + return (-1); + } + + /* Reset controller */ + WRITE4(esc, GCTL, GCTL_CORESOFTRESET); + WRITE4(esc, GUSB3PIPECTL(0), GUSB3PIPECTL_PHYSOFTRST); + WRITE4(esc, GUSB2PHYCFG(0), GUSB2PHYCFG_PHYSOFTRST); + + DELAY(100000); + + reg = READ4(esc, GUSB3PIPECTL(0)); + reg &= ~(GUSB3PIPECTL_PHYSOFTRST); + WRITE4(esc, GUSB3PIPECTL(0), reg); + + reg = READ4(esc, GUSB2PHYCFG(0)); + reg &= ~(GUSB2PHYCFG_PHYSOFTRST); + WRITE4(esc, GUSB2PHYCFG(0), reg); + + reg = READ4(esc, GCTL); + reg &= ~GCTL_CORESOFTRESET; + WRITE4(esc, GCTL, reg); + + hwparams1 = READ4(esc, GHWPARAMS1); + + reg = READ4(esc, GCTL); + reg &= ~(GCTL_SCALEDOWN_MASK << GCTL_SCALEDOWN_SHIFT); + reg &= ~(GCTL_DISSCRAMBLE); + + if (GHWPARAMS1_EN_PWROPT(hwparams1) == \ + GHWPARAMS1_EN_PWROPT_CLK) + reg &= ~(GCTL_DSBLCLKGTNG); + + if ((rev & REVISION_MASK) < 0x190a) + reg |= (GCTL_U2RSTECN); + WRITE4(esc, GCTL, reg); + + /* Set host mode */ + reg = READ4(esc, GCTL); + reg &= ~(GCTL_PRTCAPDIR(GCTL_PRTCAP_OTG)); + reg |= GCTL_PRTCAPDIR(GCTL_PRTCAP_HOST); + WRITE4(esc, GCTL, reg); + + return (0); +} + +static int +exynos_xhci_attach(device_t dev) +{ + struct exynos_xhci_softc *esc; + struct xhci_softc *sc; + bus_space_handle_t bsh; + int err; + + esc = device_get_softc(dev); + esc->dev = dev; + + sc = &esc->base; + + if (xhci_init(sc, dev)) { + device_printf(dev, "Could not initialize softc\n"); + return (ENXIO); + } + + if (bus_alloc_resources(dev, exynos_xhci_spec, esc->res)) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + usb_callout_init_mtx(&sc->sc_callout, &sc->sc_bus.bus_mtx, 0); + + /* XHCI registers */ + sc->sc_io_tag = rman_get_bustag(esc->res[0]); + bsh = rman_get_bushandle(esc->res[0]); + sc->sc_io_size = rman_get_size(esc->res[0]); + + /* DWC3 ctrl registers */ + esc->bst = rman_get_bustag(esc->res[1]); + esc->bsh = rman_get_bushandle(esc->res[1]); + + /* + * Set handle to USB related registers subregion used by + * generic XHCI driver. + */ + err = bus_space_subregion(sc->sc_io_tag, bsh, 0x0, + sc->sc_io_size, &sc->sc_io_hdl); + if (err != 0) + return (ENXIO); + + /* Setup interrupt handler */ + err = bus_setup_intr(dev, esc->res[2], INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)xhci_interrupt, sc, + &sc->sc_intr_hdl); + if (err) { + device_printf(dev, "Could not setup irq, %d\n", err); + return (1); + } + + /* Add USB device */ + sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!sc->sc_bus.bdev) { + device_printf(dev, "Could not add USB device\n"); + err = bus_teardown_intr(dev, esc->res[2], + sc->sc_intr_hdl); + if (err) + device_printf(dev, "Could not tear down irq," + " %d\n", err); + return (1); + } + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + strlcpy(sc->sc_vendor, "Samsung", sizeof(sc->sc_vendor)); + + dwc3_init(esc); + + err = xhci_halt_controller(sc); + if (err == 0) { + device_printf(dev, "Starting controller\n"); + err = xhci_start_controller(sc); + } + + if (err == 0) { + device_printf(dev, "Controller started\n"); + err = device_probe_and_attach(sc->sc_bus.bdev); + } + + if (err == 0) { + device_printf(dev, "Attached success\n"); + } else { + device_printf(dev, "USB 3.0 init failed err=%d\n", err); + + device_delete_child(dev, sc->sc_bus.bdev); + sc->sc_bus.bdev = NULL; + + err = bus_teardown_intr(dev, esc->res[2], + sc->sc_intr_hdl); + if (err) + device_printf(dev, "Could not tear down irq," + " %d\n", err); + return (1); + } + return (0); +} + +static int +exynos_xhci_detach(device_t dev) +{ + struct exynos_xhci_softc *esc; + struct xhci_softc *sc; + int err; + + esc = device_get_softc(dev); + sc = &esc->base; + + if (esc->res[2] && sc->sc_intr_hdl) { + err = bus_teardown_intr(dev, esc->res[2], + sc->sc_intr_hdl); + if (err) { + device_printf(dev, "Could not tear down irq," + " %d\n", err); + return (err); + } + sc->sc_intr_hdl = NULL; + } + + if (sc->sc_bus.bdev) { + device_delete_child(dev, sc->sc_bus.bdev); + sc->sc_bus.bdev = NULL; + } + + /* During module unload there are lots of children leftover */ + device_delete_children(dev); + + bus_release_resources(dev, exynos_xhci_spec, esc->res); + + return (0); +} diff --git a/sys/arm/samsung/exynos/files.exynos5 b/sys/arm/samsung/exynos/files.exynos5 index 90e1c9b..8dc6602 100644 --- a/sys/arm/samsung/exynos/files.exynos5 +++ b/sys/arm/samsung/exynos/files.exynos5 @@ -21,11 +21,16 @@ arm/samsung/exynos/exynos5_combiner.c standard arm/samsung/exynos/exynos5_pad.c optional gpio arm/samsung/exynos/exynos_uart.c optional uart arm/samsung/exynos/exynos5_ehci.c optional ehci +arm/samsung/exynos/exynos5_xhci.c optional xhci arm/samsung/exynos/exynos5_fimd.c optional vt arm/samsung/exynos/exynos5_i2c.c optional iicbus +arm/samsung/exynos/exynos5_usb_phy.c standard +arm/samsung/exynos/exynos5_pmu.c standard +arm/samsung/exynos/exynos5_spi.c optional exynos_spi # chromebook drivers -arm/samsung/exynos/chrome_ec.c optional chrome_ec +arm/samsung/exynos/chrome_ec.c optional chrome_ec_i2c +arm/samsung/exynos/chrome_ec_spi.c optional chrome_ec_spi arm/samsung/exynos/chrome_kb.c optional chrome_kb #dev/sdhci/sdhci_fdt.c optional sdhci -- cgit v1.1