Index: head/sys/arm/amlogic/aml8726/aml8726_ccm.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_ccm.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_ccm.c (revision 280905) @@ -0,0 +1,231 @@ +/*- + * Copyright 2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 clock control module driver. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + + +struct aml8726_ccm_softc { + device_t dev; + struct aml8726_ccm_function *soc; + struct resource *res[1]; + struct mtx mtx; +}; + +static struct resource_spec aml8726_ccm_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define AML_CCM_LOCK(sc) mtx_lock(&(sc)->mtx) +#define AML_CCM_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define AML_CCM_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "ccm", MTX_DEF) +#define AML_CCM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) + +static int +aml8726_ccm_configure_gates(struct aml8726_ccm_softc *sc) +{ + struct aml8726_ccm_function *f; + struct aml8726_ccm_gate *g; + char *function_name; + char *functions; + phandle_t node; + ssize_t len; + uint32_t value; + + node = ofw_bus_get_node(sc->dev); + + len = OF_getprop_alloc(node, "functions", sizeof(char), + (void **)&functions); + + if (len < 0) { + device_printf(sc->dev, "missing functions attribute in FDT\n"); + return (ENXIO); + } + + function_name = functions; + + while (len) { + for (f = sc->soc; f->name != NULL; f++) + if (strncmp(f->name, function_name, len) == 0) + break; + + if (f->name == NULL) { + /* display message prior to queuing up next string */ + device_printf(sc->dev, + "unknown function attribute %.*s in FDT\n", + len, function_name); + } + + /* queue up next string */ + while (*function_name && len) { + function_name++; + len--; + } + if (len) { + function_name++; + len--; + } + + if (f->name == NULL) + continue; + + AML_CCM_LOCK(sc); + + /* + * Enable the clock gates necessary for the function. + * + * In some cases a clock may be shared across functions + * (meaning don't disable a clock without ensuring that + * it's not required by someone else). + */ + for (g = f->gates; g->bits != 0x00000000; g++) { + value = CSR_READ_4(sc, g->addr); + value |= g->bits; + CSR_WRITE_4(sc, g->addr, value); + } + + AML_CCM_UNLOCK(sc); + } + + free(functions, M_OFWPROP); + + return (0); +} + +static int +aml8726_ccm_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-ccm")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 ccm"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_ccm_attach(device_t dev) +{ + struct aml8726_ccm_softc *sc = device_get_softc(dev); + + sc->dev = dev; + + switch (aml8726_soc_hw_rev) { + case AML_SOC_HW_REV_M3: + sc->soc = aml8726_m3_ccm; + break; + case AML_SOC_HW_REV_M6: + sc->soc = aml8726_m6_ccm; + break; + case AML_SOC_HW_REV_M8: + sc->soc = aml8726_m8_ccm; + break; + case AML_SOC_HW_REV_M8B: + sc->soc = aml8726_m8b_ccm; + break; + default: + device_printf(dev, "unsupported SoC\n"); + return (ENXIO); + /* NOTREACHED */ + break; + } + + if (bus_alloc_resources(dev, aml8726_ccm_spec, sc->res)) { + device_printf(dev, "can not allocate resources for device\n"); + return (ENXIO); + } + + AML_CCM_LOCK_INIT(sc); + + return (aml8726_ccm_configure_gates(sc)); +} + +static int +aml8726_ccm_detach(device_t dev) +{ + struct aml8726_ccm_softc *sc = device_get_softc(dev); + + AML_CCM_LOCK_DESTROY(sc); + + bus_release_resources(dev, aml8726_ccm_spec, sc->res); + + return (0); +} + +static device_method_t aml8726_ccm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_ccm_probe), + DEVMETHOD(device_attach, aml8726_ccm_attach), + DEVMETHOD(device_detach, aml8726_ccm_detach), + + DEVMETHOD_END +}; + +static driver_t aml8726_ccm_driver = { + "ccm", + aml8726_ccm_methods, + sizeof(struct aml8726_ccm_softc), +}; + +static devclass_t aml8726_ccm_devclass; + +EARLY_DRIVER_MODULE(ccm, simplebus, aml8726_ccm_driver, + aml8726_ccm_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_ccm.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_ccm.h =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_ccm.h (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_ccm.h (revision 280905) @@ -0,0 +1,325 @@ +/*- + * Copyright 2015 John Wehle + * 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$ + */ + +#ifndef _ARM_AMLOGIC_AML8726_CCM_H +#define _ARM_AMLOGIC_AML8726_CCM_H + + +struct aml8726_ccm_gate { + uint32_t addr; + uint32_t bits; +}; + +struct aml8726_ccm_function { + const char *name; + struct aml8726_ccm_gate *gates; +}; + + +/* + * aml8726-m3 + */ + +static struct aml8726_ccm_gate aml8726_m3_ethernet[] = { + { 4, 0x00000008 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m3_i2c[] = { + { 0, 0x00000200 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m3_rng[] = { + { 0, 0x00001000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m3_sdio[] = { + { 0, 0x00020000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m3_sdxc[] = { + { 0, 0x00004000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m3_uart_a[] = { + { 0, 0x00002000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m3_uart_b[] = { + { 4, 0x00010000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m3_uart_c[] = { + { 8, 0x00008000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m3_usb_a[] = { + { 4, 0x00200000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m3_usb_b[] = { + { 4, 0x00400000 }, + { 0, 0x00000000 } +}; + +struct aml8726_ccm_function aml8726_m3_ccm[] = { + { "ethernet", aml8726_m3_ethernet }, + { "i2c", aml8726_m3_i2c }, + { "rng", aml8726_m3_rng }, + { "sdio", aml8726_m3_sdio }, + { "sdxc", aml8726_m3_sdxc }, + { "uart-a", aml8726_m3_uart_a }, + { "uart-b", aml8726_m3_uart_b }, + { "uart-c", aml8726_m3_uart_c }, + { "usb-a", aml8726_m3_usb_a }, + { "usb-b", aml8726_m3_usb_b }, + { NULL } +}; + + +/* + * aml8726-m6 + */ + +static struct aml8726_ccm_gate aml8726_m6_ethernet[] = { + { 4, 0x00000008 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m6_i2c[] = { + { 0, 0x00000200 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m6_rng[] = { + { 0, 0x00001000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m6_sdio[] = { + { 0, 0x00020000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m6_sdxc[] = { + { 0, 0x00004000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m6_uart_a[] = { + { 0, 0x00002000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m6_uart_b[] = { + { 4, 0x00010000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m6_uart_c[] = { + { 8, 0x00008000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m6_usb_a[] = { + { 4, 0x00200000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m6_usb_b[] = { + { 4, 0x00400000 }, + { 0, 0x00000000 } +}; + +struct aml8726_ccm_function aml8726_m6_ccm[] = { + { "ethernet", aml8726_m6_ethernet }, + { "i2c", aml8726_m6_i2c }, + { "rng", aml8726_m6_rng }, + { "sdio", aml8726_m6_sdio }, + { "sdxc", aml8726_m6_sdxc }, + { "uart-a", aml8726_m6_uart_a }, + { "uart-b", aml8726_m6_uart_b }, + { "uart-c", aml8726_m6_uart_c }, + { "usb-a", aml8726_m6_usb_a }, + { "usb-b", aml8726_m6_usb_b }, + { NULL } +}; + + +/* + * aml8726-m8 + */ + +static struct aml8726_ccm_gate aml8726_m8_ethernet[] = { + { 4, 0x00000008 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8_i2c[] = { + { 0, 0x00000200 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8_rng[] = { + { 0, 0x00001000 }, + { 16, 0x00200000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8_sdio[] = { + { 0, 0x00020000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8_sdxc[] = { + { 0, 0x00004000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8_uart_a[] = { + { 0, 0x00002000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8_uart_b[] = { + { 4, 0x00010000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8_uart_c[] = { + { 8, 0x00008000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8_usb_a[] = { + { 4, 0x00200000 }, + { 4, 0x04000000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8_usb_b[] = { + { 4, 0x00400000 }, + { 4, 0x04000000 }, + { 0, 0x00000000 } +}; + +struct aml8726_ccm_function aml8726_m8_ccm[] = { + { "ethernet", aml8726_m8_ethernet }, + { "i2c", aml8726_m8_i2c }, + { "rng", aml8726_m8_rng }, + { "sdio", aml8726_m8_sdio }, + { "sdxc", aml8726_m8_sdxc }, + { "uart-a", aml8726_m8_uart_a }, + { "uart-b", aml8726_m8_uart_b }, + { "uart-c", aml8726_m8_uart_c }, + { "usb-a", aml8726_m8_usb_a }, + { "usb-b", aml8726_m8_usb_b }, + { NULL } +}; + + +/* + * aml8726-m8b + */ + +static struct aml8726_ccm_gate aml8726_m8b_ethernet[] = { + { 4, 0x00000008 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8b_i2c[] = { + { 0, 0x00000200 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8b_rng[] = { + { 0, 0x00001000 }, + { 16, 0x00200000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8b_sdio[] = { + { 0, 0x00020000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8b_sdxc[] = { + { 0, 0x00004000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8b_uart_a[] = { + { 0, 0x00002000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8b_uart_b[] = { + { 4, 0x00010000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8b_uart_c[] = { + { 8, 0x00008000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8b_usb_a[] = { + { 4, 0x00200000 }, + { 4, 0x04000000 }, + { 0, 0x00000000 } +}; + +static struct aml8726_ccm_gate aml8726_m8b_usb_b[] = { + { 4, 0x00400000 }, + { 4, 0x04000000 }, + { 0, 0x00000000 } +}; + +struct aml8726_ccm_function aml8726_m8b_ccm[] = { + { "ethernet", aml8726_m8b_ethernet }, + { "i2c", aml8726_m8b_i2c }, + { "rng", aml8726_m8b_rng }, + { "sdio", aml8726_m8b_sdio }, + { "sdxc", aml8726_m8b_sdxc }, + { "uart-a", aml8726_m8b_uart_a }, + { "uart-b", aml8726_m8b_uart_b }, + { "uart-c", aml8726_m8b_uart_c }, + { "usb-a", aml8726_m8b_usb_a }, + { "usb-b", aml8726_m8b_usb_b }, + { NULL } +}; + +#endif /* _ARM_AMLOGIC_AML8726_CCM_H */ Property changes on: head/sys/arm/amlogic/aml8726/aml8726_ccm.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_clkmsr.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_clkmsr.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_clkmsr.c (revision 280905) @@ -0,0 +1,271 @@ +/*- + * Copyright 2014-2015 John Wehle + * 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. + * + */ + +/* + * Amlogic aml8726 clock measurement driver. + * + * This allows various clock rates to be determine at runtime. + * The measurements are done once and are not expected to change + * (i.e. FDT fixup provides clk81 as bus-frequency to the MMC + * and UART drivers which use the value when programming the + * hardware). + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + + +static struct aml8726_clkmsr_clk { + const char * name; + uint32_t mux; +} aml8726_clkmsr_clks[] = { + { "clk81", 7 }, +}; + +#define AML_CLKMSR_CLK81 0 + +#define AML_CLKMSR_NCLKS (sizeof(aml8726_clkmsr_clks) \ + / sizeof(aml8726_clkmsr_clks[0])) + +struct aml8726_clkmsr_softc { + device_t dev; + struct resource * res[1]; +}; + +static struct resource_spec aml8726_clkmsr_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +/* + * Duration can range from 1uS to 65535 uS and should be chosen + * based on the expected frequency result so to maximize resolution + * and avoid overflowing the 16 bit result counter. + */ +#define AML_CLKMSR_DURATION 32 + +#define AML_CLKMSR_DUTY_REG 0 +#define AML_CLKMSR_0_REG 4 +#define AML_CLKMSR_0_BUSY (1U << 31) +#define AML_CLKMSR_0_MUX_MASK (0x3f << 20) +#define AML_CLKMSR_0_MUX_SHIFT 20 +#define AML_CLKMSR_0_MUX_EN (1 << 19) +#define AML_CLKMSR_0_MEASURE (1 << 16) +#define AML_CLKMSR_0_DURATION_MASK 0xffff +#define AML_CLKMSR_0_DURATION_SHIFT 0 +#define AML_CLKMSR_1_REG 8 +#define AML_CLKMSR_2_REG 12 +#define AML_CLKMSR_2_RESULT_MASK 0xffff +#define AML_CLKMSR_2_RESULT_SHIFT 0 + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) +#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ + (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +static int +aml8726_clkmsr_clock_frequency(struct aml8726_clkmsr_softc *sc, unsigned clock) +{ + uint32_t value; + + if (clock >= AML_CLKMSR_NCLKS) + return (0); + + /* + * Locking is not used as this is only expected to be called from + * FDT fixup (which occurs prior to driver initialization) or attach. + */ + + CSR_WRITE_4(sc, AML_CLKMSR_0_REG, 0); + + CSR_BARRIER(sc, AML_CLKMSR_0_REG); + + value = (aml8726_clkmsr_clks[clock].mux << AML_CLKMSR_0_MUX_SHIFT) + | ((AML_CLKMSR_DURATION - 1) << AML_CLKMSR_0_DURATION_SHIFT) + | AML_CLKMSR_0_MUX_EN + | AML_CLKMSR_0_MEASURE; + CSR_WRITE_4(sc, AML_CLKMSR_0_REG, value); + + CSR_BARRIER(sc, AML_CLKMSR_0_REG); + + while ((CSR_READ_4(sc, AML_CLKMSR_0_REG) & AML_CLKMSR_0_BUSY) != 0) + cpu_spinwait(); + + value &= ~AML_CLKMSR_0_MEASURE; + CSR_WRITE_4(sc, AML_CLKMSR_0_REG, value); + + CSR_BARRIER(sc, AML_CLKMSR_0_REG); + + value = (((CSR_READ_4(sc, AML_CLKMSR_2_REG) & AML_CLKMSR_2_RESULT_MASK) + >> AML_CLKMSR_2_RESULT_SHIFT) + AML_CLKMSR_DURATION / 2) / + AML_CLKMSR_DURATION; + + return value; +} + +static int +aml8726_clkmsr_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-clkmsr")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 clkmsr"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_clkmsr_attach(device_t dev) +{ + struct aml8726_clkmsr_softc *sc = device_get_softc(dev); + int freq; + + sc->dev = dev; + + if (bus_alloc_resources(dev, aml8726_clkmsr_spec, sc->res)) { + device_printf(dev, "can not allocate resources for device\n"); + return (ENXIO); + } + + freq = aml8726_clkmsr_clock_frequency(sc, AML_CLKMSR_CLK81); + device_printf(sc->dev, "bus clock %u MHz\n", freq); + + return (0); +} + +static int +aml8726_clkmsr_detach(device_t dev) +{ + struct aml8726_clkmsr_softc *sc = device_get_softc(dev); + + bus_release_resources(dev, aml8726_clkmsr_spec, sc->res); + + return (0); +} + + +static device_method_t aml8726_clkmsr_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_clkmsr_probe), + DEVMETHOD(device_attach, aml8726_clkmsr_attach), + DEVMETHOD(device_detach, aml8726_clkmsr_detach), + + DEVMETHOD_END +}; + +static driver_t aml8726_clkmsr_driver = { + "clkmsr", + aml8726_clkmsr_methods, + sizeof(struct aml8726_clkmsr_softc), +}; + +static devclass_t aml8726_clkmsr_devclass; + +DRIVER_MODULE(clkmsr, simplebus, aml8726_clkmsr_driver, + aml8726_clkmsr_devclass, 0, 0); + +int +aml8726_clkmsr_bus_frequency() +{ + struct resource mem; + struct aml8726_clkmsr_softc sc; + phandle_t node; + u_long pbase, psize; + u_long start, size; + int freq; + + /* + * Try to access the clkmsr node directly i.e. through /aliases/. + */ + + if ((node = OF_finddevice("clkmsr")) != 0) + if (fdt_is_compatible_strict(node, "amlogic,aml8726-clkmsr")) + goto moveon; + + /* + * Find the node the long way. + */ + if ((node = OF_finddevice("/soc")) == 0) + return (0); + + if ((node = fdt_find_compatible(node, + "amlogic,aml8726-clkmsr", 1)) == 0) + return (0); + +moveon: + if (fdt_get_range(OF_parent(node), 0, &pbase, &psize) != 0 + || fdt_regsize(node, &start, &size) != 0) + return (0); + + start += pbase; + + memset(&mem, 0, sizeof(mem)); + + mem.r_bustag = fdtbus_bs_tag; + + if (bus_space_map(mem.r_bustag, start, size, 0, &mem.r_bushandle) != 0) + return (0); + + /* + * Build an incomplete (however sufficient for the purpose + * of calling aml8726_clkmsr_clock_frequency) softc. + */ + + memset(&sc, 0, sizeof(sc)); + + sc.res[0] = &mem; + + freq = aml8726_clkmsr_clock_frequency(&sc, AML_CLKMSR_CLK81) * 1000000; + + bus_space_unmap(mem.r_bustag, mem.r_bushandle, size); + + return (freq); +} Property changes on: head/sys/arm/amlogic/aml8726/aml8726_clkmsr.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_clkmsr.h =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_clkmsr.h (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_clkmsr.h (revision 280905) @@ -0,0 +1,34 @@ +/*- + * Copyright 2014 John Wehle + * 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$ + */ + +#ifndef _ARM_AMLOGIC_AML8726_CLKMSR_H +#define _ARM_AMLOGIC_AML8726_CLKMSR_H + +int aml8726_clkmsr_bus_frequency(void); + +#endif /* _ARM_AMLOGIC_AML8726_CLKMSR_H */ Property changes on: head/sys/arm/amlogic/aml8726/aml8726_clkmsr.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_fb.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_fb.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_fb.c (revision 280905) @@ -0,0 +1,468 @@ +/*- + * Copyright 2013-2014 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 frame buffer driver. + * + * The current implementation has limited flexibility. + * For example only progressive scan is supported when + * using HDMI and the resolution / frame rate is not + * negotiated. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include "fb_if.h" + + +enum aml8726_fb_output { + aml8726_unknown_fb_output, + aml8726_cvbs_fb_output, + aml8726_hdmi_fb_output, + aml8726_lcd_fb_output +}; + +struct aml8726_fb_clk { + uint32_t freq; + uint32_t video_pre; + uint32_t video_post; + uint32_t video_x; + uint32_t hdmi_tx; + uint32_t encp; + uint32_t enci; + uint32_t enct; + uint32_t encl; + uint32_t vdac0; + uint32_t vdac1; +}; + +struct aml8726_fb_softc { + device_t dev; + struct resource *res[4]; + struct mtx mtx; + void *ih_cookie; + struct fb_info info; + enum aml8726_fb_output output; + struct aml8726_fb_clk clk; +}; + +static struct resource_spec aml8726_fb_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* CANVAS */ + { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* VIU */ + { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* VPP */ + { SYS_RES_IRQ, 1, RF_ACTIVE }, /* INT_VIU_VSYNC */ + { -1, 0 } +}; + +#define AML_FB_LOCK(sc) mtx_lock(&(sc)->mtx) +#define AML_FB_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define AML_FB_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "fb", MTX_DEF) +#define AML_FB_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define CAV_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CAV_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) +#define CAV_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ + (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +#define VIU_WRITE_4(sc, reg, val) bus_write_4((sc)->res[1], reg, (val)) +#define VIU_READ_4(sc, reg) bus_read_4((sc)->res[1], reg) + +#define VPP_WRITE_4(sc, reg, val) bus_write_4((sc)->res[2], reg, (val)) +#define VPP_READ_4(sc, reg) bus_read_4((sc)->res[2], reg) + +#define CLK_WRITE_4(sc, reg, val) bus_write_4((sc)->res[X], reg, (val)) +#define CLK_READ_4(sc, reg) bus_read_4((sc)->res[X], reg) + +#define AML_FB_CLK_FREQ_SD 1080 +#define AML_FB_CLK_FREQ_HD 1488 + +static void +aml8726_fb_cfg_output(struct aml8726_fb_softc *sc) +{ + /* XXX */ +} + +static void +aml8726_fb_cfg_video(struct aml8726_fb_softc *sc) +{ + uint32_t value; + + /* + * basic initialization + * + * The fifo depth is in units of 8 so programming 32 + * sets the depth to 256. + */ + + value = (32 << AML_VIU_OSD_FIFO_CTRL_DEPTH_SHIFT); + value |= AML_VIU_OSD_FIFO_CTRL_BURST_LEN_64; + value |= (4 << AML_VIU_OSD_FIFO_CTRL_HOLD_LINES_SHIFT); + + VIU_WRITE_4(sc, AML_VIU_OSD1_FIFO_CTRL_REG, value); + VIU_WRITE_4(sc, AML_VIU_OSD2_FIFO_CTRL_REG, value); + + value = VPP_READ_4(sc, AML_VPP_MISC_REG); + + value &= ~AML_VPP_MISC_PREBLEND_EN; + value |= AML_VPP_MISC_POSTBLEND_EN; + value &= ~(AML_VPP_MISC_OSD1_POSTBLEND | AML_VPP_MISC_OSD2_POSTBLEND + | AML_VPP_MISC_VD1_POSTBLEND | AML_VPP_MISC_VD2_POSTBLEND); + + VPP_WRITE_4(sc, AML_VPP_MISC_REG, value); + + value = AML_VIU_OSD_CTRL_OSD_EN; + value |= (0xff << AML_VIU_OSD_CTRL_GLOBAL_ALPHA_SHIFT); + + VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value); + VIU_WRITE_4(sc, AML_VIU_OSD2_CTRL_REG, value); + + /* color mode for OSD1 block 0 */ + + value = (AML_CAV_OSD1_INDEX << AML_VIU_OSD_BLK_CFG_W0_INDEX_SHIFT) + | AML_VIU_OSD_BLK_CFG_W0_LITTLE_ENDIAN + | AML_VIU_OSD_BLK_CFG_W0_BLKMODE_24 + | AML_VIU_OSD_BLK_CFG_W0_RGB_EN + | AML_VIU_OSD_BLK_CFG_W0_CMATRIX_RGB; + + VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W0_REG, value); + + /* geometry / scaling for OSD1 block 0 */ + + value = ((sc->info.fb_width - 1) << AML_VIU_OSD_BLK_CFG_W1_X_END_SHIFT) + & AML_VIU_OSD_BLK_CFG_W1_X_END_MASK; + value |= (0 << AML_VIU_OSD_BLK_CFG_W1_X_START_SHIFT) + & AML_VIU_OSD_BLK_CFG_W1_X_START_MASK; + + VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W1_REG, value); + + value = ((sc->info.fb_height - 1) << AML_VIU_OSD_BLK_CFG_W2_Y_END_SHIFT) + & AML_VIU_OSD_BLK_CFG_W2_Y_END_MASK; + value |= (0 << AML_VIU_OSD_BLK_CFG_W2_Y_START_SHIFT) + & AML_VIU_OSD_BLK_CFG_W2_Y_START_MASK; + + VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W2_REG, value); + + value = ((sc->info.fb_width - 1) << AML_VIU_OSD_BLK_CFG_W3_H_END_SHIFT) + & AML_VIU_OSD_BLK_CFG_W3_H_END_MASK; + value |= (0 << AML_VIU_OSD_BLK_CFG_W3_H_START_SHIFT) + & AML_VIU_OSD_BLK_CFG_W3_H_START_MASK; + + VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W3_REG, value); + + value = ((sc->info.fb_height - 1) << AML_VIU_OSD_BLK_CFG_W4_V_END_SHIFT) + & AML_VIU_OSD_BLK_CFG_W4_V_END_MASK; + value |= (0 << AML_VIU_OSD_BLK_CFG_W4_V_START_SHIFT) + & AML_VIU_OSD_BLK_CFG_W4_V_START_MASK; + + VIU_WRITE_4(sc, AML_VIU_OSD1_BLK0_CFG_W4_REG, value); + + /* Enable the OSD block now that it's fully configured */ + + value = VIU_READ_4(sc, AML_VIU_OSD1_CTRL_REG); + + value &= ~AML_VIU_OSD_CTRL_OSD_BLK_EN_MASK; + value |= 1 << AML_VIU_OSD_CTRL_OSD_BLK_EN_SHIFT; + + VIU_WRITE_4(sc, AML_VIU_OSD1_CTRL_REG, value); + + /* enable video processing of OSD1 */ + + value = VPP_READ_4(sc, AML_VPP_MISC_REG); + + value |= AML_VPP_MISC_OSD1_POSTBLEND; + + VPP_WRITE_4(sc, AML_VPP_MISC_REG, value); +} + +static void +aml8726_fb_cfg_canvas(struct aml8726_fb_softc *sc) +{ + uint32_t value; + uint32_t width; + + /* + * The frame buffer address and width are programmed in units of 8 + * (meaning they need to be aligned and the actual values divided + * by 8 prior to programming the hardware). + */ + + width = (uint32_t)sc->info.fb_stride / 8; + + /* lower bits of the width */ + value = (width << AML_CAV_LUT_DATAL_WIDTH_SHIFT) & + AML_CAV_LUT_DATAL_WIDTH_MASK; + + /* physical address */ + value |= (uint32_t)sc->info.fb_pbase / 8; + + CAV_WRITE_4(sc, AML_CAV_LUT_DATAL_REG, value); + + /* upper bits of the width */ + value = ((width >> AML_CAV_LUT_DATAL_WIDTH_WIDTH) << + AML_CAV_LUT_DATAH_WIDTH_SHIFT) & AML_CAV_LUT_DATAH_WIDTH_MASK; + + /* height */ + value |= ((uint32_t)sc->info.fb_height << + AML_CAV_LUT_DATAH_HEIGHT_SHIFT) & AML_CAV_LUT_DATAH_HEIGHT_MASK; + + /* mode */ + value |= AML_CAV_LUT_DATAH_BLKMODE_LINEAR; + + CAV_WRITE_4(sc, AML_CAV_LUT_DATAH_REG, value); + + CAV_WRITE_4(sc, AML_CAV_LUT_ADDR_REG, (AML_CAV_LUT_ADDR_WR_EN | + (AML_CAV_OSD1_INDEX << AML_CAV_LUT_ADDR_INDEX_SHIFT))); + + CAV_BARRIER(sc, AML_CAV_LUT_ADDR_REG); +} + +static void +aml8726_fb_intr(void *arg) +{ + struct aml8726_fb_softc *sc = (struct aml8726_fb_softc *)arg; + + AML_FB_LOCK(sc); + + AML_FB_UNLOCK(sc); +} + +static int +aml8726_fb_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-fb")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 FB"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_fb_attach(device_t dev) +{ + struct aml8726_fb_softc *sc = device_get_softc(dev); + int error; + device_t child; + pcell_t prop; + phandle_t node; + + sc->dev = dev; + + sc->info.fb_name = device_get_nameunit(sc->dev); + + node = ofw_bus_get_node(dev); + + if (OF_getencprop(node, "width", &prop, sizeof(prop)) <= 0) { + device_printf(dev, "missing width attribute in FDT\n"); + return (ENXIO); + } + if ((prop % 8) != 0) { + device_printf(dev, + "width attribute in FDT must be a multiple of 8\n"); + return (ENXIO); + } + sc->info.fb_width = prop; + + if (OF_getencprop(node, "height", &prop, sizeof(prop)) <= 0) { + device_printf(dev, "missing height attribute in FDT\n"); + return (ENXIO); + } + sc->info.fb_height = prop; + + if (OF_getencprop(node, "depth", &prop, sizeof(prop)) <= 0) { + device_printf(dev, "missing depth attribute in FDT\n"); + return (ENXIO); + } + if (prop != 24) { + device_printf(dev, + "depth attribute in FDT is an unsupported value\n"); + return (ENXIO); + } + sc->info.fb_depth = prop; + sc->info.fb_bpp = prop; + + if (OF_getencprop(node, "linebytes", &prop, sizeof(prop)) <= 0) { + device_printf(dev, "missing linebytes attribute in FDT\n"); + return (ENXIO); + } + if ((prop % 8) != 0) { + device_printf(dev, + "linebytes attribute in FDT must be a multiple of 8\n"); + return (ENXIO); + } + if (prop < (sc->info.fb_width * 3)) { + device_printf(dev, + "linebytes attribute in FDT is too small\n"); + return (ENXIO); + } + sc->info.fb_stride = prop; + + if (OF_getencprop(node, "address", &prop, sizeof(prop)) <= 0) { + device_printf(dev, "missing address attribute in FDT\n"); + return (ENXIO); + } + if ((prop % 8) != 0) { + device_printf(dev, + "address attribute in FDT must be a multiple of 8\n"); + return (ENXIO); + } + sc->info.fb_pbase = prop; + sc->info.fb_size = sc->info.fb_height * sc->info.fb_stride; + sc->info.fb_vbase = (intptr_t)pmap_mapdev(sc->info.fb_pbase, + sc->info.fb_size); + + if (bus_alloc_resources(dev, aml8726_fb_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); + return (ENXIO); + } + + aml8726_fb_cfg_output(sc); + + aml8726_fb_cfg_video(sc); + + aml8726_fb_cfg_canvas(sc); + + AML_FB_LOCK_INIT(sc); + + error = bus_setup_intr(dev, sc->res[3], INTR_TYPE_MISC | INTR_MPSAFE, + NULL, aml8726_fb_intr, sc, &sc->ih_cookie); + + if (error) { + device_printf(dev, "could not setup interrupt handler\n"); + goto fail; + } + + child = device_add_child(dev, "fbd", device_get_unit(dev)); + + if (!child) { + device_printf(dev, "could not add fbd\n"); + error = ENXIO; + goto fail; + } + + error = device_probe_and_attach(child); + + if (error) { + device_printf(dev, "could not attach fbd\n"); + goto fail; + } + + return (0); + +fail: + if (sc->ih_cookie) + bus_teardown_intr(dev, sc->res[3], sc->ih_cookie); + + AML_FB_LOCK_DESTROY(sc); + + bus_release_resources(dev, aml8726_fb_spec, sc->res); + + pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); + + return (error); +} + +static int +aml8726_fb_detach(device_t dev) +{ + struct aml8726_fb_softc *sc = device_get_softc(dev); + + bus_generic_detach(dev); + + bus_teardown_intr(dev, sc->res[3], sc->ih_cookie); + + AML_FB_LOCK_DESTROY(sc); + + bus_release_resources(dev, aml8726_fb_spec, sc->res); + + pmap_unmapdev(sc->info.fb_vbase, sc->info.fb_size); + + return (0); +} + +static struct fb_info * +aml8726_fb_getinfo(device_t dev) +{ + struct aml8726_fb_softc *sc = device_get_softc(dev); + + return (&sc->info); +} + +static device_method_t aml8726_fb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_fb_probe), + DEVMETHOD(device_attach, aml8726_fb_attach), + DEVMETHOD(device_detach, aml8726_fb_detach), + + /* FB interface */ + DEVMETHOD(fb_getinfo, aml8726_fb_getinfo), + + DEVMETHOD_END +}; + +static driver_t aml8726_fb_driver = { + "fb", + aml8726_fb_methods, + sizeof(struct aml8726_fb_softc), +}; + +static devclass_t aml8726_fb_devclass; + +DRIVER_MODULE(fb, simplebus, aml8726_fb_driver, aml8726_fb_devclass, 0, 0); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_fb.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_fb.h =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_fb.h (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_fb.h (revision 280905) @@ -0,0 +1,163 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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$ + */ + +#ifndef _ARM_AMLOGIC_AML8726_FB_H +#define _ARM_AMLOGIC_AML8726_FB_H + + +#define AML_CAV_OSD1_INDEX 0x40 + +#define AML_CAV_LUT_DATAL_REG 0 +#define AML_CAV_LUT_DATAL_WIDTH_MASK (7 << 29) +#define AML_CAV_LUT_DATAL_WIDTH_SHIFT 29 +#define AML_CAV_LUT_DATAL_WIDTH_WIDTH 3 +#define AML_CAV_LUT_DATAL_ADDR_MASK 0x1fffffff +#define AML_CAV_LUT_DATAL_ADDR_SHIFT 0 + +#define AML_CAV_LUT_DATAH_REG 4 +#define AML_CAV_LUT_DATAH_BLKMODE_MASK (3 << 24) +#define AML_CAV_LUT_DATAH_BLKMODE_SHIFT 24 +#define AML_CAV_LUT_DATAH_BLKMODE_LINEAR (0 << 24) +#define AML_CAV_LUT_DATAH_BLKMODE_32x32 (1 << 24) +#define AML_CAV_LUT_DATAH_BLKMODE_64x32 (2 << 24) +#define AML_CAV_LUT_DATAH_WRAP_X (1 << 23) +#define AML_CAV_LUT_DATAH_WRAP_Y (1 << 22) +#define AML_CAV_LUT_DATAH_HEIGHT_MASK (0x1fff << 9) +#define AML_CAV_LUT_DATAH_HEIGHT_SHIFT 9 +#define AML_CAV_LUT_DATAH_WIDTH_MASK 0x1ff +#define AML_CAV_LUT_DATAH_WIDTH_SHIFT 0 + +#define AML_CAV_LUT_ADDR_REG 8 +#define AML_CAV_LUT_ADDR_WR_EN (1 << 9) +#define AML_CAV_LUT_ADDR_RD_EN (1 << 8) +#define AML_CAV_LUT_ADDR_INDEX_MASK 0xff +#define AML_CAV_LUT_ADDR_INDEX_SHIFT 0 + +#define AML_VIU_OSD1_CTRL_REG 64 +#define AML_VIU_OSD_CTRL_OSD_EN (1 << 21) +#define AML_VIU_OSD_CTRL_GLOBAL_ALPHA_MASK (0x1ff << 12) +#define AML_VIU_OSD_CTRL_GLOBAL_ALPHA_SHIFT 12 +#define AML_VIU_OSD_CTRL_OSD_BLK_EN_MASK (0xf << 0) +#define AML_VIU_OSD_CTRL_OSD_BLK_EN_SHIFT 0 + +#define AML_VIU_OSD1_BLK0_CFG_W0_REG 108 +#define AML_VIU_OSD1_BLK1_CFG_W0_REG 124 +#define AML_VIU_OSD1_BLK2_CFG_W0_REG 140 +#define AML_VIU_OSD1_BLK3_CFG_W0_REG 156 +#define AML_VIU_OSD_BLK_CFG_W0_INDEX_MASK (0xff << 16) +#define AML_VIU_OSD_BLK_CFG_W0_INDEX_SHIFT 16 +#define AML_VIU_OSD_BLK_CFG_W0_LITTLE_ENDIAN (1 << 15) +#define AML_VIU_OSD_BLK_CFG_W0_BLKMODE_24 (7 << 8) +#define AML_VIU_OSD_BLK_CFG_W0_RGB_EN (1 << 7) +#define AML_VIU_OSD_BLK_CFG_W0_CMATRIX_RGB (0 << 2) + +#define AML_VIU_OSD1_BLK0_CFG_W1_REG 112 +#define AML_VIU_OSD1_BLK1_CFG_W1_REG 128 +#define AML_VIU_OSD1_BLK2_CFG_W1_REG 144 +#define AML_VIU_OSD1_BLK3_CFG_W1_REG 160 +#define AML_VIU_OSD_BLK_CFG_W1_X_END_MASK (0x1fff << 16) +#define AML_VIU_OSD_BLK_CFG_W1_X_END_SHIFT 16 +#define AML_VIU_OSD_BLK_CFG_W1_X_START_MASK 0x1fff +#define AML_VIU_OSD_BLK_CFG_W1_X_START_SHIFT 0 + +#define AML_VIU_OSD1_BLK0_CFG_W2_REG 116 +#define AML_VIU_OSD1_BLK1_CFG_W2_REG 132 +#define AML_VIU_OSD1_BLK2_CFG_W2_REG 148 +#define AML_VIU_OSD1_BLK3_CFG_W2_REG 164 +#define AML_VIU_OSD_BLK_CFG_W2_Y_END_MASK (0x1fff << 16) +#define AML_VIU_OSD_BLK_CFG_W2_Y_END_SHIFT 16 +#define AML_VIU_OSD_BLK_CFG_W2_Y_START_MASK 0x1fff +#define AML_VIU_OSD_BLK_CFG_W2_Y_START_SHIFT 0 + +#define AML_VIU_OSD1_BLK0_CFG_W3_REG 120 +#define AML_VIU_OSD1_BLK1_CFG_W3_REG 136 +#define AML_VIU_OSD1_BLK2_CFG_W3_REG 152 +#define AML_VIU_OSD1_BLK3_CFG_W3_REG 168 +#define AML_VIU_OSD_BLK_CFG_W3_H_END_MASK (0xfff << 16) +#define AML_VIU_OSD_BLK_CFG_W3_H_END_SHIFT 16 +#define AML_VIU_OSD_BLK_CFG_W3_H_START_MASK 0xfff +#define AML_VIU_OSD_BLK_CFG_W3_H_START_SHIFT 0 + +#define AML_VIU_OSD1_BLK0_CFG_W4_REG 76 +#define AML_VIU_OSD1_BLK1_CFG_W4_REG 80 +#define AML_VIU_OSD1_BLK2_CFG_W4_REG 84 +#define AML_VIU_OSD1_BLK3_CFG_W4_REG 88 +#define AML_VIU_OSD_BLK_CFG_W4_V_END_MASK (0xfff << 16) +#define AML_VIU_OSD_BLK_CFG_W4_V_END_SHIFT 16 +#define AML_VIU_OSD_BLK_CFG_W4_V_START_MASK 0xfff +#define AML_VIU_OSD_BLK_CFG_W4_V_START_SHIFT 0 + +#define AML_VIU_OSD1_FIFO_CTRL_REG 172 +#define AML_VIU_OSD_FIFO_CTRL_DEPTH_MASK (0x3f << 12) +#define AML_VIU_OSD_FIFO_CTRL_DEPTH_SHIFT 12 +#define AML_VIU_OSD_FIFO_CTRL_BURST_LEN_MASK (3 << 10) +#define AML_VIU_OSD_FIFO_CTRL_BURST_LEN_24 (0 << 10) +#define AML_VIU_OSD_FIFO_CTRL_BURST_LEN_32 (1 << 10) +#define AML_VIU_OSD_FIFO_CTRL_BURST_LEN_48 (2 << 10) +#define AML_VIU_OSD_FIFO_CTRL_BURST_LEN_64 (3 << 10) +#define AML_VIU_OSD_FIFO_CTRL_HOLD_LINES_MASK (0x1f << 5) +#define AML_VIU_OSD_FIFO_CTRL_HOLD_LINES_SHIFT 5 +#define AML_VIU_OSD_FIFO_CTRL_URGENT (1 << 0) + +/* OSD2 field meanings are the same as OSD1 */ +#define AML_VIU_OSD2_CTRL_REG 192 +#define AML_VIU_OSD2_BLK0_CFG_W0_REG 236 +#define AML_VIU_OSD2_BLK1_CFG_W0_REG 252 +#define AML_VIU_OSD2_BLK2_CFG_W0_REG 268 +#define AML_VIU_OSD2_BLK3_CFG_W0_REG 284 +#define AML_VIU_OSD2_BLK0_CFG_W1_REG 240 +#define AML_VIU_OSD2_BLK1_CFG_W1_REG 256 +#define AML_VIU_OSD2_BLK2_CFG_W1_REG 272 +#define AML_VIU_OSD2_BLK3_CFG_W1_REG 288 +#define AML_VIU_OSD2_BLK0_CFG_W2_REG 244 +#define AML_VIU_OSD2_BLK1_CFG_W2_REG 260 +#define AML_VIU_OSD2_BLK2_CFG_W2_REG 276 +#define AML_VIU_OSD2_BLK3_CFG_W2_REG 292 +#define AML_VIU_OSD2_BLK0_CFG_W3_REG 248 +#define AML_VIU_OSD2_BLK1_CFG_W3_REG 264 +#define AML_VIU_OSD2_BLK2_CFG_W3_REG 280 +#define AML_VIU_OSD2_BLK3_CFG_W3_REG 296 +#define AML_VIU_OSD2_BLK0_CFG_W4_REG 400 +#define AML_VIU_OSD2_BLK1_CFG_W4_REG 404 +#define AML_VIU_OSD2_BLK2_CFG_W4_REG 408 +#define AML_VIU_OSD2_BLK3_CFG_W4_REG 412 +#define AML_VIU_OSD2_FIFO_CTRL_REG 300 + +#define AML_VPP_MISC_REG 152 +#define AML_VPP_MISC_OSD2_PREBLEND (1 << 17) +#define AML_VPP_MISC_OSD1_PREBLEND (1 << 16) +#define AML_VPP_MISC_VD2_PREBLEND (1 << 15) +#define AML_VPP_MISC_VD1_PREBLEND (1 << 14) +#define AML_VPP_MISC_OSD2_POSTBLEND (1 << 13) +#define AML_VPP_MISC_OSD1_POSTBLEND (1 << 12) +#define AML_VPP_MISC_VD2_POSTBLEND (1 << 11) +#define AML_VPP_MISC_VD1_POSTBLEND (1 << 10) +#define AML_VPP_MISC_POSTBLEND_EN (1 << 7) +#define AML_VPP_MISC_PREBLEND_EN (1 << 6) + +#endif /* _ARM_AMLOGIC_AML8726_FB_H */ Property changes on: head/sys/arm/amlogic/aml8726/aml8726_fb.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_gpio.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_gpio.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_gpio.c (revision 280905) @@ -0,0 +1,372 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 GPIO driver. + * + * Note: The OEN register is active * low *. Setting a bit to zero + * enables the output driver, setting a bit to one disables the driver. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +#include "gpio_if.h" + + +struct aml8726_gpio_softc { + device_t dev; + struct resource *res[3]; + struct mtx mtx; + uint32_t npins; + device_t busdev; +}; + +static struct resource_spec aml8726_gpio_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, /* oen */ + { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_SHAREABLE }, /* output */ + { SYS_RES_MEMORY, 2, RF_ACTIVE }, /* input */ + { -1, 0 } +}; + +#define AML_GPIO_LOCK(sc) mtx_lock(&(sc)->mtx) +#define AML_GPIO_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define AML_GPIO_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "gpio", MTX_DEF) +#define AML_GPIO_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define AML_GPIO_OE_N_REG 0 +#define AML_GPIO_OUT_REG 1 +#define AML_GPIO_IN_REG 2 + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[reg], 0, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[reg], 0) + +static int +aml8726_gpio_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-gpio")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 GPIO"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_gpio_attach(device_t dev) +{ + struct aml8726_gpio_softc *sc = device_get_softc(dev); + phandle_t node; + pcell_t prop; + + sc->dev = dev; + + node = ofw_bus_get_node(dev); + + if (OF_getencprop(node, "pin-count", + &prop, sizeof(prop)) <= 0) { + device_printf(dev, "missing pin-count attribute in FDT\n"); + return (ENXIO); + } + sc->npins = prop; + + if (sc->npins > 32) + return (ENXIO); + + if (bus_alloc_resources(dev, aml8726_gpio_spec, sc->res)) { + device_printf(dev, "can not allocate resources for device\n"); + return (ENXIO); + } + + /* + * The GPIOAO OUT bits occupy the upper word of the OEN register. + */ + if (rman_get_start(sc->res[1]) == rman_get_start(sc->res[0])) + if (sc->npins > 16) { + device_printf(dev, + "too many pins for overlapping OEN and OUT\n"); + bus_release_resources(dev, aml8726_gpio_spec, sc->res); + return (ENXIO); + } + + AML_GPIO_LOCK_INIT(sc); + + sc->busdev = gpiobus_attach_bus(dev); + if (sc->busdev == NULL) { + AML_GPIO_LOCK_DESTROY(sc); + bus_release_resources(dev, aml8726_gpio_spec, sc->res); + return (ENXIO); + } + + return (0); +} + +static int +aml8726_gpio_detach(device_t dev) +{ + struct aml8726_gpio_softc *sc = device_get_softc(dev); + + gpiobus_detach_bus(dev); + + AML_GPIO_LOCK_DESTROY(sc); + + bus_release_resources(dev, aml8726_gpio_spec, sc->res); + + return (0); +} + +static device_t +aml8726_gpio_get_bus(device_t dev) +{ + struct aml8726_gpio_softc *sc = device_get_softc(dev); + + return (sc->busdev); +} + +static int +aml8726_gpio_pin_max(device_t dev, int *maxpin) +{ + struct aml8726_gpio_softc *sc = device_get_softc(dev); + + *maxpin = (int)sc->npins; + + return (0); +} + +/* Get a specific pin's capabilities. */ +static int +aml8726_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct aml8726_gpio_softc *sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); + + return (0); +} + +/* Get a specific pin's name. */ +static int +aml8726_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct aml8726_gpio_softc *sc = device_get_softc(dev); + + if (pin >= sc->npins) + return (EINVAL); + + snprintf(name, GPIOMAXNAME, "%s.%u", ofw_bus_get_name(dev), pin); + + return (0); +} + +/* Get a specific pin's current in/out state. */ +static int +aml8726_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct aml8726_gpio_softc *sc = device_get_softc(dev); + uint32_t mask = 1U << pin; + + if (pin >= sc->npins) + return (EINVAL); + + if ((CSR_READ_4(sc, AML_GPIO_OE_N_REG) & mask) == 0) { + /* output */ + *flags = GPIO_PIN_OUTPUT; + } else + /* input */ + *flags = GPIO_PIN_INPUT; + + return (0); +} + +/* Set a specific pin's in/out state. */ +static int +aml8726_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct aml8726_gpio_softc *sc = device_get_softc(dev); + uint32_t mask = 1U << pin; + + if (pin >= sc->npins) + return (EINVAL); + + AML_GPIO_LOCK(sc); + + if ((flags & GPIO_PIN_OUTPUT) != 0) { + /* Output. Turn on driver. */ + CSR_WRITE_4(sc, AML_GPIO_OE_N_REG, + (CSR_READ_4(sc, AML_GPIO_OE_N_REG) & ~mask)); + } else { + /* Input. Turn off driver. */ + CSR_WRITE_4(sc, AML_GPIO_OE_N_REG, + (CSR_READ_4(sc, AML_GPIO_OE_N_REG) | mask)); + } + + AML_GPIO_UNLOCK(sc); + + return (0); +} + +/* Set a specific output pin's value. */ +static int +aml8726_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct aml8726_gpio_softc *sc = device_get_softc(dev); + uint32_t mask; + + if (pin >= sc->npins || value > 1) + return (EINVAL); + + /* + * The GPIOAO OUT bits occupy the upper word of the OEN register. + */ + if (rman_get_start(sc->res[1]) == rman_get_start(sc->res[0])) + pin += 16; + + mask = 1U << pin; + + AML_GPIO_LOCK(sc); + + CSR_WRITE_4(sc, AML_GPIO_OUT_REG, + ((CSR_READ_4(sc, AML_GPIO_OUT_REG) & ~mask) | (value << pin))); + + AML_GPIO_UNLOCK(sc); + + return (0); +} + +/* Get a specific pin's input value. */ +static int +aml8726_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) +{ + struct aml8726_gpio_softc *sc = device_get_softc(dev); + uint32_t mask = 1U << pin; + + if (pin >= sc->npins) + return (EINVAL); + + *value = (CSR_READ_4(sc, AML_GPIO_IN_REG) & mask) ? 1 : 0; + + return (0); +} + +/* Toggle a pin's output value. */ +static int +aml8726_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct aml8726_gpio_softc *sc = device_get_softc(dev); + uint32_t mask; + + if (pin >= sc->npins) + return (EINVAL); + + /* + * The GPIOAO OUT bits occupy the upper word of the OEN register. + */ + if (rman_get_start(sc->res[1]) == rman_get_start(sc->res[0])) + pin += 16; + + mask = 1U << pin; + + AML_GPIO_LOCK(sc); + + CSR_WRITE_4(sc, AML_GPIO_OUT_REG, + CSR_READ_4(sc, AML_GPIO_OUT_REG) ^ mask); + + AML_GPIO_UNLOCK(sc); + + return (0); +} + +static phandle_t +aml8726_gpio_get_node(device_t bus, device_t dev) +{ + + /* We only have one child, the GPIO bus, which needs our own node. */ + return (ofw_bus_get_node(bus)); +} + +static device_method_t aml8726_gpio_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_gpio_probe), + DEVMETHOD(device_attach, aml8726_gpio_attach), + DEVMETHOD(device_detach, aml8726_gpio_detach), + + /* GPIO interface */ + DEVMETHOD(gpio_get_bus, aml8726_gpio_get_bus), + DEVMETHOD(gpio_pin_max, aml8726_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, aml8726_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, aml8726_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, aml8726_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, aml8726_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, aml8726_gpio_pin_get), + DEVMETHOD(gpio_pin_set, aml8726_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, aml8726_gpio_pin_toggle), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, aml8726_gpio_get_node), + + DEVMETHOD_END +}; + +static driver_t aml8726_gpio_driver = { + "gpio", + aml8726_gpio_methods, + sizeof(struct aml8726_gpio_softc), +}; + +static devclass_t aml8726_gpio_devclass; + +DRIVER_MODULE(aml8726_gpio, simplebus, aml8726_gpio_driver, + aml8726_gpio_devclass, 0, 0); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_gpio.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_i2c.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_i2c.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_i2c.c (revision 280905) @@ -0,0 +1,284 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 I2C driver. + * + * Currently this implementation doesn't take full advantage of the hardware. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include "iicbb_if.h" + + +struct aml8726_iic_softc { + device_t dev; + struct resource *res[1]; + struct mtx mtx; + device_t iicbb; +}; + +static struct resource_spec aml8726_iic_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define AML_I2C_LOCK(sc) mtx_lock(&(sc)->mtx) +#define AML_I2C_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define AML_I2C_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "i2c", MTX_DEF) +#define AML_I2C_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define AML_I2C_CTRL_REG 0 +#define AML_I2C_MANUAL_SDA_I (1 << 26) +#define AML_I2C_MANUAL_SCL_I (1 << 25) +#define AML_I2C_MANUAL_SDA_O (1 << 24) +#define AML_I2C_MANUAL_SCL_O (1 << 23) +#define AML_I2C_MANUAL_EN (1 << 22) + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) + +static int +aml8726_iic_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-i2c")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 I2C"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_iic_attach(device_t dev) +{ + struct aml8726_iic_softc *sc = device_get_softc(dev); + int error; + + sc->dev = dev; + + if (bus_alloc_resources(dev, aml8726_iic_spec, sc->res)) { + device_printf(dev, "can not allocate resources for device\n"); + return (ENXIO); + } + + AML_I2C_LOCK_INIT(sc); + + sc->iicbb = device_add_child(dev, "iicbb", -1); + + if (sc->iicbb == NULL) { + device_printf(dev, "could not add iicbb\n"); + error = ENXIO; + goto fail; + } + + error = device_probe_and_attach(sc->iicbb); + + if (error) { + device_printf(dev, "could not attach iicbb\n"); + goto fail; + } + + return (0); + +fail: + AML_I2C_LOCK_DESTROY(sc); + bus_release_resources(dev, aml8726_iic_spec, sc->res); + + return (error); +} + +static int +aml8726_iic_detach(device_t dev) +{ + struct aml8726_iic_softc *sc = device_get_softc(dev); + device_t child; + + /* + * Detach the children before recursively deleting + * in case a child has a pointer to a grandchild + * which is used by the child's detach routine. + * + * Remember the child before detaching so we can + * delete it (bus_generic_detach indirectly zeroes + * sc->child_dev). + */ + child = sc->iicbb; + bus_generic_detach(dev); + if (child) + device_delete_child(dev, child); + + AML_I2C_LOCK_DESTROY(sc); + + bus_release_resources(dev, aml8726_iic_spec, sc->res); + + return (0); +} + +static void +aml8726_iic_child_detached(device_t dev, device_t child) +{ + struct aml8726_iic_softc *sc = device_get_softc(dev); + + if (child == sc->iicbb) + sc->iicbb = NULL; +} + +static int +aml8726_iic_callback(device_t dev, int index, caddr_t data) +{ + + return (0); +} + +static int +aml8726_iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + struct aml8726_iic_softc *sc = device_get_softc(dev); + + AML_I2C_LOCK(sc); + + CSR_WRITE_4(sc, AML_I2C_CTRL_REG, + (CSR_READ_4(sc, AML_I2C_CTRL_REG) | AML_I2C_MANUAL_SDA_O | + AML_I2C_MANUAL_SCL_O | AML_I2C_MANUAL_EN)); + + AML_I2C_UNLOCK(sc); + + /* Wait for 10 usec */ + DELAY(10); + + return (IIC_ENOADDR); +} + +static int +aml8726_iic_getscl(device_t dev) +{ + struct aml8726_iic_softc *sc = device_get_softc(dev); + + return (CSR_READ_4(sc, AML_I2C_CTRL_REG) & AML_I2C_MANUAL_SCL_I); +} + +static int +aml8726_iic_getsda(device_t dev) +{ + struct aml8726_iic_softc *sc = device_get_softc(dev); + + return (CSR_READ_4(sc, AML_I2C_CTRL_REG) & AML_I2C_MANUAL_SDA_I); +} + +static void +aml8726_iic_setscl(device_t dev, int val) +{ + struct aml8726_iic_softc *sc = device_get_softc(dev); + + AML_I2C_LOCK(sc); + + CSR_WRITE_4(sc, AML_I2C_CTRL_REG, ((CSR_READ_4(sc, AML_I2C_CTRL_REG) & + ~AML_I2C_MANUAL_SCL_O) | (val ? AML_I2C_MANUAL_SCL_O : 0) | + AML_I2C_MANUAL_EN)); + + AML_I2C_UNLOCK(sc); +} + +static void +aml8726_iic_setsda(device_t dev, int val) +{ + struct aml8726_iic_softc *sc = device_get_softc(dev); + + AML_I2C_LOCK(sc); + + CSR_WRITE_4(sc, AML_I2C_CTRL_REG, ((CSR_READ_4(sc, AML_I2C_CTRL_REG) & + ~AML_I2C_MANUAL_SDA_O) | (val ? AML_I2C_MANUAL_SDA_O : 0) | + AML_I2C_MANUAL_EN)); + + AML_I2C_UNLOCK(sc); +} + +static device_method_t aml8726_iic_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_iic_probe), + DEVMETHOD(device_attach, aml8726_iic_attach), + DEVMETHOD(device_detach, aml8726_iic_detach), + + /* bus interface */ + DEVMETHOD(bus_child_detached, aml8726_iic_child_detached), + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* IICBB interface */ + DEVMETHOD(iicbb_callback, aml8726_iic_callback), + DEVMETHOD(iicbb_reset, aml8726_iic_reset), + + DEVMETHOD(iicbb_getscl, aml8726_iic_getscl), + DEVMETHOD(iicbb_getsda, aml8726_iic_getsda), + DEVMETHOD(iicbb_setscl, aml8726_iic_setscl), + DEVMETHOD(iicbb_setsda, aml8726_iic_setsda), + + DEVMETHOD_END +}; + +static driver_t aml8726_iic_driver = { + "aml8726_iic", + aml8726_iic_methods, + sizeof(struct aml8726_iic_softc), +}; + +static devclass_t aml8726_iic_devclass; + +DRIVER_MODULE(aml8726_iic, simplebus, aml8726_iic_driver, + aml8726_iic_devclass, 0, 0); +DRIVER_MODULE(iicbb, aml8726_iic, iicbb_driver, iicbb_devclass, 0, 0); +MODULE_DEPEND(aml8726_iic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); +MODULE_VERSION(aml8726_iic, 1); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_i2c.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_identsoc.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_identsoc.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_identsoc.c (revision 280905) @@ -0,0 +1,137 @@ +/*- + * Copyright 2015 John Wehle + * 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. + * + */ + +/* + * Amlogic aml8726 SoC identification. + * + * The SoC identification is used by some of the drivers in order to + * handle hardware differences so the identification needs to happen + * early in the boot process (e.g. before SMP startup). + * + * It's expected that the register addresses for identifying the SoC + * are set in stone. + * + * Currently missing an entry for the aml8726-m and doesn't distinguish + * between the m801, m802, m805, s802, s805, and s812 which are all + * variations of the aml8726-m8. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +uint32_t aml8726_soc_hw_rev = 0xffffffff; +uint32_t aml8726_soc_metal_rev = 0xffffffff; + +static const struct { + uint32_t hw_rev; + char *desc; +} aml8726_soc_desc[] = { + { AML_SOC_HW_REV_M3, "aml8726-m3" }, + { AML_SOC_HW_REV_M6, "aml8726-m6" }, + { AML_SOC_HW_REV_M6TV, "aml8726-m6tv" }, + { AML_SOC_HW_REV_M6TVL, "aml8726-m6tvl" }, + { AML_SOC_HW_REV_M8, "aml8726-m8" }, + { AML_SOC_HW_REV_M8B, "aml8726-m8b" }, + { 0xff, NULL } +}; + +static const struct { + uint32_t metal_rev; + char *desc; +} aml8726_m8_soc_rev[] = { + { AML_SOC_M8_METAL_REV_A, "A" }, + { AML_SOC_M8_METAL_REV_M2_A, "MarkII A" }, + { AML_SOC_M8_METAL_REV_B, "B" }, + { AML_SOC_M8_METAL_REV_C, "C" }, + { 0xff, NULL } +}; + +static void +aml8726_identify_soc(void *dummy) +{ + int err; + int i; + struct resource res; + + memset(&res, 0, sizeof(res)); + + res.r_bustag = fdtbus_bs_tag; + + err = bus_space_map(res.r_bustag, AML_SOC_CBUS_BASE_ADDR, 0x100000, + 0, &res.r_bushandle); + + if (err) + panic("Could not allocate resource for SoC identification\n"); + + aml8726_soc_hw_rev = bus_read_4(&res, AML_SOC_HW_REV_REG); + + aml8726_soc_metal_rev = bus_read_4(&res, AML_SOC_METAL_REV_REG); + + bus_space_unmap(res.r_bustag, res.r_bushandle, 0x100000); + + for (i = 0; aml8726_soc_desc[i].desc; i++) + if (aml8726_soc_desc[i].hw_rev == aml8726_soc_hw_rev) + break; + + if (aml8726_soc_desc[i].desc == NULL) + panic("Amlogic unknown aml8726 SoC %#x\n", aml8726_soc_hw_rev); + + printf("Amlogic %s SoC", aml8726_soc_desc[i].desc); + + if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8) { + for (i = 0; aml8726_m8_soc_rev[i].desc; i++) + if (aml8726_m8_soc_rev[i].metal_rev == + aml8726_soc_metal_rev) + break; + + if (aml8726_m8_soc_rev[i].desc == NULL) + printf(", unknown rev %#x", aml8726_soc_metal_rev); + else + printf(", rev %s", aml8726_m8_soc_rev[i].desc); + } + + printf("\n"); +} + +SYSINIT(aml8726_identify_soc, SI_SUB_CPU, SI_ORDER_SECOND, + aml8726_identify_soc, NULL); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_identsoc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_l2cache.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_l2cache.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_l2cache.c (revision 280905) @@ -0,0 +1,92 @@ +/*- + * Copyright 2013-2015 John Wehle + * All rights reserved. + * + * Based on omap4_l2cache.c by Olivier Houchard + * + * 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$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +void +platform_pl310_init(struct pl310_softc *sc) +{ + uint32_t aux; + + aux = pl310_read4(sc, PL310_AUX_CTRL); + + /* + * The Amlogic Linux platform code enables via AUX: + * + * Early BRESP + * Full Line of Zero (which must match processor setting) + * Data Prefetch + * + * and additionally on the m6 enables: + * + * Instruction Prefetch + * + * For the moment we only enable Data Prefetch ... + * further refinements can happen as things mature. + */ + + /* + * Disable instruction prefetch. + */ + aux &= ~AUX_CTRL_INSTR_PREFETCH; + + /* + * Enable data prefetch. + */ + aux |= AUX_CTRL_DATA_PREFETCH; + + pl310_write4(sc, PL310_AUX_CTRL, aux); +} + +void +platform_pl310_write_ctrl(struct pl310_softc *sc, uint32_t val) +{ + + pl310_write4(sc, PL310_CTRL, val); +} + +void +platform_pl310_write_debug(struct pl310_softc *sc, uint32_t val) +{ + + pl310_write4(sc, PL310_DEBUG_CTRL, val); +} Property changes on: head/sys/arm/amlogic/aml8726/aml8726_l2cache.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_machdep.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_machdep.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_machdep.c (revision 280905) @@ -0,0 +1,211 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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 "opt_global.h" + +#include +__FBSDID("$FreeBSD$"); + +#define _ARM32_BUS_DMA_PRIVATE +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#if defined(SOCDEV_PA) && defined(SOCDEV_VA) +vm_offset_t aml8726_aobus_kva_base = SOCDEV_VA; +#else +vm_offset_t aml8726_aobus_kva_base; +#endif + +static void +aml8726_fixup_busfreq() +{ + phandle_t node, child; + pcell_t freq, prop; + ssize_t len; + + /* + * Set the bus-frequency for any top level SoC simple-bus which + * needs updating (meaning the current frequency is zero). + */ + + if ((freq = aml8726_clkmsr_bus_frequency()) == 0 || + (node = OF_finddevice("/soc")) == 0 || + fdt_is_compatible_strict(node, "simple-bus") == 0) + while (1); + + freq = cpu_to_fdt32(freq); + + len = OF_getencprop(node, "bus-frequency", &prop, sizeof(prop)); + if ((len / sizeof(prop)) == 1 && prop == 0) + OF_setprop(node, "bus-frequency", (void *)&freq, sizeof(freq)); + + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + if (fdt_is_compatible_strict(child, "simple-bus")) { + len = OF_getencprop(child, "bus-frequency", + &prop, sizeof(prop)); + if ((len / sizeof(prop)) == 1 && prop == 0) + OF_setprop(child, "bus-frequency", + (void *)&freq, sizeof(freq)); + } + } +} + +vm_offset_t +platform_lastaddr(void) +{ + + return (arm_devmap_lastaddr()); +} + +void +platform_probe_and_attach(void) +{ +} + +void +platform_gpio_init(void) +{ + + /* + * The UART console driver used for debugging early boot code + * needs to know the virtual base address of the aobus. It's + * expected to equal SOCDEV_VA prior to initarm calling setttb + * ... afterwards it needs to be updated due to the new page + * tables. + * + * This means there's a deadzone in initarm between setttb + * and platform_gpio_init during which printf can't be used. + */ + aml8726_aobus_kva_base = + (vm_offset_t)arm_devmap_ptov(0xc8100000, 0x100000); + + /* + * This FDT fixup should arguably be called through fdt_fixup_table, + * however currently there's no mechanism to specify a fixup which + * should always be invoked. + * + * It needs to be called prior to the console being initialized which + * is why it's called here, rather than from platform_late_init. + */ + aml8726_fixup_busfreq(); +} + +void +platform_late_init(void) +{ +} + +/* + * Construct static devmap entries to map out the core + * peripherals using 1mb section mappings. + */ +int +platform_devmap_init(void) +{ + + arm_devmap_add_entry(0xc1100000, 0x200000); /* cbus */ + arm_devmap_add_entry(0xc4200000, 0x100000); /* pl310 */ + arm_devmap_add_entry(0xc4300000, 0x100000); /* periph */ + arm_devmap_add_entry(0xc8000000, 0x100000); /* apbbus */ + arm_devmap_add_entry(0xc8100000, 0x100000); /* aobus */ + arm_devmap_add_entry(0xc9000000, 0x800000); /* ahbbus */ + arm_devmap_add_entry(0xd9000000, 0x100000); /* ahb */ + arm_devmap_add_entry(0xda000000, 0x100000); /* secbus */ + + return (0); +} + +struct arm32_dma_range * +bus_dma_get_range(void) +{ + + return (NULL); +} + +int +bus_dma_get_range_nb(void) +{ + + return (0); +} + +struct fdt_fixup_entry fdt_fixup_table[] = { + { NULL, NULL } +}; + +static int +fdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, + int *pol) +{ + + /* + * The single core chips have just an Amlogic PIC. However the + * multi core chips also have a GIC. + */ +#ifdef SMP + if (!fdt_is_compatible_strict(node, "arm,gic")) +#else + if (!fdt_is_compatible_strict(node, "amlogic,aml8726-pic")) +#endif + return (ENXIO); + + *interrupt = fdt32_to_cpu(intr[0]); + *trig = INTR_TRIGGER_EDGE; + *pol = INTR_POLARITY_HIGH; + + switch (*interrupt) { + case 30: /* INT_USB_A */ + case 31: /* INT_USB_B */ + *trig = INTR_TRIGGER_LEVEL; + break; + default: + break; + } + +#ifdef SMP + *interrupt += 32; +#endif + + return (0); +} + +fdt_pic_decode_t fdt_pic_table[] = { + &fdt_pic_decode_ic, + NULL +}; Property changes on: head/sys/arm/amlogic/aml8726/aml8726_machdep.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_machdep.h =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_machdep.h (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_machdep.h (revision 280905) @@ -0,0 +1,41 @@ +/*- + * Copyright 2014 John Wehle + * 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$ + */ + +#ifndef _ARM_AMLOGIC_AML8726_MACHDEP_H +#define _ARM_AMLOGIC_AML8726_MACHDEP_H + +/* + * The aobus kvm base is supplied as a global variable * only * + * for the convience of the aml8726 UART console driver which + * is available for use to debug early boot code. + * + * This variable should not be used elsewhere. + */ +extern vm_offset_t aml8726_aobus_kva_base; + +#endif /* _ARM_AMLOGIC_AML8726_MACHDEP_H */ Property changes on: head/sys/arm/amlogic/aml8726/aml8726_machdep.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_mmc.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_mmc.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_mmc.c (revision 280905) @@ -0,0 +1,1041 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 MMC host controller driver. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "gpio_if.h" +#include "mmcbr_if.h" + +struct aml8726_mmc_gpio { + device_t dev; + uint32_t pin; + uint32_t pol; +}; + +struct aml8726_mmc_softc { + device_t dev; + struct resource *res[2]; + struct mtx mtx; + uint32_t port; + unsigned int ref_freq; + struct aml8726_mmc_gpio pwr_en; + int voltages[2]; + struct aml8726_mmc_gpio vselect; + bus_dma_tag_t dmatag; + bus_dmamap_t dmamap; + void *ih_cookie; + struct mmc_host host; + int bus_busy; + struct mmc_command *cmd; + unsigned int timeout_remaining; +}; + +static struct resource_spec aml8726_mmc_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define AML_MMC_LOCK(sc) mtx_lock(&(sc)->mtx) +#define AML_MMC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define AML_MMC_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "mmc", MTX_DEF) +#define AML_MMC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) +#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ + (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +#define PWR_ON_FLAG(pol) ((pol) == 0 ? GPIO_PIN_LOW : \ + GPIO_PIN_HIGH) +#define PWR_OFF_FLAG(pol) ((pol) == 0 ? GPIO_PIN_HIGH : \ + GPIO_PIN_LOW) + +static void +aml8726_mmc_mapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + bus_addr_t *busaddrp; + + /* + * There should only be one bus space address since + * bus_dma_tag_create was called with nsegments = 1. + */ + + busaddrp = (bus_addr_t *)arg; + *busaddrp = segs->ds_addr; +} + +static int +aml8726_mmc_power_off(struct aml8726_mmc_softc *sc) +{ + + if (sc->pwr_en.dev == NULL) + return (0); + + return (GPIO_PIN_SET(sc->pwr_en.dev, sc->pwr_en.pin, + PWR_OFF_FLAG(sc->pwr_en.pol))); +} + +static int +aml8726_mmc_power_on(struct aml8726_mmc_softc *sc) +{ + + if (sc->pwr_en.dev == NULL) + return (0); + + return (GPIO_PIN_SET(sc->pwr_en.dev, sc->pwr_en.pin, + PWR_ON_FLAG(sc->pwr_en.pol))); +} + +static int +aml8726_mmc_restart_timer(struct aml8726_mmc_softc *sc) +{ + uint32_t count; + uint32_t isr; + + if (sc->cmd == NULL || sc->timeout_remaining == 0) + return (0); + + count = (sc->timeout_remaining > 0x1fff) ? 0x1fff : + sc->timeout_remaining; + sc->timeout_remaining -= count; + + isr = (count << AML_MMC_IRQ_STATUS_TIMER_CNT_SHIFT) | + AML_MMC_IRQ_STATUS_TIMER_EN | AML_MMC_IRQ_STATUS_TIMEOUT_IRQ; + + CSR_WRITE_4(sc, AML_MMC_IRQ_STATUS_REG, isr); + + return (1); +} + +static int +aml8726_mmc_start_command(struct aml8726_mmc_softc *sc, struct mmc_command *cmd) +{ + struct mmc_ios *ios = &sc->host.ios; + bus_addr_t baddr; + uint32_t block_size; + uint32_t bus_width; + uint32_t cmdr; + uint32_t cycles_per_msec; + uint32_t extr; + uint32_t mcfgr; + uint32_t nbits_per_pkg; + uint32_t timeout; + int error; + struct mmc_data *data; + + if (cmd->opcode > 0x3f) + return (MMC_ERR_INVALID); + + /* + * Ensure the hardware state machine is in a known state, + * the command done interrupt is enabled, and previous + * IRQ status bits have been cleared. + */ + CSR_WRITE_4(sc, AML_MMC_IRQ_CONFIG_REG, + (AML_MMC_IRQ_CONFIG_SOFT_RESET | AML_MMC_IRQ_CONFIG_CMD_DONE_EN)); + CSR_BARRIER(sc, AML_MMC_IRQ_CONFIG_REG); + CSR_WRITE_4(sc, AML_MMC_IRQ_STATUS_REG, AML_MMC_IRQ_STATUS_CLEAR_IRQ); + + /* + * Start and transmission bits are per section 4.7.2 of the: + * + * SD Specifications Part 1 + * Physicaly Layer Simplified Specification + * Version 4.10 + */ + cmdr = AML_MMC_CMD_START_BIT | AML_MMC_CMD_TRANS_BIT_HOST | cmd->opcode; + baddr = 0; + extr = 0; + mcfgr = sc->port; + timeout = AML_MMC_CMD_TIMEOUT; + + if ((cmd->flags & MMC_RSP_136) != 0) { + cmdr |= AML_MMC_CMD_RESP_CRC7_FROM_8; + cmdr |= (133 << AML_MMC_CMD_RESP_BITS_SHIFT); + } else if ((cmd->flags & MMC_RSP_PRESENT) != 0) + cmdr |= (45 << AML_MMC_CMD_RESP_BITS_SHIFT); + + if ((cmd->flags & MMC_RSP_CRC) == 0) + cmdr |= AML_MMC_CMD_RESP_NO_CRC7; + + if ((cmd->flags & MMC_RSP_BUSY) != 0) + cmdr |= AML_MMC_CMD_CHECK_DAT0_BUSY; + + data = cmd->data; + + if (data && data->len && + (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)) != 0) { + block_size = data->len; + + if ((data->flags & MMC_DATA_MULTI) != 0) { + block_size = MMC_SECTOR_SIZE; + if ((data->len % block_size) != 0) + return (MMC_ERR_INVALID); + } + + cmdr |= (((data->len / block_size) - 1) << + AML_MMC_CMD_REP_PKG_CNT_SHIFT); + + mcfgr |= (data->flags & MMC_DATA_STREAM) ? + AML_MMC_MULT_CONFIG_STREAM_EN : 0; + + /* + * The number of bits per package equals the number + * of data bits + the number of CRC bits. There are + * 16 bits of CRC calculate per bus line. + * + * A completed package appears to be detected by when + * a counter decremented by the width underflows, thus + * a value of zero always transfers 1 (or 4 bits depending + * on the mode) which is why bus_width is subtracted. + */ + bus_width = (ios->bus_width == bus_width_4) ? 4 : 1; + nbits_per_pkg = block_size * 8 + 16 * bus_width - bus_width; + if (nbits_per_pkg > 0x3fff) + return (MMC_ERR_INVALID); + + extr |= (nbits_per_pkg << AML_MMC_EXTENSION_PKT_SIZE_SHIFT); + + error = bus_dmamap_load(sc->dmatag, sc->dmamap, + data->data, data->len, aml8726_mmc_mapmem, &baddr, + BUS_DMA_NOWAIT); + if (error) + return (MMC_ERR_NO_MEMORY); + + if ((data->flags & MMC_DATA_READ) != 0) { + cmdr |= AML_MMC_CMD_RESP_HAS_DATA; + bus_dmamap_sync(sc->dmatag, sc->dmamap, + BUS_DMASYNC_PREREAD); + timeout = AML_MMC_READ_TIMEOUT * + (data->len / block_size); + } else { + cmdr |= AML_MMC_CMD_CMD_HAS_DATA; + bus_dmamap_sync(sc->dmatag, sc->dmamap, + BUS_DMASYNC_PREWRITE); + timeout = AML_MMC_WRITE_TIMEOUT * + (data->len / block_size); + } + } + + sc->cmd = cmd; + + cmd->error = MMC_ERR_NONE; + + /* + * Round up while calculating the number of cycles which + * correspond to a millisecond. Use that to determine + * the count from the desired timeout in milliseconds. + * + * The counter has a limited range which is not sufficient + * for directly implementing worst case timeouts at high clock + * rates so a 32 bit counter is implemented in software. + * + * The documentation isn't clear on when the timer starts + * so add 48 cycles for the command and 136 cycles for the + * response (the values are from the previously mentioned + * standard). + */ + if (timeout > AML_MMC_MAX_TIMEOUT) + timeout = AML_MMC_MAX_TIMEOUT; + cycles_per_msec = (ios->clock + 1000 - 1) / 1000; + sc->timeout_remaining = 48 + 136 + timeout * cycles_per_msec; + + aml8726_mmc_restart_timer(sc); + + CSR_WRITE_4(sc, AML_MMC_CMD_ARGUMENT_REG, cmd->arg); + CSR_WRITE_4(sc, AML_MMC_MULT_CONFIG_REG, mcfgr); + CSR_WRITE_4(sc, AML_MMC_EXTENSION_REG, extr); + CSR_WRITE_4(sc, AML_MMC_DMA_ADDR_REG, (uint32_t)baddr); + + CSR_WRITE_4(sc, AML_MMC_CMD_SEND_REG, cmdr); + CSR_BARRIER(sc, AML_MMC_CMD_SEND_REG); + + return (MMC_ERR_NONE); +} + +static void +aml8726_mmc_intr(void *arg) +{ + struct aml8726_mmc_softc *sc = (struct aml8726_mmc_softc *)arg; + struct mmc_command *cmd; + struct mmc_command *stop_cmd; + struct mmc_data *data; + uint32_t cmdr; + uint32_t icr; + uint32_t isr; + uint32_t mcfgr; + uint32_t previous_byte; + uint32_t resp; + int mmc_error; + int mmc_stop_error; + unsigned int i; + + AML_MMC_LOCK(sc); + + isr = CSR_READ_4(sc, AML_MMC_IRQ_STATUS_REG); + cmdr = CSR_READ_4(sc, AML_MMC_CMD_SEND_REG); + + if (sc->cmd == NULL) + goto spurious; + + mmc_error = MMC_ERR_NONE; + + if ((isr & AML_MMC_IRQ_STATUS_CMD_DONE_IRQ) != 0) { + /* Check for CRC errors if the command has completed. */ + if ((cmdr & AML_MMC_CMD_RESP_NO_CRC7) == 0 && + (isr & AML_MMC_IRQ_STATUS_RESP_CRC7_OK) == 0) + mmc_error = MMC_ERR_BADCRC; + if ((cmdr & AML_MMC_CMD_RESP_HAS_DATA) != 0 && + (isr & AML_MMC_IRQ_STATUS_RD_CRC16_OK) == 0) + mmc_error = MMC_ERR_BADCRC; + if ((cmdr & AML_MMC_CMD_CMD_HAS_DATA) != 0 && + (isr & AML_MMC_IRQ_STATUS_WR_CRC16_OK) == 0) + mmc_error = MMC_ERR_BADCRC; + } else if ((isr & AML_MMC_IRQ_STATUS_TIMEOUT_IRQ) != 0) { + if (aml8726_mmc_restart_timer(sc) != 0) { + AML_MMC_UNLOCK(sc); + return; + } + mmc_error = MMC_ERR_TIMEOUT; + } else { +spurious: + + /* + * Clear spurious interrupts while leaving intacted any + * interrupts that may have occurred after we read the + * interrupt status register. + */ + + CSR_WRITE_4(sc, AML_MMC_IRQ_STATUS_REG, + (AML_MMC_IRQ_STATUS_CLEAR_IRQ & isr)); + CSR_BARRIER(sc, AML_MMC_IRQ_STATUS_REG); + AML_MMC_UNLOCK(sc); + return; + } + + if ((isr & AML_MMC_IRQ_STATUS_CMD_BUSY) != 0 && + /* + * A multiblock operation may keep the hardware + * busy until stop transmission is executed. + */ + (isr & AML_MMC_IRQ_STATUS_CMD_DONE_IRQ) == 0) { + if (mmc_error == MMC_ERR_NONE) + mmc_error = MMC_ERR_FAILED; + + /* + * Issue a soft reset (while leaving the command complete + * interrupt enabled) to terminate the command. + * + * Ensure the command has terminated before continuing on + * to things such as bus_dmamap_sync / bus_dmamap_unload. + */ + + icr = AML_MMC_IRQ_CONFIG_SOFT_RESET | + AML_MMC_IRQ_CONFIG_CMD_DONE_EN; + + CSR_WRITE_4(sc, AML_MMC_IRQ_CONFIG_REG, icr); + + while ((CSR_READ_4(sc, AML_MMC_IRQ_STATUS_REG) & + AML_MMC_IRQ_STATUS_CMD_BUSY) != 0) + cpu_spinwait(); + } + + /* Clear all interrupts since the request is no longer in flight. */ + CSR_WRITE_4(sc, AML_MMC_IRQ_STATUS_REG, AML_MMC_IRQ_STATUS_CLEAR_IRQ); + CSR_BARRIER(sc, AML_MMC_IRQ_STATUS_REG); + + cmd = sc->cmd; + sc->cmd = NULL; + + cmd->error = mmc_error; + + if ((cmd->flags & MMC_RSP_PRESENT) != 0 && + mmc_error == MMC_ERR_NONE) { + mcfgr = sc->port; + mcfgr |= AML_MMC_MULT_CONFIG_RESP_READOUT_EN; + CSR_WRITE_4(sc, AML_MMC_MULT_CONFIG_REG, mcfgr); + + if ((cmd->flags & MMC_RSP_136) != 0) { + + /* + * Controller supplies 135:8 instead of + * 127:0 so discard the leading 8 bits + * and provide a trailing 8 zero bits + * where the CRC belongs. + */ + + previous_byte = 0; + + for (i = 0; i < 4; i++) { + resp = CSR_READ_4(sc, AML_MMC_CMD_ARGUMENT_REG); + cmd->resp[3 - i] = (resp << 8) | previous_byte; + previous_byte = (resp >> 24) & 0xff; + } + } else + cmd->resp[0] = CSR_READ_4(sc, AML_MMC_CMD_ARGUMENT_REG); + } + + data = cmd->data; + + if (data && data->len && + (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)) != 0) { + if ((data->flags & MMC_DATA_READ) != 0) + bus_dmamap_sync(sc->dmatag, sc->dmamap, + BUS_DMASYNC_POSTREAD); + else + bus_dmamap_sync(sc->dmatag, sc->dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->dmatag, sc->dmamap); + } + + /* + * If there's a linked stop command, then start the stop command. + * In order to establish a known state attempt the stop command + * even if the original request encountered an error. + */ + + stop_cmd = (cmd->mrq->stop != cmd) ? cmd->mrq->stop : NULL; + + if (stop_cmd != NULL) { + mmc_stop_error = aml8726_mmc_start_command(sc, stop_cmd); + if (mmc_stop_error == MMC_ERR_NONE) { + AML_MMC_UNLOCK(sc); + return; + } + stop_cmd->error = mmc_stop_error; + } + + AML_MMC_UNLOCK(sc); + + /* Execute the callback after dropping the lock. */ + if (cmd->mrq) + cmd->mrq->done(cmd->mrq); +} + +static int +aml8726_mmc_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-mmc")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 MMC"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_mmc_attach(device_t dev) +{ + struct aml8726_mmc_softc *sc = device_get_softc(dev); + char *function_name; + char *voltages; + char *voltage; + int error; + int nvoltages; + pcell_t prop[3]; + phandle_t node; + ssize_t len; + device_t child; + + sc->dev = dev; + + node = ofw_bus_get_node(dev); + + len = OF_getencprop(OF_parent(node), "bus-frequency", + prop, sizeof(prop)); + if ((len / sizeof(prop[0])) != 1 || prop[0] == 0) { + device_printf(dev, "missing bus-frequency attribute in FDT\n"); + return (ENXIO); + } + + sc->ref_freq = prop[0]; + + /* + * The pins must be specified as part of the device in order + * to know which port to used. + */ + + len = OF_getencprop(node, "pinctrl-0", prop, sizeof(prop)); + + if ((len / sizeof(prop[0])) != 1 || prop[0] == 0) { + device_printf(dev, "missing pinctrl-0 attribute in FDT\n"); + return (ENXIO); + } + + len = OF_getprop_alloc(OF_node_from_xref(prop[0]), "amlogic,function", + sizeof(char), (void **)&function_name); + + if (len < 0) { + device_printf(dev, + "missing amlogic,function attribute in FDT\n"); + return (ENXIO); + } + + if (strncmp("sdio-a", function_name, len) == 0) + sc->port = AML_MMC_MULT_CONFIG_PORT_A; + else if (strncmp("sdio-b", function_name, len) == 0) + sc->port = AML_MMC_MULT_CONFIG_PORT_B; + else if (strncmp("sdio-c", function_name, len) == 0) + sc->port = AML_MMC_MULT_CONFIG_PORT_C; + else { + device_printf(dev, "unknown function attribute %.*s in FDT\n", + len, function_name); + free(function_name, M_OFWPROP); + return (ENXIO); + } + + free(function_name, M_OFWPROP); + + sc->pwr_en.dev = NULL; + + len = OF_getencprop(node, "mmc-pwr-en", prop, sizeof(prop)); + if (len > 0) { + if ((len / sizeof(prop[0])) == 3) { + sc->pwr_en.dev = OF_device_from_xref(prop[0]); + sc->pwr_en.pin = prop[1]; + sc->pwr_en.pol = prop[2]; + } + + if (sc->pwr_en.dev == NULL) { + device_printf(dev, + "unable to process mmc-pwr-en attribute in FDT\n"); + return (ENXIO); + } + + /* Turn off power and then configure the output driver. */ + if (aml8726_mmc_power_off(sc) != 0 || + GPIO_PIN_SETFLAGS(sc->pwr_en.dev, sc->pwr_en.pin, + GPIO_PIN_OUTPUT) != 0) { + device_printf(dev, + "could not use gpio to control power\n"); + return (ENXIO); + } + } + + len = OF_getprop_alloc(node, "mmc-voltages", + sizeof(char), (void **)&voltages); + + if (len < 0) { + device_printf(dev, "missing mmc-voltages attribute in FDT\n"); + return (ENXIO); + } + + sc->voltages[0] = 0; + sc->voltages[1] = 0; + + voltage = voltages; + nvoltages = 0; + + while (len && nvoltages < 2) { + if (strncmp("1.8", voltage, len) == 0) + sc->voltages[nvoltages] = MMC_OCR_LOW_VOLTAGE; + else if (strncmp("3.3", voltage, len) == 0) + sc->voltages[nvoltages] = MMC_OCR_320_330 | + MMC_OCR_330_340; + else { + device_printf(dev, + "unknown voltage attribute %.*s in FDT\n", + len, voltage); + free(voltages, M_OFWPROP); + return (ENXIO); + } + + nvoltages++; + + /* queue up next string */ + while (*voltage && len) { + voltage++; + len--; + } + if (len) { + voltage++; + len--; + } + } + + free(voltages, M_OFWPROP); + + sc->vselect.dev = NULL; + + len = OF_getencprop(node, "mmc-vselect", prop, sizeof(prop)); + if (len > 0) { + if ((len / sizeof(prop[0])) == 2) { + sc->vselect.dev = OF_device_from_xref(prop[0]); + sc->vselect.pin = prop[1]; + sc->vselect.pol = 1; + } + + if (sc->vselect.dev == NULL) { + device_printf(dev, + "unable to process mmc-vselect attribute in FDT\n"); + return (ENXIO); + } + + /* + * With the power off select voltage 0 and then + * configure the output driver. + */ + if (GPIO_PIN_SET(sc->vselect.dev, sc->vselect.pin, 0) != 0 || + GPIO_PIN_SETFLAGS(sc->vselect.dev, sc->vselect.pin, + GPIO_PIN_OUTPUT) != 0) { + device_printf(dev, + "could not use gpio to set voltage\n"); + return (ENXIO); + } + } + + if (nvoltages == 0) { + device_printf(dev, "no voltages in FDT\n"); + return (ENXIO); + } else if (nvoltages == 1 && sc->vselect.dev != NULL) { + device_printf(dev, "only one voltage in FDT\n"); + return (ENXIO); + } else if (nvoltages == 2 && sc->vselect.dev == NULL) { + device_printf(dev, "too many voltages in FDT\n"); + return (ENXIO); + } + + if (bus_alloc_resources(dev, aml8726_mmc_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + return (ENXIO); + } + + AML_MMC_LOCK_INIT(sc); + + error = bus_dma_tag_create(bus_get_dma_tag(dev), AML_MMC_ALIGN_DMA, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + AML_MMC_MAX_DMA, 1, AML_MMC_MAX_DMA, 0, NULL, NULL, &sc->dmatag); + if (error) + goto fail; + + error = bus_dmamap_create(sc->dmatag, 0, &sc->dmamap); + + if (error) + goto fail; + + error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, + NULL, aml8726_mmc_intr, sc, &sc->ih_cookie); + if (error) { + device_printf(dev, "could not setup interrupt handler\n"); + goto fail; + } + + sc->host.f_min = 200000; + sc->host.f_max = 50000000; + sc->host.host_ocr = sc->voltages[0] | sc->voltages[1]; + sc->host.caps = MMC_CAP_4_BIT_DATA | MMC_CAP_HSPEED; + + child = device_add_child(dev, "mmc", -1); + + if (!child) { + device_printf(dev, "could not add mmc\n"); + error = ENXIO; + goto fail; + } + + error = device_probe_and_attach(child); + + if (error) { + device_printf(dev, "could not attach mmc\n"); + goto fail; + } + + return (0); + +fail: + if (sc->ih_cookie) + bus_teardown_intr(dev, sc->res[1], sc->ih_cookie); + + if (sc->dmamap) + bus_dmamap_destroy(sc->dmatag, sc->dmamap); + + if (sc->dmatag) + bus_dma_tag_destroy(sc->dmatag); + + AML_MMC_LOCK_DESTROY(sc); + + aml8726_mmc_power_off(sc); + + bus_release_resources(dev, aml8726_mmc_spec, sc->res); + + return (error); +} + +static int +aml8726_mmc_detach(device_t dev) +{ + struct aml8726_mmc_softc *sc = device_get_softc(dev); + + AML_MMC_LOCK(sc); + + if (sc->cmd != NULL) { + AML_MMC_UNLOCK(sc); + return (EBUSY); + } + + /* + * Turn off the power, reset the hardware state machine, + * disable the interrupts, and clear the interrupts. + */ + (void)aml8726_mmc_power_off(sc); + CSR_WRITE_4(sc, AML_MMC_IRQ_CONFIG_REG, AML_MMC_IRQ_CONFIG_SOFT_RESET); + CSR_BARRIER(sc, AML_MMC_IRQ_CONFIG_REG); + CSR_WRITE_4(sc, AML_MMC_IRQ_STATUS_REG, AML_MMC_IRQ_STATUS_CLEAR_IRQ); + + AML_MMC_UNLOCK(sc); + + bus_generic_detach(dev); + + bus_teardown_intr(dev, sc->res[1], sc->ih_cookie); + + bus_dmamap_destroy(sc->dmatag, sc->dmamap); + + bus_dma_tag_destroy(sc->dmatag); + + AML_MMC_LOCK_DESTROY(sc); + + bus_release_resources(dev, aml8726_mmc_spec, sc->res); + + return (0); +} + +static int +aml8726_mmc_shutdown(device_t dev) +{ + struct aml8726_mmc_softc *sc = device_get_softc(dev); + + /* + * Turn off the power, reset the hardware state machine, + * disable the interrupts, and clear the interrupts. + */ + (void)aml8726_mmc_power_off(sc); + CSR_WRITE_4(sc, AML_MMC_IRQ_CONFIG_REG, AML_MMC_IRQ_CONFIG_SOFT_RESET); + CSR_BARRIER(sc, AML_MMC_IRQ_CONFIG_REG); + CSR_WRITE_4(sc, AML_MMC_IRQ_STATUS_REG, AML_MMC_IRQ_STATUS_CLEAR_IRQ); + + return (0); +} + +static int +aml8726_mmc_update_ios(device_t bus, device_t child) +{ + struct aml8726_mmc_softc *sc = device_get_softc(bus); + struct mmc_ios *ios = &sc->host.ios; + unsigned int divisor; + int error; + int i; + uint32_t cfgr; + + cfgr = (2 << AML_MMC_CONFIG_WR_CRC_STAT_SHIFT) | + (2 << AML_MMC_CONFIG_WR_DELAY_SHIFT) | + AML_MMC_CONFIG_DMA_ENDIAN_SBW | + (39 << AML_MMC_CONFIG_CMD_ARG_BITS_SHIFT); + + switch (ios->bus_width) { + case bus_width_4: + cfgr |= AML_MMC_CONFIG_BUS_WIDTH_4; + break; + case bus_width_1: + cfgr |= AML_MMC_CONFIG_BUS_WIDTH_1; + break; + default: + return (EINVAL); + } + + divisor = sc->ref_freq / (ios->clock * 2) - 1; + if (divisor == 0 || divisor == -1) + divisor = 1; + if ((sc->ref_freq / ((divisor + 1) * 2)) > ios->clock) + divisor += 1; + if (divisor > 0x3ff) + divisor = 0x3ff; + + cfgr |= divisor; + + CSR_WRITE_4(sc, AML_MMC_CONFIG_REG, cfgr); + + error = 0; + + switch (ios->power_mode) { + case power_up: + /* + * Configure and power on the regulator so that the + * voltage stabilizes prior to powering on the card. + */ + if (sc->vselect.dev != NULL) { + for (i = 0; i < 2; i++) + if ((sc->voltages[i] & (1 << ios->vdd)) != 0) + break; + if (i >= 2) + return (EINVAL); + error = GPIO_PIN_SET(sc->vselect.dev, + sc->vselect.pin, i); + } + break; + case power_on: + error = aml8726_mmc_power_on(sc); + break; + case power_off: + error = aml8726_mmc_power_off(sc); + break; + default: + return (EINVAL); + } + + return (error); +} + +static int +aml8726_mmc_request(device_t bus, device_t child, struct mmc_request *req) +{ + struct aml8726_mmc_softc *sc = device_get_softc(bus); + int mmc_error; + + AML_MMC_LOCK(sc); + + if (sc->cmd != NULL) { + AML_MMC_UNLOCK(sc); + return (EBUSY); + } + + mmc_error = aml8726_mmc_start_command(sc, req->cmd); + + AML_MMC_UNLOCK(sc); + + /* Execute the callback after dropping the lock. */ + if (mmc_error != MMC_ERR_NONE) { + req->cmd->error = mmc_error; + req->done(req); + } + + return (0); +} + +static int +aml8726_mmc_read_ivar(device_t bus, device_t child, + int which, uintptr_t *result) +{ + struct aml8726_mmc_softc *sc = device_get_softc(bus); + + switch (which) { + case MMCBR_IVAR_BUS_MODE: + *(int *)result = sc->host.ios.bus_mode; + break; + case MMCBR_IVAR_BUS_WIDTH: + *(int *)result = sc->host.ios.bus_width; + break; + case MMCBR_IVAR_CHIP_SELECT: + *(int *)result = sc->host.ios.chip_select; + break; + case MMCBR_IVAR_CLOCK: + *(int *)result = sc->host.ios.clock; + break; + case MMCBR_IVAR_F_MIN: + *(int *)result = sc->host.f_min; + break; + case MMCBR_IVAR_F_MAX: + *(int *)result = sc->host.f_max; + break; + case MMCBR_IVAR_HOST_OCR: + *(int *)result = sc->host.host_ocr; + break; + case MMCBR_IVAR_MODE: + *(int *)result = sc->host.mode; + break; + case MMCBR_IVAR_OCR: + *(int *)result = sc->host.ocr; + break; + case MMCBR_IVAR_POWER_MODE: + *(int *)result = sc->host.ios.power_mode; + break; + case MMCBR_IVAR_VDD: + *(int *)result = sc->host.ios.vdd; + break; + case MMCBR_IVAR_CAPS: + *(int *)result = sc->host.caps; + break; + case MMCBR_IVAR_MAX_DATA: + *(int *)result = AML_MMC_MAX_DMA / MMC_SECTOR_SIZE; + break; + default: + return (EINVAL); + } + + return (0); +} + +static int +aml8726_mmc_write_ivar(device_t bus, device_t child, + int which, uintptr_t value) +{ + struct aml8726_mmc_softc *sc = device_get_softc(bus); + + switch (which) { + case MMCBR_IVAR_BUS_MODE: + sc->host.ios.bus_mode = value; + break; + case MMCBR_IVAR_BUS_WIDTH: + sc->host.ios.bus_width = value; + break; + case MMCBR_IVAR_CHIP_SELECT: + sc->host.ios.chip_select = value; + break; + case MMCBR_IVAR_CLOCK: + sc->host.ios.clock = value; + break; + case MMCBR_IVAR_MODE: + sc->host.mode = value; + break; + case MMCBR_IVAR_OCR: + sc->host.ocr = value; + break; + case MMCBR_IVAR_POWER_MODE: + sc->host.ios.power_mode = value; + break; + case MMCBR_IVAR_VDD: + sc->host.ios.vdd = value; + break; + /* These are read-only */ + case MMCBR_IVAR_CAPS: + case MMCBR_IVAR_HOST_OCR: + case MMCBR_IVAR_F_MIN: + case MMCBR_IVAR_F_MAX: + case MMCBR_IVAR_MAX_DATA: + default: + return (EINVAL); + } + + return (0); +} + +static int +aml8726_mmc_get_ro(device_t bus, device_t child) +{ + + return (0); +} + +static int +aml8726_mmc_acquire_host(device_t bus, device_t child) +{ + struct aml8726_mmc_softc *sc = device_get_softc(bus); + + AML_MMC_LOCK(sc); + + while (sc->bus_busy) + mtx_sleep(sc, &sc->mtx, PZERO, "mmc", hz / 5); + sc->bus_busy++; + + AML_MMC_UNLOCK(sc); + + return (0); +} + +static int +aml8726_mmc_release_host(device_t bus, device_t child) +{ + struct aml8726_mmc_softc *sc = device_get_softc(bus); + + AML_MMC_LOCK(sc); + + sc->bus_busy--; + wakeup(sc); + + AML_MMC_UNLOCK(sc); + + return (0); +} + +static device_method_t aml8726_mmc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_mmc_probe), + DEVMETHOD(device_attach, aml8726_mmc_attach), + DEVMETHOD(device_detach, aml8726_mmc_detach), + DEVMETHOD(device_shutdown, aml8726_mmc_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, aml8726_mmc_read_ivar), + DEVMETHOD(bus_write_ivar, aml8726_mmc_write_ivar), + + /* MMC bridge interface */ + DEVMETHOD(mmcbr_update_ios, aml8726_mmc_update_ios), + DEVMETHOD(mmcbr_request, aml8726_mmc_request), + DEVMETHOD(mmcbr_get_ro, aml8726_mmc_get_ro), + DEVMETHOD(mmcbr_acquire_host, aml8726_mmc_acquire_host), + DEVMETHOD(mmcbr_release_host, aml8726_mmc_release_host), + + DEVMETHOD_END +}; + +static driver_t aml8726_mmc_driver = { + "aml8726_mmc", + aml8726_mmc_methods, + sizeof(struct aml8726_mmc_softc), +}; + +static devclass_t aml8726_mmc_devclass; + +DRIVER_MODULE(aml8726_mmc, simplebus, aml8726_mmc_driver, + aml8726_mmc_devclass, 0, 0); +MODULE_DEPEND(aml8726_mmc, aml8726_gpio, 1, 1, 1); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_mmc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_mmc.h =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_mmc.h (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_mmc.h (revision 280905) @@ -0,0 +1,137 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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$ + */ + +#ifndef _ARM_AMLOGIC_AML8726_MMC_H +#define _ARM_AMLOGIC_AML8726_MMC_H + + +#define AML_MMC_ALIGN_DMA 4 +#define AML_MMC_MAX_DMA 4096 + +/* + * Timeouts are in milliseconds + * + * Read and write are per section 4.6.2 of the: + * + * SD Specifications Part 1 + * Physicaly Layer Simplified Specification + * Version 4.10 + */ +#define AML_MMC_CMD_TIMEOUT 50 +#define AML_MMC_READ_TIMEOUT 100 +#define AML_MMC_WRITE_TIMEOUT 500 +#define AML_MMC_MAX_TIMEOUT 5000 + +/* + * Internally the timeout is implemented by counting clock cycles. + * + * Since the hardware implements timeouts by counting cycles + * the minimum read / write timeout (assuming the minimum + * conversion factor of 1 cycle per usec) is: + * + * (8 bits * 512 bytes per block + 16 bits CRC) = 4112 usec + */ +#if ((AML_MMC_READ_TIMEOUT * 1000) < 4112 || \ + (AML_MMC_WRITE_TIMEOUT * 1000) < 4112) +#error "Single block timeout is smaller than supported" +#endif + +#define AML_MMC_CMD_ARGUMENT_REG 0 + +#define AML_MMC_CMD_SEND_REG 4 +#define AML_MMC_CMD_REP_PKG_CNT_MASK (0xffU << 24) +#define AML_MMC_CMD_REP_PKG_CNT_SHIFT 24 +#define AML_MMC_CMD_CMD_HAS_DATA (1 << 20) +#define AML_MMC_CMD_CHECK_DAT0_BUSY (1 << 19) +#define AML_MMC_CMD_RESP_CRC7_FROM_8 (1 << 18) +#define AML_MMC_CMD_RESP_HAS_DATA (1 << 17) +#define AML_MMC_CMD_RESP_NO_CRC7 (1 << 16) +#define AML_MMC_CMD_RESP_BITS_MASK (0xff << 8) +#define AML_MMC_CMD_RESP_BITS_SHIFT 8 +#define AML_MMC_CMD_START_BIT (0 << 7) +#define AML_MMC_CMD_TRANS_BIT_HOST (1 << 6) +#define AML_MMC_CMD_INDEX_MASK 0x3f +#define AML_MMC_CMD_INDEX_SHIFT 0 + +#define AML_MMC_CONFIG_REG 8 +#define AML_MMC_CONFIG_WR_CRC_STAT_MASK (7U << 29) +#define AML_MMC_CONFIG_WR_CRC_STAT_SHIFT 29 +#define AML_MMC_CONFIG_WR_DELAY_MASK (0x3f << 23) +#define AML_MMC_CONFIG_WR_DELAY_SHIFT 23 +#define AML_MMC_CONFIG_DMA_ENDIAN_MASK (3 << 21) +#define AML_MMC_CONFIG_DMA_ENDIAN_NC (0 << 21) +#define AML_MMC_CONFIG_DMA_ENDIAN_SB (1 << 21) +#define AML_MMC_CONFIG_DMA_ENDIAN_SW (2 << 21) +#define AML_MMC_CONFIG_DMA_ENDIAN_SBW (3 << 21) +#define AML_MMC_CONFIG_BUS_WIDTH_MASK (1 << 20) +#define AML_MMC_CONFIG_BUS_WIDTH_1 (0 << 20) +#define AML_MMC_CONFIG_BUS_WIDTH_4 (1 << 20) +#define AML_MMC_CONFIG_DATA_NEG_EDGE (1 << 19) +#define AML_MMC_CONFIG_DONT_DELAY_DATA (1 << 18) +#define AML_MMC_CONFIG_CMD_ARG_BITS_MASK (0x3f << 12) +#define AML_MMC_CONFIG_CMD_ARG_BITS_SHIFT 12 +#define AML_MMC_CONFIG_CMD_POS_EDGE (1 << 11) +#define AML_MMC_CONFIG_CMD_NO_CRC (1 << 10) +#define AML_MMC_CONFIG_CMD_CLK_DIV_MASK 0x3ff +#define AML_MMC_CONFIG_CMD_CLK_DIV_SHIFT 0 + +#define AML_MMC_IRQ_STATUS_REG 12 +#define AML_MMC_IRQ_STATUS_TIMER_CNT_MASK (0x1fffU << 19) +#define AML_MMC_IRQ_STATUS_TIMER_CNT_SHIFT 19 +#define AML_MMC_IRQ_STATUS_TIMER_EN (1 << 18) +#define AML_MMC_IRQ_STATUS_TIMEOUT_IRQ (1 << 16) +#define AML_MMC_IRQ_STATUS_CMD_DONE_IRQ (1 << 9) +#define AML_MMC_IRQ_STATUS_WR_CRC16_OK (1 << 7) +#define AML_MMC_IRQ_STATUS_RD_CRC16_OK (1 << 6) +#define AML_MMC_IRQ_STATUS_RESP_CRC7_OK (1 << 5) +#define AML_MMC_IRQ_STATUS_CMD_BUSY (1 << 4) +#define AML_MMC_IRQ_STATUS_CLEAR_IRQ 0x10700 + +#define AML_MMC_IRQ_CONFIG_REG 16 +#define AML_MMC_IRQ_CONFIG_SOFT_RESET (1 << 15) +#define AML_MMC_IRQ_CONFIG_CMD_DONE_EN (1 << 4) + +#define AML_MMC_MULT_CONFIG_REG 20 +#define AML_MMC_MULT_CONFIG_RESP_INDEX_MASK (0xf << 12) +#define AML_MMC_MULT_CONFIG_RESP_INDEX_SHIFT 12 +#define AML_MMC_MULT_CONFIG_RESP_READOUT_EN (1 << 8) +#define AML_MMC_MULT_CONFIG_STREAM_8_MODE (1 << 5) +#define AML_MMC_MULT_CONFIG_STREAM_EN (1 << 4) +#define AML_MMC_MULT_CONFIG_PORT_MASK 3 +#define AML_MMC_MULT_CONFIG_PORT_A 0 +#define AML_MMC_MULT_CONFIG_PORT_B 1 +#define AML_MMC_MULT_CONFIG_PORT_C 2 + +#define AML_MMC_DMA_ADDR_REG 24 + +#define AML_MMC_EXTENSION_REG 28 +#define AML_MMC_EXTENSION_NO_CRC16 (1 << 30) +#define AML_MMC_EXTENSION_PKT_SIZE_MASK (0x3fff << 16) +#define AML_MMC_EXTENSION_PKT_SIZE_SHIFT 16 + +#endif /* _ARM_AMLOGIC_AML8726_MMC_H */ Property changes on: head/sys/arm/amlogic/aml8726/aml8726_mmc.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_mp.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_mp.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_mp.c (revision 280905) @@ -0,0 +1,657 @@ +/*- + * Copyright 2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 multiprocessor support. + * + * Some processors require powering on which involves poking registers + * on the aobus and cbus ... it's expected that these locations are set + * in stone. + * + * Locking is not used as these routines should only be called by the BP + * during startup and should complete prior to the APs being released (the + * issue being to ensure that a register such as AML_SOC_CPU_CLK_CNTL_REG + * is not concurrently modified). + */ + +#include +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +static const char *scu_compatible[] = { + "arm,cortex-a5-scu", + "arm,cortex-a9-scu", + NULL +}; + +static const char *scu_errata_764369[] = { + "arm,cortex-a9-scu", + NULL +}; + +static const char *cpucfg_compatible[] = { + "amlogic,aml8726-cpuconfig", + NULL +}; + +static struct { + boolean_t errata_764369; + u_long scu_size; + struct resource scu_res; + u_long cpucfg_size; + struct resource cpucfg_res; + struct resource aobus_res; + struct resource cbus_res; +} aml8726_smp; + +#define AML_SCU_CONTROL_REG 0 +#define AML_SCU_CONTROL_ENABLE 1 +#define AML_SCU_CONFIG_REG 4 +#define AML_SCU_CONFIG_NCPU_MASK 0x3 +#define AML_SCU_CPU_PWR_STATUS_REG 8 +#define AML_SCU_CPU_PWR_STATUS_CPU3_MASK (3 << 24) +#define AML_SCU_CPU_PWR_STATUS_CPU2_MASK (3 << 16) +#define AML_SCU_CPU_PWR_STATUS_CPU1_MASK (3 << 8) +#define AML_SCU_CPU_PWR_STATUS_CPU0_MASK 3 +#define AML_SCU_INV_TAGS_REG 12 +#define AML_SCU_DIAG_CONTROL_REG 48 +#define AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT 1 + +#define AML_CPUCONF_CONTROL_REG 0 +#define AML_CPUCONF_CPU1_ADDR_REG 4 +#define AML_CPUCONF_CPU2_ADDR_REG 8 +#define AML_CPUCONF_CPU3_ADDR_REG 12 + +/* aobus */ + +#define AML_M8_CPU_PWR_CNTL0_REG 0xe0 +#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU3_MASK (3 << 22) +#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU2_MASK (3 << 20) +#define AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK (3 << 18) + +#define AML_M8_CPU_PWR_CNTL0_ISO_CPU3 (1 << 3) +#define AML_M8_CPU_PWR_CNTL0_ISO_CPU2 (1 << 2) +#define AML_M8_CPU_PWR_CNTL0_ISO_CPU1 (1 << 1) + +#define AML_M8_CPU_PWR_CNTL1_REG 0xe4 +#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU3 (1 << 19) +#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU2 (1 << 18) +#define AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 (1 << 17) + +#define AML_M8_CPU_PWR_CNTL1_MODE_CPU3_MASK (3 << 8) +#define AML_M8_CPU_PWR_CNTL1_MODE_CPU2_MASK (3 << 6) +#define AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK (3 << 4) + +#define AML_M8B_CPU_PWR_MEM_PD0_REG 0xf4 +#define AML_M8B_CPU_PWR_MEM_PD0_CPU3 (0xf << 20) +#define AML_M8B_CPU_PWR_MEM_PD0_CPU2 (0xf << 24) +#define AML_M8B_CPU_PWR_MEM_PD0_CPU1 (0xf << 28) + +/* cbus */ + +#define AML_SOC_CPU_CLK_CNTL_REG 0x419c +#define AML_M8_CPU_CLK_CNTL_RESET_CPU3 (1 << 27) +#define AML_M8_CPU_CLK_CNTL_RESET_CPU2 (1 << 26) +#define AML_M8_CPU_CLK_CNTL_RESET_CPU1 (1 << 25) + +#define SCU_WRITE_4(reg, value) bus_write_4(&aml8726_smp.scu_res, \ + (reg), (value)) +#define SCU_READ_4(reg) bus_read_4(&aml8726_smp.scu_res, (reg)) +#define SCU_BARRIER(reg) bus_barrier(&aml8726_smp.scu_res, \ + (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +#define CPUCONF_WRITE_4(reg, value) bus_write_4(&aml8726_smp.cpucfg_res, \ + (reg), (value)) +#define CPUCONF_READ_4(reg) bus_read_4(&aml8726_smp.cpucfg_res, \ + (reg)) +#define CPUCONF_BARRIER(reg) bus_barrier(&aml8726_smp.cpucfg_res, \ + (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +#define AOBUS_WRITE_4(reg, value) bus_write_4(&aml8726_smp.aobus_res, \ + (reg), (value)) +#define AOBUS_READ_4(reg) bus_read_4(&aml8726_smp.aobus_res, \ + (reg)) +#define AOBUS_BARRIER(reg) bus_barrier(&aml8726_smp.aobus_res, \ + (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +#define CBUS_WRITE_4(reg, value) bus_write_4(&aml8726_smp.cbus_res, \ + (reg), (value)) +#define CBUS_READ_4(reg) bus_read_4(&aml8726_smp.cbus_res, \ + (reg)) +#define CBUS_BARRIER(reg) bus_barrier(&aml8726_smp.cbus_res, \ + (reg), 4, (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +static phandle_t +find_node_for_device(const char *device, const char **compatible) +{ + int i; + phandle_t node; + + /* + * Try to access the node directly i.e. through /aliases/. + */ + + if ((node = OF_finddevice(device)) != 0) + for (i = 0; compatible[i]; i++) + if (fdt_is_compatible_strict(node, compatible[i])) + return node; + + /* + * Find the node the long way. + */ + + for (i = 0; compatible[i]; i++) { + if ((node = OF_finddevice("/soc")) == 0) + return (0); + + if ((node = fdt_find_compatible(node, compatible[i], 1)) != 0) + return node; + } + + return (0); +} + + +static int +alloc_resource_for_node(phandle_t node, struct resource *res, u_long *size) +{ + int err; + u_long pbase, psize; + u_long start; + + if ((err = fdt_get_range(OF_parent(node), 0, &pbase, &psize)) != 0 || + (err = fdt_regsize(node, &start, size)) != 0) + return (err); + + start += pbase; + + memset(res, 0, sizeof(*res)); + + res->r_bustag = fdtbus_bs_tag; + + err = bus_space_map(res->r_bustag, start, *size, 0, &res->r_bushandle); + + return (err); +} + + +static void +power_on_cpu(int cpu) +{ + uint32_t scpsr; + uint32_t value; + + if (cpu <= 0) + return; + + /* + * Power on the CPU if the intricate details are known, otherwise + * just hope for the best (it may have already be powered on by + * the hardware / firmware). + */ + + switch (aml8726_soc_hw_rev) { + case AML_SOC_HW_REV_M8: + case AML_SOC_HW_REV_M8B: + /* + * Set the SCU power status for the CPU to normal mode. + */ + scpsr = SCU_READ_4(AML_SCU_CPU_PWR_STATUS_REG); + scpsr &= ~(AML_SCU_CPU_PWR_STATUS_CPU1_MASK << ((cpu - 1) * 8)); + SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr); + SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG); + + if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { + /* + * Reset may cause the current power status from the + * actual CPU to be written to the SCU (over-writing + * the value we've just written) so set it to normal + * mode as well. + */ + value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG); + value &= ~(AML_M8B_CPU_PWR_CNTL0_MODE_CPU1_MASK << + ((cpu - 1) * 2)); + AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value); + AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG); + } + + DELAY(5); + + /* + * Assert reset. + */ + value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG); + value |= AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1); + CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value); + CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG); + + if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { + /* + * Release RAM pull-down. + */ + value = AOBUS_READ_4(AML_M8B_CPU_PWR_MEM_PD0_REG); + value &= ~((uint32_t)AML_M8B_CPU_PWR_MEM_PD0_CPU1 >> + ((cpu - 1) * 4)); + AOBUS_WRITE_4(AML_M8B_CPU_PWR_MEM_PD0_REG, value); + AOBUS_BARRIER(AML_M8B_CPU_PWR_MEM_PD0_REG); + } + + /* + * Power on CPU. + */ + value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG); + value &= ~(AML_M8_CPU_PWR_CNTL1_MODE_CPU1_MASK << + ((cpu - 1) * 2)); + AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL1_REG, value); + AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL1_REG); + + DELAY(10); + + if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { + /* + * Wait for power on confirmation. + */ + for ( ; ; ) { + value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL1_REG); + value &= AML_M8B_CPU_PWR_CNTL1_PWR_CPU1 << + (cpu - 1); + if (value) + break; + DELAY(10); + } + } + + /* + * Release peripheral clamp. + */ + value = AOBUS_READ_4(AML_M8_CPU_PWR_CNTL0_REG); + value &= ~(AML_M8_CPU_PWR_CNTL0_ISO_CPU1 << (cpu - 1)); + AOBUS_WRITE_4(AML_M8_CPU_PWR_CNTL0_REG, value); + AOBUS_BARRIER(AML_M8_CPU_PWR_CNTL0_REG); + + /* + * Release reset. + */ + value = CBUS_READ_4(AML_SOC_CPU_CLK_CNTL_REG); + value &= ~(AML_M8_CPU_CLK_CNTL_RESET_CPU1 << (cpu - 1)); + CBUS_WRITE_4(AML_SOC_CPU_CLK_CNTL_REG, value); + CBUS_BARRIER(AML_SOC_CPU_CLK_CNTL_REG); + + if (aml8726_soc_hw_rev == AML_SOC_HW_REV_M8B) { + /* + * The Amlogic Linux platform code sets the SCU power + * status for the CPU again for some reason so we + * follow suit (perhaps in case the reset caused + * a stale power status from the actual CPU to be + * written to the SCU). + */ + SCU_WRITE_4(AML_SCU_CPU_PWR_STATUS_REG, scpsr); + SCU_BARRIER(AML_SCU_CPU_PWR_STATUS_REG); + } + break; + default: + break; + } +} + + +void +platform_mp_init_secondary(void) +{ + + /* + * Consider modifying the timer driver to support + * per-cpu timers and then enabling the timer for + * each AP. + */ + + arm_init_secondary_ic(); +} + + +void +platform_mp_setmaxid(void) +{ + int err; + int i; + int ncpu; + phandle_t cpucfg_node; + phandle_t scu_node; + uint32_t value; + + if (mp_ncpus != 0) + return; + + ncpu = 1; + + /* + * Is the hardware necessary for SMP present? + */ + + if ((scu_node = find_node_for_device("scu", scu_compatible)) == 0) + goto moveon; + + if ((cpucfg_node = find_node_for_device("cpuconfig", + cpucfg_compatible)) == 0) + goto moveon; + + if (alloc_resource_for_node(scu_node, &aml8726_smp.scu_res, + &aml8726_smp.scu_size) != 0) + panic("Could not allocate resource for SCU"); + + if (alloc_resource_for_node(cpucfg_node, &aml8726_smp.cpucfg_res, + &aml8726_smp.cpucfg_size) != 0) + panic("Could not allocate resource for CPUCONFIG"); + + + /* + * Strictly speaking the aobus and cbus may not be required in + * order to start an AP (it depends on the processor), however + * always mapping them in simplifies the code. + */ + + aml8726_smp.aobus_res.r_bustag = fdtbus_bs_tag; + + err = bus_space_map(aml8726_smp.aobus_res.r_bustag, + AML_SOC_AOBUS_BASE_ADDR, 0x100000, + 0, &aml8726_smp.aobus_res.r_bushandle); + + if (err) + panic("Could not allocate resource for AOBUS"); + + aml8726_smp.cbus_res.r_bustag = fdtbus_bs_tag; + + err = bus_space_map(aml8726_smp.cbus_res.r_bustag, + AML_SOC_CBUS_BASE_ADDR, 0x100000, + 0, &aml8726_smp.cbus_res.r_bushandle); + + if (err) + panic("Could not allocate resource for CBUS"); + + aml8726_smp.errata_764369 = false; + for (i = 0; scu_errata_764369[i]; i++) + if (fdt_is_compatible_strict(scu_node, scu_errata_764369[i])) { + aml8726_smp.errata_764369 = true; + break; + } + + /* + * Read the number of CPUs present. + */ + value = SCU_READ_4(AML_SCU_CONFIG_REG); + ncpu = (value & AML_SCU_CONFIG_NCPU_MASK) + 1; + +moveon: + mp_ncpus = ncpu; + mp_maxid = ncpu - 1; +} + + +int +platform_mp_probe(void) +{ + + if (mp_ncpus == 0) + platform_mp_setmaxid(); + + return (mp_ncpus > 1); +} + + +void +platform_mp_start_ap(void) +{ + int i; + uint32_t reg; + uint32_t value; + vm_paddr_t paddr; + + if (mp_ncpus < 2) + return; + + /* + * Invalidate SCU cache tags. The 0x0000ffff constant invalidates + * all ways on all cores 0-3. Per the ARM docs, it's harmless to + * write to the bits for cores that are not present. + */ + SCU_WRITE_4(AML_SCU_INV_TAGS_REG, 0x0000ffff); + + if (aml8726_smp.errata_764369) { + /* + * Erratum ARM/MP: 764369 (problems with cache maintenance). + * Setting the "disable-migratory bit" in the undocumented SCU + * Diagnostic Control Register helps work around the problem. + */ + value = SCU_READ_4(AML_SCU_DIAG_CONTROL_REG); + value |= AML_SCU_DIAG_CONTROL_DISABLE_MIGBIT; + SCU_WRITE_4(AML_SCU_DIAG_CONTROL_REG, value); + } + + /* + * Enable the SCU, then clean the cache on this core. After these + * two operations the cache tag ram in the SCU is coherent with + * the contents of the cache on this core. The other cores aren't + * running yet so their caches can't contain valid data yet, however + * we've initialized their SCU tag ram above, so they will be + * coherent from startup. + */ + value = SCU_READ_4(AML_SCU_CONTROL_REG); + value |= AML_SCU_CONTROL_ENABLE; + SCU_WRITE_4(AML_SCU_CONTROL_REG, value); + SCU_BARRIER(AML_SCU_CONTROL_REG); + cpu_idcache_wbinv_all(); + + /* Set the boot address and power on each AP. */ + paddr = pmap_kextract((vm_offset_t)mpentry); + for (i = 1; i < mp_ncpus; i++) { + reg = AML_CPUCONF_CPU1_ADDR_REG + ((i - 1) * 4); + CPUCONF_WRITE_4(reg, paddr); + CPUCONF_BARRIER(reg); + + power_on_cpu(i); + } + + /* + * Enable the APs. + * + * The Amlogic Linux platform code sets the lsb for some reason + * in addition to the enable bit for each AP so we follow suit + * (the lsb may be the enable bit for the BP, though in that case + * it should already be set since it's currently running). + */ + value = CPUCONF_READ_4(AML_CPUCONF_CONTROL_REG); + value |= 1; + for (i = 1; i < mp_ncpus; i++) + value |= (1 << i); + CPUCONF_WRITE_4(AML_CPUCONF_CONTROL_REG, value); + CPUCONF_BARRIER(AML_CPUCONF_CONTROL_REG); + + /* Wakeup the now enabled APs */ + armv7_sev(); + + /* + * Free the resources which are not needed after startup. + */ + bus_space_unmap(aml8726_smp.scu_res.r_bustag, + aml8726_smp.scu_res.r_bushandle, + aml8726_smp.scu_size); + bus_space_unmap(aml8726_smp.cpucfg_res.r_bustag, + aml8726_smp.cpucfg_res.r_bushandle, + aml8726_smp.cpucfg_size); + bus_space_unmap(aml8726_smp.aobus_res.r_bustag, + aml8726_smp.aobus_res.r_bushandle, + 0x100000); + bus_space_unmap(aml8726_smp.cbus_res.r_bustag, + aml8726_smp.cbus_res.r_bushandle, + 0x100000); + memset(&aml8726_smp, 0, sizeof(aml8726_smp)); +} + +void +platform_ipi_send(cpuset_t cpus, u_int ipi) +{ + + pic_ipi_send(cpus, ipi); +} + +/* + * Stub drivers for cosmetic purposes. + */ +struct aml8726_scu_softc { + device_t dev; +}; + +static int +aml8726_scu_probe(device_t dev) +{ + int i; + + for (i = 0; scu_compatible[i]; i++) + if (ofw_bus_is_compatible(dev, scu_compatible[i])) + break; + + if (!scu_compatible[i]) + return (ENXIO); + + device_set_desc(dev, "ARM Snoop Control Unit"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_scu_attach(device_t dev) +{ + struct aml8726_scu_softc *sc = device_get_softc(dev); + + sc->dev = dev; + + return (0); +} + +static int +aml8726_scu_detach(device_t dev) +{ + + return (0); +} + +static device_method_t aml8726_scu_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_scu_probe), + DEVMETHOD(device_attach, aml8726_scu_attach), + DEVMETHOD(device_detach, aml8726_scu_detach), + + DEVMETHOD_END +}; + +static driver_t aml8726_scu_driver = { + "scu", + aml8726_scu_methods, + sizeof(struct aml8726_scu_softc), +}; + +static devclass_t aml8726_scu_devclass; + +EARLY_DRIVER_MODULE(scu, simplebus, aml8726_scu_driver, aml8726_scu_devclass, + 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE); + +struct aml8726_cpucfg_softc { + device_t dev; +}; + +static int +aml8726_cpucfg_probe(device_t dev) +{ + int i; + + for (i = 0; cpucfg_compatible[i]; i++) + if (ofw_bus_is_compatible(dev, cpucfg_compatible[i])) + break; + + if (!cpucfg_compatible[i]) + return (ENXIO); + + device_set_desc(dev, "Amlogic CPU Config"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_cpucfg_attach(device_t dev) +{ + struct aml8726_cpucfg_softc *sc = device_get_softc(dev); + + sc->dev = dev; + + return (0); +} + +static int +aml8726_cpucfg_detach(device_t dev) +{ + + return (0); +} + +static device_method_t aml8726_cpucfg_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_cpucfg_probe), + DEVMETHOD(device_attach, aml8726_cpucfg_attach), + DEVMETHOD(device_detach, aml8726_cpucfg_detach), + + DEVMETHOD_END +}; + +static driver_t aml8726_cpucfg_driver = { + "cpuconfig", + aml8726_cpucfg_methods, + sizeof(struct aml8726_cpucfg_softc), +}; + +static devclass_t aml8726_cpucfg_devclass; + +EARLY_DRIVER_MODULE(cpuconfig, simplebus, aml8726_cpucfg_driver, + aml8726_cpucfg_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_MIDDLE); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_mp.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_pic.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_pic.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_pic.c (revision 280905) @@ -0,0 +1,272 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 PIC driver. + * + * The current implementation doesn't include support for FIQ. + * + * There is a set of four interrupt controllers per cpu located in adjacent + * memory addresses (the set for cpu 1 starts right after the set for cpu 0) + * ... this allows for interrupt handling to be spread across the cpus. + * + * The multicore chips also have a GIC ... typically they run SMP kernels + * which include the GIC driver in which case this driver is simply used + * to disable the PIC. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +struct aml8726_pic_softc { + device_t dev; + struct resource * res[1]; +}; + +static struct resource_spec aml8726_pic_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +/* + * devclass_get_device / device_get_softc could be used + * to dynamically locate this, however the pic is a + * required device which can't be unloaded so there's + * no need for the overhead. + */ +static struct aml8726_pic_softc *aml8726_pic_sc = NULL; + +#define AML_PIC_NCNTRLS 4 +#define AML_PIC_IRQS_PER_CNTRL 32 + +#define AML_PIC_NIRQS (AML_PIC_NCNTRLS * AML_PIC_IRQS_PER_CNTRL) + +#define AML_PIC_0_STAT_REG 0 +#define AML_PIC_0_STAT_CLR_REG 4 +#define AML_PIC_0_MASK_REG 8 +#define AML_PIC_0_FIRQ_SEL 12 + +#define AML_PIC_1_STAT_REG 16 +#define AML_PIC_1_STAT_CLR_REG 20 +#define AML_PIC_1_MASK_REG 24 +#define AML_PIC_1_FIRQ_SEL 28 + +#define AML_PIC_2_STAT_REG 32 +#define AML_PIC_2_STAT_CLR_REG 36 +#define AML_PIC_2_MASK_REG 40 +#define AML_PIC_2_FIRQ_SEL 44 + +#define AML_PIC_3_STAT_REG 48 +#define AML_PIC_3_STAT_CLR_REG 52 +#define AML_PIC_3_MASK_REG 56 +#define AML_PIC_3_FIRQ_SEL 60 + +#define AML_PIC_CTRL(x) ((x) >> 5) +#define AML_PIC_BIT(x) (1 << ((x) & 0x1f)) + +#define AML_PIC_STAT_REG(x) (AML_PIC_0_STAT_REG + AML_PIC_CTRL(x) * 16) +#define AML_PIC_STAT_CLR_REG(x) (AML_PIC_0_STAT_CLR_REG + AML_PIC_CTRL(x) * 16) +#define AML_PIC_MASK_REG(x) (AML_PIC_0_MASK_REG + AML_PIC_CTRL(x) * 16) +#define AML_PIC_FIRQ_SEL(x) (AML_PIC_0_FIRQ_REG + AML_PIC_CTRL(x) * 16) + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) +#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ + (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +static void +aml8726_pic_eoi(void *arg) +{ + uintptr_t nb = (uintptr_t) arg; + + if (nb >= AML_PIC_NIRQS) + return; + + CSR_WRITE_4(aml8726_pic_sc, AML_PIC_STAT_CLR_REG(nb), AML_PIC_BIT(nb)); + + CSR_BARRIER(aml8726_pic_sc, AML_PIC_STAT_CLR_REG(nb)); +} + +static int +aml8726_pic_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-pic")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 PIC"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_pic_attach(device_t dev) +{ + struct aml8726_pic_softc *sc = device_get_softc(dev); + int i; + + /* There should be exactly one instance. */ + if (aml8726_pic_sc != NULL) + return (ENXIO); + + sc->dev = dev; + + if (bus_alloc_resources(dev, aml8726_pic_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + return (ENXIO); + } + + /* + * Disable, clear, and set the interrupts to normal mode. + */ + for (i = 0; i < AML_PIC_NCNTRLS; i++) { + CSR_WRITE_4(sc, AML_PIC_0_MASK_REG + i * 16, 0); + CSR_WRITE_4(sc, AML_PIC_0_STAT_CLR_REG + i * 16, ~0u); + CSR_WRITE_4(sc, AML_PIC_0_FIRQ_SEL + i * 16, 0); + } + +#ifndef SMP + arm_post_filter = aml8726_pic_eoi; +#else + device_printf(dev, "disabled in favor of gic\n"); +#endif + + aml8726_pic_sc = sc; + + return (0); +} + +static int +aml8726_pic_detach(device_t dev) +{ + + return (EBUSY); +} + +static device_method_t aml8726_pic_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_pic_probe), + DEVMETHOD(device_attach, aml8726_pic_attach), + DEVMETHOD(device_detach, aml8726_pic_detach), + + DEVMETHOD_END +}; + +static driver_t aml8726_pic_driver = { + "pic", + aml8726_pic_methods, + sizeof(struct aml8726_pic_softc), +}; + +static devclass_t aml8726_pic_devclass; + +EARLY_DRIVER_MODULE(pic, simplebus, aml8726_pic_driver, aml8726_pic_devclass, + 0, 0, BUS_PASS_INTERRUPT); + +#ifndef SMP +int +arm_get_next_irq(int last) +{ + uint32_t value; + int irq; + int start; + + /* + * The extra complexity is simply so that all IRQs are checked + * round robin so a particularly busy interrupt can't prevent + * other interrupts from being serviced. + */ + + start = (last + 1) % AML_PIC_NIRQS; + irq = start; + + for ( ; ; ) { + value = CSR_READ_4(aml8726_pic_sc, AML_PIC_STAT_REG(irq)); + + for ( ; ; ) { + if ((value & AML_PIC_BIT(irq)) != 0) + return (irq); + + irq = (irq + 1) % AML_PIC_NIRQS; + + if (irq == start) + return (-1); + + if ((irq % AML_PIC_IRQS_PER_CNTRL) == 0) + break; + } + } +} + +void +arm_mask_irq(uintptr_t nb) +{ + uint32_t mask; + + if (nb >= AML_PIC_NIRQS) + return; + + mask = CSR_READ_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb)); + mask &= ~AML_PIC_BIT(nb); + CSR_WRITE_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb), mask); + + CSR_BARRIER(aml8726_pic_sc, AML_PIC_MASK_REG(nb)); + + aml8726_pic_eoi((void *)nb); +} + +void +arm_unmask_irq(uintptr_t nb) +{ + uint32_t mask; + + if (nb >= AML_PIC_NIRQS) + return; + + mask = CSR_READ_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb)); + mask |= AML_PIC_BIT(nb); + CSR_WRITE_4(aml8726_pic_sc, AML_PIC_MASK_REG(nb), mask); +} +#endif Property changes on: head/sys/arm/amlogic/aml8726/aml8726_pic.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_pinctrl.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_pinctrl.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_pinctrl.c (revision 280905) @@ -0,0 +1,434 @@ +/*- + * Copyright 2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 pinctrl driver. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +struct aml8726_pinctrl_softc { + device_t dev; + struct { + struct aml8726_pinctrl_function *func; + struct aml8726_pinctrl_pkg_pin *ppin; + boolean_t pud_ctrl; + } soc; + struct resource *res[6]; + struct mtx mtx; +}; + +static struct resource_spec aml8726_pinctrl_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* mux */ + { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_SHAREABLE }, /* pu/pd */ + { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_SHAREABLE }, /* pull enable */ + { SYS_RES_MEMORY, 3, RF_ACTIVE }, /* ao mux */ + { SYS_RES_MEMORY, 4, RF_ACTIVE | RF_SHAREABLE }, /* ao pu/pd */ + { SYS_RES_MEMORY, 5, RF_ACTIVE | RF_SHAREABLE }, /* ao pull enable */ + { -1, 0 } +}; + +#define AML_PINCTRL_LOCK(sc) mtx_lock(&(sc)->mtx) +#define AML_PINCTRL_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define AML_PINCTRL_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "pinctrl", MTX_DEF) +#define AML_PINCTRL_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define MUX_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define MUX_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) + +#define PUD_WRITE_4(sc, reg, val) bus_write_4((sc)->res[1], reg, (val)) +#define PUD_READ_4(sc, reg) bus_read_4((sc)->res[1], reg) + +#define PEN_WRITE_4(sc, reg, val) bus_write_4((sc)->res[2], reg, (val)) +#define PEN_READ_4(sc, reg) bus_read_4((sc)->res[2], reg) + +#define AOMUX_WRITE_4(sc, reg, val) bus_write_4((sc)->res[3], reg, (val)) +#define AOMUX_READ_4(sc, reg) bus_read_4((sc)->res[3], reg) + +#define AOPUD_WRITE_4(sc, reg, val) bus_write_4((sc)->res[4], reg, (val)) +#define AOPUD_READ_4(sc, reg) bus_read_4((sc)->res[4], reg) + +#define AOPEN_WRITE_4(sc, reg, val) bus_write_4((sc)->res[5], reg, (val)) +#define AOPEN_READ_4(sc, reg) bus_read_4((sc)->res[5], reg) + +static int +aml8726_pinctrl_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-pinctrl")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 pinctrl"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_pinctrl_attach(device_t dev) +{ + struct aml8726_pinctrl_softc *sc = device_get_softc(dev); + + sc->dev = dev; + + sc->soc.pud_ctrl = false; + + switch (aml8726_soc_hw_rev) { + case AML_SOC_HW_REV_M3: + sc->soc.func = aml8726_m3_pinctrl; + sc->soc.ppin = aml8726_m3_pkg_pin; + break; + case AML_SOC_HW_REV_M6: + sc->soc.func = aml8726_m6_pinctrl; + sc->soc.ppin = aml8726_m6_pkg_pin; + break; + case AML_SOC_HW_REV_M8: + sc->soc.func = aml8726_m8_pinctrl; + sc->soc.ppin = aml8726_m8_pkg_pin; + sc->soc.pud_ctrl = true; + break; + case AML_SOC_HW_REV_M8B: + sc->soc.func = aml8726_m8b_pinctrl; + sc->soc.ppin = aml8726_m8b_pkg_pin; + sc->soc.pud_ctrl = true; + break; + default: + device_printf(dev, "unsupported SoC\n"); + return (ENXIO); + /* NOTREACHED */ + break; + } + + if (bus_alloc_resources(dev, aml8726_pinctrl_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + return (ENXIO); + } + + AML_PINCTRL_LOCK_INIT(sc); + + fdt_pinctrl_register(dev, "amlogic,pins"); + fdt_pinctrl_configure_tree(dev); + + return (0); +} + +static int +aml8726_pinctrl_detach(device_t dev) +{ + struct aml8726_pinctrl_softc *sc = device_get_softc(dev); + + AML_PINCTRL_LOCK_DESTROY(sc); + + bus_release_resources(dev, aml8726_pinctrl_spec, sc->res); + + return (0); +} + + +static int +aml8726_pinctrl_configure_pins(device_t dev, phandle_t cfgxref) +{ + struct aml8726_pinctrl_softc *sc = device_get_softc(dev); + struct aml8726_pinctrl_function *cf; + struct aml8726_pinctrl_function *f; + struct aml8726_pinctrl_pkg_pin *pp; + struct aml8726_pinctrl_pin *cp; + struct aml8726_pinctrl_pin *p; + enum aml8726_pinctrl_pull_mode pm; + char *function_name; + char *pins; + char *pin_name; + char *pull; + phandle_t node; + ssize_t len; + uint32_t value; + + node = OF_node_from_xref(cfgxref); + + len = OF_getprop_alloc(node, "amlogic,function", + sizeof(char), (void **)&function_name); + + if (len < 0) { + device_printf(dev, + "missing amlogic,function attribute in FDT\n"); + return (ENXIO); + } + + for (f = sc->soc.func; f->name != NULL; f++) + if (strncmp(f->name, function_name, len) == 0) + break; + + if (f->name == NULL) { + device_printf(dev, "unknown function attribute %.*s in FDT\n", + len, function_name); + free(function_name, M_OFWPROP); + return (ENXIO); + } + + free(function_name, M_OFWPROP); + + len = OF_getprop_alloc(node, "amlogic,pull", + sizeof(char), (void **)&pull); + + pm = aml8726_unknown_pm; + + if (len > 0) { + if (strncmp(pull, "enable", len) == 0) + pm = aml8726_enable_pm; + else if (strncmp(pull, "disable", len) == 0) + pm = aml8726_disable_pm; + else if (strncmp(pull, "down", len) == 0) + pm = aml8726_enable_down_pm; + else if (strncmp(pull, "up", len) == 0) + pm = aml8726_enable_up_pm; + else { + device_printf(dev, + "unknown pull attribute %.*s in FDT\n", + len, pull); + free(pull, M_OFWPROP); + return (ENXIO); + } + } + + free(pull, M_OFWPROP); + + /* + * Setting the pull direction isn't supported on all SoC. + */ + switch (pm) { + case aml8726_enable_down_pm: + case aml8726_enable_up_pm: + if (sc->soc.pud_ctrl == false) { + device_printf(dev, + "SoC doesn't support setting pull direction.\n"); + return (ENXIO); + } + break; + default: + break; + } + + len = OF_getprop_alloc(node, "amlogic,pins", + sizeof(char), (void **)&pins); + + if (len < 0) { + device_printf(dev, "missing amlogic,pins attribute in FDT\n"); + return (ENXIO); + } + + pin_name = pins; + + while (len) { + for (p = f->pins; p->name != NULL; p++) + if (strncmp(p->name, pin_name, len) == 0) + break; + + if (p->name == NULL) { + /* display message prior to queuing up next string */ + device_printf(dev, "unknown pin attribute %.*s in FDT\n", + len, pin_name); + } + + /* queue up next string */ + while (*pin_name && len) { + pin_name++; + len--; + } + if (len) { + pin_name++; + len--; + } + + if (p->name == NULL) + continue; + + for (pp = sc->soc.ppin; pp->pkg_name != NULL; pp++) + if (strcmp(pp->pkg_name, p->pkg_name) == 0) + break; + + if (pp->pkg_name == NULL) { + device_printf(dev, + "missing entry for package pin %s\n", + p->pkg_name); + continue; + } + + if (pm != aml8726_unknown_pm && pp->pull_bits == 0x00000000) { + device_printf(dev, + "missing pull info for package pin %s\n", + p->pkg_name); + continue; + } + + AML_PINCTRL_LOCK(sc); + + /* + * First clear all other mux bits associated with this + * package pin. This may briefly configure the pin as + * GPIO ... however this should be fine since after + * reset the default GPIO mode is input. + */ + + for (cf = sc->soc.func; cf->name != NULL; cf++) + for (cp = cf->pins; cp->name != NULL; cp++) { + if (cp == p) + continue; + if (strcmp(cp->pkg_name, p->pkg_name) != 0) + continue; + if (cp->mux_bits == 0) + continue; + if (pp->aobus == false) { + value = MUX_READ_4(sc, cp->mux_addr); + value &= ~cp->mux_bits; + MUX_WRITE_4(sc, cp->mux_addr, value); + } else { + value = AOMUX_READ_4(sc, cp->mux_addr); + value &= ~cp->mux_bits; + AOMUX_WRITE_4(sc, cp->mux_addr, value); + } + } + + /* + * Now set the desired mux bits. + * + * In the case of GPIO there's no bits to set. + */ + + if (p->mux_bits != 0) { + if (pp->aobus == false) { + value = MUX_READ_4(sc, p->mux_addr); + value |= p->mux_bits; + MUX_WRITE_4(sc, p->mux_addr, value); + } else { + value = AOMUX_READ_4(sc, p->mux_addr); + value |= p->mux_bits; + AOMUX_WRITE_4(sc, p->mux_addr, value); + } + } + + /* + * Finally set the pull mode if it was specified. + */ + + switch (pm) { + case aml8726_enable_down_pm: + case aml8726_enable_up_pm: + if (pp->aobus == false) { + value = PUD_READ_4(sc, pp->pull_addr); + if (pm == aml8726_enable_down_pm) + value &= ~pp->pull_bits; + else + value |= pp->pull_bits; + PUD_WRITE_4(sc, pp->pull_addr, value); + } else { + value = AOPUD_READ_4(sc, pp->pull_addr); + if (pm == aml8726_enable_down_pm) + value &= ~(pp->pull_bits << 16); + else + value |= (pp->pull_bits << 16); + AOPUD_WRITE_4(sc, pp->pull_addr, value); + } + /* FALLTHROUGH */ + case aml8726_disable_pm: + case aml8726_enable_pm: + if (pp->aobus == false) { + value = PEN_READ_4(sc, pp->pull_addr); + if (pm == aml8726_disable_pm) + value &= ~pp->pull_bits; + else + value |= pp->pull_bits; + PEN_WRITE_4(sc, pp->pull_addr, value); + } else { + value = AOPEN_READ_4(sc, pp->pull_addr); + if (pm == aml8726_disable_pm) + value &= ~pp->pull_bits; + else + value |= pp->pull_bits; + AOPEN_WRITE_4(sc, pp->pull_addr, value); + } + break; + default: + break; + } + + AML_PINCTRL_UNLOCK(sc); + } + + free(pins, M_OFWPROP); + + return (0); +} + + +static device_method_t aml8726_pinctrl_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_pinctrl_probe), + DEVMETHOD(device_attach, aml8726_pinctrl_attach), + DEVMETHOD(device_detach, aml8726_pinctrl_detach), + + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure,aml8726_pinctrl_configure_pins), + + DEVMETHOD_END +}; + +static driver_t aml8726_pinctrl_driver = { + "pinctrl", + aml8726_pinctrl_methods, + sizeof(struct aml8726_pinctrl_softc), +}; + +static devclass_t aml8726_pinctrl_devclass; + +EARLY_DRIVER_MODULE(pinctrl, simplebus, aml8726_pinctrl_driver, + aml8726_pinctrl_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_pinctrl.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_pinctrl.h =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_pinctrl.h (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_pinctrl.h (revision 280905) @@ -0,0 +1,1084 @@ +/*- + * Copyright 2015 John Wehle + * 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$ + */ + +/* + * In addition to supplying entries for pins which need to be configured + * by the operating system it's also necessary to supply entries for pins + * which may have been configured by the firmware for a different purpose. + */ + +#ifndef _ARM_AMLOGIC_AML8726_PINCTRL_H +#define _ARM_AMLOGIC_AML8726_PINCTRL_H + +enum aml8726_pinctrl_pull_mode { + aml8726_unknown_pm, + aml8726_disable_pm, + aml8726_enable_pm, + aml8726_enable_down_pm, + aml8726_enable_up_pm +}; + +struct aml8726_pinctrl_pkg_pin { + const char *pkg_name; + boolean_t aobus; + uint32_t pull_addr; + uint32_t pull_bits; +}; + +struct aml8726_pinctrl_pin { + const char *name; + const char *pkg_name; + uint32_t mux_addr; + uint32_t mux_bits; +}; + +struct aml8726_pinctrl_function { + const char *name; + struct aml8726_pinctrl_pin *pins; +}; + +/* + * aml8726-m3 + * + * start size + * cbus mux 0x202c 36 + * cbus pu_pd 0x203a 24 + * cbus pull_en 0x203a 24 + * aobus mux 0x0005 4 + * aobus pu_pd 0x000b 4 + * aobus pull_en 0x000b 4 + */ + +static struct aml8726_pinctrl_pkg_pin aml8726_m3_pkg_pin[] = { + { "card_0", false, 0, 0x00000000 }, + { "card_1", false, 0, 0x00000000 }, + { "card_2", false, 0, 0x00000000 }, + { "card_3", false, 0, 0x00000000 }, + { "card_4", false, 0, 0x00000000 }, + { "card_5", false, 0, 0x00000000 }, + { "card_6", false, 0, 0x00000000 }, + + { "gpioc_10", false, 0, 0x00000000 }, + { "gpioc_11", false, 0, 0x00000000 }, + { "gpioc_12", false, 0, 0x00000000 }, + { "gpioc_13", false, 0, 0x00000000 }, + + { "gpiox_13", false, 0, 0x00000000 }, + { "gpiox_14", false, 0, 0x00000000 }, + { "gpiox_15", false, 0, 0x00000000 }, + { "gpiox_16", false, 0, 0x00000000 }, + { "gpiox_17", false, 0, 0x00000000 }, + { "gpiox_18", false, 0, 0x00000000 }, + { "gpiox_19", false, 0, 0x00000000 }, + { "gpiox_20", false, 0, 0x00000000 }, + { "gpiox_21", false, 0, 0x00000000 }, + { "gpiox_22", false, 0, 0x00000000 }, + { "gpiox_23", false, 0, 0x00000000 }, + { "gpiox_24", false, 0, 0x00000000 }, + { "gpiox_25", false, 0, 0x00000000 }, + { "gpiox_26", false, 0, 0x00000000 }, + { "gpiox_27", false, 0, 0x00000000 }, + { "gpiox_28", false, 0, 0x00000000 }, + + { "gpioy_0", false, 0, 0x00000000 }, + { "gpioy_1", false, 0, 0x00000000 }, + { "gpioy_2", false, 0, 0x00000000 }, + { "gpioy_3", false, 0, 0x00000000 }, + { "gpioy_4", false, 0, 0x00000000 }, + { "gpioy_5", false, 0, 0x00000000 }, + { "gpioy_6", false, 0, 0x00000000 }, + { "gpioy_7", false, 0, 0x00000000 }, + { "gpioy_8", false, 0, 0x00000000 }, + { "gpioy_9", false, 0, 0x00000000 }, + + { "gpioao_0", true, 0, 0x00000000 }, + { "gpioao_1", true, 0, 0x00000000 }, + { "gpioao_2", true, 0, 0x00000000 }, + { "gpioao_3", true, 0, 0x00000000 }, + { "gpioao_4", true, 0, 0x00000000 }, + { "gpioao_5", true, 0, 0x00000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_gpio[] = { + { "card_0", "card_0", 0, 0x00000000 }, + { "card_1", "card_1", 0, 0x00000000 }, + { "card_2", "card_2", 0, 0x00000000 }, + { "card_3", "card_3", 0, 0x00000000 }, + { "card_4", "card_4", 0, 0x00000000 }, + { "card_5", "card_5", 0, 0x00000000 }, + { "card_6", "card_6", 0, 0x00000000 }, + + { "gpioc_10", "gpioc_10", 0, 0x00000000 }, + { "gpioc_11", "gpioc_11", 0, 0x00000000 }, + { "gpioc_12", "gpioc_12", 0, 0x00000000 }, + { "gpioc_13", "gpioc_13", 0, 0x00000000 }, + + { "gpiox_13", "gpiox_13", 0, 0x00000000 }, + { "gpiox_14", "gpiox_14", 0, 0x00000000 }, + { "gpiox_15", "gpiox_15", 0, 0x00000000 }, + { "gpiox_16", "gpiox_16", 0, 0x00000000 }, + { "gpiox_17", "gpiox_17", 0, 0x00000000 }, + { "gpiox_18", "gpiox_18", 0, 0x00000000 }, + { "gpiox_19", "gpiox_19", 0, 0x00000000 }, + { "gpiox_20", "gpiox_20", 0, 0x00000000 }, + { "gpiox_21", "gpiox_21", 0, 0x00000000 }, + { "gpiox_22", "gpiox_22", 0, 0x00000000 }, + { "gpiox_23", "gpiox_23", 0, 0x00000000 }, + { "gpiox_24", "gpiox_24", 0, 0x00000000 }, + { "gpiox_25", "gpiox_25", 0, 0x00000000 }, + { "gpiox_26", "gpiox_26", 0, 0x00000000 }, + { "gpiox_27", "gpiox_27", 0, 0x00000000 }, + { "gpiox_28", "gpiox_28", 0, 0x00000000 }, + + { "gpioy_0", "gpioy_0", 0, 0x00000000 }, + { "gpioy_1", "gpioy_1", 0, 0x00000000 }, + { "gpioy_2", "gpioy_2", 0, 0x00000000 }, + { "gpioy_3", "gpioy_3", 0, 0x00000000 }, + { "gpioy_4", "gpioy_4", 0, 0x00000000 }, + { "gpioy_5", "gpioy_5", 0, 0x00000000 }, + { "gpioy_6", "gpioy_6", 0, 0x00000000 }, + { "gpioy_7", "gpioy_7", 0, 0x00000000 }, + { "gpioy_8", "gpioy_8", 0, 0x00000000 }, + { "gpioy_9", "gpioy_9", 0, 0x00000000 }, + + { "gpioao_0", "gpioao_0", 0, 0x00000000 }, + { "gpioao_1", "gpioao_1", 0, 0x00000000 }, + { "gpioao_2", "gpioao_2", 0, 0x00000000 }, + { "gpioao_3", "gpioao_3", 0, 0x00000000 }, + { "gpioao_4", "gpioao_4", 0, 0x00000000 }, + { "gpioao_5", "gpioao_5", 0, 0x00000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_ethernet[] = { + { "clk50_in", "gpioy_0", 24, 0x00040000 }, + { "clk_out", "gpioy_0", 24, 0x00020000 }, + { "tx_en", "gpioy_5", 24, 0x00001000 }, + { "tx_d0", "gpioy_7", 24, 0x00000400 }, + { "tx_d1", "gpioy_6", 24, 0x00000800 }, + { "crs_dv", "gpioy_2", 24, 0x00008000 }, + { "rx_err", "gpioy_1", 24, 0x00010000 }, + { "rx_d0", "gpioy_4", 24, 0x00002000 }, + { "rx_d1", "gpioy_3", 24, 0x00004000 }, + { "mdc", "gpioy_8", 24, 0x00000200 }, + { "mdio", "gpioy_9", 24, 0x00000100 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_hdmi[] = { + { "cec", "gpioc_13", 4, 0x02000000 }, + { "hpd", "gpioc_10", 4, 0x00400000 }, + { "scl", "gpioc_12", 4, 0x01000000 }, + { "sda", "gpioc_11", 4, 0x00800000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_i2c_a[] = { + { "scl", "gpiox_26", 20, 0x04000000 }, + { "sda", "gpiox_25", 20, 0x08000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_i2c_b[] = { + { "scl", "gpiox_28", 20, 0x40000000 }, + { "sda", "gpiox_27", 20, 0x80000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_sdio_b[] = { + { "clk", "card_4", 8, 0x00000800 }, + { "cmd", "card_5", 8, 0x00000400 }, + { "d0", "card_0", 8, 0x00008000 }, + { "d1", "card_1", 8, 0x00004000 }, + { "d2", "card_2", 8, 0x00002000 }, + { "d3", "card_3", 8, 0x00001000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_sdxc_b[] = { + { "clk", "card_4", 8, 0x00000020 }, + { "cmd", "card_5", 8, 0x00000010 }, + { "d0", "card_0", 8, 0x00000080 }, + { "d1", "card_1", 8, 0x00000040 }, + { "d2", "card_2", 8, 0x00000040 }, + { "d3", "card_3", 8, 0x00000040 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_uart_a[] = { + { "tx", "gpiox_13", 16, 0x00002000 }, + { "rx", "gpiox_14", 16, 0x00001000 }, + { "cts", "gpiox_15", 16, 0x00000800 }, + { "rts", "gpiox_16", 16, 0x00000400 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_uart_b[] = { + { "tx", "gpiox_17", 16, 0x00000200 }, + { "rx", "gpiox_18", 16, 0x00000100 }, + { "cts", "gpiox_19", 16, 0x00000080 }, + { "rts", "gpiox_20", 16, 0x00000040 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_uart_c[] = { + { "tx", "gpiox_21", 16, 0x00000008 }, + { "rx", "gpiox_22", 16, 0x00000004 }, + { "cts", "gpiox_23", 16, 0x00000002 }, + { "rts", "gpiox_24", 16, 0x00000001 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_i2c_ao[] = { + { "scl", "gpioao_4", 0, 0x00000400 }, + { "sda", "gpioao_5", 0, 0x00000200 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m3_uart_ao[] = { + { "tx", "gpioao_0", 0, 0x00001000 }, + { "rx", "gpioao_1", 0, 0x00000800 }, + { "cts", "gpioao_2", 0, 0x00000400 }, + { "rts", "gpioao_3", 0, 0x00000200 }, + { NULL } +}; + +struct aml8726_pinctrl_function aml8726_m3_pinctrl[] = { + { "gpio", aml8726_m3_gpio }, + { "ethernet", aml8726_m3_ethernet }, + { "hdmi", aml8726_m3_hdmi }, + { "i2c-a", aml8726_m3_i2c_a }, + { "i2c-b", aml8726_m3_i2c_b }, + { "sdio-b", aml8726_m3_sdio_b }, + { "sdxc-b", aml8726_m3_sdxc_b }, + { "uart-a", aml8726_m3_uart_a }, + { "uart-b", aml8726_m3_uart_b }, + { "uart-c", aml8726_m3_uart_c }, + { "i2c-ao", aml8726_m3_i2c_ao }, + { "uart-ao", aml8726_m3_uart_ao }, + { NULL } +}; + +/* + * aml8726-m6 + * + * start size + * cbus mux 0x202c 40 + * cbus pu_pd 0x203a 24 + * cbus pull_en 0x203a 24 + * aobus mux 0x0005 4 + * aobus pu_pd 0x000b 4 + * aobus pull_en 0x000b 4 + * + * For simplicity we don't support setting pull for gpioe and gpioz. + */ + +static struct aml8726_pinctrl_pkg_pin aml8726_m6_pkg_pin[] = { + { "card_0", false, 12, 0x00100000 }, + { "card_1", false, 12, 0x00200000 }, + { "card_2", false, 12, 0x00400000 }, + { "card_3", false, 12, 0x00800000 }, + { "card_4", false, 12, 0x01000000 }, + { "card_5", false, 12, 0x02000000 }, + { "card_6", false, 12, 0x04000000 }, + + { "gpioc_10", false, 8, 0x00000400 }, + { "gpioc_11", false, 8, 0x00000800 }, + { "gpioc_12", false, 8, 0x00001000 }, + { "gpioc_13", false, 8, 0x00002000 }, + + { "gpiox_13", false, 16, 0x00002000 }, + { "gpiox_14", false, 16, 0x00004000 }, + { "gpiox_15", false, 16, 0x00008000 }, + { "gpiox_16", false, 16, 0x00010000 }, + { "gpiox_17", false, 16, 0x00020000 }, + { "gpiox_18", false, 16, 0x00040000 }, + { "gpiox_19", false, 16, 0x00080000 }, + { "gpiox_20", false, 16, 0x00100000 }, + { "gpiox_21", false, 16, 0x00200000 }, + { "gpiox_22", false, 16, 0x00400000 }, + { "gpiox_23", false, 16, 0x00800000 }, + { "gpiox_24", false, 16, 0x01000000 }, + { "gpiox_25", false, 16, 0x02000000 }, + { "gpiox_26", false, 16, 0x04000000 }, + { "gpiox_27", false, 16, 0x08000000 }, + { "gpiox_28", false, 16, 0x10000000 }, + + { "gpioy_0", false, 20, 0x00000010 }, + { "gpioy_1", false, 20, 0x00000020 }, + { "gpioy_2", false, 20, 0x00000040 }, + { "gpioy_3", false, 20, 0x00000080 }, + { "gpioy_4", false, 20, 0x00000100 }, + { "gpioy_5", false, 20, 0x00000200 }, + { "gpioy_6", false, 20, 0x00000400 }, + { "gpioy_7", false, 20, 0x00000800 }, + { "gpioy_8", false, 20, 0x00001000 }, + { "gpioy_9", false, 20, 0x00002000 }, + { "gpioy_10", false, 20, 0x00004000 }, + { "gpioy_11", false, 20, 0x00008000 }, + { "gpioy_12", false, 20, 0x00010000 }, + { "gpioy_13", false, 20, 0x00020000 }, + { "gpioy_14", false, 20, 0x00040000 }, + + { "gpioao_0", true, 0, 0x00000001 }, + { "gpioao_1", true, 0, 0x00000002 }, + { "gpioao_2", true, 0, 0x00000004 }, + { "gpioao_3", true, 0, 0x00000008 }, + { "gpioao_4", true, 0, 0x00000010 }, + { "gpioao_5", true, 0, 0x00000020 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_gpio[] = { + { "card_0", "card_0", 0, 0x00000000 }, + { "card_1", "card_1", 0, 0x00000000 }, + { "card_2", "card_2", 0, 0x00000000 }, + { "card_3", "card_3", 0, 0x00000000 }, + { "card_4", "card_4", 0, 0x00000000 }, + { "card_5", "card_5", 0, 0x00000000 }, + { "card_6", "card_6", 0, 0x00000000 }, + + { "gpioc_10", "gpioc_10", 0, 0x00000000 }, + { "gpioc_11", "gpioc_11", 0, 0x00000000 }, + { "gpioc_12", "gpioc_12", 0, 0x00000000 }, + { "gpioc_13", "gpioc_13", 0, 0x00000000 }, + + { "gpiox_13", "gpiox_13", 0, 0x00000000 }, + { "gpiox_14", "gpiox_14", 0, 0x00000000 }, + { "gpiox_15", "gpiox_15", 0, 0x00000000 }, + { "gpiox_16", "gpiox_16", 0, 0x00000000 }, + { "gpiox_17", "gpiox_17", 0, 0x00000000 }, + { "gpiox_18", "gpiox_18", 0, 0x00000000 }, + { "gpiox_19", "gpiox_19", 0, 0x00000000 }, + { "gpiox_20", "gpiox_20", 0, 0x00000000 }, + { "gpiox_21", "gpiox_21", 0, 0x00000000 }, + { "gpiox_22", "gpiox_22", 0, 0x00000000 }, + { "gpiox_23", "gpiox_23", 0, 0x00000000 }, + { "gpiox_24", "gpiox_24", 0, 0x00000000 }, + { "gpiox_25", "gpiox_25", 0, 0x00000000 }, + { "gpiox_26", "gpiox_26", 0, 0x00000000 }, + { "gpiox_27", "gpiox_27", 0, 0x00000000 }, + { "gpiox_28", "gpiox_28", 0, 0x00000000 }, + + { "gpioy_0", "gpioy_0", 0, 0x00000000 }, + { "gpioy_1", "gpioy_1", 0, 0x00000000 }, + { "gpioy_2", "gpioy_2", 0, 0x00000000 }, + { "gpioy_3", "gpioy_3", 0, 0x00000000 }, + { "gpioy_4", "gpioy_4", 0, 0x00000000 }, + { "gpioy_5", "gpioy_5", 0, 0x00000000 }, + { "gpioy_6", "gpioy_6", 0, 0x00000000 }, + { "gpioy_7", "gpioy_7", 0, 0x00000000 }, + { "gpioy_8", "gpioy_8", 0, 0x00000000 }, + { "gpioy_9", "gpioy_9", 0, 0x00000000 }, + { "gpioy_10", "gpioy_10", 0, 0x00000000 }, + { "gpioy_11", "gpioy_11", 0, 0x00000000 }, + { "gpioy_12", "gpioy_12", 0, 0x00000000 }, + { "gpioy_13", "gpioy_13", 0, 0x00000000 }, + { "gpioy_14", "gpioy_14", 0, 0x00000000 }, + + { "gpioao_0", "gpioao_0", 0, 0x00000000 }, + { "gpioao_1", "gpioao_1", 0, 0x00000000 }, + { "gpioao_2", "gpioao_2", 0, 0x00000000 }, + { "gpioao_3", "gpioao_3", 0, 0x00000000 }, + { "gpioao_4", "gpioao_4", 0, 0x00000000 }, + { "gpioao_5", "gpioao_5", 0, 0x00000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_ethernet[] = { + { "ref_clk_in", "gpioy_0", 24, 0x80000000 }, + { "ref_clk_out", "gpioy_0", 24, 0x40000000 }, + { "tx_clk", "gpioy_1", 24, 0x00040000 }, + { "tx_en", "gpioy_2", 24, 0x00020000 }, + { "tx_d0", "gpioy_6", 24, 0x00002000 }, + { "tx_d1", "gpioy_5", 24, 0x00004000 }, + { "tx_d2", "gpioy_4", 24, 0x00008000 }, + { "tx_d3", "gpioy_3", 24, 0x00010000 }, + { "rx_clk", "gpioy_7", 24, 0x00001000 }, + { "rx_dv", "gpioy_8", 24, 0x00000800 }, + { "rx_d0", "gpioy_12", 24, 0x00000080 }, + { "rx_d1", "gpioy_11", 24, 0x00000100 }, + { "rx_d2", "gpioy_10", 24, 0x00000200 }, + { "rx_d3", "gpioy_9", 24, 0x00000400 }, + { "mdc", "gpioy_14", 24, 0x00000020 }, + { "mdio", "gpioy_13", 24, 0x00000040 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_hdmi[] = { + { "cec", "gpioc_13", 4, 0x02000000 }, + { "hpd", "gpioc_10", 4, 0x00400000 }, + { "scl", "gpioc_12", 4, 0x01000000 }, + { "sda", "gpioc_11", 4, 0x00800000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_i2c_a[] = { + { "scl", "gpiox_26", 20, 0x04000000 }, + { "sda", "gpiox_25", 20, 0x08000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_i2c_b[] = { + { "scl", "gpiox_28", 20, 0x40000000 }, + { "sda", "gpiox_27", 20, 0x80000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_sdio_b[] = { + { "clk", "card_4", 8, 0x00000800 }, + { "cmd", "card_5", 8, 0x00000400 }, + { "d0", "card_0", 8, 0x00008000 }, + { "d1", "card_1", 8, 0x00004000 }, + { "d2", "card_2", 8, 0x00002000 }, + { "d3", "card_3", 8, 0x00001000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_sdxc_b[] = { + { "clk", "card_4", 8, 0x00000020 }, + { "cmd", "card_5", 8, 0x00000010 }, + { "d0", "card_0", 8, 0x00000080 }, + { "d1", "card_1", 8, 0x00000040 }, + { "d2", "card_2", 8, 0x00000040 }, + { "d3", "card_3", 8, 0x00000040 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_uart_a[] = { + { "tx", "gpiox_13", 16, 0x00002000 }, + { "rx", "gpiox_14", 16, 0x00001000 }, + { "cts", "gpiox_15", 16, 0x00000800 }, + { "rts", "gpiox_16", 16, 0x00000400 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_uart_b[] = { + { "tx", "gpiox_17", 16, 0x00000200 }, + { "rx", "gpiox_18", 16, 0x00000100 }, + { "cts", "gpiox_19", 16, 0x00000080 }, + { "rts", "gpiox_20", 16, 0x00000040 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_uart_c[] = { + { "tx", "gpiox_21", 16, 0x00000008 }, + { "rx", "gpiox_22", 16, 0x00000004 }, + { "cts", "gpiox_23", 16, 0x00000002 }, + { "rts", "gpiox_24", 16, 0x00000001 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_i2c_ao[] = { + { "scl", "gpioao_4", 0, 0x00000400 }, + { "sda", "gpioao_5", 0, 0x00000200 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m6_uart_ao[] = { + { "tx", "gpioao_0", 0, 0x00001000 }, + { "rx", "gpioao_1", 0, 0x00000800 }, + { "cts", "gpioao_2", 0, 0x00000400 }, + { "rts", "gpioao_3", 0, 0x00000200 }, + { NULL } +}; + +struct aml8726_pinctrl_function aml8726_m6_pinctrl[] = { + { "gpio", aml8726_m6_gpio }, + { "ethernet", aml8726_m6_ethernet }, + { "hdmi", aml8726_m6_hdmi }, + { "i2c-a", aml8726_m6_i2c_a }, + { "i2c-b", aml8726_m6_i2c_b }, + { "sdio-b", aml8726_m6_sdio_b }, + { "sdxc-b", aml8726_m6_sdxc_b }, + { "uart-a", aml8726_m6_uart_a }, + { "uart-b", aml8726_m6_uart_b }, + { "uart-c", aml8726_m6_uart_c }, + { "i2c-ao", aml8726_m6_i2c_ao }, + { "uart-ao", aml8726_m6_uart_ao }, + { NULL } +}; + + +/* + * aml8726-m8 + * + * start size + * cbus mux 0x202c 40 + * cbus pu_pd 0x203a 20 + * cbus pull_en 0x2048 20 + * aobus mux 0x0005 4 + * aobus pu_pd 0x000b 4 + * aobus pull_en 0x000b 4 + */ + +static struct aml8726_pinctrl_pkg_pin aml8726_m8_pkg_pin[] = { + { "boot_0", false, 8, 0x00000001 }, + { "boot_1", false, 8, 0x00000002 }, + { "boot_2", false, 8, 0x00000004 }, + { "boot_3", false, 8, 0x00000008 }, + { "boot_4", false, 8, 0x00000010 }, + { "boot_5", false, 8, 0x00000020 }, + { "boot_6", false, 8, 0x00000040 }, + { "boot_7", false, 8, 0x00000080 }, + + { "boot_16", false, 8, 0x00010000 }, + { "boot_17", false, 8, 0x00020000 }, + + { "card_0", false, 8, 0x00100000 }, + { "card_1", false, 8, 0x00200000 }, + { "card_2", false, 8, 0x00400000 }, + { "card_3", false, 8, 0x00800000 }, + { "card_4", false, 8, 0x01000000 }, + { "card_5", false, 8, 0x02000000 }, + { "card_6", false, 8, 0x04000000 }, + + { "gpioh_0", false, 4, 0x00001000 }, + { "gpioh_1", false, 4, 0x00002000 }, + { "gpioh_2", false, 4, 0x00004000 }, + { "gpioh_3", false, 4, 0x00008000 }, + + { "gpiox_12", false, 16, 0x00001000 }, + { "gpiox_13", false, 16, 0x00002000 }, + { "gpiox_14", false, 16, 0x00004000 }, + { "gpiox_15", false, 16, 0x00008000 }, + { "gpiox_16", false, 16, 0x00010000 }, + { "gpiox_17", false, 16, 0x00020000 }, + { "gpiox_18", false, 16, 0x00040000 }, + { "gpiox_19", false, 16, 0x00080000 }, + + { "gpioy_0", false, 12, 0x00000001 }, + { "gpioy_1", false, 12, 0x00000002 }, + { "gpioy_2", false, 12, 0x00000004 }, + { "gpioy_3", false, 12, 0x00000008 }, + + { "gpioz_2", false, 4, 0x00000004 }, + { "gpioz_3", false, 4, 0x00000008 }, + { "gpioz_4", false, 4, 0x00000010 }, + { "gpioz_5", false, 4, 0x00000020 }, + { "gpioz_6", false, 4, 0x00000040 }, + { "gpioz_7", false, 4, 0x00000080 }, + { "gpioz_8", false, 4, 0x00000100 }, + { "gpioz_9", false, 4, 0x00000200 }, + { "gpioz_10", false, 4, 0x00000400 }, + { "gpioz_11", false, 4, 0x00000800 }, + { "gpioz_12", false, 4, 0x00001000 }, + { "gpioz_13", false, 4, 0x00002000 }, + + { "gpioao_0", true, 0, 0x00000001 }, + { "gpioao_1", true, 0, 0x00000002 }, + { "gpioao_2", true, 0, 0x00000004 }, + { "gpioao_3", true, 0, 0x00000008 }, + { "gpioao_4", true, 0, 0x00000010 }, + { "gpioao_5", true, 0, 0x00000020 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_gpio[] = { + { "boot_0", "boot_0", 0, 0x00000000 }, + { "boot_1", "boot_1", 0, 0x00000000 }, + { "boot_2", "boot_2", 0, 0x00000000 }, + { "boot_3", "boot_3", 0, 0x00000000 }, + { "boot_4", "boot_4", 0, 0x00000000 }, + { "boot_5", "boot_5", 0, 0x00000000 }, + { "boot_6", "boot_6", 0, 0x00000000 }, + { "boot_7", "boot_7", 0, 0x00000000 }, + + { "boot_16", "boot_16", 0, 0x00000000 }, + { "boot_17", "boot_17", 0, 0x00000000 }, + + { "card_0", "card_0", 0, 0x00000000 }, + { "card_1", "card_1", 0, 0x00000000 }, + { "card_2", "card_2", 0, 0x00000000 }, + { "card_3", "card_3", 0, 0x00000000 }, + { "card_4", "card_4", 0, 0x00000000 }, + { "card_5", "card_5", 0, 0x00000000 }, + { "card_6", "card_6", 0, 0x00000000 }, + + { "gpioh_0", "gpioh_0", 0, 0x00000000 }, + { "gpioh_1", "gpioh_1", 0, 0x00000000 }, + { "gpioh_2", "gpioh_2", 0, 0x00000000 }, + { "gpioh_3", "gpioh_3", 0, 0x00000000 }, + + { "gpiox_12", "gpiox_12", 0, 0x00000000 }, + { "gpiox_13", "gpiox_13", 0, 0x00000000 }, + { "gpiox_14", "gpiox_14", 0, 0x00000000 }, + { "gpiox_15", "gpiox_15", 0, 0x00000000 }, + { "gpiox_16", "gpiox_16", 0, 0x00000000 }, + { "gpiox_17", "gpiox_17", 0, 0x00000000 }, + { "gpiox_18", "gpiox_18", 0, 0x00000000 }, + { "gpiox_19", "gpiox_19", 0, 0x00000000 }, + + { "gpioy_0", "gpioy_0", 0, 0x00000000 }, + { "gpioy_1", "gpioy_1", 0, 0x00000000 }, + { "gpioy_2", "gpioy_2", 0, 0x00000000 }, + { "gpioy_3", "gpioy_3", 0, 0x00000000 }, + + { "gpioz_2", "gpioz_2", 0, 0x00000000 }, + { "gpioz_3", "gpioz_3", 0, 0x00000000 }, + { "gpioz_4", "gpioz_4", 0, 0x00000000 }, + { "gpioz_5", "gpioz_5", 0, 0x00000000 }, + { "gpioz_6", "gpioz_6", 0, 0x00000000 }, + { "gpioz_7", "gpioz_7", 0, 0x00000000 }, + { "gpioz_8", "gpioz_8", 0, 0x00000000 }, + { "gpioz_9", "gpioz_9", 0, 0x00000000 }, + { "gpioz_10", "gpioz_10", 0, 0x00000000 }, + { "gpioz_11", "gpioz_11", 0, 0x00000000 }, + { "gpioz_12", "gpioz_12", 0, 0x00000000 }, + { "gpioz_13", "gpioz_13", 0, 0x00000000 }, + + { "gpioao_0", "gpioao_0", 0, 0x00000000 }, + { "gpioao_1", "gpioao_1", 0, 0x00000000 }, + { "gpioao_2", "gpioao_2", 0, 0x00000000 }, + { "gpioao_3", "gpioao_3", 0, 0x00000000 }, + { "gpioao_4", "gpioao_4", 0, 0x00000000 }, + { "gpioao_5", "gpioao_5", 0, 0x00000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_ethernet[] = { + { "tx_clk", "gpioz_4", 24, 0x00008000 }, + { "tx_en", "gpioz_5", 24, 0x00004000 }, + { "tx_d0", "gpioz_7", 24, 0x00001000 }, + { "tx_d1", "gpioz_6", 24, 0x00002000 }, + { "rx_clk_in", "gpioz_8", 24, 0x00000400 }, + { "rx_clk_out", "gpioz_8", 24, 0x00000200 }, + { "rx_dv", "gpioz_9", 24, 0x00000800 }, + { "rx_d0", "gpioz_11", 24, 0x00000080 }, + { "rx_d1", "gpioz_10", 24, 0x00000100 }, + { "mdc", "gpioz_13", 24, 0x00000020 }, + { "mdio", "gpioz_12", 24, 0x00000040 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_hdmi[] = { + { "cec", "gpioh_3", 4, 0x00800000 }, + { "hpd", "gpioh_0", 4, 0x04000000 }, + { "scl", "gpioh_2", 4, 0x01000000 }, + { "sda", "gpioh_1", 4, 0x02000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_i2c_a[] = { + { "scl", "gpioz_12", 20, 0x00000040 }, + { "sda", "gpioz_11", 20, 0x00000080 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_i2c_b[] = { + { "scl", "gpioz_3", 20, 0x04000000 }, + { "sda", "gpioz_2", 20, 0x08000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_sdio_b[] = { + { "clk", "card_2", 8, 0x00000800 }, + { "cmd", "card_3", 8, 0x00000400 }, + { "d0", "card_1", 8, 0x00008000 }, + { "d1", "card_0", 8, 0x00004000 }, + { "d2", "card_5", 8, 0x00002000 }, + { "d3", "card_4", 8, 0x00001000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_sdxc_b[] = { + { "clk", "card_2", 8, 0x00000020 }, + { "cmd", "card_3", 8, 0x00000010 }, + { "d0", "card_1", 8, 0x00000080 }, + { "d1", "card_0", 8, 0x00000040 }, + { "d2", "card_5", 8, 0x00000040 }, + { "d3", "card_4", 8, 0x00000040 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_sdio_c[] = { + { "clk", "boot_17", 24, 0x01000000 }, + { "cmd", "boot_16", 24, 0x02000000 }, + { "d0", "boot_0", 24, 0x20000000 }, + { "d1", "boot_1", 24, 0x10000000 }, + { "d2", "boot_2", 24, 0x08000000 }, + { "d3", "boot_3", 24, 0x04000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_sdxc_c[] = { + { "clk", "boot_17", 16, 0x04000000 }, + { "cmd", "boot_16", 16, 0x08000000 }, + { "d0", "boot_0", 16, 0x40000000 }, + { "d1", "boot_1", 16, 0x20000000 }, + { "d2", "boot_2", 16, 0x20000000 }, + { "d3", "boot_3", 16, 0x20000000 }, + { "d4", "boot_4", 16, 0x10000000 }, + { "d5", "boot_5", 16, 0x10000000 }, + { "d6", "boot_6", 16, 0x10000000 }, + { "d7", "boot_7", 16, 0x10000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_uart_a[] = { + { "tx", "gpiox_4", 16, 0x00020000 }, + { "rx", "gpiox_5", 16, 0x00010000 }, + { "cts", "gpiox_6", 16, 0x00008000 }, + { "rts", "gpiox_7", 16, 0x00004000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_uart_b[] = { + { "tx", "gpiox_16", 16, 0x00000200 }, + { "rx", "gpiox_17", 16, 0x00000100 }, + { "cts", "gpiox_18", 16, 0x00000080 }, + { "rts", "gpiox_19", 16, 0x00000040 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_uart_c[] = { + { "tx", "gpioy_0", 4, 0x00080000 }, + { "rx", "gpioy_1", 4, 0x00040000 }, + { "cts", "gpioy_2", 4, 0x00020000 }, + { "rts", "gpioy_3", 4, 0x00010000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_i2c_ao[] = { + { "scl", "gpioao_4", 0, 0x00000400 }, + { "sda", "gpioao_5", 0, 0x00000200 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8_uart_ao[] = { + { "tx", "gpioao_0", 0, 0x00001000 }, + { "rx", "gpioao_1", 0, 0x00000800 }, + { "cts", "gpioao_2", 0, 0x00000400 }, + { "rts", "gpioao_3", 0, 0x00000200 }, + { NULL } +}; + +struct aml8726_pinctrl_function aml8726_m8_pinctrl[] = { + { "gpio", aml8726_m8_gpio }, + { "ethernet", aml8726_m8_ethernet }, + { "hdmi", aml8726_m8_hdmi }, + { "i2c-a", aml8726_m8_i2c_a }, + { "i2c-b", aml8726_m8_i2c_b }, + { "sdio-b", aml8726_m8_sdio_b }, + { "sdxc-b", aml8726_m8_sdxc_b }, + { "sdio-c", aml8726_m8_sdio_c }, + { "sdxc-c", aml8726_m8_sdxc_c }, + { "uart-a", aml8726_m8_uart_a }, + { "uart-b", aml8726_m8_uart_b }, + { "uart-c", aml8726_m8_uart_c }, + { "i2c-ao", aml8726_m8_i2c_ao }, + { "uart-ao", aml8726_m8_uart_ao }, + { NULL } +}; + + +/* + * aml8726-m8b + * + * start size + * cbus mux 0x202c 40 + * cbus pu_pd 0x203a 24 + * cbus pull_en 0x2048 24 + * aobus mux 0x0005 4 + * aobus pu_pd 0x000b 4 + * aobus pull_en 0x000b 4 + */ + +static struct aml8726_pinctrl_pkg_pin aml8726_m8b_pkg_pin[] = { + { "boot_0", false, 8, 0x00000001 }, + { "boot_1", false, 8, 0x00000002 }, + { "boot_2", false, 8, 0x00000004 }, + { "boot_3", false, 8, 0x00000008 }, + { "boot_4", false, 8, 0x00000010 }, + { "boot_5", false, 8, 0x00000020 }, + { "boot_6", false, 8, 0x00000040 }, + { "boot_7", false, 8, 0x00000080 }, + { "boot_8", false, 8, 0x00000100 }, + { "boot_9", false, 8, 0x00000200 }, + { "boot_10", false, 8, 0x00000400 }, + + { "card_0", false, 8, 0x00100000 }, + { "card_1", false, 8, 0x00200000 }, + { "card_2", false, 8, 0x00400000 }, + { "card_3", false, 8, 0x00800000 }, + { "card_4", false, 8, 0x01000000 }, + { "card_5", false, 8, 0x02000000 }, + { "card_6", false, 8, 0x04000000 }, + + { "dif_0p", false, 20, 0x00000100 }, + { "dif_0n", false, 20, 0x00000200 }, + { "dif_1p", false, 20, 0x00000400 }, + { "dif_1n", false, 20, 0x00000800 }, + { "dif_2p", false, 20, 0x00001000 }, + { "dif_2n", false, 20, 0x00002000 }, + { "dif_3p", false, 20, 0x00004000 }, + { "dif_3n", false, 20, 0x00008000 }, + { "dif_4p", false, 20, 0x00010000 }, + { "dif_4n", false, 20, 0x00020000 }, + + { "gpiodv_24", false, 0, 0x01000000 }, + { "gpiodv_25", false, 0, 0x02000000 }, + { "gpiodv_26", false, 0, 0x04000000 }, + { "gpiodv_27", false, 0, 0x08000000 }, + + { "gpioh_0", false, 4, 0x00010000 }, + { "gpioh_1", false, 4, 0x00020000 }, + { "gpioh_2", false, 4, 0x00040000 }, + { "gpioh_3", false, 4, 0x00080000 }, + { "gpioh_4", false, 4, 0x00100000 }, + { "gpioh_5", false, 4, 0x00200000 }, + { "gpioh_6", false, 4, 0x00400000 }, + { "gpioh_7", false, 4, 0x00800000 }, + { "gpioh_8", false, 4, 0x01000000 }, + { "gpioh_9", false, 4, 0x02000000 }, + + { "gpiox_4", false, 16, 0x00000010 }, + { "gpiox_5", false, 16, 0x00000020 }, + { "gpiox_6", false, 16, 0x00000040 }, + { "gpiox_7", false, 16, 0x00000080 }, + { "gpiox_16", false, 16, 0x00010000 }, + { "gpiox_17", false, 16, 0x00020000 }, + { "gpiox_18", false, 16, 0x00040000 }, + { "gpiox_19", false, 16, 0x00080000 }, + + { "gpioao_0", true, 0, 0x00000001 }, + { "gpioao_1", true, 0, 0x00000002 }, + { "gpioao_2", true, 0, 0x00000004 }, + { "gpioao_3", true, 0, 0x00000008 }, + { "gpioao_4", true, 0, 0x00000010 }, + { "gpioao_5", true, 0, 0x00000020 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_gpio[] = { + { "boot_0", "boot_0", 0, 0x00000000 }, + { "boot_1", "boot_1", 0, 0x00000000 }, + { "boot_2", "boot_2", 0, 0x00000000 }, + { "boot_3", "boot_3", 0, 0x00000000 }, + { "boot_4", "boot_4", 0, 0x00000000 }, + { "boot_5", "boot_5", 0, 0x00000000 }, + { "boot_6", "boot_6", 0, 0x00000000 }, + { "boot_7", "boot_7", 0, 0x00000000 }, + { "boot_8", "boot_8", 0, 0x00000000 }, + { "boot_9", "boot_9", 0, 0x00000000 }, + { "boot_10", "boot_10", 0, 0x00000000 }, + + { "card_0", "card_0", 0, 0x00000000 }, + { "card_1", "card_1", 0, 0x00000000 }, + { "card_2", "card_2", 0, 0x00000000 }, + { "card_3", "card_3", 0, 0x00000000 }, + { "card_4", "card_4", 0, 0x00000000 }, + { "card_5", "card_5", 0, 0x00000000 }, + { "card_6", "card_6", 0, 0x00000000 }, + + { "dif_0p", "dif_0p", 0, 0x00000000 }, + { "dif_0n", "dif_0n", 0, 0x00000000 }, + { "dif_1p", "dif_1p", 0, 0x00000000 }, + { "dif_1n", "dif_1n", 0, 0x00000000 }, + { "dif_2p", "dif_2p", 0, 0x00000000 }, + { "dif_2n", "dif_2n", 0, 0x00000000 }, + { "dif_3p", "dif_3p", 0, 0x00000000 }, + { "dif_3n", "dif_3n", 0, 0x00000000 }, + { "dif_4p", "dif_4p", 0, 0x00000000 }, + { "dif_4n", "dif_4n", 0, 0x00000000 }, + + { "gpiodv_24", "gpiodv_24", 0, 0x00000000 }, + { "gpiodv_25", "gpiodv_25", 0, 0x00000000 }, + { "gpiodv_26", "gpiodv_26", 0, 0x00000000 }, + { "gpiodv_27", "gpiodv_27", 0, 0x00000000 }, + + { "gpioh_0", "gpioh_0", 0, 0x00000000 }, + { "gpioh_1", "gpioh_1", 0, 0x00000000 }, + { "gpioh_2", "gpioh_2", 0, 0x00000000 }, + { "gpioh_3", "gpioh_3", 0, 0x00000000 }, + { "gpioh_4", "gpioh_4", 0, 0x00000000 }, + { "gpioh_5", "gpioh_5", 0, 0x00000000 }, + { "gpioh_6", "gpioh_6", 0, 0x00000000 }, + { "gpioh_7", "gpioh_7", 0, 0x00000000 }, + { "gpioh_8", "gpioh_8", 0, 0x00000000 }, + { "gpioh_9", "gpioh_9", 0, 0x00000000 }, + + { "gpiox_4", "gpiox_4", 0, 0x00000000 }, + { "gpiox_5", "gpiox_5", 0, 0x00000000 }, + { "gpiox_6", "gpiox_6", 0, 0x00000000 }, + { "gpiox_7", "gpiox_7", 0, 0x00000000 }, + { "gpiox_16", "gpiox_16", 0, 0x00000000 }, + { "gpiox_17", "gpiox_17", 0, 0x00000000 }, + { "gpiox_18", "gpiox_18", 0, 0x00000000 }, + { "gpiox_19", "gpiox_19", 0, 0x00000000 }, + + { "gpioao_0", "gpioao_0", 0, 0x00000000 }, + { "gpioao_1", "gpioao_1", 0, 0x00000000 }, + { "gpioao_2", "gpioao_2", 0, 0x00000000 }, + { "gpioao_3", "gpioao_3", 0, 0x00000000 }, + { "gpioao_4", "gpioao_4", 0, 0x00000000 }, + { "gpioao_5", "gpioao_5", 0, 0x00000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_ethernet[] = { + { "ref_clk", "dif_3n", 24, 0x00000100 }, + { "tx_clk", "gpioh_9", 24, 0x00000800 }, + { "tx_en", "dif_3p", 24, 0x00000040 }, + { "tx_d0", "gpioh_6", 28, 0x00100000 }, + { "tx_d1", "gpioh_5", 28, 0x00200000 }, + { "tx_d2", "gpioh_8", 24, 0x00001000 }, + { "tx_d3", "gpioh_7", 24, 0x00002000 }, + { "rx_clk", "dif_1n", 24, 0x00000008 }, + { "rx_dv", "dif_1p", 24, 0x00000004 }, + { "rx_d0", "dif_0n", 24, 0x00000002 }, + { "rx_d1", "dif_0p", 24, 0x00000001 }, + { "rx_d2", "dif_2n", 28, 0x00800000 }, + { "rx_d3", "dif_2p", 28, 0x00400000 }, + { "mdc", "dif_4p", 24, 0x00000200 }, + { "mdio", "dif_4n", 24, 0x00000400 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_hdmi[] = { + { "cec", "gpioh_3", 4, 0x00800000 }, + { "hpd", "gpioh_0", 4, 0x04000000 }, + { "scl", "gpioh_2", 4, 0x01000000 }, + { "sda", "gpioh_1", 4, 0x02000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_i2c_a[] = { + { "scl", "gpiodv_25", 36, 0x40000000 }, + { "sda", "gpiodv_24", 36, 0x80000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_i2c_b[] = { + { "scl", "gpiodv_27", 36, 0x10000000 }, + { "sda", "gpiodv_26", 36, 0x20000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_sdio_b[] = { + { "clk", "card_2", 8, 0x00000800 }, + { "cmd", "card_3", 8, 0x00000400 }, + { "d0", "card_1", 8, 0x00008000 }, + { "d1", "card_0", 8, 0x00004000 }, + { "d2", "card_5", 8, 0x00002000 }, + { "d3", "card_4", 8, 0x00001000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_sdxc_b[] = { + { "clk", "card_2", 8, 0x00000020 }, + { "cmd", "card_3", 8, 0x00000010 }, + { "d0", "card_1", 8, 0x00000080 }, + { "d1", "card_0", 8, 0x00000040 }, + { "d2", "card_5", 8, 0x00000040 }, + { "d3", "card_4", 8, 0x00000040 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_sdio_c[] = { + { "clk", "boot_8", 24, 0x80000000 }, + { "cmd", "boot_10", 24, 0x40000000 }, + { "d0", "boot_0", 24, 0x20000000 }, + { "d1", "boot_1", 24, 0x10000000 }, + { "d2", "boot_2", 24, 0x08000000 }, + { "d3", "boot_3", 24, 0x04000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_sdxc_c[] = { + { "clk", "boot_8", 28, 0x00080000 }, + { "cmd", "boot_10", 28, 0x00040000 }, + { "d0", "boot_0", 16, 0x40000000 }, + { "d1", "boot_1", 16, 0x20000000 }, + { "d2", "boot_2", 16, 0x20000000 }, + { "d3", "boot_3", 16, 0x20000000 }, + { "d4", "boot_4", 16, 0x10000000 }, + { "d5", "boot_5", 16, 0x10000000 }, + { "d6", "boot_6", 16, 0x10000000 }, + { "d7", "boot_7", 16, 0x10000000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_uart_a[] = { + { "tx", "gpiox_4", 16, 0x00020000 }, + { "rx", "gpiox_5", 16, 0x00010000 }, + { "cts", "gpiox_6", 16, 0x00008000 }, + { "rts", "gpiox_7", 16, 0x00004000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_uart_b[] = { + { "tx", "gpiox_16", 16, 0x00000200 }, + { "rx", "gpiox_17", 16, 0x00000100 }, + { "cts", "gpiox_18", 16, 0x00000080 }, + { "rts", "gpiox_19", 16, 0x00000040 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_uart_c[] = { + { "tx", "gpiodv_24", 24, 0x00800000 }, + { "rx", "gpiodv_25", 24, 0x00400000 }, + { "cts", "gpiodv_26", 24, 0x00200000 }, + { "rts", "gpiodv_27", 24, 0x00100000 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_i2c_ao[] = { + { "scl", "gpioao_4", 0, 0x00000400 }, + { "sda", "gpioao_5", 0, 0x00000200 }, + { NULL } +}; + +static struct aml8726_pinctrl_pin aml8726_m8b_uart_ao[] = { + { "tx", "gpioao_0", 0, 0x00001000 }, + { "rx", "gpioao_1", 0, 0x00000800 }, + { "cts", "gpioao_2", 0, 0x00000400 }, + { "rts", "gpioao_3", 0, 0x00000200 }, + { NULL } +}; + +struct aml8726_pinctrl_function aml8726_m8b_pinctrl[] = { + { "gpio", aml8726_m8b_gpio }, + { "ethernet", aml8726_m8b_ethernet }, + { "hdmi", aml8726_m8b_hdmi }, + { "i2c-a", aml8726_m8b_i2c_a }, + { "i2c-b", aml8726_m8b_i2c_b }, + { "sdio-b", aml8726_m8b_sdio_b }, + { "sdxc-b", aml8726_m8b_sdxc_b }, + { "sdio-c", aml8726_m8b_sdio_c }, + { "sdxc-c", aml8726_m8b_sdxc_c }, + { "uart-a", aml8726_m8b_uart_a }, + { "uart-b", aml8726_m8b_uart_b }, + { "uart-c", aml8726_m8b_uart_c }, + { "i2c-ao", aml8726_m8b_i2c_ao }, + { "uart-ao", aml8726_m8b_uart_ao }, + { NULL } +}; + +#endif /* _ARM_AMLOGIC_AML8726_PINCTRL_H */ Property changes on: head/sys/arm/amlogic/aml8726/aml8726_pinctrl.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_rng.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_rng.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_rng.c (revision 280905) @@ -0,0 +1,155 @@ +/*- + * Copyright 2014 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 random number generator driver. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + + +struct aml8726_rng_softc { + device_t dev; + struct resource *res[1]; + struct callout co; + int ticks; +}; + +static struct resource_spec aml8726_rng_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define AML_RNG_0_REG 0 +#define AML_RNG_1_REG 4 + +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) + +static void +aml8726_rng_harvest(void *arg) +{ + struct aml8726_rng_softc *sc = arg; + uint32_t rn[2]; + + rn[0] = CSR_READ_4(sc, AML_RNG_0_REG); + rn[1] = CSR_READ_4(sc, AML_RNG_1_REG); + + random_harvest(rn, sizeof(rn), sizeof(rn) * NBBY / 2, + RANDOM_PURE_AML8726); + + callout_reset(&sc->co, sc->ticks, aml8726_rng_harvest, sc); +} + +static int +aml8726_rng_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-rng")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 RNG"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_rng_attach(device_t dev) +{ + struct aml8726_rng_softc *sc = device_get_softc(dev); + + sc->dev = dev; + + if (bus_alloc_resources(dev, aml8726_rng_spec, sc->res)) { + device_printf(dev, "can not allocate resources for device\n"); + return (ENXIO); + } + + /* Install a periodic collector for the RNG */ + if (hz > 100) + sc->ticks = hz / 100; + else + sc->ticks = 1; + + callout_init(&sc->co, CALLOUT_MPSAFE); + callout_reset(&sc->co, sc->ticks, aml8726_rng_harvest, sc); + + return (0); +} + +static int +aml8726_rng_detach(device_t dev) +{ + struct aml8726_rng_softc *sc = device_get_softc(dev); + + callout_drain(&sc->co); + + bus_release_resources(dev, aml8726_rng_spec, sc->res); + + return (0); +} + +static device_method_t aml8726_rng_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_rng_probe), + DEVMETHOD(device_attach, aml8726_rng_attach), + DEVMETHOD(device_detach, aml8726_rng_detach), + + DEVMETHOD_END +}; + +static driver_t aml8726_rng_driver = { + "rng", + aml8726_rng_methods, + sizeof(struct aml8726_rng_softc), +}; + +static devclass_t aml8726_rng_devclass; + +DRIVER_MODULE(aml8726_rng, simplebus, aml8726_rng_driver, + aml8726_rng_devclass, 0, 0); +MODULE_DEPEND(aml8726_rng, random, 1, 1, 1); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_rng.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_rtc.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_rtc.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_rtc.c (revision 280905) @@ -0,0 +1,505 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 RTC driver. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include "clock_if.h" + +/* + * The RTC initialization various slightly between the different chips. + * + * aml8726-m1 aml8726-m3 aml8726-m6 (and later) + * init-always true true false + * xo-init 0x0004 0x3c0a 0x180a + * gpo-init 0x100000 0x100000 0x500000 + */ + +struct aml8726_rtc_init { + boolean_t always; + uint16_t xo; + uint32_t gpo; +}; + +struct aml8726_rtc_softc { + device_t dev; + struct aml8726_rtc_init init; + struct resource * res[2]; + struct mtx mtx; +}; + +static struct resource_spec aml8726_rtc_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define AML_RTC_LOCK(sc) mtx_lock_spin(&(sc)->mtx) +#define AML_RTC_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx) +#define AML_RTC_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "rtc", MTX_SPIN) +#define AML_RTC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define AML_RTC_0_REG 0 +#define AML_RTC_SCLK (1 << 0) +#define AML_RTC_SDI (1 << 2) +#define AML_RTC_SEN (1 << 1) +#define AML_RTC_AS (1 << 17) +#define AML_RTC_ABSY (1 << 22) +#define AML_RTC_IRQ_DIS (1 << 12) +#define AML_RTC_1_REG 4 +#define AML_RTC_SDO (1 << 0) +#define AML_RTC_SRDY (1 << 1) +#define AML_RTC_2_REG 8 +#define AML_RTC_3_REG 12 +#define AML_RTC_MSR_BUSY (1 << 20) +#define AML_RTC_MSR_CA (1 << 17) +#define AML_RTC_MSR_DURATION_EN (1 << 16) +#define AML_RTC_MSR_DURATION_MASK 0xffff +#define AML_RTC_MSR_DURATION_SHIFT 0 +#define AML_RTC_4_REG 16 + +#define AML_RTC_TIME_SREG 0 +#define AML_RTC_GPO_SREG 1 +#define AML_RTC_GPO_LEVEL (1 << 24) +#define AML_RTC_GPO_BUSY (1 << 23) +#define AML_RTC_GPO_ACTIVE_HIGH (1 << 22) +#define AML_RTC_GPO_CMD_MASK (3 << 20) +#define AML_RTC_GPO_CMD_SHIFT 20 +#define AML_RTC_GPO_CMD_NOW (1 << 20) +#define AML_RTC_GPO_CMD_COUNT (2 << 20) +#define AML_RTC_GPO_CMD_PULSE (3 << 20) +#define AML_RTC_GPO_CNT_MASK 0xfffff +#define AML_RTC_GPO_CNT_SHIFT 0 + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) +#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ + (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +static int +aml8726_rtc_start_transfer(struct aml8726_rtc_softc *sc) +{ + unsigned i; + + /* idle the serial interface */ + CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) & + ~(AML_RTC_SCLK | AML_RTC_SEN | AML_RTC_SDI))); + + CSR_BARRIER(sc, AML_RTC_0_REG); + + /* see if it is ready for a new cycle */ + for (i = 40; i; i--) { + DELAY(5); + if ( (CSR_READ_4(sc, AML_RTC_1_REG) & AML_RTC_SRDY) ) + break; + } + + if (i == 0) + return (EIO); + + /* start the cycle */ + CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) | + AML_RTC_SEN)); + + return (0); +} + +static inline void +aml8726_rtc_sclk_pulse(struct aml8726_rtc_softc *sc) +{ + + DELAY(5); + + CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) | + AML_RTC_SCLK)); + + CSR_BARRIER(sc, AML_RTC_0_REG); + + DELAY(5); + + CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) & + ~AML_RTC_SCLK)); + + CSR_BARRIER(sc, AML_RTC_0_REG); +} + +static inline void +aml8726_rtc_send_bit(struct aml8726_rtc_softc *sc, unsigned bit) +{ + + if (bit) { + CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) | + AML_RTC_SDI)); + } else { + CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) & + ~AML_RTC_SDI)); + } + + aml8726_rtc_sclk_pulse(sc); +} + +static inline void +aml8726_rtc_send_addr(struct aml8726_rtc_softc *sc, u_char addr) +{ + unsigned mask; + + for (mask = 1 << 3; mask; mask >>= 1) { + if (mask == 1) { + /* final bit indicates read / write mode */ + CSR_WRITE_4(sc, AML_RTC_0_REG, + (CSR_READ_4(sc, AML_RTC_0_REG) & ~AML_RTC_SEN)); + } + aml8726_rtc_send_bit(sc, (addr & mask)); + } +} + +static inline void +aml8726_rtc_send_data(struct aml8726_rtc_softc *sc, uint32_t data) +{ + unsigned mask; + + for (mask = 1U << 31; mask; mask >>= 1) + aml8726_rtc_send_bit(sc, (data & mask)); +} + +static inline void +aml8726_rtc_recv_data(struct aml8726_rtc_softc *sc, uint32_t *dp) +{ + uint32_t data; + unsigned i; + + data = 0; + + for (i = 0; i < 32; i++) { + aml8726_rtc_sclk_pulse(sc); + data <<= 1; + data |= (CSR_READ_4(sc, AML_RTC_1_REG) & AML_RTC_SDO) ? 1 : 0; + } + + *dp = data; +} + +static int +aml8726_rtc_sreg_read(struct aml8726_rtc_softc *sc, u_char sreg, uint32_t *val) +{ + u_char addr; + int error; + + /* read is indicated by lsb = 0 */ + addr = (sreg << 1) | 0; + + error = aml8726_rtc_start_transfer(sc); + + if (error) + return (error); + + aml8726_rtc_send_addr(sc, addr); + aml8726_rtc_recv_data(sc, val); + + return (0); +} + +static int +aml8726_rtc_sreg_write(struct aml8726_rtc_softc *sc, u_char sreg, uint32_t val) +{ + u_char addr; + int error; + + /* write is indicated by lsb = 1 */ + addr = (sreg << 1) | 1; + + error = aml8726_rtc_start_transfer(sc); + + if (error) + return (error); + + aml8726_rtc_send_data(sc, val); + aml8726_rtc_send_addr(sc, addr); + + return (0); +} + +static int +aml8726_rtc_initialize(struct aml8726_rtc_softc *sc) +{ + int error; + unsigned i; + + /* idle the serial interface */ + CSR_WRITE_4(sc, AML_RTC_0_REG, (CSR_READ_4(sc, AML_RTC_0_REG) & + ~(AML_RTC_SCLK | AML_RTC_SEN | AML_RTC_SDI))); + + CSR_BARRIER(sc, AML_RTC_0_REG); + + /* see if it is ready for a new cycle */ + for (i = 40; i; i--) { + DELAY(5); + if ( (CSR_READ_4(sc, AML_RTC_1_REG) & AML_RTC_SRDY) ) + break; + } + + if (sc->init.always == TRUE || (CSR_READ_4(sc, AML_RTC_1_REG) & + AML_RTC_SRDY) == 0) { + + /* + * The RTC has a 16 bit initialization register. The upper + * bits can be written directly. The lower bits are written + * through a shift register. + */ + + CSR_WRITE_4(sc, AML_RTC_4_REG, ((sc->init.xo >> 8) & 0xff)); + + CSR_WRITE_4(sc, AML_RTC_0_REG, + ((CSR_READ_4(sc, AML_RTC_0_REG) & 0xffffff) | + ((uint32_t)(sc->init.xo & 0xff) << 24) | AML_RTC_AS | + AML_RTC_IRQ_DIS)); + + while ((CSR_READ_4(sc, AML_RTC_0_REG) & AML_RTC_ABSY) != 0) + cpu_spinwait(); + + DELAY(2); + + error = aml8726_rtc_sreg_write(sc, AML_RTC_GPO_SREG, + sc->init.gpo); + + if (error) + return (error); + } + + return (0); +} + +static int +aml8726_rtc_check_xo(struct aml8726_rtc_softc *sc) +{ + uint32_t now, previous; + int i; + + /* + * The RTC is driven by a 32.768khz clock meaning it's period + * is roughly 30.5 us. Check that it's working (implying the + * RTC could contain a valid value) by enabling count always + * and seeing if the value changes after 200 us (per RTC User + * Guide ... presumably the extra time is to cover XO startup). + */ + + CSR_WRITE_4(sc, AML_RTC_3_REG, (CSR_READ_4(sc, AML_RTC_3_REG) | + AML_RTC_MSR_CA)); + + previous = CSR_READ_4(sc, AML_RTC_2_REG); + + for (i = 0; i < 4; i++) { + DELAY(50); + now = CSR_READ_4(sc, AML_RTC_2_REG); + if (now != previous) + break; + } + + CSR_WRITE_4(sc, AML_RTC_3_REG, (CSR_READ_4(sc, AML_RTC_3_REG) & + ~AML_RTC_MSR_CA)); + + if (now == previous) + return (EINVAL); + + return (0); +} + +static int +aml8726_rtc_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-rtc")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 RTC"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_rtc_attach(device_t dev) +{ + struct aml8726_rtc_softc *sc = device_get_softc(dev); + boolean_t init_always_valid; + char *init_always; + pcell_t prop; + phandle_t node; + ssize_t len; + + sc->dev = dev; + + node = ofw_bus_get_node(dev); + + len = OF_getprop_alloc(node, "init-always", + sizeof(char), (void **)&init_always); + sc->init.always = FALSE; + init_always_valid = FALSE; + if (len > 0) { + if (strncmp(init_always, "true", len) == 0) { + sc->init.always = TRUE; + init_always_valid = TRUE; + } else if (strncmp(init_always, "false", len) == 0) + init_always_valid = TRUE; + free(init_always, M_OFWPROP); + } + if (init_always_valid == FALSE) { + device_printf(dev, "missing init-always attribute in FDT\n"); + return (ENXIO); + } + + if (OF_getencprop(node, "xo-init", &prop, sizeof(prop)) <= 0) { + device_printf(dev, "missing xo-init attribute in FDT\n"); + return (ENXIO); + } + sc->init.xo = prop; + + if (OF_getencprop(node, "gpo-init", &prop, sizeof(prop)) <= 0) { + device_printf(dev, "missing gpo-init attribute in FDT\n"); + return (ENXIO); + } + sc->init.gpo = prop; + + if (bus_alloc_resources(dev, aml8726_rtc_spec, sc->res)) { + device_printf(dev, "can not allocate resources for device\n"); + return (ENXIO); + } + + aml8726_rtc_initialize(sc); + + if (aml8726_rtc_check_xo(sc) != 0) { + device_printf(dev, "crystal oscillator check failed\n"); + + bus_release_resources(dev, aml8726_rtc_spec, sc->res); + + return (ENXIO); + } + + AML_RTC_LOCK_INIT(sc); + + clock_register(dev, 1000000); + + return (0); +} + +static int +aml8726_rtc_detach(device_t dev) +{ + + return (EBUSY); +} + +static int +aml8726_rtc_gettime(device_t dev, struct timespec *ts) +{ + struct aml8726_rtc_softc *sc = device_get_softc(dev); + uint32_t sec; + int error; + + AML_RTC_LOCK(sc); + + error = aml8726_rtc_sreg_read(sc, AML_RTC_TIME_SREG, &sec); + + AML_RTC_UNLOCK(sc); + + ts->tv_sec = sec; + ts->tv_nsec = 0; + + return (error); +} + +static int +aml8726_rtc_settime(device_t dev, struct timespec *ts) +{ + struct aml8726_rtc_softc *sc = device_get_softc(dev); + uint32_t sec; + int error; + + sec = ts->tv_sec; + + /* Accuracy is only one second. */ + if (ts->tv_nsec >= 500000000) + sec++; + + AML_RTC_LOCK(sc); + + error = aml8726_rtc_sreg_write(sc, AML_RTC_TIME_SREG, sec); + + AML_RTC_UNLOCK(sc); + + return (error); +} + +static device_method_t aml8726_rtc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_rtc_probe), + DEVMETHOD(device_attach, aml8726_rtc_attach), + DEVMETHOD(device_detach, aml8726_rtc_detach), + + /* Clock interface */ + DEVMETHOD(clock_gettime, aml8726_rtc_gettime), + DEVMETHOD(clock_settime, aml8726_rtc_settime), + + DEVMETHOD_END +}; + +static driver_t aml8726_rtc_driver = { + "rtc", + aml8726_rtc_methods, + sizeof(struct aml8726_rtc_softc), +}; + +static devclass_t aml8726_rtc_devclass; + +DRIVER_MODULE(rtc, simplebus, aml8726_rtc_driver, aml8726_rtc_devclass, 0, 0); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_rtc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c (revision 280905) @@ -0,0 +1,1380 @@ +/*- + * Copyright 2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726-m8 (and later) SDXC host controller driver. + */ + +#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" +#include "mmcbr_if.h" + +/* + * The table is sorted from highest to lowest and + * last entry in the table is mark by freq == 0. + */ +struct { + uint32_t voltage; + uint32_t freq; + uint32_t rx_phase; +} aml8726_sdxc_clk_phases[] = { + { + MMC_OCR_LOW_VOLTAGE | MMC_OCR_320_330 | MMC_OCR_330_340, + 100000000, + 1 + }, + { + MMC_OCR_320_330 | MMC_OCR_330_340, + 45000000, + 15 + }, + { + MMC_OCR_LOW_VOLTAGE, + 45000000, + 11 + }, + { + MMC_OCR_LOW_VOLTAGE | MMC_OCR_320_330 | MMC_OCR_330_340, + 24999999, + 15 + }, + { + MMC_OCR_LOW_VOLTAGE | MMC_OCR_320_330 | MMC_OCR_330_340, + 5000000, + 23 + }, + { + MMC_OCR_LOW_VOLTAGE | MMC_OCR_320_330 | MMC_OCR_330_340, + 1000000, + 55 + }, + { + MMC_OCR_LOW_VOLTAGE | MMC_OCR_320_330 | MMC_OCR_330_340, + 0, + 1061 + }, +}; + +struct aml8726_sdxc_gpio { + device_t dev; + uint32_t pin; + uint32_t pol; +}; + +struct aml8726_sdxc_softc { + device_t dev; + boolean_t auto_fill_flush; + struct resource *res[2]; + struct mtx mtx; + struct callout ch; + unsigned int ref_freq; + struct aml8726_sdxc_gpio pwr_en; + int voltages[2]; + struct aml8726_sdxc_gpio vselect; + struct aml8726_sdxc_gpio card_rst; + bus_dma_tag_t dmatag; + bus_dmamap_t dmamap; + void *ih_cookie; + struct mmc_host host; + int bus_busy; + struct { + uint32_t time; + uint32_t error; + } busy; + struct mmc_command *cmd; +}; + +static struct resource_spec aml8726_sdxc_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define AML_SDXC_LOCK(sc) mtx_lock(&(sc)->mtx) +#define AML_SDXC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define AML_SDXC_LOCK_ASSERT(sc) mtx_assert(&(sc)->mtx, MA_OWNED) +#define AML_SDXC_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "sdxc", MTX_DEF) +#define AML_SDXC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) +#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ + (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +#define PIN_ON_FLAG(pol) ((pol) == 0 ? \ + GPIO_PIN_LOW : GPIO_PIN_HIGH) +#define PIN_OFF_FLAG(pol) ((pol) == 0 ? \ + GPIO_PIN_HIGH : GPIO_PIN_LOW) + +#define msecs_to_ticks(ms) (((ms)*hz)/1000 + 1) + +static void aml8726_sdxc_timeout(void *arg); + +static void +aml8726_sdxc_mapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + bus_addr_t *busaddrp; + + /* + * There should only be one bus space address since + * bus_dma_tag_create was called with nsegments = 1. + */ + + busaddrp = (bus_addr_t *)arg; + *busaddrp = segs->ds_addr; +} + +static int +aml8726_sdxc_power_off(struct aml8726_sdxc_softc *sc) +{ + + if (sc->pwr_en.dev == NULL) + return (0); + + return (GPIO_PIN_SET(sc->pwr_en.dev, sc->pwr_en.pin, + PIN_OFF_FLAG(sc->pwr_en.pol))); +} + +static int +aml8726_sdxc_power_on(struct aml8726_sdxc_softc *sc) +{ + + if (sc->pwr_en.dev == NULL) + return (0); + + return (GPIO_PIN_SET(sc->pwr_en.dev, sc->pwr_en.pin, + PIN_ON_FLAG(sc->pwr_en.pol))); +} + +static void +aml8726_sdxc_soft_reset(struct aml8726_sdxc_softc *sc) +{ + + CSR_WRITE_4(sc, AML_SDXC_SOFT_RESET_REG, AML_SDXC_SOFT_RESET); + CSR_BARRIER(sc, AML_SDXC_SOFT_RESET_REG); + DELAY(5); +} + +static void +aml8726_sdxc_engage_dma(struct aml8726_sdxc_softc *sc) +{ + int i; + uint32_t pdmar; + uint32_t sr; + struct mmc_data *data; + + data = sc->cmd->data; + + if (data == NULL || data->len == 0) + return; + + /* + * Engaging the DMA hardware is recommended before writing + * to AML_SDXC_SEND_REG so that the FIFOs are ready to go. + * + * Presumably AML_SDXC_CNTRL_REG and AML_SDXC_DMA_ADDR_REG + * must be set up prior to this happening. + */ + + pdmar = CSR_READ_4(sc, AML_SDXC_PDMA_REG); + + pdmar &= ~AML_SDXC_PDMA_RX_FLUSH_MODE_SW; + pdmar |= AML_SDXC_PDMA_DMA_EN; + + if (sc->auto_fill_flush == true) { + CSR_WRITE_4(sc, AML_SDXC_PDMA_REG, pdmar); + CSR_BARRIER(sc, AML_SDXC_PDMA_REG); + return; + } + + if ((data->flags & MMC_DATA_READ) != 0) { + pdmar |= AML_SDXC_PDMA_RX_FLUSH_MODE_SW; + CSR_WRITE_4(sc, AML_SDXC_PDMA_REG, pdmar); + CSR_BARRIER(sc, AML_SDXC_PDMA_REG); + } else { + pdmar |= AML_SDXC_PDMA_TX_FILL; + CSR_WRITE_4(sc, AML_SDXC_PDMA_REG, pdmar); + CSR_BARRIER(sc, AML_SDXC_PDMA_REG); + + /* + * Wait up to 100us for data to show up. + */ + for (i = 0; i < 100; i++) { + sr = CSR_READ_4(sc, AML_SDXC_STATUS_REG); + if ((sr & AML_SDXC_STATUS_TX_CNT_MASK) != 0) + break; + DELAY(1); + } + if (i >= 100) + device_printf(sc->dev, "TX FIFO fill timeout\n"); + } +} + +static void +aml8726_sdxc_disengage_dma(struct aml8726_sdxc_softc *sc) +{ + int i; + uint32_t pdmar; + uint32_t sr; + struct mmc_data *data; + + data = sc->cmd->data; + + if (data == NULL || data->len == 0) + return; + + pdmar = CSR_READ_4(sc, AML_SDXC_PDMA_REG); + + if (sc->auto_fill_flush == true) { + pdmar &= ~AML_SDXC_PDMA_DMA_EN; + CSR_WRITE_4(sc, AML_SDXC_PDMA_REG, pdmar); + CSR_BARRIER(sc, AML_SDXC_PDMA_REG); + return; + } + + if ((data->flags & MMC_DATA_READ) != 0) { + pdmar |= AML_SDXC_PDMA_RX_FLUSH_NOW; + CSR_WRITE_4(sc, AML_SDXC_PDMA_REG, pdmar); + CSR_BARRIER(sc, AML_SDXC_PDMA_REG); + + /* + * Wait up to 100us for data to drain. + */ + for (i = 0; i < 100; i++) { + sr = CSR_READ_4(sc, AML_SDXC_STATUS_REG); + if ((sr & AML_SDXC_STATUS_RX_CNT_MASK) == 0) + break; + DELAY(1); + } + if (i >= 100) + device_printf(sc->dev, "RX FIFO drain timeout\n"); + } + + pdmar &= ~(AML_SDXC_PDMA_DMA_EN | AML_SDXC_PDMA_RX_FLUSH_MODE_SW); + + CSR_WRITE_4(sc, AML_SDXC_PDMA_REG, pdmar); + CSR_BARRIER(sc, AML_SDXC_PDMA_REG); +} + +static int +aml8726_sdxc_start_command(struct aml8726_sdxc_softc *sc, + struct mmc_command *cmd) +{ + bus_addr_t baddr; + uint32_t block_size; + uint32_t ctlr; + uint32_t ier; + uint32_t sndr; + uint32_t timeout; + int error; + struct mmc_data *data; + + AML_SDXC_LOCK_ASSERT(sc); + + if (cmd->opcode > 0x3f) + return (MMC_ERR_INVALID); + + /* + * Ensure the hardware state machine is in a known state. + */ + aml8726_sdxc_soft_reset(sc); + + sndr = cmd->opcode; + + if ((cmd->flags & MMC_RSP_136) != 0) { + sndr |= AML_SDXC_SEND_CMD_HAS_RESP; + sndr |= AML_SDXC_SEND_RESP_136; + /* + * According to the SD spec the 136 bit response is + * used for getting the CID or CSD in which case the + * CRC7 is embedded in the contents rather than being + * calculated over the entire response (the controller + * always checks the CRC7 over the entire response). + */ + sndr |= AML_SDXC_SEND_RESP_NO_CRC7_CHECK; + } else if ((cmd->flags & MMC_RSP_PRESENT) != 0) + sndr |= AML_SDXC_SEND_CMD_HAS_RESP; + + if ((cmd->flags & MMC_RSP_CRC) == 0) + sndr |= AML_SDXC_SEND_RESP_NO_CRC7_CHECK; + + if (cmd->opcode == MMC_STOP_TRANSMISSION) + sndr |= AML_SDXC_SEND_DATA_STOP; + + data = cmd->data; + + baddr = 0; + ctlr = CSR_READ_4(sc, AML_SDXC_CNTRL_REG); + ier = AML_SDXC_IRQ_ENABLE_STANDARD; + timeout = AML_SDXC_CMD_TIMEOUT; + + ctlr &= ~AML_SDXC_CNTRL_PKG_LEN_MASK; + + if (data && data->len && + (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)) != 0) { + block_size = data->len; + + if ((data->flags & MMC_DATA_MULTI) != 0) { + block_size = MMC_SECTOR_SIZE; + if ((data->len % block_size) != 0) + return (MMC_ERR_INVALID); + } + + if (block_size > 512) + return (MMC_ERR_INVALID); + + sndr |= AML_SDXC_SEND_CMD_HAS_DATA; + sndr |= ((data->flags & MMC_DATA_WRITE) != 0) ? + AML_SDXC_SEND_DATA_WRITE : 0; + sndr |= (((data->len / block_size) - 1) << + AML_SDXC_SEND_REP_PKG_CNT_SHIFT); + + ctlr |= ((block_size < 512) ? block_size : 0) << + AML_SDXC_CNTRL_PKG_LEN_SHIFT; + + ier &= ~AML_SDXC_IRQ_ENABLE_RESP_OK; + ier |= (sc->auto_fill_flush == true || + (data->flags & MMC_DATA_WRITE) != 0) ? + AML_SDXC_IRQ_ENABLE_DMA_DONE : + AML_SDXC_IRQ_ENABLE_TRANSFER_DONE_OK; + + error = bus_dmamap_load(sc->dmatag, sc->dmamap, + data->data, data->len, aml8726_sdxc_mapmem, &baddr, + BUS_DMA_NOWAIT); + if (error) + return (MMC_ERR_NO_MEMORY); + + if ((data->flags & MMC_DATA_READ) != 0) { + bus_dmamap_sync(sc->dmatag, sc->dmamap, + BUS_DMASYNC_PREREAD); + timeout = AML_SDXC_READ_TIMEOUT * + (data->len / block_size); + } else { + bus_dmamap_sync(sc->dmatag, sc->dmamap, + BUS_DMASYNC_PREWRITE); + timeout = AML_SDXC_WRITE_TIMEOUT * + (data->len / block_size); + } + } + + sc->cmd = cmd; + + cmd->error = MMC_ERR_NONE; + + sc->busy.time = 0; + sc->busy.error = MMC_ERR_NONE; + + if (timeout > AML_SDXC_MAX_TIMEOUT) + timeout = AML_SDXC_MAX_TIMEOUT; + + callout_reset(&sc->ch, msecs_to_ticks(timeout), + aml8726_sdxc_timeout, sc); + + CSR_WRITE_4(sc, AML_SDXC_IRQ_ENABLE_REG, ier); + + CSR_WRITE_4(sc, AML_SDXC_CNTRL_REG, ctlr); + CSR_WRITE_4(sc, AML_SDXC_DMA_ADDR_REG, (uint32_t)baddr); + CSR_WRITE_4(sc, AML_SDXC_CMD_ARGUMENT_REG, cmd->arg); + + aml8726_sdxc_engage_dma(sc); + + CSR_WRITE_4(sc, AML_SDXC_SEND_REG, sndr); + CSR_BARRIER(sc, AML_SDXC_SEND_REG); + + return (MMC_ERR_NONE); +} + +static void +aml8726_sdxc_finish_command(struct aml8726_sdxc_softc *sc, int mmc_error) +{ + int mmc_stop_error; + struct mmc_command *cmd; + struct mmc_command *stop_cmd; + struct mmc_data *data; + + AML_SDXC_LOCK_ASSERT(sc); + + /* Clear all interrupts since the request is no longer in flight. */ + CSR_WRITE_4(sc, AML_SDXC_IRQ_STATUS_REG, AML_SDXC_IRQ_STATUS_CLEAR); + CSR_BARRIER(sc, AML_SDXC_IRQ_STATUS_REG); + + /* In some cases (e.g. finish called via timeout) this is a NOP. */ + callout_stop(&sc->ch); + + cmd = sc->cmd; + sc->cmd = NULL; + + cmd->error = mmc_error; + + data = cmd->data; + + if (data && data->len + && (data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)) != 0) { + if ((data->flags & MMC_DATA_READ) != 0) + bus_dmamap_sync(sc->dmatag, sc->dmamap, + BUS_DMASYNC_POSTREAD); + else + bus_dmamap_sync(sc->dmatag, sc->dmamap, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->dmatag, sc->dmamap); + } + + /* + * If there's a linked stop command, then start the stop command. + * In order to establish a known state attempt the stop command + * even if the original request encountered an error. + */ + + stop_cmd = (cmd->mrq->stop != cmd) ? cmd->mrq->stop : NULL; + + if (stop_cmd != NULL) { + + /* + * If the original command executed successfuly, then + * the hardware will also have automatically executed + * a stop command so don't bother with the one supplied + * with the original request. + */ + + if (mmc_error == MMC_ERR_NONE) { + stop_cmd->error = MMC_ERR_NONE; + stop_cmd->resp[0] = cmd->resp[0]; + stop_cmd->resp[1] = cmd->resp[1]; + stop_cmd->resp[2] = cmd->resp[2]; + stop_cmd->resp[3] = cmd->resp[3]; + } else { + mmc_stop_error = aml8726_sdxc_start_command(sc, + stop_cmd); + if (mmc_stop_error == MMC_ERR_NONE) { + AML_SDXC_UNLOCK(sc); + return; + } + stop_cmd->error = mmc_stop_error; + } + } + + AML_SDXC_UNLOCK(sc); + + /* Execute the callback after dropping the lock. */ + if (cmd->mrq != NULL) + cmd->mrq->done(cmd->mrq); +} + +static void +aml8726_sdxc_timeout(void *arg) +{ + struct aml8726_sdxc_softc *sc = (struct aml8726_sdxc_softc *)arg; + + /* + * The command failed to complete in time so forcefully + * terminate it. + */ + aml8726_sdxc_soft_reset(sc); + + /* + * Ensure the command has terminated before continuing on + * to things such as bus_dmamap_sync / bus_dmamap_unload. + */ + while ((CSR_READ_4(sc, AML_SDXC_STATUS_REG) & + AML_SDXC_STATUS_BUSY) != 0) + cpu_spinwait(); + + aml8726_sdxc_finish_command(sc, MMC_ERR_TIMEOUT); +} + +static void +aml8726_sdxc_busy_check(void *arg) +{ + struct aml8726_sdxc_softc *sc = (struct aml8726_sdxc_softc *)arg; + uint32_t sr; + + sc->busy.time += AML_SDXC_BUSY_POLL_INTVL; + + sr = CSR_READ_4(sc, AML_SDXC_STATUS_REG); + + if ((sr & AML_SDXC_STATUS_DAT0) == 0) { + if (sc->busy.time < AML_SDXC_BUSY_TIMEOUT) { + callout_reset(&sc->ch, + msecs_to_ticks(AML_SDXC_BUSY_POLL_INTVL), + aml8726_sdxc_busy_check, sc); + AML_SDXC_UNLOCK(sc); + return; + } + if (sc->busy.error == MMC_ERR_NONE) + sc->busy.error = MMC_ERR_TIMEOUT; + } + + aml8726_sdxc_finish_command(sc, sc->busy.error); +} + +static void +aml8726_sdxc_intr(void *arg) +{ + struct aml8726_sdxc_softc *sc = (struct aml8726_sdxc_softc *)arg; + uint32_t isr; + uint32_t pdmar; + uint32_t sndr; + uint32_t sr; + int i; + int mmc_error; + int start; + int stop; + + AML_SDXC_LOCK(sc); + + isr = CSR_READ_4(sc, AML_SDXC_IRQ_STATUS_REG); + sndr = CSR_READ_4(sc, AML_SDXC_SEND_REG); + sr = CSR_READ_4(sc, AML_SDXC_STATUS_REG); + + if (sc->cmd == NULL) + goto spurious; + + mmc_error = MMC_ERR_NONE; + + if ((isr & (AML_SDXC_IRQ_STATUS_TX_FIFO_EMPTY | + AML_SDXC_IRQ_STATUS_RX_FIFO_FULL)) != 0) + mmc_error = MMC_ERR_FIFO; + else if ((isr & (AML_SDXC_IRQ_ENABLE_A_PKG_CRC_ERR | + AML_SDXC_IRQ_ENABLE_RESP_CRC_ERR)) != 0) + mmc_error = MMC_ERR_BADCRC; + else if ((isr & (AML_SDXC_IRQ_ENABLE_A_PKG_TIMEOUT_ERR | + AML_SDXC_IRQ_ENABLE_RESP_TIMEOUT_ERR)) != 0) + mmc_error = MMC_ERR_TIMEOUT; + else if ((isr & (AML_SDXC_IRQ_STATUS_RESP_OK | + AML_SDXC_IRQ_STATUS_DMA_DONE | + AML_SDXC_IRQ_STATUS_TRANSFER_DONE_OK)) != 0) { + ; + } + else { +spurious: + /* + * Clear spurious interrupts while leaving intacted any + * interrupts that may have occurred after we read the + * interrupt status register. + */ + + CSR_WRITE_4(sc, AML_SDXC_IRQ_STATUS_REG, + (AML_SDXC_IRQ_STATUS_CLEAR & isr)); + CSR_BARRIER(sc, AML_SDXC_IRQ_STATUS_REG); + AML_SDXC_UNLOCK(sc); + return; + } + + aml8726_sdxc_disengage_dma(sc); + + if ((sndr & AML_SDXC_SEND_CMD_HAS_RESP) != 0) { + start = 0; + stop = 1; + if ((sndr & AML_SDXC_SEND_RESP_136) != 0) { + start = 1; + stop = start + 4;; + } + for (i = start; i < stop; i++) { + pdmar = CSR_READ_4(sc, AML_SDXC_PDMA_REG); + pdmar &= ~(AML_SDXC_PDMA_DMA_EN | + AML_SDXC_PDMA_RESP_INDEX_MASK); + pdmar |= i << AML_SDXC_PDMA_RESP_INDEX_SHIFT; + CSR_WRITE_4(sc, AML_SDXC_PDMA_REG, pdmar); + sc->cmd->resp[(stop - 1) - i] = CSR_READ_4(sc, + AML_SDXC_CMD_ARGUMENT_REG); + } + } + + if ((sr & AML_SDXC_STATUS_BUSY) != 0 && + /* + * A multiblock operation may keep the hardware + * busy until stop transmission is executed. + */ + (isr & (AML_SDXC_IRQ_STATUS_DMA_DONE | + AML_SDXC_IRQ_STATUS_TRANSFER_DONE_OK)) == 0) { + if (mmc_error == MMC_ERR_NONE) + mmc_error = MMC_ERR_FAILED; + + /* + * Issue a soft reset to terminate the command. + * + * Ensure the command has terminated before continuing on + * to things such as bus_dmamap_sync / bus_dmamap_unload. + */ + + aml8726_sdxc_soft_reset(sc); + + while ((CSR_READ_4(sc, AML_SDXC_STATUS_REG) & + AML_SDXC_STATUS_BUSY) != 0) + cpu_spinwait(); + } + + /* + * The stop command can be generated either manually or + * automatically by the hardware if MISC_MANUAL_STOP_MODE + * has not been set. In either case check for busy. + */ + + if (((sc->cmd->flags & MMC_RSP_BUSY) != 0 || + (sndr & AML_SDXC_SEND_INDEX_MASK) == MMC_STOP_TRANSMISSION) && + (sr & AML_SDXC_STATUS_DAT0) == 0) { + sc->busy.error = mmc_error; + callout_reset(&sc->ch, + msecs_to_ticks(AML_SDXC_BUSY_POLL_INTVL), + aml8726_sdxc_busy_check, sc); + CSR_WRITE_4(sc, AML_SDXC_IRQ_STATUS_REG, + (AML_SDXC_IRQ_STATUS_CLEAR & isr)); + CSR_BARRIER(sc, AML_SDXC_IRQ_STATUS_REG); + AML_SDXC_UNLOCK(sc); + return; + } + + aml8726_sdxc_finish_command(sc, mmc_error); +} + +static int +aml8726_sdxc_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-sdxc-m8")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726-m8 SDXC"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_sdxc_attach(device_t dev) +{ + struct aml8726_sdxc_softc *sc = device_get_softc(dev); + char *voltages; + char *voltage; + int error; + int nvoltages; + pcell_t prop[3]; + phandle_t node; + ssize_t len; + device_t child; + uint32_t ectlr; + uint32_t miscr; + uint32_t pdmar; + + sc->dev = dev; + + sc->auto_fill_flush = false; + + pdmar = AML_SDXC_PDMA_DMA_URGENT | + (49 << AML_SDXC_PDMA_TX_THOLD_SHIFT) | + (7 << AML_SDXC_PDMA_RX_THOLD_SHIFT) | + (15 << AML_SDXC_PDMA_RD_BURST_SHIFT) | + (7 << AML_SDXC_PDMA_WR_BURST_SHIFT); + + miscr = (2 << AML_SDXC_MISC_WCRC_OK_PAT_SHIFT) | + (5 << AML_SDXC_MISC_WCRC_ERR_PAT_SHIFT); + + ectlr = (12 << AML_SDXC_ENH_CNTRL_SDIO_IRQ_PERIOD_SHIFT); + + /* + * Certain bitfields are dependent on the hardware revision. + */ + switch (aml8726_soc_hw_rev) { + case AML_SOC_HW_REV_M8: + switch (aml8726_soc_metal_rev) { + case AML_SOC_M8_METAL_REV_M2_A: + sc->auto_fill_flush = true; + miscr |= (6 << AML_SDXC_MISC_TXSTART_THOLD_SHIFT); + ectlr |= (64 << AML_SDXC_ENH_CNTRL_RX_FULL_THOLD_SHIFT) | + AML_SDXC_ENH_CNTRL_WR_RESP_MODE_SKIP_M8M2; + break; + default: + miscr |= (7 << AML_SDXC_MISC_TXSTART_THOLD_SHIFT); + ectlr |= (63 << AML_SDXC_ENH_CNTRL_RX_FULL_THOLD_SHIFT) | + AML_SDXC_ENH_CNTRL_DMA_NO_WR_RESP_CHECK_M8 | + (255 << AML_SDXC_ENH_CNTRL_RX_TIMEOUT_SHIFT_M8); + + break; + } + break; + case AML_SOC_HW_REV_M8B: + miscr |= (7 << AML_SDXC_MISC_TXSTART_THOLD_SHIFT); + ectlr |= (63 << AML_SDXC_ENH_CNTRL_RX_FULL_THOLD_SHIFT) | + AML_SDXC_ENH_CNTRL_DMA_NO_WR_RESP_CHECK_M8 | + (255 << AML_SDXC_ENH_CNTRL_RX_TIMEOUT_SHIFT_M8); + break; + default: + device_printf(dev, "unsupported SoC\n"); + return (ENXIO); + /* NOTREACHED */ + break; + } + + node = ofw_bus_get_node(dev); + + len = OF_getencprop(node, "clock-frequency", prop, sizeof(prop)); + if ((len / sizeof(prop[0])) != 1 || prop[0] == 0) { + device_printf(dev, + "missing clock-frequency attribute in FDT\n"); + return (ENXIO); + } + + sc->ref_freq = prop[0]; + + sc->pwr_en.dev = NULL; + + len = OF_getencprop(node, "mmc-pwr-en", prop, sizeof(prop)); + if (len > 0) { + if ((len / sizeof(prop[0])) == 3) { + sc->pwr_en.dev = OF_device_from_xref(prop[0]); + sc->pwr_en.pin = prop[1]; + sc->pwr_en.pol = prop[2]; + } + + if (sc->pwr_en.dev == NULL) { + device_printf(dev, + "unable to process mmc-pwr-en attribute in FDT\n"); + return (ENXIO); + } + + /* Turn off power and then configure the output driver. */ + if (aml8726_sdxc_power_off(sc) != 0 || + GPIO_PIN_SETFLAGS(sc->pwr_en.dev, sc->pwr_en.pin, + GPIO_PIN_OUTPUT) != 0) { + device_printf(dev, + "could not use gpio to control power\n"); + return (ENXIO); + } + } + + len = OF_getprop_alloc(node, "mmc-voltages", + sizeof(char), (void **)&voltages); + + if (len < 0) { + device_printf(dev, "missing mmc-voltages attribute in FDT\n"); + return (ENXIO); + } + + sc->voltages[0] = 0; + sc->voltages[1] = 0; + + voltage = voltages; + nvoltages = 0; + + while (len && nvoltages < 2) { + if (strncmp("1.8", voltage, len) == 0) + sc->voltages[nvoltages] = MMC_OCR_LOW_VOLTAGE; + else if (strncmp("3.3", voltage, len) == 0) + sc->voltages[nvoltages] = MMC_OCR_320_330 | + MMC_OCR_330_340; + else { + device_printf(dev, + "unknown voltage attribute %.*s in FDT\n", + len, voltage); + free(voltages, M_OFWPROP); + return (ENXIO); + } + + nvoltages++; + + /* queue up next string */ + while (*voltage && len) { + voltage++; + len--; + } + if (len) { + voltage++; + len--; + } + } + + free(voltages, M_OFWPROP); + + sc->vselect.dev = NULL; + + len = OF_getencprop(node, "mmc-vselect", prop, sizeof(prop)); + if (len > 0) { + if ((len / sizeof(prop[0])) == 2) { + sc->vselect.dev = OF_device_from_xref(prop[0]); + sc->vselect.pin = prop[1]; + sc->vselect.pol = 1; + } + + if (sc->vselect.dev == NULL) { + device_printf(dev, + "unable to process mmc-vselect attribute in FDT\n"); + return (ENXIO); + } + + /* + * With the power off select voltage 0 and then + * configure the output driver. + */ + if (GPIO_PIN_SET(sc->vselect.dev, sc->vselect.pin, 0) != 0 || + GPIO_PIN_SETFLAGS(sc->vselect.dev, sc->vselect.pin, + GPIO_PIN_OUTPUT) != 0) { + device_printf(dev, + "could not use gpio to set voltage\n"); + return (ENXIO); + } + } + + if (nvoltages == 0) { + device_printf(dev, "no voltages in FDT\n"); + return (ENXIO); + } else if (nvoltages == 1 && sc->vselect.dev != NULL) { + device_printf(dev, "only one voltage in FDT\n"); + return (ENXIO); + } else if (nvoltages == 2 && sc->vselect.dev == NULL) { + device_printf(dev, "too many voltages in FDT\n"); + return (ENXIO); + } + + sc->card_rst.dev = NULL; + + len = OF_getencprop(node, "mmc-rst", prop, sizeof(prop)); + if (len > 0) { + if ((len / sizeof(prop[0])) == 3) { + sc->card_rst.dev = OF_device_from_xref(prop[0]); + sc->card_rst.pin = prop[1]; + sc->card_rst.pol = prop[2]; + } + + if (sc->card_rst.dev == NULL) { + device_printf(dev, + "unable to process mmc-rst attribute in FDT\n"); + return (ENXIO); + } + } + + if (bus_alloc_resources(dev, aml8726_sdxc_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + return (ENXIO); + } + + AML_SDXC_LOCK_INIT(sc); + + error = bus_dma_tag_create(bus_get_dma_tag(dev), AML_SDXC_ALIGN_DMA, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + AML_SDXC_MAX_DMA, 1, AML_SDXC_MAX_DMA, 0, NULL, NULL, &sc->dmatag); + if (error) + goto fail; + + error = bus_dmamap_create(sc->dmatag, 0, &sc->dmamap); + + if (error) + goto fail; + + error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, + NULL, aml8726_sdxc_intr, sc, &sc->ih_cookie); + if (error) { + device_printf(dev, "could not setup interrupt handler\n"); + goto fail; + } + + callout_init_mtx(&sc->ch, &sc->mtx, CALLOUT_RETURNUNLOCKED); + + sc->host.f_min = 200000; + sc->host.f_max = 100000000; + sc->host.host_ocr = sc->voltages[0] | sc->voltages[1]; + sc->host.caps = MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA | + MMC_CAP_HSPEED; + + aml8726_sdxc_soft_reset(sc); + + CSR_WRITE_4(sc, AML_SDXC_PDMA_REG, pdmar); + + CSR_WRITE_4(sc, AML_SDXC_MISC_REG, miscr); + + CSR_WRITE_4(sc, AML_SDXC_ENH_CNTRL_REG, ectlr); + + child = device_add_child(dev, "mmc", -1); + + if (!child) { + device_printf(dev, "could not add mmc\n"); + error = ENXIO; + goto fail; + } + + error = device_probe_and_attach(child); + + if (error) { + device_printf(dev, "could not attach mmc\n"); + goto fail; + } + + return (0); + +fail: + if (sc->ih_cookie) + bus_teardown_intr(dev, sc->res[1], sc->ih_cookie); + + if (sc->dmamap) + bus_dmamap_destroy(sc->dmatag, sc->dmamap); + + if (sc->dmatag) + bus_dma_tag_destroy(sc->dmatag); + + AML_SDXC_LOCK_DESTROY(sc); + + (void)aml8726_sdxc_power_off(sc); + + bus_release_resources(dev, aml8726_sdxc_spec, sc->res); + + return (error); +} + +static int +aml8726_sdxc_detach(device_t dev) +{ + struct aml8726_sdxc_softc *sc = device_get_softc(dev); + + AML_SDXC_LOCK(sc); + + if (sc->cmd != NULL) { + AML_SDXC_UNLOCK(sc); + return (EBUSY); + } + + /* + * Turn off the power, reset the hardware state machine, + * and disable the interrupts. + */ + aml8726_sdxc_power_off(sc); + aml8726_sdxc_soft_reset(sc); + CSR_WRITE_4(sc, AML_SDXC_IRQ_ENABLE_REG, 0); + + AML_SDXC_UNLOCK(sc); + + bus_generic_detach(dev); + + bus_teardown_intr(dev, sc->res[1], sc->ih_cookie); + + bus_dmamap_destroy(sc->dmatag, sc->dmamap); + + bus_dma_tag_destroy(sc->dmatag); + + AML_SDXC_LOCK_DESTROY(sc); + + bus_release_resources(dev, aml8726_sdxc_spec, sc->res); + + return (0); +} + +static int +aml8726_sdxc_shutdown(device_t dev) +{ + struct aml8726_sdxc_softc *sc = device_get_softc(dev); + + /* + * Turn off the power, reset the hardware state machine, + * and disable the interrupts. + */ + aml8726_sdxc_power_off(sc); + aml8726_sdxc_soft_reset(sc); + CSR_WRITE_4(sc, AML_SDXC_IRQ_ENABLE_REG, 0); + + return (0); +} + +static int +aml8726_sdxc_update_ios(device_t bus, device_t child) +{ + struct aml8726_sdxc_softc *sc = device_get_softc(bus); + struct mmc_ios *ios = &sc->host.ios; + unsigned int divisor; + int error; + int i; + uint32_t cctlr; + uint32_t clk2r; + uint32_t ctlr; + uint32_t freq; + + ctlr = (7 << AML_SDXC_CNTRL_TX_ENDIAN_SHIFT) | + (7 << AML_SDXC_CNTRL_RX_ENDIAN_SHIFT) | + (0xf << AML_SDXC_CNTRL_RX_PERIOD_SHIFT) | + (0x7f << AML_SDXC_CNTRL_RX_TIMEOUT_SHIFT); + + switch (ios->bus_width) { + case bus_width_8: + ctlr |= AML_SDXC_CNTRL_BUS_WIDTH_8; + break; + case bus_width_4: + ctlr |= AML_SDXC_CNTRL_BUS_WIDTH_4; + break; + case bus_width_1: + ctlr |= AML_SDXC_CNTRL_BUS_WIDTH_1; + break; + default: + return (EINVAL); + } + + CSR_WRITE_4(sc, AML_SDXC_CNTRL_REG, ctlr); + + /* + * Disable clocks and then clock module prior to setting desired values. + */ + cctlr = CSR_READ_4(sc, AML_SDXC_CLK_CNTRL_REG); + cctlr &= ~(AML_SDXC_CLK_CNTRL_TX_CLK_EN | AML_SDXC_CLK_CNTRL_RX_CLK_EN | + AML_SDXC_CLK_CNTRL_SD_CLK_EN); + CSR_WRITE_4(sc, AML_SDXC_CLK_CNTRL_REG, cctlr); + CSR_BARRIER(sc, AML_SDXC_CLK_CNTRL_REG); + cctlr &= ~AML_SDXC_CLK_CNTRL_CLK_MODULE_EN; + CSR_WRITE_4(sc, AML_SDXC_CLK_CNTRL_REG, cctlr); + CSR_BARRIER(sc, AML_SDXC_CLK_CNTRL_REG); + + /* + * aml8726-m8 + * + * Clock select 1 fclk_div2 (1.275 GHz) + */ + cctlr &= ~AML_SDXC_CLK_CNTRL_CLK_SEL_MASK; + cctlr |= (1 << AML_SDXC_CLK_CNTRL_CLK_SEL_SHIFT); + + divisor = sc->ref_freq / ios->clock - 1; + if (divisor == 0 || divisor == -1) + divisor = 1; + if ((sc->ref_freq / (divisor + 1)) > ios->clock) + divisor += 1; + if (divisor > (AML_SDXC_CLK_CNTRL_CLK_DIV_MASK >> + AML_SDXC_CLK_CNTRL_CLK_DIV_SHIFT)) + divisor = AML_SDXC_CLK_CNTRL_CLK_DIV_MASK >> + AML_SDXC_CLK_CNTRL_CLK_DIV_SHIFT; + + cctlr &= ~AML_SDXC_CLK_CNTRL_CLK_DIV_MASK; + cctlr |= divisor << AML_SDXC_CLK_CNTRL_CLK_DIV_SHIFT; + + cctlr &= ~AML_SDXC_CLK_CNTRL_MEM_PWR_MASK; + cctlr |= AML_SDXC_CLK_CNTRL_MEM_PWR_ON; + + CSR_WRITE_4(sc, AML_SDXC_CLK_CNTRL_REG, cctlr); + CSR_BARRIER(sc, AML_SDXC_CLK_CNTRL_REG); + + /* + * Enable clock module and then clocks after setting desired values. + */ + cctlr |= AML_SDXC_CLK_CNTRL_CLK_MODULE_EN; + CSR_WRITE_4(sc, AML_SDXC_CLK_CNTRL_REG, cctlr); + CSR_BARRIER(sc, AML_SDXC_CLK_CNTRL_REG); + cctlr |= AML_SDXC_CLK_CNTRL_TX_CLK_EN | AML_SDXC_CLK_CNTRL_RX_CLK_EN | + AML_SDXC_CLK_CNTRL_SD_CLK_EN; + CSR_WRITE_4(sc, AML_SDXC_CLK_CNTRL_REG, cctlr); + CSR_BARRIER(sc, AML_SDXC_CLK_CNTRL_REG); + + freq = sc->ref_freq / divisor; + + for (i = 0; aml8726_sdxc_clk_phases[i].voltage; i++) { + if ((aml8726_sdxc_clk_phases[i].voltage & + (1 << ios->vdd)) != 0 && + freq > aml8726_sdxc_clk_phases[i].freq) + break; + if (aml8726_sdxc_clk_phases[i].freq == 0) + break; + } + + clk2r = (1 << AML_SDXC_CLK2_SD_PHASE_SHIFT) | + (aml8726_sdxc_clk_phases[i].rx_phase << + AML_SDXC_CLK2_RX_PHASE_SHIFT); + CSR_WRITE_4(sc, AML_SDXC_CLK2_REG, clk2r); + CSR_BARRIER(sc, AML_SDXC_CLK2_REG); + + error = 0; + + switch (ios->power_mode) { + case power_up: + /* + * Configure and power on the regulator so that the + * voltage stabilizes prior to powering on the card. + */ + if (sc->vselect.dev != NULL) { + for (i = 0; i < 2; i++) + if ((sc->voltages[i] & (1 << ios->vdd)) != 0) + break; + if (i >= 2) + return (EINVAL); + error = GPIO_PIN_SET(sc->vselect.dev, + sc->vselect.pin, i); + } + break; + case power_on: + error = aml8726_sdxc_power_on(sc); + if (error) + break; + + if (sc->card_rst.dev != NULL) { + if (GPIO_PIN_SET(sc->card_rst.dev, sc->card_rst.pin, + PIN_ON_FLAG(sc->card_rst.pol)) != 0 || + GPIO_PIN_SETFLAGS(sc->card_rst.dev, + sc->card_rst.pin, + GPIO_PIN_OUTPUT) != 0) + error = ENXIO; + + DELAY(5); + + if (GPIO_PIN_SET(sc->card_rst.dev, sc->card_rst.pin, + PIN_OFF_FLAG(sc->card_rst.pol)) != 0) + error = ENXIO; + + DELAY(5); + + if (error) { + device_printf(sc->dev, + "could not use gpio to reset card\n"); + break; + } + } + break; + case power_off: + error = aml8726_sdxc_power_off(sc); + break; + default: + return (EINVAL); + } + + return (error); +} + +static int +aml8726_sdxc_request(device_t bus, device_t child, struct mmc_request *req) +{ + struct aml8726_sdxc_softc *sc = device_get_softc(bus); + int mmc_error; + + AML_SDXC_LOCK(sc); + + if (sc->cmd != NULL) { + AML_SDXC_UNLOCK(sc); + return (EBUSY); + } + + mmc_error = aml8726_sdxc_start_command(sc, req->cmd); + + AML_SDXC_UNLOCK(sc); + + /* Execute the callback after dropping the lock. */ + if (mmc_error != MMC_ERR_NONE) { + req->cmd->error = mmc_error; + req->done(req); + } + + return (0); +} + +static int +aml8726_sdxc_read_ivar(device_t bus, device_t child, + int which, uintptr_t *result) +{ + struct aml8726_sdxc_softc *sc = device_get_softc(bus); + + switch (which) { + case MMCBR_IVAR_BUS_MODE: + *(int *)result = sc->host.ios.bus_mode; + break; + case MMCBR_IVAR_BUS_WIDTH: + *(int *)result = sc->host.ios.bus_width; + break; + case MMCBR_IVAR_CHIP_SELECT: + *(int *)result = sc->host.ios.chip_select; + break; + case MMCBR_IVAR_CLOCK: + *(int *)result = sc->host.ios.clock; + break; + case MMCBR_IVAR_F_MIN: + *(int *)result = sc->host.f_min; + break; + case MMCBR_IVAR_F_MAX: + *(int *)result = sc->host.f_max; + break; + case MMCBR_IVAR_HOST_OCR: + *(int *)result = sc->host.host_ocr; + break; + case MMCBR_IVAR_MODE: + *(int *)result = sc->host.mode; + break; + case MMCBR_IVAR_OCR: + *(int *)result = sc->host.ocr; + break; + case MMCBR_IVAR_POWER_MODE: + *(int *)result = sc->host.ios.power_mode; + break; + case MMCBR_IVAR_VDD: + *(int *)result = sc->host.ios.vdd; + break; + case MMCBR_IVAR_CAPS: + *(int *)result = sc->host.caps; + break; + case MMCBR_IVAR_MAX_DATA: + *(int *)result = AML_SDXC_MAX_DMA / MMC_SECTOR_SIZE; + break; + default: + return (EINVAL); + } + + return (0); +} + +static int +aml8726_sdxc_write_ivar(device_t bus, device_t child, + int which, uintptr_t value) +{ + struct aml8726_sdxc_softc *sc = device_get_softc(bus); + + switch (which) { + case MMCBR_IVAR_BUS_MODE: + sc->host.ios.bus_mode = value; + break; + case MMCBR_IVAR_BUS_WIDTH: + sc->host.ios.bus_width = value; + break; + case MMCBR_IVAR_CHIP_SELECT: + sc->host.ios.chip_select = value; + break; + case MMCBR_IVAR_CLOCK: + sc->host.ios.clock = value; + break; + case MMCBR_IVAR_MODE: + sc->host.mode = value; + break; + case MMCBR_IVAR_OCR: + sc->host.ocr = value; + break; + case MMCBR_IVAR_POWER_MODE: + sc->host.ios.power_mode = value; + break; + case MMCBR_IVAR_VDD: + sc->host.ios.vdd = value; + break; + /* These are read-only */ + case MMCBR_IVAR_CAPS: + case MMCBR_IVAR_HOST_OCR: + case MMCBR_IVAR_F_MIN: + case MMCBR_IVAR_F_MAX: + case MMCBR_IVAR_MAX_DATA: + default: + return (EINVAL); + } + + return (0); +} + +static int +aml8726_sdxc_get_ro(device_t bus, device_t child) +{ + + return (0); +} + +static int +aml8726_sdxc_acquire_host(device_t bus, device_t child) +{ + struct aml8726_sdxc_softc *sc = device_get_softc(bus); + + AML_SDXC_LOCK(sc); + + while (sc->bus_busy) + mtx_sleep(sc, &sc->mtx, PZERO, "sdxc", hz / 5); + sc->bus_busy++; + + AML_SDXC_UNLOCK(sc); + + return (0); +} + +static int +aml8726_sdxc_release_host(device_t bus, device_t child) +{ + struct aml8726_sdxc_softc *sc = device_get_softc(bus); + + AML_SDXC_LOCK(sc); + + sc->bus_busy--; + wakeup(sc); + + AML_SDXC_UNLOCK(sc); + + return (0); +} + +static device_method_t aml8726_sdxc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_sdxc_probe), + DEVMETHOD(device_attach, aml8726_sdxc_attach), + DEVMETHOD(device_detach, aml8726_sdxc_detach), + DEVMETHOD(device_shutdown, aml8726_sdxc_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, aml8726_sdxc_read_ivar), + DEVMETHOD(bus_write_ivar, aml8726_sdxc_write_ivar), + + /* MMC bridge interface */ + DEVMETHOD(mmcbr_update_ios, aml8726_sdxc_update_ios), + DEVMETHOD(mmcbr_request, aml8726_sdxc_request), + DEVMETHOD(mmcbr_get_ro, aml8726_sdxc_get_ro), + DEVMETHOD(mmcbr_acquire_host, aml8726_sdxc_acquire_host), + DEVMETHOD(mmcbr_release_host, aml8726_sdxc_release_host), + + DEVMETHOD_END +}; + +static driver_t aml8726_sdxc_driver = { + "aml8726_sdxc", + aml8726_sdxc_methods, + sizeof(struct aml8726_sdxc_softc), +}; + +static devclass_t aml8726_sdxc_devclass; + +DRIVER_MODULE(aml8726_sdxc, simplebus, aml8726_sdxc_driver, + aml8726_sdxc_devclass, 0, 0); +MODULE_DEPEND(aml8726_sdxc, aml8726_gpio, 1, 1, 1); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.h =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.h (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.h (revision 280905) @@ -0,0 +1,223 @@ +/*- + * Copyright 2015 John Wehle + * 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$ + */ + +#ifndef _ARM_AMLOGIC_AML8726_SDXC_M8_H +#define _ARM_AMLOGIC_AML8726_SDXC_M8_H + +#define AML_SDXC_ALIGN_DMA 4 +#define AML_SDXC_MAX_DMA 4096 + +/* + * Timeouts are in milliseconds + * + * Read and write are per section 4.6.2 of the: + * + * SD Specifications Part 1 + * Physicaly Layer Simplified Specification + * Version 4.10 + */ +#define AML_SDXC_CMD_TIMEOUT 50 +#define AML_SDXC_READ_TIMEOUT 100 +#define AML_SDXC_WRITE_TIMEOUT 500 +#define AML_SDXC_MAX_TIMEOUT 5000 + +#define AML_SDXC_BUSY_POLL_INTVL 1 +#define AML_SDXC_BUSY_TIMEOUT 1000 + +/* + * There's some disagreements between the S805 documentation + * and the Amlogic Linux platform code regarding the exact + * layout of various registers ... when in doubt we follow + * the platform code. + */ + +#define AML_SDXC_CMD_ARGUMENT_REG 0 + +#define AML_SDXC_SEND_REG 4 +#define AML_SDXC_SEND_REP_PKG_CNT_MASK (0xffffU << 16) +#define AML_SDXC_SEND_REP_PKG_CNT_SHIFT 16 +#define AML_SDXC_SEND_DATA_STOP (1 << 11) +#define AML_SDXC_SEND_DATA_WRITE (1 << 10) +#define AML_SDXC_SEND_RESP_NO_CRC7_CHECK (1 << 9) +#define AML_SDXC_SEND_RESP_136 (1 << 8) +#define AML_SDXC_SEND_CMD_HAS_DATA (1 << 7) +#define AML_SDXC_SEND_CMD_HAS_RESP (1 << 6) +#define AML_SDXC_SEND_INDEX_MASK 0x3f +#define AML_SDXC_SEND_INDEX_SHIFT 0 + +#define AML_SDXC_CNTRL_REG 8 +#define AML_SDXC_CNTRL_TX_ENDIAN_MASK (7 << 29) +#define AML_SDXC_CNTRL_TX_ENDIAN_SHIFT 29 +#define AML_SDXC_CNTRL_RX_ENDIAN_MASK (7 << 24) +#define AML_SDXC_CNTRL_RX_ENDIAN_SHIFT 24 +#define AML_SDXC_CNTRL_RX_PERIOD_SHIFT 20 +#define AML_SDXC_CNTRL_RX_TIMEOUT_SHIFT 13 +#define AML_SDXC_CNTRL_PKG_LEN_MASK (0x1ff << 4) +#define AML_SDXC_CNTRL_PKG_LEN_SHIFT 4 +#define AML_SDXC_CNTRL_BUS_WIDTH_MASK (3 << 0) +#define AML_SDXC_CNTRL_BUS_WIDTH_1 (0 << 0) +#define AML_SDXC_CNTRL_BUS_WIDTH_4 (1 << 0) +#define AML_SDXC_CNTRL_BUS_WIDTH_8 (2 << 0) + +#define AML_SDXC_STATUS_REG 12 +#define AML_SDXC_STATUS_TX_CNT_MASK (0x7f << 13) +#define AML_SDXC_STATUS_TX_CNT_SHIFT 13 +#define AML_SDXC_STATUS_RX_CNT_MASK (0x7f << 6) +#define AML_SDXC_STATUS_RX_CNT_SHIFT 6 +#define AML_SDXC_STATUS_CMD (1 << 5) +#define AML_SDXC_STATUS_DAT3 (1 << 4) +#define AML_SDXC_STATUS_DAT2 (1 << 3) +#define AML_SDXC_STATUS_DAT1 (1 << 2) +#define AML_SDXC_STATUS_DAT0 (1 << 1) +#define AML_SDXC_STATUS_BUSY (1 << 0) + +#define AML_SDXC_CLK_CNTRL_REG 16 +#define AML_SDXC_CLK_CNTRL_MEM_PWR_MASK (3 << 25) +#define AML_SDXC_CLK_CNTRL_MEM_PWR_OFF (3 << 25) +#define AML_SDXC_CLK_CNTRL_MEM_PWR_ON (0 << 25) +#define AML_SDXC_CLK_CNTRL_CLK_SEL_MASK (3 << 16) +#define AML_SDXC_CLK_CNTRL_CLK_SEL_SHIFT 16 +#define AML_SDXC_CLK_CNTRL_CLK_MODULE_EN (1 << 15) +#define AML_SDXC_CLK_CNTRL_SD_CLK_EN (1 << 14) +#define AML_SDXC_CLK_CNTRL_RX_CLK_EN (1 << 13) +#define AML_SDXC_CLK_CNTRL_TX_CLK_EN (1 << 12) +#define AML_SDXC_CLK_CNTRL_CLK_DIV_MASK 0x0fff +#define AML_SDXC_CLK_CNTRL_CLK_DIV_SHIFT 0 + +#define AML_SDXC_DMA_ADDR_REG 20 + +#define AML_SDXC_PDMA_REG 24 +#define AML_SDXC_PDMA_TX_FILL (1U << 31) +#define AML_SDXC_PDMA_RX_FLUSH_NOW (1 << 30) +#define AML_SDXC_PDMA_RX_FLUSH_MODE_SW (1 << 29) +#define AML_SDXC_PDMA_TX_THOLD_MASK (0x3f << 22) +#define AML_SDXC_PDMA_TX_THOLD_SHIFT 22 +#define AML_SDXC_PDMA_RX_THOLD_MASK (0x3f << 15) +#define AML_SDXC_PDMA_RX_THOLD_SHIFT 15 +#define AML_SDXC_PDMA_RD_BURST_MASK (0x1f << 10) +#define AML_SDXC_PDMA_RD_BURST_SHIFT 10 +#define AML_SDXC_PDMA_WR_BURST_MASK (0x1f << 5) +#define AML_SDXC_PDMA_WR_BURST_SHIFT 5 +#define AML_SDXC_PDMA_DMA_URGENT (1 << 4) +#define AML_SDXC_PDMA_RESP_INDEX_MASK (7 << 1) +#define AML_SDXC_PDMA_RESP_INDEX_SHIFT 1 +#define AML_SDXC_PDMA_DMA_EN (1 << 0) + +#define AML_SDXC_MISC_REG 28 +#define AML_SDXC_MISC_TXSTART_THOLD_MASK (7U << 29) +#define AML_SDXC_MISC_TXSTART_THOLD_SHIFT 29 +#define AML_SDXC_MISC_MANUAL_STOP_MODE (1 << 28) +#define AML_SDXC_MISC_WCRC_OK_PAT_MASK (7 << 7) +#define AML_SDXC_MISC_WCRC_OK_PAT_SHIFT 7 +#define AML_SDXC_MISC_WCRC_ERR_PAT_MASK (7 << 4) +#define AML_SDXC_MISC_WCRC_ERR_PAT_SHIFT 4 + +#define AML_SDXC_DATA_REG 32 + +#define AML_SDXC_IRQ_ENABLE_REG 36 +#define AML_SDXC_IRQ_ENABLE_TX_FIFO_EMPTY (1 << 13) +#define AML_SDXC_IRQ_ENABLE_RX_FIFO_FULL (1 << 12) +#define AML_SDXC_IRQ_ENABLE_DMA_DONE (1 << 11) +#define AML_SDXC_IRQ_ENABLE_TRANSFER_DONE_OK (1 << 7) +#define AML_SDXC_IRQ_ENABLE_A_PKG_CRC_ERR (1 << 6) +#define AML_SDXC_IRQ_ENABLE_A_PKG_TIMEOUT_ERR (1 << 5) +#define AML_SDXC_IRQ_ENABLE_A_PKG_DONE_OK (1 << 4) +#define AML_SDXC_IRQ_ENABLE_RESP_CRC_ERR (1 << 2) +#define AML_SDXC_IRQ_ENABLE_RESP_TIMEOUT_ERR (1 << 1) +#define AML_SDXC_IRQ_ENABLE_RESP_OK (1 << 0) + +#define AML_SDXC_IRQ_ENABLE_STANDARD \ + (AML_SDXC_IRQ_ENABLE_TX_FIFO_EMPTY | \ + AML_SDXC_IRQ_ENABLE_RX_FIFO_FULL | \ + AML_SDXC_IRQ_ENABLE_A_PKG_CRC_ERR | \ + AML_SDXC_IRQ_ENABLE_A_PKG_TIMEOUT_ERR | \ + AML_SDXC_IRQ_ENABLE_RESP_CRC_ERR | \ + AML_SDXC_IRQ_ENABLE_RESP_TIMEOUT_ERR | \ + AML_SDXC_IRQ_ENABLE_RESP_OK) + +#define AML_SDXC_IRQ_STATUS_REG 40 +#define AML_SDXC_IRQ_STATUS_TX_FIFO_EMPTY (1 << 13) +#define AML_SDXC_IRQ_STATUS_RX_FIFO_FULL (1 << 12) +#define AML_SDXC_IRQ_STATUS_DMA_DONE (1 << 11) +#define AML_SDXC_IRQ_STATUS_TRANSFER_DONE_OK (1 << 7) +#define AML_SDXC_IRQ_STATUS_A_PKG_CRC_ERR (1 << 6) +#define AML_SDXC_IRQ_STATUS_A_PKG_TIMEOUT_ERR (1 << 5) +#define AML_SDXC_IRQ_STATUS_A_PKG_DONE_OK (1 << 4) +#define AML_SDXC_IRQ_STATUS_RESP_CRC_ERR (1 << 2) +#define AML_SDXC_IRQ_STATUS_RESP_TIMEOUT_ERR (1 << 1) +#define AML_SDXC_IRQ_STATUS_RESP_OK (1 << 0) + +#define AML_SDXC_IRQ_STATUS_CLEAR \ + (AML_SDXC_IRQ_STATUS_TX_FIFO_EMPTY | \ + AML_SDXC_IRQ_STATUS_RX_FIFO_FULL | \ + AML_SDXC_IRQ_STATUS_DMA_DONE | \ + AML_SDXC_IRQ_STATUS_TRANSFER_DONE_OK | \ + AML_SDXC_IRQ_STATUS_A_PKG_CRC_ERR | \ + AML_SDXC_IRQ_STATUS_A_PKG_TIMEOUT_ERR | \ + AML_SDXC_IRQ_STATUS_RESP_CRC_ERR | \ + AML_SDXC_IRQ_STATUS_RESP_TIMEOUT_ERR | \ + AML_SDXC_IRQ_STATUS_RESP_OK) + +#define AML_SDXC_SOFT_RESET_REG 44 +#define AML_SDXC_SOFT_RESET_DMA (1 << 5) +#define AML_SDXC_SOFT_RESET_TX_PHY (1 << 4) +#define AML_SDXC_SOFT_RESET_RX_PHY (1 << 3) +#define AML_SDXC_SOFT_RESET_TX_FIFO (1 << 2) +#define AML_SDXC_SOFT_RESET_RX_FIFO (1 << 1) +#define AML_SDXC_SOFT_RESET_MAIN (1 << 0) + +#define AML_SDXC_SOFT_RESET \ + (AML_SDXC_SOFT_RESET_DMA | \ + AML_SDXC_SOFT_RESET_TX_FIFO | \ + AML_SDXC_SOFT_RESET_RX_FIFO | \ + AML_SDXC_SOFT_RESET_MAIN) + +#define AML_SDXC_ENH_CNTRL_REG 52 +#define AML_SDXC_ENH_CNTRL_TX_EMPTY_THOLD_MASK (0x7f << 25) +#define AML_SDXC_ENH_CNTRL_TX_EMPTY_THOLD_SHIFT 25 +#define AML_SDXC_ENH_CNTRL_RX_FULL_THOLD_MASK (0x7f << 18) +#define AML_SDXC_ENH_CNTRL_RX_FULL_THOLD_SHIFT 18 +#define AML_SDXC_ENH_CNTRL_SDIO_IRQ_PERIOD_MASK (0xff << 8) +#define AML_SDXC_ENH_CNTRL_SDIO_IRQ_PERIOD_SHIFT 8 + +#define AML_SDXC_ENH_CNTRL_DMA_NO_WR_RESP_CHECK_M8 (1 << 17) +#define AML_SDXC_ENH_CNTRL_DMA_NO_RD_RESP_CHECK_M8 (1 << 16) +#define AML_SDXC_ENH_CNTRL_RX_TIMEOUT_MASK_M8 (0xff << 0) +#define AML_SDXC_ENH_CNTRL_RX_TIMEOUT_SHIFT_M8 0 + +#define AML_SDXC_ENH_CNTRL_NO_DMA_CHECK_M8M2 (1 << 2) +#define AML_SDXC_ENH_CNTRL_NO_WR_RESP_CHECK_M8M2 (1 << 1) +#define AML_SDXC_ENH_CNTRL_WR_RESP_MODE_SKIP_M8M2 (1 << 0) + +#define AML_SDXC_CLK2_REG 56 +#define AML_SDXC_CLK2_SD_PHASE_MASK (0x3ff << 12) +#define AML_SDXC_CLK2_SD_PHASE_SHIFT 12 +#define AML_SDXC_CLK2_RX_PHASE_MASK (0x3ff << 0) +#define AML_SDXC_CLK2_RX_PHASE_SHIFT 0 + +#endif /* _ARM_AMLOGIC_AML8726_SDXC_M8_H */ Property changes on: head/sys/arm/amlogic/aml8726/aml8726_sdxc-m8.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_soc.h =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_soc.h (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_soc.h (revision 280905) @@ -0,0 +1,54 @@ +/*- + * Copyright 2015 John Wehle + * 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$ + */ + +#ifndef _ARM_AMLOGIC_AML8726_SOC_H +#define _ARM_AMLOGIC_AML8726_SOC_H + +#define AML_SOC_AOBUS_BASE_ADDR 0xc8100000 +#define AML_SOC_CBUS_BASE_ADDR 0xc1100000 + +/* cbus */ +#define AML_SOC_HW_REV_REG 0x7d4c +#define AML_SOC_HW_REV_M3 0x15 +#define AML_SOC_HW_REV_M6 0x16 +#define AML_SOC_HW_REV_M6TV 0x17 +#define AML_SOC_HW_REV_M6TVL 0x18 +#define AML_SOC_HW_REV_M8 0x19 +#define AML_SOC_HW_REV_M8B 0x1b + +#define AML_SOC_METAL_REV_REG 0x81a8 +#define AML_SOC_M8_METAL_REV_A 0x11111111 +#define AML_SOC_M8_METAL_REV_M2_A 0x11111112 +#define AML_SOC_M8_METAL_REV_B 0x11111113 +#define AML_SOC_M8_METAL_REV_C 0x11111133 +#define AML_SOC_M8B_METAL_REV_A 0x11111111 + +extern uint32_t aml8726_soc_hw_rev; +extern uint32_t aml8726_soc_metal_rev; + +#endif /* _ARM_AMLOGIC_AML8726_SOC_H */ Property changes on: head/sys/arm/amlogic/aml8726/aml8726_soc.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_timer.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_timer.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_timer.c (revision 280905) @@ -0,0 +1,395 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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. + * + */ + +/* + * Amlogic aml8726 timer driver. + * + * 16 bit Timer A is used for the event timer / hard clock. + * 32 bit Timer E is used for the timecounter / DELAY. + * + * The current implementation doesn't use Timers B-D. Another approach is + * to split the timers between the cores implementing per cpu event timers. + * + * The timers all share the MUX register which requires a mutex to serialize + * access. The mutex is also used to avoid potential problems between the + * interrupt handler and timer_start / timer_stop. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +struct aml8726_timer_softc { + device_t dev; + struct resource * res[2]; + struct mtx mtx; + void * ih_cookie; + struct eventtimer et; + uint32_t first_ticks; + uint32_t period_ticks; + struct timecounter tc; +}; + +static struct resource_spec aml8726_timer_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, /* INT_TIMER_A */ + { -1, 0 } +}; + +/* + * devclass_get_device / device_get_softc could be used + * to dynamically locate this, however the timers are a + * required device which can't be unloaded so there's + * no need for the overhead. + */ +static struct aml8726_timer_softc *aml8726_timer_sc = NULL; + +#define AML_TIMER_LOCK(sc) mtx_lock_spin(&(sc)->mtx) +#define AML_TIMER_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx) +#define AML_TIMER_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "timer", MTX_SPIN) +#define AML_TIMER_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define AML_TIMER_MUX_REG 0 +#define AML_TIMER_INPUT_1us 0 +#define AML_TIMER_INPUT_10us 1 +#define AML_TIMER_INPUT_100us 2 +#define AML_TIMER_INPUT_1ms 3 +#define AML_TIMER_INPUT_MASK 3 +#define AML_TIMER_A_INPUT_MASK 3 +#define AML_TIMER_A_INPUT_SHIFT 0 +#define AML_TIMER_B_INPUT_MASK (3 << 2) +#define AML_TIMER_B_INPUT_SHIFT 2 +#define AML_TIMER_C_INPUT_MASK (3 << 4) +#define AML_TIMER_C_INPUT_SHIFT 4 +#define AML_TIMER_D_INPUT_MASK (3 << 6) +#define AML_TIMER_D_INPUT_SHIFT 6 +#define AML_TIMER_E_INPUT_SYS 0 +#define AML_TIMER_E_INPUT_1us 1 +#define AML_TIMER_E_INPUT_10us 2 +#define AML_TIMER_E_INPUT_100us 3 +#define AML_TIMER_E_INPUT_1ms 4 +#define AML_TIMER_E_INPUT_MASK (7 << 8) +#define AML_TIMER_E_INPUT_SHIFT 8 +#define AML_TIMER_A_PERIODIC (1 << 12) +#define AML_TIMER_B_PERIODIC (1 << 13) +#define AML_TIMER_C_PERIODIC (1 << 14) +#define AML_TIMER_D_PERIODIC (1 << 15) +#define AML_TIMER_A_EN (1 << 16) +#define AML_TIMER_B_EN (1 << 17) +#define AML_TIMER_C_EN (1 << 18) +#define AML_TIMER_D_EN (1 << 19) +#define AML_TIMER_E_EN (1 << 20) +#define AML_TIMER_A_REG 4 +#define AML_TIMER_B_REG 8 +#define AML_TIMER_C_REG 12 +#define AML_TIMER_D_REG 16 +#define AML_TIMER_E_REG 20 + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) + +static unsigned +aml8726_get_timecount(struct timecounter *tc) +{ + struct aml8726_timer_softc *sc = + (struct aml8726_timer_softc *)tc->tc_priv; + + return CSR_READ_4(sc, AML_TIMER_E_REG); +} + +static int +aml8726_hardclock(void *arg) +{ + struct aml8726_timer_softc *sc = (struct aml8726_timer_softc *)arg; + + AML_TIMER_LOCK(sc); + + if (sc->first_ticks != 0 && sc->period_ticks != 0) { + sc->first_ticks = 0; + + CSR_WRITE_4(sc, AML_TIMER_A_REG, sc->period_ticks); + CSR_WRITE_4(sc, AML_TIMER_MUX_REG, + (CSR_READ_4(sc, AML_TIMER_MUX_REG) | + AML_TIMER_A_PERIODIC | AML_TIMER_A_EN)); + } + + AML_TIMER_UNLOCK(sc); + + if (sc->et.et_active) + sc->et.et_event_cb(&sc->et, sc->et.et_arg); + + return (FILTER_HANDLED); +} + +static int +aml8726_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) +{ + struct aml8726_timer_softc *sc = + (struct aml8726_timer_softc *)et->et_priv; + uint32_t first_ticks; + uint32_t period_ticks; + uint32_t periodic; + uint32_t ticks; + + first_ticks = (first * et->et_frequency) / SBT_1S; + period_ticks = (period * et->et_frequency) / SBT_1S; + + if (first_ticks != 0) { + ticks = first_ticks; + periodic = 0; + + } else { + ticks = period_ticks; + periodic = AML_TIMER_A_PERIODIC; + } + + if (ticks == 0) + return (EINVAL); + + AML_TIMER_LOCK(sc); + + sc->first_ticks = first_ticks; + sc->period_ticks = period_ticks; + + CSR_WRITE_4(sc, AML_TIMER_A_REG, ticks); + CSR_WRITE_4(sc, AML_TIMER_MUX_REG, + ((CSR_READ_4(sc, AML_TIMER_MUX_REG) & ~AML_TIMER_A_PERIODIC) | + AML_TIMER_A_EN | periodic)); + + AML_TIMER_UNLOCK(sc); + + return (0); +} + +static int +aml8726_timer_stop(struct eventtimer *et) +{ + struct aml8726_timer_softc *sc = + (struct aml8726_timer_softc *)et->et_priv; + + AML_TIMER_LOCK(sc); + + CSR_WRITE_4(sc, AML_TIMER_MUX_REG, + (CSR_READ_4(sc, AML_TIMER_MUX_REG) & ~AML_TIMER_A_EN)); + + AML_TIMER_UNLOCK(sc); + + return (0); +} + +static int +aml8726_timer_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-timer")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 timer"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_timer_attach(device_t dev) +{ + struct aml8726_timer_softc *sc = device_get_softc(dev); + + /* There should be exactly one instance. */ + if (aml8726_timer_sc != NULL) + return (ENXIO); + + sc->dev = dev; + + if (bus_alloc_resources(dev, aml8726_timer_spec, sc->res)) { + device_printf(dev, "can not allocate resources for device\n"); + return (ENXIO); + } + + /* + * Disable the timers, select the input for each timer, + * clear timer E, and then enable timer E. + */ + CSR_WRITE_4(sc, AML_TIMER_MUX_REG, + ((CSR_READ_4(sc, AML_TIMER_MUX_REG) & + ~(AML_TIMER_A_EN | AML_TIMER_A_INPUT_MASK | + AML_TIMER_E_EN | AML_TIMER_E_INPUT_MASK)) | + (AML_TIMER_INPUT_1us << AML_TIMER_A_INPUT_SHIFT) | + (AML_TIMER_E_INPUT_1us << AML_TIMER_E_INPUT_SHIFT))); + + CSR_WRITE_4(sc, AML_TIMER_E_REG, 0); + + CSR_WRITE_4(sc, AML_TIMER_MUX_REG, + (CSR_READ_4(sc, AML_TIMER_MUX_REG) | AML_TIMER_E_EN)); + + /* + * Initialize the mutex prior to installing the interrupt handler + * in case of a spurious interrupt. + */ + AML_TIMER_LOCK_INIT(sc); + + if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_CLK, + aml8726_hardclock, NULL, sc, &sc->ih_cookie)) { + device_printf(dev, "could not setup interrupt handler\n"); + bus_release_resources(dev, aml8726_timer_spec, sc->res); + AML_TIMER_LOCK_DESTROY(sc); + return (ENXIO); + } + + aml8726_timer_sc = sc; + + sc->et.et_name = "aml8726 timer A"; + sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT; + sc->et.et_frequency = 1000000; + sc->et.et_quality = 1000; + sc->et.et_min_period = (0x00000002LLU * SBT_1S) / sc->et.et_frequency; + sc->et.et_max_period = (0x0000fffeLLU * SBT_1S) / sc->et.et_frequency; + sc->et.et_start = aml8726_timer_start; + sc->et.et_stop = aml8726_timer_stop; + sc->et.et_priv = sc; + + et_register(&sc->et); + + sc->tc.tc_get_timecount = aml8726_get_timecount; + sc->tc.tc_name = "aml8726 timer E"; + sc->tc.tc_frequency = 1000000; + sc->tc.tc_counter_mask = ~0u; + sc->tc.tc_quality = 1000; + sc->tc.tc_priv = sc; + + tc_init(&sc->tc); + + return (0); +} + +static int +aml8726_timer_detach(device_t dev) +{ + + return (EBUSY); +} + +static device_method_t aml8726_timer_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_timer_probe), + DEVMETHOD(device_attach, aml8726_timer_attach), + DEVMETHOD(device_detach, aml8726_timer_detach), + + DEVMETHOD_END +}; + +static driver_t aml8726_timer_driver = { + "timer", + aml8726_timer_methods, + sizeof(struct aml8726_timer_softc), +}; + +static devclass_t aml8726_timer_devclass; + +EARLY_DRIVER_MODULE(timer, simplebus, aml8726_timer_driver, + aml8726_timer_devclass, 0, 0, BUS_PASS_TIMER); + +void +DELAY(int usec) +{ + uint32_t counter; + uint32_t delta, now, previous, remaining; + + /* Timer has not yet been initialized */ + if (aml8726_timer_sc == NULL) { + for (; usec > 0; usec--) + for (counter = 200; counter > 0; counter--) { + /* Prevent gcc from optimizing out the loop */ + cpufunc_nullop(); + } + return; + } + + /* + * Some of the other timers in the source tree do this calculation as: + * + * usec * ((sc->tc.tc_frequency / 1000000) + 1) + * + * which gives a fairly pessimistic result when tc_frequency is an exact + * multiple of 1000000. Given the data type and typical values for + * tc_frequency adding 999999 shouldn't overflow. + */ + remaining = usec * ((aml8726_timer_sc->tc.tc_frequency + 999999) / + 1000000); + + /* + * We add one since the first iteration may catch the counter just + * as it is changing. + */ + remaining += 1; + + previous = aml8726_get_timecount(&aml8726_timer_sc->tc); + + for ( ; ; ) { + now = aml8726_get_timecount(&aml8726_timer_sc->tc); + + /* + * If the timer has rolled over, then we have the case: + * + * if (previous > now) { + * delta = (0 - previous) + now + * } + * + * which is really no different then the normal case. + * Both cases are simply: + * + * delta = now - previous. + */ + delta = now - previous; + + if (delta >= remaining) + break; + + previous = now; + remaining -= delta; + } +} Property changes on: head/sys/arm/amlogic/aml8726/aml8726_timer.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_uart.h =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_uart.h (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_uart.h (revision 280905) @@ -0,0 +1,97 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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$ + */ + +#ifndef _ARM_AMLOGIC_AML8726_UART_H +#define _ARM_AMLOGIC_AML8726_UART_H + +#define AML_UART_WFIFO_REG 0 + +#define AML_UART_RFIFO_REG 4 + +#define AML_UART_CONTROL_REG 8 +#define AML_UART_CONTROL_TX_INT_EN (1 << 28) +#define AML_UART_CONTROL_RX_INT_EN (1 << 27) +#define AML_UART_CONTROL_CLR_ERR (1 << 24) +#define AML_UART_CONTROL_RX_RST (1 << 23) +#define AML_UART_CONTROL_TX_RST (1 << 22) +#define AML_UART_CONTROL_DB_MASK (3 << 20) +#define AML_UART_CONTROL_8_DB (0 << 20) +#define AML_UART_CONTROL_7_DB (1 << 20) +#define AML_UART_CONTROL_6_DB (2 << 20) +#define AML_UART_CONTROL_5_DB (3 << 20) +#define AML_UART_CONTROL_P_MASK (3 << 18) +#define AML_UART_CONTROL_P_EN (1 << 19) +#define AML_UART_CONTROL_P_EVEN (0 << 18) +#define AML_UART_CONTROL_P_ODD (1 << 18) +#define AML_UART_CONTROL_SB_MASK (3 << 16) +#define AML_UART_CONTROL_1_SB (0 << 16) +#define AML_UART_CONTROL_2_SB (1 << 16) +#define AML_UART_CONTROL_TWO_WIRE_EN (1 << 15) +#define AML_UART_CONTROL_RX_EN (1 << 13) +#define AML_UART_CONTROL_TX_EN (1 << 12) +#define AML_UART_CONTROL_BAUD_MASK 0xfff +#define AML_UART_CONTROL_BAUD_WIDTH 12 + +#define AML_UART_STATUS_REG 12 +#define AML_UART_STATUS_RECV_BUSY (1 << 26) +#define AML_UART_STATUS_XMIT_BUSY (1 << 25) +#define AML_UART_STATUS_RX_FIFO_OVERFLOW (1 << 24) +#define AML_UART_STATUS_TX_FIFO_EMPTY (1 << 22) +#define AML_UART_STATUS_TX_FIFO_FULL (1 << 21) +#define AML_UART_STATUS_RX_FIFO_EMPTY (1 << 20) +#define AML_UART_STATUS_RX_FIFO_FULL (1 << 19) +#define AML_UART_STATUS_TX_FIFO_WRITE_ERR (1 << 18) +#define AML_UART_STATUS_FRAME_ERR (1 << 17) +#define AML_UART_STATUS_PARITY_ERR (1 << 16) +#define AML_UART_STATUS_TX_FIFO_CNT_MASK (0x7f << 8) +#define AML_UART_STATUS_TX_FIFO_CNT_SHIFT 8 +#define AML_UART_STATUS_RX_FIFO_CNT_MASK (0x7f << 0) +#define AML_UART_STATUS_RX_FIFO_CNT_SHIFT 0 + +#define AML_UART_MISC_REG 16 +#define AML_UART_MISC_OLD_RX_BAUD (1 << 30) +#define AML_UART_MISC_BAUD_EXT_MASK (0xf << 20) +#define AML_UART_MISC_BAUD_EXT_SHIFT 20 + +/* + * The documentation appears to be incorrect as the + * IRQ is actually generated when TX FIFO count is + * * equal to * or less than the selected threshold. + */ +#define AML_UART_MISC_XMIT_IRQ_CNT_MASK (0xff << 8) +#define AML_UART_MISC_XMIT_IRQ_CNT_SHIFT 8 + +/* + * The documentation appears to be incorrect as the + * IRQ is actually generated when RX FIFO count is + * * equal to * or greater than the selected threshold. + */ +#define AML_UART_MISC_RECV_IRQ_CNT_MASK 0xff +#define AML_UART_MISC_RECV_IRQ_CNT_SHIFT 0 + +#endif /* _ARM_AMLOGIC_AML8726_UART_H */ Property changes on: head/sys/arm/amlogic/aml8726/aml8726_uart.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_uart_console.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_uart_console.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_uart_console.c (revision 280905) @@ -0,0 +1,180 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 UART console driver. + * + * This is only necessary to use when debugging early boot code. + * The standard uart driver is available for use later in the boot. + * + * It's assumed the SoC uart is mapped into AML_UART_KVM_BASE meaning + * when using EARLY_PRINTF you'll need to define SOCDEV_VA to be + * 0xd8100000 and SOCDEV_PA to be 0xc8100000 in your config file. + */ + +#include "opt_global.h" + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define AML_UART_KVM_BASE (aml8726_aobus_kva_base + 0x130 * 4) + +static uint32_t +ub_getreg(uint32_t off) +{ + + return *((volatile uint32_t *)(AML_UART_KVM_BASE + off)); +} + +static void +ub_setreg(uint32_t off, uint32_t val) +{ + + *((volatile uint32_t *)(AML_UART_KVM_BASE + off)) = val; +} + +static void +uart_cnprobe(struct consdev *cp) +{ + + sprintf(cp->cn_name, "uart"); + cp->cn_pri = CN_REMOTE; +} + +static void +uart_cngrab(struct consdev *cp) +{ +} + +static void +uart_cnungrab(struct consdev *cp) +{ +} + +static void +uart_cninit(struct consdev *cp) +{ + uint32_t cr; + uint32_t mr; + +#ifdef EARLY_PRINTF + if (early_putc != NULL) { + printf("Early printf yielding control to the real console.\n"); + early_putc = NULL; + } + + /* + * Give pending characters a chance to drain. + */ + DELAY(4000); +#endif + + cr = ub_getreg(AML_UART_CONTROL_REG); + /* Disable all interrupt sources. */ + cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN); + /* Reset the transmitter and receiver. */ + cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); + /* Use two wire mode. */ + cr |= AML_UART_CONTROL_TWO_WIRE_EN; + /* Enable the transmitter and receiver. */ + cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN); + ub_setreg(AML_UART_CONTROL_REG, cr); + + /* Clear RX FIFO level for generating interrupts. */ + mr = ub_getreg(AML_UART_MISC_REG); + mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK; + ub_setreg(AML_UART_MISC_REG, mr); + + /* Ensure the reset bits are clear. */ + cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); + ub_setreg(AML_UART_CONTROL_REG, cr); +} + +static void +uart_cnterm(struct consdev * cp) +{ +} + +static void +uart_cnputc(struct consdev *cp, int c) +{ + + while ((ub_getreg(AML_UART_STATUS_REG) & + AML_UART_STATUS_TX_FIFO_FULL) != 0) + cpu_spinwait(); + + ub_setreg(AML_UART_WFIFO_REG, c); +} + +static int +uart_cngetc(struct consdev * cp) +{ + int c; + + if ((ub_getreg(AML_UART_STATUS_REG) & + AML_UART_STATUS_RX_FIFO_EMPTY) != 0) + return (-1); + + c = ub_getreg(AML_UART_RFIFO_REG) & 0xff; + + return (c); +} + +CONSOLE_DRIVER(uart); + +#ifdef EARLY_PRINTF + +#if !(defined(SOCDEV_PA) && defined(SOCDEV_VA)) +#error SOCDEV_PA and SOCDEV_VA must be defined. +#endif + +static void +eputc(int c) +{ + + if (c == '\n') + eputc('\r'); + + uart_cnputc(NULL, c); +} + +early_putc_t *early_putc = eputc; +#endif Property changes on: head/sys/arm/amlogic/aml8726/aml8726_uart_console.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_usb_phy-m6.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_usb_phy-m6.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_usb_phy-m6.c (revision 280905) @@ -0,0 +1,417 @@ +/*- + * Copyright 2014-2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726-m6 (and later) USB physical layer driver. + * + * Each USB physical interface has a dedicated register block. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include "gpio_if.h" + +struct aml8726_usb_phy_gpio { + device_t dev; + uint32_t pin; + uint32_t pol; +}; + +struct aml8726_usb_phy_softc { + device_t dev; + struct resource *res[1]; + uint32_t npwr_en; + struct aml8726_usb_phy_gpio *pwr_en; + boolean_t force_aca; + struct aml8726_usb_phy_gpio hub_rst; +}; + +static struct resource_spec aml8726_usb_phy_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define AML_USB_PHY_CFG_REG 0 +#define AML_USB_PHY_CFG_CLK_SEL_32K_ALT (1 << 15) +#define AML_USB_PHY_CFG_CLK_DIV_MASK (0x7f << 4) +#define AML_USB_PHY_CFG_CLK_DIV_SHIFT 4 +#define AML_USB_PHY_CFG_CLK_SEL_MASK (7 << 1) +#define AML_USB_PHY_CFG_CLK_SEL_XTAL (0 << 1) +#define AML_USB_PHY_CFG_CLK_SEL_XTAL_DIV2 (1 << 1) +#define AML_USB_PHY_CFG_CLK_EN (1 << 0) + +#define AML_USB_PHY_CTRL_REG 4 +#define AML_USB_PHY_CTRL_FSEL_MASK (7 << 22) +#define AML_USB_PHY_CTRL_FSEL_12M (2 << 22) +#define AML_USB_PHY_CTRL_FSEL_24M (5 << 22) +#define AML_USB_PHY_CTRL_POR (1 << 15) +#define AML_USB_PHY_CTRL_CLK_DETECTED (1 << 8) + +#define AML_USB_PHY_ADP_BC_REG 12 +#define AML_USB_PHY_ADP_BC_ACA_FLOATING (1 << 26) +#define AML_USB_PHY_ADP_BC_ACA_EN (1 << 16) + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) +#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ + (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +#define PIN_ON_FLAG(pol) ((pol) == 0 ? \ + GPIO_PIN_LOW : GPIO_PIN_HIGH) +#define PIN_OFF_FLAG(pol) ((pol) == 0 ? \ + GPIO_PIN_HIGH : GPIO_PIN_LOW) + +static int +aml8726_usb_phy_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-m6-usb-phy") && + !ofw_bus_is_compatible(dev, "amlogic,aml8726-m8-usb-phy")) + return (ENXIO); + + switch (aml8726_soc_hw_rev) { + case AML_SOC_HW_REV_M8: + case AML_SOC_HW_REV_M8B: + device_set_desc(dev, "Amlogic aml8726-m8 USB PHY"); + break; + default: + device_set_desc(dev, "Amlogic aml8726-m6 USB PHY"); + break; + } + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_usb_phy_attach(device_t dev) +{ + struct aml8726_usb_phy_softc *sc = device_get_softc(dev); + char *force_aca; + int err; + int npwr_en; + pcell_t *prop; + phandle_t node; + ssize_t len; + uint32_t div; + uint32_t i; + uint32_t value; + + sc->dev = dev; + + if (bus_alloc_resources(dev, aml8726_usb_phy_spec, sc->res)) { + device_printf(dev, "can not allocate resources for device\n"); + return (ENXIO); + } + + node = ofw_bus_get_node(dev); + + len = OF_getprop_alloc(node, "force-aca", + sizeof(char), (void **)&force_aca); + + sc->force_aca = FALSE; + + if (len > 0) { + if (strncmp(force_aca, "true", len) == 0) + sc->force_aca = TRUE; + } + + free(force_aca, M_OFWPROP); + + err = 0; + + len = OF_getencprop_alloc(node, "usb-pwr-en", + 3 * sizeof(pcell_t), (void **)&prop); + npwr_en = (len > 0) ? len : 0; + + sc->npwr_en = 0; + sc->pwr_en = (struct aml8726_usb_phy_gpio *) + malloc(npwr_en * sizeof (*sc->pwr_en), M_DEVBUF, M_WAITOK); + + for (i = 0; i < npwr_en; i++) { + sc->pwr_en[i].dev = OF_device_from_xref(prop[i * 3]); + sc->pwr_en[i].pin = prop[i * 3 + 1]; + sc->pwr_en[i].pol = prop[i * 3 + 2]; + + if (sc->pwr_en[i].dev == NULL) + err = 1; + } + + free(prop, M_OFWPROP); + + len = OF_getencprop_alloc(node, "usb-hub-rst", + 3 * sizeof(pcell_t), (void **)&prop); + if (len > 0) { + sc->hub_rst.dev = OF_device_from_xref(prop[0]); + sc->hub_rst.pin = prop[1]; + sc->hub_rst.pol = prop[2]; + + if (len > 1 || sc->hub_rst.dev == NULL) + err = 1; + } + + free(prop, M_OFWPROP); + + if (err) { + device_printf(dev, "unable to parse gpio\n"); + goto fail; + } + + /* Turn on power by setting pin and then enabling output driver. */ + for (i = 0; i < npwr_en; i++) { + if (GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin, + PIN_ON_FLAG(sc->pwr_en[i].pol)) != 0 || + GPIO_PIN_SETFLAGS(sc->pwr_en[i].dev, sc->pwr_en[i].pin, + GPIO_PIN_OUTPUT) != 0) { + device_printf(dev, + "could not use gpio to control power\n"); + goto fail; + } + + sc->npwr_en++; + } + + /* + * Configure the clock source and divider. + */ + + value = CSR_READ_4(sc, AML_USB_PHY_CFG_REG); + + value &= ~(AML_USB_PHY_CFG_CLK_SEL_32K_ALT | + AML_USB_PHY_CFG_CLK_DIV_MASK | + AML_USB_PHY_CFG_CLK_SEL_MASK | + AML_USB_PHY_CFG_CLK_EN); + + switch (aml8726_soc_hw_rev) { + case AML_SOC_HW_REV_M8: + case AML_SOC_HW_REV_M8B: + value |= AML_USB_PHY_CFG_CLK_SEL_32K_ALT; + break; + default: + div = 2; + value |= AML_USB_PHY_CFG_CLK_SEL_XTAL; + value |= ((div - 1) << AML_USB_PHY_CFG_CLK_DIV_SHIFT) & + AML_USB_PHY_CFG_CLK_DIV_MASK; + value |= AML_USB_PHY_CFG_CLK_EN; + break; + } + + CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value); + + CSR_BARRIER(sc, AML_USB_PHY_CFG_REG); + + /* + * Configure the clock frequency and issue a power on reset. + */ + + value = CSR_READ_4(sc, AML_USB_PHY_CTRL_REG); + + value &= ~AML_USB_PHY_CTRL_FSEL_MASK; + + switch (aml8726_soc_hw_rev) { + case AML_SOC_HW_REV_M8: + case AML_SOC_HW_REV_M8B: + value |= AML_USB_PHY_CTRL_FSEL_24M; + break; + default: + value |= AML_USB_PHY_CTRL_FSEL_12M; + break; + } + + value |= AML_USB_PHY_CTRL_POR; + + CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value); + + CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG); + + DELAY(500); + + /* + * Enable by clearing the power on reset. + */ + + value &= ~AML_USB_PHY_CTRL_POR; + + CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value); + + CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG); + + DELAY(1000); + + /* + * Check if the clock was detected. + */ + value = CSR_READ_4(sc, AML_USB_PHY_CTRL_REG); + if ((value & AML_USB_PHY_CTRL_CLK_DETECTED) == 0) + device_printf(dev, "PHY Clock not detected\n"); + + /* + * If necessary enabled Accessory Charger Adaptor detection + * so that the port knows what mode to operate in. + */ + if (sc->force_aca) { + value = CSR_READ_4(sc, AML_USB_PHY_ADP_BC_REG); + + value |= AML_USB_PHY_ADP_BC_ACA_EN; + + CSR_WRITE_4(sc, AML_USB_PHY_ADP_BC_REG, value); + + CSR_BARRIER(sc, AML_USB_PHY_ADP_BC_REG); + + DELAY(50); + + value = CSR_READ_4(sc, AML_USB_PHY_ADP_BC_REG); + + if ((value & AML_USB_PHY_ADP_BC_ACA_FLOATING) != 0) { + device_printf(dev, + "force-aca requires newer silicon\n"); + goto fail; + } + } + + /* + * Reset the hub. + */ + if (sc->hub_rst.dev != NULL) { + err = 0; + + if (GPIO_PIN_SET(sc->hub_rst.dev, sc->hub_rst.pin, + PIN_ON_FLAG(sc->hub_rst.pol)) != 0 || + GPIO_PIN_SETFLAGS(sc->hub_rst.dev, sc->hub_rst.pin, + GPIO_PIN_OUTPUT) != 0) + err = 1; + + DELAY(30); + + if (GPIO_PIN_SET(sc->hub_rst.dev, sc->hub_rst.pin, + PIN_OFF_FLAG(sc->hub_rst.pol)) != 0) + err = 1; + + DELAY(60000); + + if (err) { + device_printf(dev, + "could not use gpio to reset hub\n"); + goto fail; + } + } + + return (0); + +fail: + /* In the event of problems attempt to turn things back off. */ + i = sc->npwr_en; + while (i-- != 0) { + GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin, + PIN_OFF_FLAG(sc->pwr_en[i].pol)); + } + + free (sc->pwr_en, M_DEVBUF); + sc->pwr_en = NULL; + + bus_release_resources(dev, aml8726_usb_phy_spec, sc->res); + + return (ENXIO); +} + +static int +aml8726_usb_phy_detach(device_t dev) +{ + struct aml8726_usb_phy_softc *sc = device_get_softc(dev); + uint32_t i; + uint32_t value; + + /* + * Disable by issuing a power on reset. + */ + + value = CSR_READ_4(sc, AML_USB_PHY_CTRL_REG); + + value |= AML_USB_PHY_CTRL_POR; + + CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value); + + CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG); + + /* Turn off power */ + i = sc->npwr_en; + while (i-- != 0) { + GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin, + PIN_OFF_FLAG(sc->pwr_en[i].pol)); + } + free (sc->pwr_en, M_DEVBUF); + sc->pwr_en = NULL; + + bus_release_resources(dev, aml8726_usb_phy_spec, sc->res); + + return (0); +} + +static device_method_t aml8726_usb_phy_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_usb_phy_probe), + DEVMETHOD(device_attach, aml8726_usb_phy_attach), + DEVMETHOD(device_detach, aml8726_usb_phy_detach), + + DEVMETHOD_END +}; + +static driver_t aml8726_usb_phy_driver = { + "usbphy", + aml8726_usb_phy_methods, + sizeof(struct aml8726_usb_phy_softc), +}; + +static devclass_t aml8726_usb_phy_devclass; + +DRIVER_MODULE(aml8726_m6usbphy, simplebus, aml8726_usb_phy_driver, + aml8726_usb_phy_devclass, 0, 0); +MODULE_DEPEND(aml8726_m6usbphy, aml8726_gpio, 1, 1, 1); Property changes on: head/sys/arm/amlogic/aml8726/aml8726_usb_phy-m6.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/aml8726_wdt.c =================================================================== --- head/sys/arm/amlogic/aml8726/aml8726_wdt.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/aml8726_wdt.c (revision 280905) @@ -0,0 +1,308 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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. + * + */ + +/* + * Amlogic aml8726 watchdog driver. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include + + +struct aml8726_wdt_softc { + device_t dev; + struct resource * res[2]; + struct mtx mtx; + void * ih_cookie; +}; + +static struct resource_spec aml8726_wdt_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static struct { + uint32_t ctrl_cpu_mask; + uint32_t ctrl_en; + uint32_t term_cnt_mask; + uint32_t reset_cnt_mask; +} aml8726_wdt_soc_params; + +/* + * devclass_get_device / device_get_softc could be used + * to dynamically locate this, however the wdt is a + * required device which can't be unloaded so there's + * no need for the overhead. + */ +static struct aml8726_wdt_softc *aml8726_wdt_sc = NULL; + +#define AML_WDT_LOCK(sc) mtx_lock_spin(&(sc)->mtx) +#define AML_WDT_UNLOCK(sc) mtx_unlock_spin(&(sc)->mtx) +#define AML_WDT_LOCK_INIT(sc) \ + mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ + "wdt", MTX_SPIN) +#define AML_WDT_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); + +#define AML_WDT_CTRL_REG 0 +#define AML_WDT_CTRL_CPU_WDRESET_MASK aml8726_wdt_soc_params.ctrl_cpu_mask +#define AML_WDT_CTRL_CPU_WDRESET_SHIFT 24 +#define AML_WDT_CTRL_IRQ_EN (1 << 23) +#define AML_WDT_CTRL_EN aml8726_wdt_soc_params.ctrl_en +#define AML_WDT_CTRL_TERMINAL_CNT_MASK aml8726_wdt_soc_params.term_cnt_mask +#define AML_WDT_CTRL_TERMINAL_CNT_SHIFT 0 +#define AML_WDT_RESET_REG 4 +#define AML_WDT_RESET_CNT_MASK aml8726_wdt_soc_params.reset_cnt_mask +#define AML_WDT_RESET_CNT_SHIFT 0 + +#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) +#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) +#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ + (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) + +static void +aml8726_wdt_watchdog(void *private, u_int cmd, int *error) +{ + struct aml8726_wdt_softc *sc = (struct aml8726_wdt_softc *)private; + uint32_t wcr; + uint64_t tens_of_usec; + + AML_WDT_LOCK(sc); + + tens_of_usec = (((uint64_t)1 << (cmd & WD_INTERVAL)) + 9999) / 10000; + + if (cmd != 0 && tens_of_usec <= (AML_WDT_CTRL_TERMINAL_CNT_MASK >> + AML_WDT_CTRL_TERMINAL_CNT_SHIFT)) { + + wcr = AML_WDT_CTRL_CPU_WDRESET_MASK | + AML_WDT_CTRL_EN | ((uint32_t)tens_of_usec << + AML_WDT_CTRL_TERMINAL_CNT_SHIFT); + + CSR_WRITE_4(sc, AML_WDT_RESET_REG, 0); + CSR_WRITE_4(sc, AML_WDT_CTRL_REG, wcr); + + *error = 0; + } else + CSR_WRITE_4(sc, AML_WDT_CTRL_REG, + (CSR_READ_4(sc, AML_WDT_CTRL_REG) & + ~(AML_WDT_CTRL_IRQ_EN | AML_WDT_CTRL_EN))); + + AML_WDT_UNLOCK(sc); +} + +static int +aml8726_wdt_intr(void *arg) +{ + struct aml8726_wdt_softc *sc = (struct aml8726_wdt_softc *)arg; + + /* + * Normally a timeout causes a hardware reset, however + * the watchdog timer can be configured to cause an + * interrupt instead by setting AML_WDT_CTRL_IRQ_EN + * and clearing AML_WDT_CTRL_CPU_WDRESET_MASK. + */ + + AML_WDT_LOCK(sc); + + CSR_WRITE_4(sc, AML_WDT_CTRL_REG, + (CSR_READ_4(sc, AML_WDT_CTRL_REG) & ~(AML_WDT_CTRL_IRQ_EN | + AML_WDT_CTRL_EN))); + + CSR_BARRIER(sc, AML_WDT_CTRL_REG); + + AML_WDT_UNLOCK(sc); + + device_printf(sc->dev, "timeout expired\n"); + + return (FILTER_HANDLED); +} + +static int +aml8726_wdt_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-wdt")) + return (ENXIO); + + device_set_desc(dev, "Amlogic aml8726 WDT"); + + return (BUS_PROBE_DEFAULT); +} + +static int +aml8726_wdt_attach(device_t dev) +{ + struct aml8726_wdt_softc *sc = device_get_softc(dev); + + /* There should be exactly one instance. */ + if (aml8726_wdt_sc != NULL) + return (ENXIO); + + sc->dev = dev; + + if (bus_alloc_resources(dev, aml8726_wdt_spec, sc->res)) { + device_printf(dev, "can not allocate resources for device\n"); + return (ENXIO); + } + + /* + * Certain bitfields are dependent on the hardware revision. + */ + switch (aml8726_soc_hw_rev) { + case AML_SOC_HW_REV_M8: + aml8726_wdt_soc_params.ctrl_cpu_mask = 0xf << + AML_WDT_CTRL_CPU_WDRESET_SHIFT; + switch (aml8726_soc_metal_rev) { + case AML_SOC_M8_METAL_REV_M2_A: + aml8726_wdt_soc_params.ctrl_en = 1 << 19; + aml8726_wdt_soc_params.term_cnt_mask = 0x07ffff << + AML_WDT_CTRL_TERMINAL_CNT_SHIFT; + aml8726_wdt_soc_params.reset_cnt_mask = 0x07ffff << + AML_WDT_RESET_CNT_SHIFT; + break; + default: + aml8726_wdt_soc_params.ctrl_en = 1 << 22; + aml8726_wdt_soc_params.term_cnt_mask = 0x3fffff << + AML_WDT_CTRL_TERMINAL_CNT_SHIFT; + aml8726_wdt_soc_params.reset_cnt_mask = 0x3fffff << + AML_WDT_RESET_CNT_SHIFT; + break; + } + break; + case AML_SOC_HW_REV_M8B: + aml8726_wdt_soc_params.ctrl_cpu_mask = 0xf << + AML_WDT_CTRL_CPU_WDRESET_SHIFT; + aml8726_wdt_soc_params.ctrl_en = 1 << 19; + aml8726_wdt_soc_params.term_cnt_mask = 0x07ffff << + AML_WDT_CTRL_TERMINAL_CNT_SHIFT; + aml8726_wdt_soc_params.reset_cnt_mask = 0x07ffff << + AML_WDT_RESET_CNT_SHIFT; + break; + default: + aml8726_wdt_soc_params.ctrl_cpu_mask = 3 << + AML_WDT_CTRL_CPU_WDRESET_SHIFT; + aml8726_wdt_soc_params.ctrl_en = 1 << 22; + aml8726_wdt_soc_params.term_cnt_mask = 0x3fffff << + AML_WDT_CTRL_TERMINAL_CNT_SHIFT; + aml8726_wdt_soc_params.reset_cnt_mask = 0x3fffff << + AML_WDT_RESET_CNT_SHIFT; + break; + } + + /* + * Disable the watchdog. + */ + CSR_WRITE_4(sc, AML_WDT_CTRL_REG, + (CSR_READ_4(sc, AML_WDT_CTRL_REG) & ~(AML_WDT_CTRL_IRQ_EN | + AML_WDT_CTRL_EN))); + + /* + * Initialize the mutex prior to installing the interrupt handler + * in case of a spurious interrupt. + */ + AML_WDT_LOCK_INIT(sc); + + if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, + aml8726_wdt_intr, NULL, sc, &sc->ih_cookie)) { + device_printf(dev, "could not setup interrupt handler\n"); + bus_release_resources(dev, aml8726_wdt_spec, sc->res); + AML_WDT_LOCK_DESTROY(sc); + return (ENXIO); + } + + aml8726_wdt_sc = sc; + + EVENTHANDLER_REGISTER(watchdog_list, aml8726_wdt_watchdog, sc, 0); + + return (0); +} + +static int +aml8726_wdt_detach(device_t dev) +{ + + return (EBUSY); +} + +static device_method_t aml8726_wdt_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, aml8726_wdt_probe), + DEVMETHOD(device_attach, aml8726_wdt_attach), + DEVMETHOD(device_detach, aml8726_wdt_detach), + + DEVMETHOD_END +}; + +static driver_t aml8726_wdt_driver = { + "wdt", + aml8726_wdt_methods, + sizeof(struct aml8726_wdt_softc), +}; + +static devclass_t aml8726_wdt_devclass; + +EARLY_DRIVER_MODULE(wdt, simplebus, aml8726_wdt_driver, aml8726_wdt_devclass, + 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); + +void +cpu_reset() +{ + + /* Watchdog has not yet been initialized */ + if (aml8726_wdt_sc == NULL) + printf("Reset hardware has not yet been initialized.\n"); + else { + CSR_WRITE_4(aml8726_wdt_sc, AML_WDT_RESET_REG, 0); + CSR_WRITE_4(aml8726_wdt_sc, AML_WDT_CTRL_REG, + (AML_WDT_CTRL_CPU_WDRESET_MASK | AML_WDT_CTRL_EN | + (10 << AML_WDT_CTRL_TERMINAL_CNT_SHIFT))); + } + + while (1); +} Property changes on: head/sys/arm/amlogic/aml8726/aml8726_wdt.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/files.aml8726 =================================================================== --- head/sys/arm/amlogic/aml8726/files.aml8726 (nonexistent) +++ head/sys/arm/amlogic/aml8726/files.aml8726 (revision 280905) @@ -0,0 +1,35 @@ +#$FreeBSD$ + +kern/kern_clocksource.c standard + +arm/arm/bus_space_base.c standard +arm/arm/bus_space_generic.c standard +arm/arm/bus_space_asm_generic.S standard + +arm/arm/pl310.c standard +arm/amlogic/aml8726/aml8726_l2cache.c standard + +arm/amlogic/aml8726/aml8726_machdep.c standard +arm/amlogic/aml8726/aml8726_identsoc.c standard +arm/amlogic/aml8726/aml8726_ccm.c standard +arm/amlogic/aml8726/aml8726_clkmsr.c standard +arm/amlogic/aml8726/aml8726_pic.c standard +arm/amlogic/aml8726/aml8726_rtc.c standard +arm/amlogic/aml8726/aml8726_timer.c standard +arm/amlogic/aml8726/aml8726_wdt.c standard + +# serial console for debugging early boot code +# also define SOCDEV_PA and SOCDEV_VA in std.aml8726 +#arm/amlogic/aml8726/aml8726_uart_console.c standard + +arm/amlogic/aml8726/aml8726_fb.c optional vt +arm/amlogic/aml8726/aml8726_gpio.c optional gpio +arm/amlogic/aml8726/aml8726_i2c.c optional iicbus +arm/amlogic/aml8726/aml8726_mmc.c optional mmc gpio +arm/amlogic/aml8726/aml8726_sdxc-m8.c optional mmc gpio +arm/amlogic/aml8726/aml8726_pinctrl.c optional fdt_pinctrl +#arm/amlogic/aml8726/aml8726_rng.c optional random +arm/amlogic/aml8726/uart_dev_aml8726.c optional uart +arm/amlogic/aml8726/aml8726_usb_phy-m6.c optional dwcotg usb gpio + +dev/dwc/if_dwc.c optional dwc Property changes on: head/sys/arm/amlogic/aml8726/files.aml8726 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/files.smp =================================================================== --- head/sys/arm/amlogic/aml8726/files.smp (nonexistent) +++ head/sys/arm/amlogic/aml8726/files.smp (revision 280905) @@ -0,0 +1,4 @@ +#$FreeBSD$ + +arm/arm/gic.c standard +arm/amlogic/aml8726/aml8726_mp.c standard Property changes on: head/sys/arm/amlogic/aml8726/files.smp ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/std.aml8726 =================================================================== --- head/sys/arm/amlogic/aml8726/std.aml8726 (nonexistent) +++ head/sys/arm/amlogic/aml8726/std.aml8726 (revision 280905) @@ -0,0 +1,21 @@ +# $FreeBSD$ + +cpu CPU_CORTEXA +machine arm armv6 +makeoptions CONF_CFLAGS="-march=armv7a" + +device fdt_pinctrl + +files "../amlogic/aml8726/files.aml8726" + +options ARM_L2_PIPT + +# Set all global interrupts to be edge triggered, active high. +options GIC_DEFAULT_ICFGR_INIT=0xffffffff + +options IPI_IRQ_START=0 +options IPI_IRQ_END=15 + +#options EARLY_PRINTF +#options SOCDEV_PA=0xc8100000 +#options SOCDEV_VA=0xd8100000 Property changes on: head/sys/arm/amlogic/aml8726/std.aml8726 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/std.odroidc1 =================================================================== --- head/sys/arm/amlogic/aml8726/std.odroidc1 (nonexistent) +++ head/sys/arm/amlogic/aml8726/std.odroidc1 (revision 280905) @@ -0,0 +1,17 @@ +# $FreeBSD$ + +include "../amlogic/aml8726/std.aml8726" + +makeoptions FDT_DTS_FILE=odroidc1.dts + +options SMP # Enable multiple cores +files "../amlogic/aml8726/files.smp" + +# Physical memory starts at 0x00000000. We assume the kernel is loaded +# at 0x00100000 by u-boot (which doesn't support ubldr since it's missing +# CONFIG_API). The kernel must be supplied as a binary since u-boot is +# also missing CONFIG_CMD_ELF. +# +# +options KERNVIRTADDR=0xc0100000 # Used in ldscript.arm +makeoptions KERNVIRTADDR=0xc0100000 Property changes on: head/sys/arm/amlogic/aml8726/std.odroidc1 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/std.vsatv102-m6 =================================================================== --- head/sys/arm/amlogic/aml8726/std.vsatv102-m6 (nonexistent) +++ head/sys/arm/amlogic/aml8726/std.vsatv102-m6 (revision 280905) @@ -0,0 +1,17 @@ +# $FreeBSD$ + +include "../amlogic/aml8726/std.aml8726" + +makeoptions FDT_DTS_FILE=vsatv102-m6.dts + +options SMP # Enable multiple cores +files "../amlogic/aml8726/files.smp" + +# Physical memory starts at 0x80000000. We assume the kernel is loaded +# at 0x80100000 by u-boot (which doesn't support ubldr since it's missing +# CONFIG_API). The kernel must be supplied as a binary since u-boot is +# also missing CONFIG_CMD_ELF. +# +# +options KERNVIRTADDR=0xc0100000 # Used in ldscript.arm +makeoptions KERNVIRTADDR=0xc0100000 Property changes on: head/sys/arm/amlogic/aml8726/std.vsatv102-m6 ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/arm/amlogic/aml8726/uart_dev_aml8726.c =================================================================== --- head/sys/arm/amlogic/aml8726/uart_dev_aml8726.c (nonexistent) +++ head/sys/arm/amlogic/aml8726/uart_dev_aml8726.c (revision 280905) @@ -0,0 +1,709 @@ +/*- + * Copyright 2013-2015 John Wehle + * 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. + */ + +/* + * Amlogic aml8726 UART driver. + * + * The current implementation only targets features common to all + * uarts. For example ... though UART A as a 128 byte FIFO, the + * others only have a 64 byte FIFO. + * + * Also, it's assumed that register 5 (the new baud rate register + * present on the aml8726-m6) has not been activated. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "uart_if.h" + +#undef uart_getreg +#undef uart_setreg + +#define uart_getreg(bas, reg) \ + bus_space_read_4((bas)->bst, (bas)->bsh, reg) +#define uart_setreg(bas, reg, value) \ + bus_space_write_4((bas)->bst, (bas)->bsh, reg, value) + +#define SIGCHG(c, i, s, d) \ + do { \ + if (c) { \ + i |= (i & s) ? s : s | d; \ + } else { \ + i = (i & s) ? (i & ~s) | d : i; \ + } \ + } while (0) + +static int +aml8726_uart_divisor(int rclk, int baudrate) +{ + int actual_baud, divisor; + int error; + + if (baudrate == 0) + return (0); + + /* integer version of (rclk / baudrate + .5) */ + divisor = ((rclk << 1) + baudrate) / (baudrate << 1); + if (divisor == 0 || divisor >= 65536) + return (0); + actual_baud = rclk / divisor; + + /* 10 times error in percent: */ + error = (((actual_baud - baudrate) * 2000) / baudrate + 1) >> 1; + + /* 3.0% maximum error tolerance: */ + if (error < -30 || error > 30) + return (0); + + return (divisor); +} + +static int +aml8726_uart_param(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + uint32_t cr; + uint32_t mr; + int divisor; + + cr = uart_getreg(bas, AML_UART_CONTROL_REG); + + cr &= ~(AML_UART_CONTROL_DB_MASK | AML_UART_CONTROL_SB_MASK | + AML_UART_CONTROL_P_MASK); + + switch (databits) { + case 5: cr |= AML_UART_CONTROL_5_DB; break; + case 6: cr |= AML_UART_CONTROL_6_DB; break; + case 7: cr |= AML_UART_CONTROL_7_DB; break; + case 8: cr |= AML_UART_CONTROL_8_DB; break; + default: return (EINVAL); + } + + switch (stopbits) { + case 1: cr |= AML_UART_CONTROL_1_SB; break; + case 2: cr |= AML_UART_CONTROL_2_SB; break; + default: return (EINVAL); + } + + switch (parity) { + case UART_PARITY_EVEN: cr |= AML_UART_CONTROL_P_EVEN; + cr |= AML_UART_CONTROL_P_EN; + break; + + case UART_PARITY_ODD: cr |= AML_UART_CONTROL_P_ODD; + cr |= AML_UART_CONTROL_P_EN; + break; + + case UART_PARITY_NONE: break; + + default: return (EINVAL); + } + + /* Set baudrate. */ + if (baudrate > 0 && bas->rclk != 0) { + divisor = aml8726_uart_divisor(bas->rclk / 4, baudrate) - 1; + if (divisor > 0xffff) + return (EINVAL); + + cr &= ~AML_UART_CONTROL_BAUD_MASK; + cr |= (divisor & AML_UART_CONTROL_BAUD_MASK); + + divisor >>= AML_UART_CONTROL_BAUD_WIDTH; + + mr = uart_getreg(bas, AML_UART_MISC_REG); + mr &= ~(AML_UART_MISC_OLD_RX_BAUD | + AML_UART_MISC_BAUD_EXT_MASK); + mr |= ((divisor << AML_UART_MISC_BAUD_EXT_SHIFT) & + AML_UART_MISC_BAUD_EXT_MASK); + uart_setreg(bas, AML_UART_MISC_REG, mr); + } + + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); + + return (0); +} + +/* + * Low-level UART interface. + */ + +static int +aml8726_uart_probe(struct uart_bas *bas) +{ + + return (0); +} + +static void +aml8726_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + uint32_t cr; + uint32_t mr; + + aml8726_uart_param(bas, baudrate, databits, stopbits, parity); + + cr = uart_getreg(bas, AML_UART_CONTROL_REG); + /* Disable all interrupt sources. */ + cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN); + /* Reset the transmitter and receiver. */ + cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); + /* Enable the transmitter and receiver. */ + cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN); + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); + + /* Clear RX FIFO level for generating interrupts. */ + mr = uart_getreg(bas, AML_UART_MISC_REG); + mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK; + uart_setreg(bas, AML_UART_MISC_REG, mr); + uart_barrier(bas); + + /* Ensure the reset bits are clear. */ + cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); +} + +static void +aml8726_uart_term(struct uart_bas *bas) +{ +} + +static void +aml8726_uart_putc(struct uart_bas *bas, int c) +{ + + while ((uart_getreg(bas, AML_UART_STATUS_REG) & + AML_UART_STATUS_TX_FIFO_FULL) != 0) + cpu_spinwait(); + + uart_setreg(bas, AML_UART_WFIFO_REG, c); + uart_barrier(bas); +} + +static int +aml8726_uart_rxready(struct uart_bas *bas) +{ + + return ((uart_getreg(bas, AML_UART_STATUS_REG) & + AML_UART_STATUS_RX_FIFO_EMPTY) == 0 ? 1 : 0); +} + +static int +aml8726_uart_getc(struct uart_bas *bas, struct mtx *hwmtx) +{ + int c; + + uart_lock(hwmtx); + + while ((uart_getreg(bas, AML_UART_STATUS_REG) & + AML_UART_STATUS_RX_FIFO_EMPTY) != 0) { + uart_unlock(hwmtx); + DELAY(4); + uart_lock(hwmtx); + } + + c = uart_getreg(bas, AML_UART_RFIFO_REG) & 0xff; + + uart_unlock(hwmtx); + + return (c); +} + +struct uart_ops aml8726_uart_ops = { + .probe = aml8726_uart_probe, + .init = aml8726_uart_init, + .term = aml8726_uart_term, + .putc = aml8726_uart_putc, + .rxready = aml8726_uart_rxready, + .getc = aml8726_uart_getc, +}; + +static int +aml8726_uart_bus_probe(struct uart_softc *sc) +{ + int error; + + error = aml8726_uart_probe(&sc->sc_bas); + if (error) + return (error); + + sc->sc_rxfifosz = 64; + sc->sc_txfifosz = 64; + sc->sc_hwiflow = 1; + sc->sc_hwoflow = 1; + + device_set_desc(sc->sc_dev, "Amlogic aml8726 UART"); + + return (0); +} + +static int +aml8726_uart_bus_getsig(struct uart_softc *sc) +{ + uint32_t new, old, sig; + + /* + * Treat DSR, DCD, and CTS as always on. + */ + + do { + old = sc->sc_hwsig; + sig = old; + SIGCHG(1, sig, SER_DSR, SER_DDSR); + SIGCHG(1, sig, SER_DCD, SER_DDCD); + SIGCHG(1, sig, SER_CTS, SER_DCTS); + new = sig & ~SER_MASK_DELTA; + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + + return (sig); +} + +static int +aml8726_uart_bus_setsig(struct uart_softc *sc, int sig) +{ + uint32_t new, old; + + do { + old = sc->sc_hwsig; + new = old; + if (sig & SER_DDTR) { + SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); + } + if (sig & SER_DRTS) { + SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); + } + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + + return (0); +} + +static int +aml8726_uart_bus_attach(struct uart_softc *sc) +{ + struct uart_bas *bas; + uint32_t cr; + uint32_t mr; + + bas = &sc->sc_bas; + + if (bas->rclk == 0) { + device_printf(sc->sc_dev, "missing clock attribute in FDT\n"); + return (ENXIO); + } + + cr = uart_getreg(bas, AML_UART_CONTROL_REG); + /* Disable all interrupt sources. */ + cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN); + /* Ensure the reset bits are clear. */ + cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); + + /* + * Reset the transmitter and receiver only if not acting as a + * console, otherwise it means that: + * + * 1) aml8726_uart_init was already called which did the reset + * + * 2) there may be console bytes sitting in the transmit fifo + */ + if (sc->sc_sysdev != NULL && sc->sc_sysdev->type == UART_DEV_CONSOLE) + ; + else + cr |= (AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); + + /* Default to two wire mode. */ + cr |= AML_UART_CONTROL_TWO_WIRE_EN; + /* Enable the transmitter and receiver. */ + cr |= (AML_UART_CONTROL_TX_EN | AML_UART_CONTROL_RX_EN); + /* Reset error bits. */ + cr |= AML_UART_CONTROL_CLR_ERR; + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); + + /* Set FIFO levels for generating interrupts. */ + mr = uart_getreg(bas, AML_UART_MISC_REG); + mr &= ~AML_UART_MISC_XMIT_IRQ_CNT_MASK; + mr |= (0 << AML_UART_MISC_XMIT_IRQ_CNT_SHIFT); + mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK; + mr |= (1 << AML_UART_MISC_RECV_IRQ_CNT_SHIFT); + uart_setreg(bas, AML_UART_MISC_REG, mr); + uart_barrier(bas); + + aml8726_uart_bus_getsig(sc); + + /* Ensure the reset bits are clear. */ + cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); + cr &= ~AML_UART_CONTROL_CLR_ERR; + /* Enable the receive interrupt. */ + cr |= AML_UART_CONTROL_RX_INT_EN; + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); + + return (0); +} + +static int +aml8726_uart_bus_detach(struct uart_softc *sc) +{ + struct uart_bas *bas; + uint32_t cr; + uint32_t mr; + + bas = &sc->sc_bas; + + /* Disable all interrupt sources. */ + cr = uart_getreg(bas, AML_UART_CONTROL_REG); + cr &= ~(AML_UART_CONTROL_TX_INT_EN | AML_UART_CONTROL_RX_INT_EN); + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); + + /* Clear RX FIFO level for generating interrupts. */ + mr = uart_getreg(bas, AML_UART_MISC_REG); + mr &= ~AML_UART_MISC_RECV_IRQ_CNT_MASK; + uart_setreg(bas, AML_UART_MISC_REG, mr); + uart_barrier(bas); + + return (0); +} + +static int +aml8726_uart_bus_flush(struct uart_softc *sc, int what) +{ + struct uart_bas *bas; + uint32_t cr; + + bas = &sc->sc_bas; + uart_lock(sc->sc_hwmtx); + + cr = uart_getreg(bas, AML_UART_CONTROL_REG); + if (what & UART_FLUSH_TRANSMITTER) + cr |= AML_UART_CONTROL_TX_RST; + if (what & UART_FLUSH_RECEIVER) + cr |= AML_UART_CONTROL_RX_RST; + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); + + /* Ensure the reset bits are clear. */ + cr &= ~(AML_UART_CONTROL_TX_RST | AML_UART_CONTROL_RX_RST); + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); + + uart_unlock(sc->sc_hwmtx); + + return (0); +} + +static int +aml8726_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) +{ + struct uart_bas *bas; + int baudrate, divisor, error; + uint32_t cr, mr; + + bas = &sc->sc_bas; + uart_lock(sc->sc_hwmtx); + + error = 0; + switch (request) { + case UART_IOCTL_BAUD: + cr = uart_getreg(bas, AML_UART_CONTROL_REG); + cr &= AML_UART_CONTROL_BAUD_MASK; + + mr = uart_getreg(bas, AML_UART_MISC_REG); + mr &= AML_UART_MISC_BAUD_EXT_MASK; + + divisor = ((mr >> AML_UART_MISC_BAUD_EXT_SHIFT) << + AML_UART_CONTROL_BAUD_WIDTH) | cr; + + baudrate = bas->rclk / 4 / (divisor + 1); + if (baudrate > 0) + *(int*)data = baudrate; + else + error = ENXIO; + break; + + case UART_IOCTL_IFLOW: + case UART_IOCTL_OFLOW: + cr = uart_getreg(bas, AML_UART_CONTROL_REG); + if (data) + cr &= ~AML_UART_CONTROL_TWO_WIRE_EN; + else + cr |= AML_UART_CONTROL_TWO_WIRE_EN; + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + break; + + default: + error = EINVAL; + break; + } + + uart_unlock(sc->sc_hwmtx); + + return (error); +} + +static int +aml8726_uart_bus_ipend(struct uart_softc *sc) +{ + struct uart_bas *bas; + int ipend; + uint32_t sr; + uint32_t cr; + + bas = &sc->sc_bas; + uart_lock(sc->sc_hwmtx); + + ipend = 0; + sr = uart_getreg(bas, AML_UART_STATUS_REG); + cr = uart_getreg(bas, AML_UART_CONTROL_REG); + + if ((sr & AML_UART_STATUS_RX_FIFO_OVERFLOW) != 0) + ipend |= SER_INT_OVERRUN; + + if ((sr & AML_UART_STATUS_TX_FIFO_EMPTY) != 0 && + (cr & AML_UART_CONTROL_TX_INT_EN) != 0) { + ipend |= SER_INT_TXIDLE; + + cr &= ~AML_UART_CONTROL_TX_INT_EN; + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); + } + + if ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) + ipend |= SER_INT_RXREADY; + + uart_unlock(sc->sc_hwmtx); + + return (ipend); +} + +static int +aml8726_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, + int stopbits, int parity) +{ + struct uart_bas *bas; + int error; + + bas = &sc->sc_bas; + uart_lock(sc->sc_hwmtx); + + error = aml8726_uart_param(bas, baudrate, databits, stopbits, parity); + + uart_unlock(sc->sc_hwmtx); + + return (error); +} + +static int +aml8726_uart_bus_receive(struct uart_softc *sc) +{ + struct uart_bas *bas; + int xc; + uint32_t sr; + + bas = &sc->sc_bas; + uart_lock(sc->sc_hwmtx); + + sr = uart_getreg(bas, AML_UART_STATUS_REG); + while ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) { + if (uart_rx_full(sc)) { + sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN; + break; + } + xc = uart_getreg(bas, AML_UART_RFIFO_REG) & 0xff; + if (sr & AML_UART_STATUS_FRAME_ERR) + xc |= UART_STAT_FRAMERR; + if (sr & AML_UART_STATUS_PARITY_ERR) + xc |= UART_STAT_PARERR; + uart_rx_put(sc, xc); + sr = uart_getreg(bas, AML_UART_STATUS_REG); + } + /* Discard everything left in the RX FIFO. */ + while ((sr & AML_UART_STATUS_RX_FIFO_EMPTY) == 0) { + (void)uart_getreg(bas, AML_UART_RFIFO_REG); + sr = uart_getreg(bas, AML_UART_STATUS_REG); + } + /* Reset error bits */ + if ((sr & (AML_UART_STATUS_FRAME_ERR | AML_UART_STATUS_PARITY_ERR)) != 0) { + uart_setreg(bas, AML_UART_CONTROL_REG, + (uart_getreg(bas, AML_UART_CONTROL_REG) | + AML_UART_CONTROL_CLR_ERR)); + uart_barrier(bas); + uart_setreg(bas, AML_UART_CONTROL_REG, + (uart_getreg(bas, AML_UART_CONTROL_REG) & + ~AML_UART_CONTROL_CLR_ERR)); + uart_barrier(bas); + } + + uart_unlock(sc->sc_hwmtx); + + return (0); +} + +static int +aml8726_uart_bus_transmit(struct uart_softc *sc) +{ + struct uart_bas *bas; + int i; + uint32_t cr; + + bas = &sc->sc_bas; + uart_lock(sc->sc_hwmtx); + + /* + * Wait for sufficient space since aml8726_uart_putc + * may have been called after SER_INT_TXIDLE occurred. + */ + while ((uart_getreg(bas, AML_UART_STATUS_REG) & + AML_UART_STATUS_TX_FIFO_EMPTY) == 0) + cpu_spinwait(); + + for (i = 0; i < sc->sc_txdatasz; i++) { + uart_setreg(bas, AML_UART_WFIFO_REG, sc->sc_txbuf[i]); + uart_barrier(bas); + } + + sc->sc_txbusy = 1; + + cr = uart_getreg(bas, AML_UART_CONTROL_REG); + cr |= AML_UART_CONTROL_TX_INT_EN; + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); + + uart_unlock(sc->sc_hwmtx); + + return (0); +} + +static void +aml8726_uart_bus_grab(struct uart_softc *sc) +{ + struct uart_bas *bas; + uint32_t cr; + + /* + * Disable the receive interrupt to avoid a race between + * aml8726_uart_getc and aml8726_uart_bus_receive which + * can trigger: + * + * panic: bad stray interrupt + * + * due to the RX FIFO receiving a character causing an + * interrupt which gets serviced after aml8726_uart_getc + * has been called (meaning the RX FIFO is now empty). + */ + + bas = &sc->sc_bas; + uart_lock(sc->sc_hwmtx); + + cr = uart_getreg(bas, AML_UART_CONTROL_REG); + cr &= ~AML_UART_CONTROL_RX_INT_EN; + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); + + uart_unlock(sc->sc_hwmtx); +} + +static void +aml8726_uart_bus_ungrab(struct uart_softc *sc) +{ + struct uart_bas *bas; + uint32_t cr; + uint32_t mr; + + /* + * The RX FIFO level being set indicates that the device + * is currently attached meaning the receive interrupt + * should be enabled. + */ + + bas = &sc->sc_bas; + uart_lock(sc->sc_hwmtx); + + mr = uart_getreg(bas, AML_UART_MISC_REG); + mr &= AML_UART_MISC_RECV_IRQ_CNT_MASK; + + if (mr != 0) { + cr = uart_getreg(bas, AML_UART_CONTROL_REG); + cr |= AML_UART_CONTROL_RX_INT_EN; + uart_setreg(bas, AML_UART_CONTROL_REG, cr); + uart_barrier(bas); + } + + uart_unlock(sc->sc_hwmtx); +} + +static kobj_method_t aml8726_uart_methods[] = { + KOBJMETHOD(uart_probe, aml8726_uart_bus_probe), + KOBJMETHOD(uart_attach, aml8726_uart_bus_attach), + KOBJMETHOD(uart_detach, aml8726_uart_bus_detach), + KOBJMETHOD(uart_flush, aml8726_uart_bus_flush), + KOBJMETHOD(uart_getsig, aml8726_uart_bus_getsig), + KOBJMETHOD(uart_setsig, aml8726_uart_bus_setsig), + KOBJMETHOD(uart_ioctl, aml8726_uart_bus_ioctl), + KOBJMETHOD(uart_ipend, aml8726_uart_bus_ipend), + KOBJMETHOD(uart_param, aml8726_uart_bus_param), + KOBJMETHOD(uart_receive, aml8726_uart_bus_receive), + KOBJMETHOD(uart_transmit, aml8726_uart_bus_transmit), + KOBJMETHOD(uart_grab, aml8726_uart_bus_grab), + KOBJMETHOD(uart_ungrab, aml8726_uart_bus_ungrab), + { 0, 0 } +}; + +struct uart_class uart_aml8726_class = { + "uart", + aml8726_uart_methods, + sizeof(struct uart_softc), + .uc_ops = &aml8726_uart_ops, + .uc_range = 24, + .uc_rclk = 0 +}; + +static struct ofw_compat_data compat_data[] = { + { "amlogic,aml8726-uart", (uintptr_t)&uart_aml8726_class }, + { NULL, (uintptr_t)NULL } +}; +UART_FDT_CLASS_AND_DEVICE(compat_data); Property changes on: head/sys/arm/amlogic/aml8726/uart_dev_aml8726.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/arm/arm/gic.c =================================================================== --- head/sys/arm/arm/gic.c (revision 280904) +++ head/sys/arm/arm/gic.c (revision 280905) @@ -1,538 +1,542 @@ /*- * Copyright (c) 2011 The FreeBSD Foundation * All rights reserved. * * Developed by Damjan Marion * * Based on OMAP4 GIC code by Ben Gray * * 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. * 3. The name of the company nor the name of the author may be used to * endorse or promote products derived from this software without specific * prior written permission. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* We are using GICv2 register naming */ /* Distributor Registers */ #define GICD_CTLR 0x000 /* v1 ICDDCR */ #define GICD_TYPER 0x004 /* v1 ICDICTR */ #define GICD_IIDR 0x008 /* v1 ICDIIDR */ #define GICD_IGROUPR(n) (0x0080 + ((n) * 4)) /* v1 ICDISER */ #define GICD_ISENABLER(n) (0x0100 + ((n) * 4)) /* v1 ICDISER */ #define GICD_ICENABLER(n) (0x0180 + ((n) * 4)) /* v1 ICDICER */ #define GICD_ISPENDR(n) (0x0200 + ((n) * 4)) /* v1 ICDISPR */ #define GICD_ICPENDR(n) (0x0280 + ((n) * 4)) /* v1 ICDICPR */ #define GICD_ICACTIVER(n) (0x0380 + ((n) * 4)) /* v1 ICDABR */ #define GICD_IPRIORITYR(n) (0x0400 + ((n) * 4)) /* v1 ICDIPR */ #define GICD_ITARGETSR(n) (0x0800 + ((n) * 4)) /* v1 ICDIPTR */ #define GICD_ICFGR(n) (0x0C00 + ((n) * 4)) /* v1 ICDICFR */ #define GICD_SGIR(n) (0x0F00 + ((n) * 4)) /* v1 ICDSGIR */ /* CPU Registers */ #define GICC_CTLR 0x0000 /* v1 ICCICR */ #define GICC_PMR 0x0004 /* v1 ICCPMR */ #define GICC_BPR 0x0008 /* v1 ICCBPR */ #define GICC_IAR 0x000C /* v1 ICCIAR */ #define GICC_EOIR 0x0010 /* v1 ICCEOIR */ #define GICC_RPR 0x0014 /* v1 ICCRPR */ #define GICC_HPPIR 0x0018 /* v1 ICCHPIR */ #define GICC_ABPR 0x001C /* v1 ICCABPR */ #define GICC_IIDR 0x00FC /* v1 ICCIIDR*/ #define GIC_FIRST_IPI 0 /* Irqs 0-15 are SGIs/IPIs. */ #define GIC_LAST_IPI 15 #define GIC_FIRST_PPI 16 /* Irqs 16-31 are private (per */ #define GIC_LAST_PPI 31 /* core) peripheral interrupts. */ #define GIC_FIRST_SPI 32 /* Irqs 32+ are shared peripherals. */ /* First bit is a polarity bit (0 - low, 1 - high) */ #define GICD_ICFGR_POL_LOW (0 << 0) #define GICD_ICFGR_POL_HIGH (1 << 0) #define GICD_ICFGR_POL_MASK 0x1 /* Second bit is a trigger bit (0 - level, 1 - edge) */ #define GICD_ICFGR_TRIG_LVL (0 << 1) #define GICD_ICFGR_TRIG_EDGE (1 << 1) #define GICD_ICFGR_TRIG_MASK 0x2 +#ifndef GIC_DEFAULT_ICFGR_INIT +#define GIC_DEFAULT_ICFGR_INIT 0x00000000 +#endif + struct arm_gic_softc { device_t gic_dev; struct resource * gic_res[3]; bus_space_tag_t gic_c_bst; bus_space_tag_t gic_d_bst; bus_space_handle_t gic_c_bsh; bus_space_handle_t gic_d_bsh; uint8_t ver; struct mtx mutex; uint32_t nirqs; }; static struct resource_spec arm_gic_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */ { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */ { -1, 0 } }; static struct arm_gic_softc *arm_gic_sc = NULL; #define gic_c_read_4(_sc, _reg) \ bus_space_read_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg)) #define gic_c_write_4(_sc, _reg, _val) \ bus_space_write_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg), (_val)) #define gic_d_read_4(_sc, _reg) \ bus_space_read_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg)) #define gic_d_write_4(_sc, _reg, _val) \ bus_space_write_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val)) static int gic_config_irq(int irq, enum intr_trigger trig, enum intr_polarity pol); static void gic_post_filter(void *); static struct ofw_compat_data compat_data[] = { {"arm,gic", true}, /* Non-standard, used in FreeBSD dts. */ {"arm,gic-400", true}, {"arm,cortex-a15-gic", true}, {"arm,cortex-a9-gic", true}, {"arm,cortex-a7-gic", true}, {"arm,arm11mp-gic", true}, {"brcm,brahma-b15-gic", true}, {NULL, false} }; static int arm_gic_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "ARM Generic Interrupt Controller"); return (BUS_PROBE_DEFAULT); } static void arm_gic_init_secondary(device_t dev) { struct arm_gic_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->nirqs; i += 4) gic_d_write_4(sc, GICD_IPRIORITYR(i >> 2), 0); /* Set all the interrupts to be in Group 0 (secure) */ for (i = 0; i < sc->nirqs; i += 32) { gic_d_write_4(sc, GICD_IGROUPR(i >> 5), 0); } /* Enable CPU interface */ gic_c_write_4(sc, GICC_CTLR, 1); /* Set priority mask register. */ gic_c_write_4(sc, GICC_PMR, 0xff); /* Enable interrupt distribution */ gic_d_write_4(sc, GICD_CTLR, 0x01); /* * Activate the timer interrupts: virtual, secure, and non-secure. */ gic_d_write_4(sc, GICD_ISENABLER(27 >> 5), (1UL << (27 & 0x1F))); gic_d_write_4(sc, GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F))); gic_d_write_4(sc, GICD_ISENABLER(30 >> 5), (1UL << (30 & 0x1F))); } int gic_decode_fdt(uint32_t iparent, uint32_t *intr, int *interrupt, int *trig, int *pol) { static u_int num_intr_cells; if (num_intr_cells == 0) { if (OF_searchencprop(OF_node_from_xref(iparent), "#interrupt-cells", &num_intr_cells, sizeof(num_intr_cells)) == -1) { num_intr_cells = 1; } } if (num_intr_cells == 1) { *interrupt = fdt32_to_cpu(intr[0]); *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; } else { if (fdt32_to_cpu(intr[0]) == 0) *interrupt = fdt32_to_cpu(intr[1]) + GIC_FIRST_SPI; else *interrupt = fdt32_to_cpu(intr[1]) + GIC_FIRST_PPI; /* * In intr[2], bits[3:0] are trigger type and level flags. * 1 = low-to-high edge triggered * 2 = high-to-low edge triggered * 4 = active high level-sensitive * 8 = active low level-sensitive * The hardware only supports active-high-level or rising-edge. */ if (fdt32_to_cpu(intr[2]) & 0x0a) { printf("unsupported trigger/polarity configuration " "0x%2x\n", fdt32_to_cpu(intr[2]) & 0x0f); return (ENOTSUP); } *pol = INTR_POLARITY_CONFORM; if (fdt32_to_cpu(intr[2]) & 0x01) *trig = INTR_TRIGGER_EDGE; else *trig = INTR_TRIGGER_LEVEL; } return (0); } static int arm_gic_attach(device_t dev) { struct arm_gic_softc *sc; int i; uint32_t icciidr; if (arm_gic_sc) return (ENXIO); sc = device_get_softc(dev); if (bus_alloc_resources(dev, arm_gic_spec, sc->gic_res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->gic_dev = dev; arm_gic_sc = sc; /* Initialize mutex */ mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN); /* Distributor Interface */ sc->gic_d_bst = rman_get_bustag(sc->gic_res[0]); sc->gic_d_bsh = rman_get_bushandle(sc->gic_res[0]); /* CPU Interface */ sc->gic_c_bst = rman_get_bustag(sc->gic_res[1]); sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]); /* Disable interrupt forwarding to the CPU interface */ gic_d_write_4(sc, GICD_CTLR, 0x00); /* Get the number of interrupts */ sc->nirqs = gic_d_read_4(sc, GICD_TYPER); sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1); /* Set up function pointers */ arm_post_filter = gic_post_filter; arm_config_irq = gic_config_irq; icciidr = gic_c_read_4(sc, GICC_IIDR); device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x irqs %u\n", icciidr>>20, (icciidr>>16) & 0xF, (icciidr>>12) & 0xf, (icciidr & 0xfff), sc->nirqs); /* Set all global interrupts to be level triggered, active low. */ for (i = 32; i < sc->nirqs; i += 16) { - gic_d_write_4(sc, GICD_ICFGR(i >> 4), 0x00000000); + gic_d_write_4(sc, GICD_ICFGR(i >> 4), GIC_DEFAULT_ICFGR_INIT); } /* Disable all interrupts. */ for (i = 32; i < sc->nirqs; i += 32) { gic_d_write_4(sc, GICD_ICENABLER(i >> 5), 0xFFFFFFFF); } for (i = 0; i < sc->nirqs; i += 4) { gic_d_write_4(sc, GICD_IPRIORITYR(i >> 2), 0); gic_d_write_4(sc, GICD_ITARGETSR(i >> 2), 1 << 0 | 1 << 8 | 1 << 16 | 1 << 24); } /* Set all the interrupts to be in Group 0 (secure) */ for (i = 0; i < sc->nirqs; i += 32) { gic_d_write_4(sc, GICD_IGROUPR(i >> 5), 0); } /* Enable CPU interface */ gic_c_write_4(sc, GICC_CTLR, 1); /* Set priority mask register. */ gic_c_write_4(sc, GICC_PMR, 0xff); /* Enable interrupt distribution */ gic_d_write_4(sc, GICD_CTLR, 0x01); return (0); } static int arm_gic_next_irq(struct arm_gic_softc *sc, int last_irq) { uint32_t active_irq; active_irq = gic_c_read_4(sc, GICC_IAR); /* * Immediatly EOIR the SGIs, because doing so requires the other * bits (ie CPU number), not just the IRQ number, and we do not * have this information later. */ if ((active_irq & 0x3ff) <= GIC_LAST_IPI) gic_c_write_4(sc, GICC_EOIR, active_irq); active_irq &= 0x3FF; if (active_irq == 0x3FF) { if (last_irq == -1) printf("Spurious interrupt detected\n"); return -1; } return active_irq; } static int arm_gic_config(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { struct arm_gic_softc *sc = device_get_softc(dev); uint32_t reg; uint32_t mask; /* Function is public-accessible, so validate input arguments */ if ((irq < 0) || (irq >= sc->nirqs)) goto invalid_args; if ((trig != INTR_TRIGGER_EDGE) && (trig != INTR_TRIGGER_LEVEL) && (trig != INTR_TRIGGER_CONFORM)) goto invalid_args; if ((pol != INTR_POLARITY_HIGH) && (pol != INTR_POLARITY_LOW) && (pol != INTR_POLARITY_CONFORM)) goto invalid_args; mtx_lock_spin(&sc->mutex); reg = gic_d_read_4(sc, GICD_ICFGR(irq >> 4)); mask = (reg >> 2*(irq % 16)) & 0x3; if (pol == INTR_POLARITY_LOW) { mask &= ~GICD_ICFGR_POL_MASK; mask |= GICD_ICFGR_POL_LOW; } else if (pol == INTR_POLARITY_HIGH) { mask &= ~GICD_ICFGR_POL_MASK; mask |= GICD_ICFGR_POL_HIGH; } if (trig == INTR_TRIGGER_LEVEL) { mask &= ~GICD_ICFGR_TRIG_MASK; mask |= GICD_ICFGR_TRIG_LVL; } else if (trig == INTR_TRIGGER_EDGE) { mask &= ~GICD_ICFGR_TRIG_MASK; mask |= GICD_ICFGR_TRIG_EDGE; } /* Set mask */ reg = reg & ~(0x3 << 2*(irq % 16)); reg = reg | (mask << 2*(irq % 16)); gic_d_write_4(sc, GICD_ICFGR(irq >> 4), reg); mtx_unlock_spin(&sc->mutex); return (0); invalid_args: device_printf(dev, "gic_config_irg, invalid parameters\n"); return (EINVAL); } static void arm_gic_mask(device_t dev, int irq) { struct arm_gic_softc *sc = device_get_softc(dev); gic_d_write_4(sc, GICD_ICENABLER(irq >> 5), (1UL << (irq & 0x1F))); gic_c_write_4(sc, GICC_EOIR, irq); } static void arm_gic_unmask(device_t dev, int irq) { struct arm_gic_softc *sc = device_get_softc(dev); if (irq > GIC_LAST_IPI) arm_irq_memory_barrier(irq); gic_d_write_4(sc, GICD_ISENABLER(irq >> 5), (1UL << (irq & 0x1F))); } #ifdef SMP static void arm_gic_ipi_send(device_t dev, cpuset_t cpus, u_int ipi) { struct arm_gic_softc *sc = device_get_softc(dev); uint32_t val = 0, i; for (i = 0; i < MAXCPU; i++) if (CPU_ISSET(i, &cpus)) val |= 1 << (16 + i); gic_d_write_4(sc, GICD_SGIR(0), val | ipi); } static int arm_gic_ipi_read(device_t dev, int i) { if (i != -1) { /* * The intr code will automagically give the frame pointer * if the interrupt argument is 0. */ if ((unsigned int)i > 16) return (0); return (i); } return (0x3ff); } static void arm_gic_ipi_clear(device_t dev, int ipi) { /* no-op */ } #endif static void gic_post_filter(void *arg) { struct arm_gic_softc *sc = arm_gic_sc; uintptr_t irq = (uintptr_t) arg; if (irq > GIC_LAST_IPI) arm_irq_memory_barrier(irq); gic_c_write_4(sc, GICC_EOIR, irq); } static int gic_config_irq(int irq, enum intr_trigger trig, enum intr_polarity pol) { return (arm_gic_config(arm_gic_sc->gic_dev, irq, trig, pol)); } void arm_mask_irq(uintptr_t nb) { arm_gic_mask(arm_gic_sc->gic_dev, nb); } void arm_unmask_irq(uintptr_t nb) { arm_gic_unmask(arm_gic_sc->gic_dev, nb); } int arm_get_next_irq(int last_irq) { return (arm_gic_next_irq(arm_gic_sc, last_irq)); } void arm_init_secondary_ic(void) { arm_gic_init_secondary(arm_gic_sc->gic_dev); } #ifdef SMP void pic_ipi_send(cpuset_t cpus, u_int ipi) { arm_gic_ipi_send(arm_gic_sc->gic_dev, cpus, ipi); } int pic_ipi_read(int i) { return (arm_gic_ipi_read(arm_gic_sc->gic_dev, i)); } void pic_ipi_clear(int ipi) { arm_gic_ipi_clear(arm_gic_sc->gic_dev, ipi); } #endif static device_method_t arm_gic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, arm_gic_probe), DEVMETHOD(device_attach, arm_gic_attach), { 0, 0 } }; static driver_t arm_gic_driver = { "gic", arm_gic_methods, sizeof(struct arm_gic_softc), }; static devclass_t arm_gic_devclass; EARLY_DRIVER_MODULE(gic, simplebus, arm_gic_driver, arm_gic_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); EARLY_DRIVER_MODULE(gic, ofwbus, arm_gic_driver, arm_gic_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); Index: head/sys/conf/options.arm =================================================================== --- head/sys/conf/options.arm (revision 280904) +++ head/sys/conf/options.arm (revision 280905) @@ -1,65 +1,66 @@ #$FreeBSD$ ARMV6 opt_global.h ARM_CACHE_LOCK_ENABLE opt_global.h ARM_KERN_DIRECTMAP opt_vm.h ARM_L2_PIPT opt_global.h ARM_MANY_BOARD opt_global.h ARM_NEW_PMAP opt_global.h NKPT2PG opt_pmap.h ARM_WANT_TP_ADDRESS opt_global.h COUNTS_PER_SEC opt_timer.h CPU_ARM9 opt_global.h CPU_ARM9E opt_global.h CPU_ARM1176 opt_global.h CPU_CORTEXA opt_global.h CPU_KRAIT opt_global.h CPU_FA526 opt_global.h CPU_MV_PJ4B opt_global.h CPU_XSCALE_80219 opt_global.h CPU_XSCALE_80321 opt_global.h CPU_XSCALE_81342 opt_global.h CPU_XSCALE_IXP425 opt_global.h CPU_XSCALE_IXP435 opt_global.h CPU_XSCALE_PXA2X0 opt_global.h FLASHADDR opt_global.h +GIC_DEFAULT_ICFGR_INIT opt_global.h IPI_IRQ_START opt_smp.h IPI_IRQ_END opt_smp.h FREEBSD_BOOT_LOADER opt_global.h IXP4XX_FLASH_SIZE opt_global.h KERNBASE opt_global.h KERNPHYSADDR opt_global.h KERNVIRTADDR opt_global.h LINUX_BOOT_ABI opt_global.h LOADERRAMADDR opt_global.h PHYSADDR opt_global.h PLATFORM opt_global.h SOCDEV_PA opt_global.h SOCDEV_VA opt_global.h PV_STATS opt_pmap.h QEMU_WORKAROUNDS opt_global.h SOC_BCM2835 opt_global.h SOC_BCM2836 opt_global.h SOC_MV_ARMADAXP opt_global.h SOC_MV_DISCOVERY opt_global.h SOC_MV_DOVE opt_global.h SOC_MV_FREY opt_global.h SOC_MV_KIRKWOOD opt_global.h SOC_MV_LOKIPLUS opt_global.h SOC_MV_ORION opt_global.h SOC_OMAP3 opt_global.h SOC_OMAP4 opt_global.h SOC_TI_AM335X opt_global.h SOC_TEGRA2 opt_global.h XSCALE_CACHE_READ_WRITE_ALLOCATE opt_global.h XSACLE_DISABLE_CCNT opt_timer.h VERBOSE_INIT_ARM opt_global.h VM_MAXUSER_ADDRESS opt_global.h AT91_ATE_USE_RMII opt_at91.h AT91_MCI_ALLOW_OVERCLOCK opt_at91.h AT91_MCI_HAS_4WIRE opt_at91.h AT91_MCI_SLOT_B opt_at91.h GFB_DEBUG opt_gfb.h GFB_NO_FONT_LOADING opt_gfb.h GFB_NO_MODE_CHANGE opt_gfb.h AT91C_MAIN_CLOCK opt_at91.h VFP opt_global.h Index: head/sys/dev/mmc/mmc.c =================================================================== --- head/sys/dev/mmc/mmc.c (revision 280904) +++ head/sys/dev/mmc/mmc.c (revision 280905) @@ -1,1808 +1,1810 @@ /*- * Copyright (c) 2006 Bernd Walter. All rights reserved. * Copyright (c) 2006 M. Warner Losh. 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 ``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 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. * * Portions of this software may have been developed with reference to * the SD Simplified Specification. The following disclaimer may apply: * * The following conditions apply to the release of the simplified * specification ("Simplified Specification") by the SD Card Association and * the SD Group. The Simplified Specification is a subset of the complete SD * Specification which is owned by the SD Card Association and the SD * Group. This Simplified Specification is provided on a non-confidential * basis subject to the disclaimers below. Any implementation of the * Simplified Specification may require a license from the SD Card * Association, SD Group, SD-3C LLC or other third parties. * * Disclaimers: * * The information contained in the Simplified Specification is presented only * as a standard specification for SD Cards and SD Host/Ancillary products and * is provided "AS-IS" without any representations or warranties of any * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD * Card Association for any damages, any infringements of patents or other * right of the SD Group, SD-3C LLC, the SD Card Association or any third * parties, which may result from its use. No license is granted by * implication, estoppel or otherwise under any patent or other rights of the * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing * herein shall be construed as an obligation by the SD Group, the SD-3C LLC * or the SD Card Association to disclose or distribute any technical * information, know-how or other confidential information to any third party. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmcbr_if.h" #include "mmcbus_if.h" struct mmc_softc { device_t dev; struct mtx sc_mtx; struct intr_config_hook config_intrhook; device_t owner; uint32_t last_rca; int squelched; /* suppress reporting of (expected) errors */ int log_count; struct timeval log_time; }; #define LOG_PPS 5 /* Log no more than 5 errors per second. */ /* * Per-card data */ struct mmc_ivars { uint32_t raw_cid[4]; /* Raw bits of the CID */ uint32_t raw_csd[4]; /* Raw bits of the CSD */ uint32_t raw_scr[2]; /* Raw bits of the SCR */ uint8_t raw_ext_csd[512]; /* Raw bits of the EXT_CSD */ uint32_t raw_sd_status[16]; /* Raw bits of the SD_STATUS */ uint16_t rca; enum mmc_card_mode mode; struct mmc_cid cid; /* cid decoded */ struct mmc_csd csd; /* csd decoded */ struct mmc_scr scr; /* scr decoded */ struct mmc_sd_status sd_status; /* SD_STATUS decoded */ u_char read_only; /* True when the device is read-only */ u_char bus_width; /* Bus width to use */ u_char timing; /* Bus timing support */ u_char high_cap; /* High Capacity card (block addressed) */ uint32_t sec_count; /* Card capacity in 512byte blocks */ uint32_t tran_speed; /* Max speed in normal mode */ uint32_t hs_tran_speed; /* Max speed in high speed mode */ uint32_t erase_sector; /* Card native erase sector size */ char card_id_string[64];/* Formatted CID info (serial, MFG, etc) */ char card_sn_string[16];/* Formatted serial # for disk->d_ident */ }; #define CMD_RETRIES 3 #define CARD_ID_FREQUENCY 400000 /* Spec requires 400kHz max during ID phase. */ static SYSCTL_NODE(_hw, OID_AUTO, mmc, CTLFLAG_RD, NULL, "mmc driver"); static int mmc_debug; SYSCTL_INT(_hw_mmc, OID_AUTO, debug, CTLFLAG_RW, &mmc_debug, 0, "Debug level"); /* bus entry points */ static int mmc_acquire_bus(device_t busdev, device_t dev); static int mmc_attach(device_t dev); static int mmc_child_location_str(device_t dev, device_t child, char *buf, size_t buflen); static int mmc_detach(device_t dev); static int mmc_probe(device_t dev); static int mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result); static int mmc_release_bus(device_t busdev, device_t dev); static int mmc_resume(device_t dev); static int mmc_suspend(device_t dev); static int mmc_wait_for_request(device_t brdev, device_t reqdev, struct mmc_request *req); static int mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value); #define MMC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define MMC_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ "mmc", MTX_DEF) #define MMC_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define MMC_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define MMC_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); static int mmc_all_send_cid(struct mmc_softc *sc, uint32_t *rawcid); static void mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr); static void mmc_app_decode_sd_status(uint32_t *raw_sd_status, struct mmc_sd_status *sd_status); static int mmc_app_sd_status(struct mmc_softc *sc, uint16_t rca, uint32_t *rawsdstatus); static int mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, uint32_t *rawscr); static int mmc_calculate_clock(struct mmc_softc *sc); static void mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid); static void mmc_decode_cid_sd(uint32_t *raw_cid, struct mmc_cid *cid); static void mmc_decode_csd_mmc(uint32_t *raw_csd, struct mmc_csd *csd); static void mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd); static void mmc_delayed_attach(void *xsc); static int mmc_delete_cards(struct mmc_softc *sc); static void mmc_discover_cards(struct mmc_softc *sc); static void mmc_format_card_id_string(struct mmc_ivars *ivar); static void mmc_go_discovery(struct mmc_softc *sc); static uint32_t mmc_get_bits(uint32_t *bits, int bit_len, int start, int size); static int mmc_highest_voltage(uint32_t ocr); static void mmc_idle_cards(struct mmc_softc *sc); static void mmc_ms_delay(int ms); static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard); static void mmc_power_down(struct mmc_softc *sc); static void mmc_power_up(struct mmc_softc *sc); static void mmc_rescan_cards(struct mmc_softc *sc); static void mmc_scan(struct mmc_softc *sc); static int mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, uint8_t value, uint8_t *res); static int mmc_select_card(struct mmc_softc *sc, uint16_t rca); static uint32_t mmc_select_vdd(struct mmc_softc *sc, uint32_t ocr); static int mmc_send_app_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr); static int mmc_send_csd(struct mmc_softc *sc, uint16_t rca, uint32_t *rawcsd); static int mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd); static int mmc_send_if_cond(struct mmc_softc *sc, uint8_t vhs); static int mmc_send_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr); static int mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp); static int mmc_send_status(struct mmc_softc *sc, uint16_t rca, uint32_t *status); static int mmc_set_blocklen(struct mmc_softc *sc, uint32_t len); static int mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, int width); static int mmc_set_relative_addr(struct mmc_softc *sc, uint16_t resp); static int mmc_set_timing(struct mmc_softc *sc, int timing); static int mmc_switch(struct mmc_softc *sc, uint8_t set, uint8_t index, uint8_t value); static int mmc_test_bus_width(struct mmc_softc *sc); static int mmc_wait_for_app_cmd(struct mmc_softc *sc, uint32_t rca, struct mmc_command *cmd, int retries); static int mmc_wait_for_cmd(struct mmc_softc *sc, struct mmc_command *cmd, int retries); static int mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode, uint32_t arg, uint32_t flags, uint32_t *resp, int retries); static int mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req); static void mmc_wakeup(struct mmc_request *req); static void mmc_ms_delay(int ms) { DELAY(1000 * ms); /* XXX BAD */ } static int mmc_probe(device_t dev) { device_set_desc(dev, "MMC/SD bus"); return (0); } static int mmc_attach(device_t dev) { struct mmc_softc *sc; sc = device_get_softc(dev); sc->dev = dev; MMC_LOCK_INIT(sc); /* We'll probe and attach our children later, but before / mount */ sc->config_intrhook.ich_func = mmc_delayed_attach; sc->config_intrhook.ich_arg = sc; if (config_intrhook_establish(&sc->config_intrhook) != 0) device_printf(dev, "config_intrhook_establish failed\n"); return (0); } static int mmc_detach(device_t dev) { struct mmc_softc *sc = device_get_softc(dev); int err; if ((err = mmc_delete_cards(sc)) != 0) return (err); mmc_power_down(sc); MMC_LOCK_DESTROY(sc); return (0); } static int mmc_suspend(device_t dev) { struct mmc_softc *sc = device_get_softc(dev); int err; err = bus_generic_suspend(dev); if (err) return (err); mmc_power_down(sc); return (0); } static int mmc_resume(device_t dev) { struct mmc_softc *sc = device_get_softc(dev); mmc_scan(sc); return (bus_generic_resume(dev)); } static int mmc_acquire_bus(device_t busdev, device_t dev) { struct mmc_softc *sc; struct mmc_ivars *ivar; int err; int rca; err = MMCBR_ACQUIRE_HOST(device_get_parent(busdev), busdev); if (err) return (err); sc = device_get_softc(busdev); MMC_LOCK(sc); if (sc->owner) panic("mmc: host bridge didn't serialize us."); sc->owner = dev; MMC_UNLOCK(sc); if (busdev != dev) { /* * Keep track of the last rca that we've selected. If * we're asked to do it again, don't. We never * unselect unless the bus code itself wants the mmc * bus, and constantly reselecting causes problems. */ rca = mmc_get_rca(dev); if (sc->last_rca != rca) { mmc_select_card(sc, rca); sc->last_rca = rca; /* Prepare bus width for the new card. */ ivar = device_get_ivars(dev); if (bootverbose || mmc_debug) { device_printf(busdev, "setting bus width to %d bits\n", (ivar->bus_width == bus_width_4) ? 4 : (ivar->bus_width == bus_width_8) ? 8 : 1); } mmc_set_card_bus_width(sc, rca, ivar->bus_width); mmcbr_set_bus_width(busdev, ivar->bus_width); mmcbr_update_ios(busdev); } } else { /* * If there's a card selected, stand down. */ if (sc->last_rca != 0) { mmc_select_card(sc, 0); sc->last_rca = 0; } } return (0); } static int mmc_release_bus(device_t busdev, device_t dev) { struct mmc_softc *sc; int err; sc = device_get_softc(busdev); MMC_LOCK(sc); if (!sc->owner) panic("mmc: releasing unowned bus."); if (sc->owner != dev) panic("mmc: you don't own the bus. game over."); MMC_UNLOCK(sc); err = MMCBR_RELEASE_HOST(device_get_parent(busdev), busdev); if (err) return (err); MMC_LOCK(sc); sc->owner = NULL; MMC_UNLOCK(sc); return (0); } static uint32_t mmc_select_vdd(struct mmc_softc *sc, uint32_t ocr) { return (ocr & MMC_OCR_VOLTAGE); } static int mmc_highest_voltage(uint32_t ocr) { int i; for (i = MMC_OCR_MAX_VOLTAGE_SHIFT; i >= MMC_OCR_MIN_VOLTAGE_SHIFT; i--) if (ocr & (1 << i)) return (i); return (-1); } static void mmc_wakeup(struct mmc_request *req) { struct mmc_softc *sc; sc = (struct mmc_softc *)req->done_data; MMC_LOCK(sc); req->flags |= MMC_REQ_DONE; MMC_UNLOCK(sc); wakeup(req); } static int mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req) { req->done = mmc_wakeup; req->done_data = sc; if (mmc_debug > 1) { device_printf(sc->dev, "REQUEST: CMD%d arg %#x flags %#x", req->cmd->opcode, req->cmd->arg, req->cmd->flags); if (req->cmd->data) { printf(" data %d\n", (int)req->cmd->data->len); } else printf("\n"); } MMCBR_REQUEST(device_get_parent(sc->dev), sc->dev, req); MMC_LOCK(sc); while ((req->flags & MMC_REQ_DONE) == 0) msleep(req, &sc->sc_mtx, 0, "mmcreq", 0); MMC_UNLOCK(sc); if (mmc_debug > 2 || (mmc_debug > 0 && req->cmd->error != MMC_ERR_NONE)) device_printf(sc->dev, "CMD%d RESULT: %d\n", req->cmd->opcode, req->cmd->error); return (0); } static int mmc_wait_for_request(device_t brdev, device_t reqdev, struct mmc_request *req) { struct mmc_softc *sc = device_get_softc(brdev); return (mmc_wait_for_req(sc, req)); } static int mmc_wait_for_cmd(struct mmc_softc *sc, struct mmc_command *cmd, int retries) { struct mmc_request mreq; int err; do { memset(&mreq, 0, sizeof(mreq)); memset(cmd->resp, 0, sizeof(cmd->resp)); cmd->retries = 0; /* Retries done here, not in hardware. */ cmd->mrq = &mreq; mreq.cmd = cmd; if (mmc_wait_for_req(sc, &mreq) != 0) err = MMC_ERR_FAILED; else err = cmd->error; } while (err != MMC_ERR_NONE && retries-- > 0); if (err != MMC_ERR_NONE && sc->squelched == 0) { if (ppsratecheck(&sc->log_time, &sc->log_count, LOG_PPS)) { device_printf(sc->dev, "CMD%d failed, RESULT: %d\n", cmd->opcode, err); } } return (err); } static int mmc_wait_for_app_cmd(struct mmc_softc *sc, uint32_t rca, struct mmc_command *cmd, int retries) { struct mmc_command appcmd; int err; /* Squelch error reporting at lower levels, we report below. */ sc->squelched++; do { memset(&appcmd, 0, sizeof(appcmd)); appcmd.opcode = MMC_APP_CMD; appcmd.arg = rca << 16; appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC; appcmd.data = NULL; if (mmc_wait_for_cmd(sc, &appcmd, 0) != 0) err = MMC_ERR_FAILED; else err = appcmd.error; if (err == MMC_ERR_NONE) { if (!(appcmd.resp[0] & R1_APP_CMD)) err = MMC_ERR_FAILED; else if (mmc_wait_for_cmd(sc, cmd, 0) != 0) err = MMC_ERR_FAILED; else err = cmd->error; } } while (err != MMC_ERR_NONE && retries-- > 0); sc->squelched--; if (err != MMC_ERR_NONE && sc->squelched == 0) { if (ppsratecheck(&sc->log_time, &sc->log_count, LOG_PPS)) { device_printf(sc->dev, "ACMD%d failed, RESULT: %d\n", cmd->opcode, err); } } return (err); } static int mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode, uint32_t arg, uint32_t flags, uint32_t *resp, int retries) { struct mmc_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = opcode; cmd.arg = arg; cmd.flags = flags; cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, retries); if (err) return (err); if (resp) { if (flags & MMC_RSP_136) memcpy(resp, cmd.resp, 4 * sizeof(uint32_t)); else *resp = cmd.resp[0]; } return (0); } static void mmc_idle_cards(struct mmc_softc *sc) { device_t dev; struct mmc_command cmd; dev = sc->dev; mmcbr_set_chip_select(dev, cs_high); mmcbr_update_ios(dev); mmc_ms_delay(1); memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_GO_IDLE_STATE; cmd.arg = 0; cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; cmd.data = NULL; mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); mmc_ms_delay(1); mmcbr_set_chip_select(dev, cs_dontcare); mmcbr_update_ios(dev); mmc_ms_delay(1); } static int mmc_send_app_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr) { struct mmc_command cmd; int err = MMC_ERR_NONE, i; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = ACMD_SD_SEND_OP_COND; cmd.arg = ocr; cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; cmd.data = NULL; for (i = 0; i < 1000; i++) { err = mmc_wait_for_app_cmd(sc, 0, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) break; if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) || (ocr & MMC_OCR_VOLTAGE) == 0) break; err = MMC_ERR_TIMEOUT; mmc_ms_delay(10); } if (rocr && err == MMC_ERR_NONE) *rocr = cmd.resp[0]; return (err); } static int mmc_send_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr) { struct mmc_command cmd; int err = MMC_ERR_NONE, i; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SEND_OP_COND; cmd.arg = ocr; cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; cmd.data = NULL; for (i = 0; i < 1000; i++) { err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) break; if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) || (ocr & MMC_OCR_VOLTAGE) == 0) break; err = MMC_ERR_TIMEOUT; mmc_ms_delay(10); } if (rocr && err == MMC_ERR_NONE) *rocr = cmd.resp[0]; return (err); } static int mmc_send_if_cond(struct mmc_softc *sc, uint8_t vhs) { struct mmc_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = SD_SEND_IF_COND; cmd.arg = (vhs << 8) + 0xAA; cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); return (err); } static void mmc_power_up(struct mmc_softc *sc) { device_t dev; dev = sc->dev; mmcbr_set_vdd(dev, mmc_highest_voltage(mmcbr_get_host_ocr(dev))); mmcbr_set_bus_mode(dev, opendrain); mmcbr_set_chip_select(dev, cs_dontcare); mmcbr_set_bus_width(dev, bus_width_1); mmcbr_set_power_mode(dev, power_up); mmcbr_set_clock(dev, 0); mmcbr_update_ios(dev); mmc_ms_delay(1); mmcbr_set_clock(dev, CARD_ID_FREQUENCY); mmcbr_set_timing(dev, bus_timing_normal); mmcbr_set_power_mode(dev, power_on); mmcbr_update_ios(dev); mmc_ms_delay(2); } static void mmc_power_down(struct mmc_softc *sc) { device_t dev = sc->dev; mmcbr_set_bus_mode(dev, opendrain); mmcbr_set_chip_select(dev, cs_dontcare); mmcbr_set_bus_width(dev, bus_width_1); mmcbr_set_power_mode(dev, power_off); mmcbr_set_clock(dev, 0); mmcbr_set_timing(dev, bus_timing_normal); mmcbr_update_ios(dev); } static int mmc_select_card(struct mmc_softc *sc, uint16_t rca) { int flags; flags = (rca ? MMC_RSP_R1B : MMC_RSP_NONE) | MMC_CMD_AC; return (mmc_wait_for_command(sc, MMC_SELECT_CARD, (uint32_t)rca << 16, flags, NULL, CMD_RETRIES)); } static int mmc_switch(struct mmc_softc *sc, uint8_t set, uint8_t index, uint8_t value) { struct mmc_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SWITCH_FUNC; cmd.arg = (MMC_SWITCH_FUNC_WR << 24) | (index << 16) | (value << 8) | set; cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); return (err); } static int mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, uint8_t value, uint8_t *res) { int err; struct mmc_command cmd; struct mmc_data data; memset(&cmd, 0, sizeof(cmd)); memset(&data, 0, sizeof(data)); memset(res, 0, 64); cmd.opcode = SD_SWITCH_FUNC; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.arg = mode << 31; /* 0 - check, 1 - set */ cmd.arg |= 0x00FFFFFF; cmd.arg &= ~(0xF << (grp * 4)); cmd.arg |= value << (grp * 4); cmd.data = &data; data.data = res; data.len = 64; data.flags = MMC_DATA_READ; err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); return (err); } static int mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, int width) { struct mmc_command cmd; int err; uint8_t value; if (mmcbr_get_mode(sc->dev) == mode_sd) { memset(&cmd, 0, sizeof(cmd)); cmd.opcode = ACMD_SET_CLR_CARD_DETECT; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.arg = SD_CLR_CARD_DETECT; err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); if (err != 0) return (err); memset(&cmd, 0, sizeof(cmd)); cmd.opcode = ACMD_SET_BUS_WIDTH; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; switch (width) { case bus_width_1: cmd.arg = SD_BUS_WIDTH_1; break; case bus_width_4: cmd.arg = SD_BUS_WIDTH_4; break; default: return (MMC_ERR_INVALID); } err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); } else { switch (width) { case bus_width_1: value = EXT_CSD_BUS_WIDTH_1; break; case bus_width_4: value = EXT_CSD_BUS_WIDTH_4; break; case bus_width_8: value = EXT_CSD_BUS_WIDTH_8; break; default: return (MMC_ERR_INVALID); } err = mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, value); } return (err); } static int mmc_set_timing(struct mmc_softc *sc, int timing) { int err; uint8_t value; u_char switch_res[64]; switch (timing) { case bus_timing_normal: value = 0; break; case bus_timing_hs: value = 1; break; default: return (MMC_ERR_INVALID); } if (mmcbr_get_mode(sc->dev) == mode_sd) err = mmc_sd_switch(sc, SD_SWITCH_MODE_SET, SD_SWITCH_GROUP1, value, switch_res); else err = mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, value); return (err); } static int mmc_test_bus_width(struct mmc_softc *sc) { struct mmc_command cmd; struct mmc_data data; int err; uint8_t buf[8]; uint8_t p8[8] = { 0x55, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t p8ok[8] = { 0xAA, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t p4[4] = { 0x5A, 0x00, 0x00, 0x00, }; uint8_t p4ok[4] = { 0xA5, 0x00, 0x00, 0x00, }; if (mmcbr_get_caps(sc->dev) & MMC_CAP_8_BIT_DATA) { mmcbr_set_bus_width(sc->dev, bus_width_8); mmcbr_update_ios(sc->dev); sc->squelched++; /* Errors are expected, squelch reporting. */ memset(&cmd, 0, sizeof(cmd)); memset(&data, 0, sizeof(data)); cmd.opcode = MMC_BUSTEST_W; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.data = &data; data.data = p8; data.len = 8; data.flags = MMC_DATA_WRITE; mmc_wait_for_cmd(sc, &cmd, 0); memset(&cmd, 0, sizeof(cmd)); memset(&data, 0, sizeof(data)); cmd.opcode = MMC_BUSTEST_R; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.data = &data; data.data = buf; data.len = 8; data.flags = MMC_DATA_READ; err = mmc_wait_for_cmd(sc, &cmd, 0); sc->squelched--; mmcbr_set_bus_width(sc->dev, bus_width_1); mmcbr_update_ios(sc->dev); if (err == MMC_ERR_NONE && memcmp(buf, p8ok, 8) == 0) return (bus_width_8); } if (mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA) { mmcbr_set_bus_width(sc->dev, bus_width_4); mmcbr_update_ios(sc->dev); sc->squelched++; /* Errors are expected, squelch reporting. */ memset(&cmd, 0, sizeof(cmd)); memset(&data, 0, sizeof(data)); cmd.opcode = MMC_BUSTEST_W; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.data = &data; data.data = p4; data.len = 4; data.flags = MMC_DATA_WRITE; mmc_wait_for_cmd(sc, &cmd, 0); memset(&cmd, 0, sizeof(cmd)); memset(&data, 0, sizeof(data)); cmd.opcode = MMC_BUSTEST_R; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.data = &data; data.data = buf; data.len = 4; data.flags = MMC_DATA_READ; err = mmc_wait_for_cmd(sc, &cmd, 0); sc->squelched--; mmcbr_set_bus_width(sc->dev, bus_width_1); mmcbr_update_ios(sc->dev); if (err == MMC_ERR_NONE && memcmp(buf, p4ok, 4) == 0) return (bus_width_4); } return (bus_width_1); } static uint32_t mmc_get_bits(uint32_t *bits, int bit_len, int start, int size) { const int i = (bit_len / 32) - (start / 32) - 1; const int shift = start & 31; uint32_t retval = bits[i] >> shift; if (size + shift > 32) retval |= bits[i - 1] << (32 - shift); return (retval & ((1llu << size) - 1)); } static void mmc_decode_cid_sd(uint32_t *raw_cid, struct mmc_cid *cid) { int i; /* There's no version info, so we take it on faith */ memset(cid, 0, sizeof(*cid)); cid->mid = mmc_get_bits(raw_cid, 128, 120, 8); cid->oid = mmc_get_bits(raw_cid, 128, 104, 16); for (i = 0; i < 5; i++) cid->pnm[i] = mmc_get_bits(raw_cid, 128, 96 - i * 8, 8); cid->pnm[5] = 0; cid->prv = mmc_get_bits(raw_cid, 128, 56, 8); cid->psn = mmc_get_bits(raw_cid, 128, 24, 32); cid->mdt_year = mmc_get_bits(raw_cid, 128, 12, 8) + 2000; cid->mdt_month = mmc_get_bits(raw_cid, 128, 8, 4); } static void mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid) { int i; /* There's no version info, so we take it on faith */ memset(cid, 0, sizeof(*cid)); cid->mid = mmc_get_bits(raw_cid, 128, 120, 8); cid->oid = mmc_get_bits(raw_cid, 128, 104, 8); for (i = 0; i < 6; i++) cid->pnm[i] = mmc_get_bits(raw_cid, 128, 96 - i * 8, 8); cid->pnm[6] = 0; cid->prv = mmc_get_bits(raw_cid, 128, 48, 8); cid->psn = mmc_get_bits(raw_cid, 128, 16, 32); cid->mdt_month = mmc_get_bits(raw_cid, 128, 12, 4); cid->mdt_year = mmc_get_bits(raw_cid, 128, 8, 4) + 1997; } static void mmc_format_card_id_string(struct mmc_ivars *ivar) { char oidstr[8]; uint8_t c1; uint8_t c2; /* * Format a card ID string for use by the mmcsd driver, it's what * appears between the <> in the following: * mmcsd0: 968MB at mmc0 * 22.5MHz/4bit/128-block * * Also format just the card serial number, which the mmcsd driver will * use as the disk->d_ident string. * * The card_id_string in mmc_ivars is currently allocated as 64 bytes, * and our max formatted length is currently 55 bytes if every field * contains the largest value. * * Sometimes the oid is two printable ascii chars; when it's not, * format it as 0xnnnn instead. */ c1 = (ivar->cid.oid >> 8) & 0x0ff; c2 = ivar->cid.oid & 0x0ff; if (c1 > 0x1f && c1 < 0x7f && c2 > 0x1f && c2 < 0x7f) snprintf(oidstr, sizeof(oidstr), "%c%c", c1, c2); else snprintf(oidstr, sizeof(oidstr), "0x%04x", ivar->cid.oid); snprintf(ivar->card_sn_string, sizeof(ivar->card_sn_string), "%08X", ivar->cid.psn); snprintf(ivar->card_id_string, sizeof(ivar->card_id_string), "%s%s %s %d.%d SN %08X MFG %02d/%04d by %d %s", ivar->mode == mode_sd ? "SD" : "MMC", ivar->high_cap ? "HC" : "", ivar->cid.pnm, ivar->cid.prv >> 4, ivar->cid.prv & 0x0f, ivar->cid.psn, ivar->cid.mdt_month, ivar->cid.mdt_year, ivar->cid.mid, oidstr); } static const int exp[8] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 }; static const int mant[16] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 }; static const int cur_min[8] = { 500, 1000, 5000, 10000, 25000, 35000, 60000, 100000 }; static const int cur_max[8] = { 1000, 5000, 10000, 25000, 35000, 45000, 800000, 200000 }; static void mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd) { int v; int m; int e; memset(csd, 0, sizeof(*csd)); csd->csd_structure = v = mmc_get_bits(raw_csd, 128, 126, 2); if (v == 0) { m = mmc_get_bits(raw_csd, 128, 115, 4); e = mmc_get_bits(raw_csd, 128, 112, 3); csd->tacc = (exp[e] * mant[m] + 9) / 10; csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100; m = mmc_get_bits(raw_csd, 128, 99, 4); e = mmc_get_bits(raw_csd, 128, 96, 3); csd->tran_speed = exp[e] * 10000 * mant[m]; csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12); csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4); csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1); csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1); csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1); csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1); csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 59, 3)]; csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 56, 3)]; csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 53, 3)]; csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 50, 3)]; m = mmc_get_bits(raw_csd, 128, 62, 12); e = mmc_get_bits(raw_csd, 128, 47, 3); csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len; csd->erase_blk_en = mmc_get_bits(raw_csd, 128, 46, 1); csd->erase_sector = mmc_get_bits(raw_csd, 128, 39, 7) + 1; csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 7); csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1); csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3); csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4); csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1); } else if (v == 1) { m = mmc_get_bits(raw_csd, 128, 115, 4); e = mmc_get_bits(raw_csd, 128, 112, 3); csd->tacc = (exp[e] * mant[m] + 9) / 10; csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100; m = mmc_get_bits(raw_csd, 128, 99, 4); e = mmc_get_bits(raw_csd, 128, 96, 3); csd->tran_speed = exp[e] * 10000 * mant[m]; csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12); csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4); csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1); csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1); csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1); csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1); csd->capacity = ((uint64_t)mmc_get_bits(raw_csd, 128, 48, 22) + 1) * 512 * 1024; csd->erase_blk_en = mmc_get_bits(raw_csd, 128, 46, 1); csd->erase_sector = mmc_get_bits(raw_csd, 128, 39, 7) + 1; csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 7); csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1); csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3); csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4); csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1); } else panic("unknown SD CSD version"); } static void mmc_decode_csd_mmc(uint32_t *raw_csd, struct mmc_csd *csd) { int m; int e; memset(csd, 0, sizeof(*csd)); csd->csd_structure = mmc_get_bits(raw_csd, 128, 126, 2); csd->spec_vers = mmc_get_bits(raw_csd, 128, 122, 4); m = mmc_get_bits(raw_csd, 128, 115, 4); e = mmc_get_bits(raw_csd, 128, 112, 3); csd->tacc = exp[e] * mant[m] + 9 / 10; csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100; m = mmc_get_bits(raw_csd, 128, 99, 4); e = mmc_get_bits(raw_csd, 128, 96, 3); csd->tran_speed = exp[e] * 10000 * mant[m]; csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12); csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4); csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1); csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1); csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1); csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1); csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 59, 3)]; csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 56, 3)]; csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 53, 3)]; csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 50, 3)]; m = mmc_get_bits(raw_csd, 128, 62, 12); e = mmc_get_bits(raw_csd, 128, 47, 3); csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len; csd->erase_blk_en = 0; csd->erase_sector = (mmc_get_bits(raw_csd, 128, 42, 5) + 1) * (mmc_get_bits(raw_csd, 128, 37, 5) + 1); csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 5); csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1); csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3); csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4); csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1); } static void mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr) { unsigned int scr_struct; memset(scr, 0, sizeof(*scr)); scr_struct = mmc_get_bits(raw_scr, 64, 60, 4); if (scr_struct != 0) { printf("Unrecognised SCR structure version %d\n", scr_struct); return; } scr->sda_vsn = mmc_get_bits(raw_scr, 64, 56, 4); scr->bus_widths = mmc_get_bits(raw_scr, 64, 48, 4); } static void mmc_app_decode_sd_status(uint32_t *raw_sd_status, struct mmc_sd_status *sd_status) { memset(sd_status, 0, sizeof(*sd_status)); sd_status->bus_width = mmc_get_bits(raw_sd_status, 512, 510, 2); sd_status->secured_mode = mmc_get_bits(raw_sd_status, 512, 509, 1); sd_status->card_type = mmc_get_bits(raw_sd_status, 512, 480, 16); sd_status->prot_area = mmc_get_bits(raw_sd_status, 512, 448, 12); sd_status->speed_class = mmc_get_bits(raw_sd_status, 512, 440, 8); sd_status->perf_move = mmc_get_bits(raw_sd_status, 512, 432, 8); sd_status->au_size = mmc_get_bits(raw_sd_status, 512, 428, 4); sd_status->erase_size = mmc_get_bits(raw_sd_status, 512, 408, 16); sd_status->erase_timeout = mmc_get_bits(raw_sd_status, 512, 402, 6); sd_status->erase_offset = mmc_get_bits(raw_sd_status, 512, 400, 2); } static int mmc_all_send_cid(struct mmc_softc *sc, uint32_t *rawcid) { struct mmc_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_ALL_SEND_CID; cmd.arg = 0; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); memcpy(rawcid, cmd.resp, 4 * sizeof(uint32_t)); return (err); } static int mmc_send_csd(struct mmc_softc *sc, uint16_t rca, uint32_t *rawcsd) { struct mmc_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SEND_CSD; cmd.arg = rca << 16; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); memcpy(rawcsd, cmd.resp, 4 * sizeof(uint32_t)); return (err); } static int mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, uint32_t *rawscr) { int err; struct mmc_command cmd; struct mmc_data data; memset(&cmd, 0, sizeof(cmd)); memset(&data, 0, sizeof(data)); memset(rawscr, 0, 8); cmd.opcode = ACMD_SEND_SCR; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.arg = 0; cmd.data = &data; data.data = rawscr; data.len = 8; data.flags = MMC_DATA_READ; err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); rawscr[0] = be32toh(rawscr[0]); rawscr[1] = be32toh(rawscr[1]); return (err); } static int mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd) { int err; struct mmc_command cmd; struct mmc_data data; memset(&cmd, 0, sizeof(cmd)); memset(&data, 0, sizeof(data)); memset(rawextcsd, 0, 512); cmd.opcode = MMC_SEND_EXT_CSD; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.arg = 0; cmd.data = &data; data.data = rawextcsd; data.len = 512; data.flags = MMC_DATA_READ; err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); return (err); } static int mmc_app_sd_status(struct mmc_softc *sc, uint16_t rca, uint32_t *rawsdstatus) { int err, i; struct mmc_command cmd; struct mmc_data data; memset(&cmd, 0, sizeof(cmd)); memset(&data, 0, sizeof(data)); memset(rawsdstatus, 0, 64); cmd.opcode = ACMD_SD_STATUS; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.arg = 0; cmd.data = &data; data.data = rawsdstatus; data.len = 64; data.flags = MMC_DATA_READ; err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); for (i = 0; i < 16; i++) rawsdstatus[i] = be32toh(rawsdstatus[i]); return (err); } static int mmc_set_relative_addr(struct mmc_softc *sc, uint16_t resp) { struct mmc_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SET_RELATIVE_ADDR; cmd.arg = resp << 16; cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); return (err); } static int mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp) { struct mmc_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = SD_SEND_RELATIVE_ADDR; cmd.arg = 0; cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); *resp = cmd.resp[0]; return (err); } static int mmc_send_status(struct mmc_softc *sc, uint16_t rca, uint32_t *status) { struct mmc_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SEND_STATUS; cmd.arg = rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); *status = cmd.resp[0]; return (err); } static int mmc_set_blocklen(struct mmc_softc *sc, uint32_t len) { struct mmc_command cmd; int err; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = MMC_SET_BLOCKLEN; cmd.arg = len; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); return (err); } static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard) { device_printf(dev, "Card at relative address 0x%04x%s:\n", ivar->rca, newcard ? " added" : ""); device_printf(dev, " card: %s\n", ivar->card_id_string); device_printf(dev, " bus: %ubit, %uMHz%s\n", (ivar->bus_width == bus_width_1 ? 1 : (ivar->bus_width == bus_width_4 ? 4 : 8)), (ivar->timing == bus_timing_hs ? ivar->hs_tran_speed : ivar->tran_speed) / 1000000, ivar->timing == bus_timing_hs ? ", high speed timing" : ""); device_printf(dev, " memory: %u blocks, erase sector %u blocks%s\n", ivar->sec_count, ivar->erase_sector, ivar->read_only ? ", read-only" : ""); } static void mmc_discover_cards(struct mmc_softc *sc) { struct mmc_ivars *ivar = NULL; device_t *devlist; int err, i, devcount, newcard; uint32_t raw_cid[4], resp, sec_count, status; device_t child; uint16_t rca = 2; u_char switch_res[64]; if (bootverbose || mmc_debug) device_printf(sc->dev, "Probing cards\n"); while (1) { sc->squelched++; /* Errors are expected, squelch reporting. */ err = mmc_all_send_cid(sc, raw_cid); sc->squelched--; if (err == MMC_ERR_TIMEOUT) break; if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading CID %d\n", err); break; } newcard = 1; if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) return; for (i = 0; i < devcount; i++) { ivar = device_get_ivars(devlist[i]); if (memcmp(ivar->raw_cid, raw_cid, sizeof(raw_cid)) == 0) { newcard = 0; break; } } free(devlist, M_TEMP); if (bootverbose || mmc_debug) { device_printf(sc->dev, "%sard detected (CID %08x%08x%08x%08x)\n", newcard ? "New c" : "C", raw_cid[0], raw_cid[1], raw_cid[2], raw_cid[3]); } if (newcard) { ivar = malloc(sizeof(struct mmc_ivars), M_DEVBUF, M_WAITOK | M_ZERO); memcpy(ivar->raw_cid, raw_cid, sizeof(raw_cid)); } if (mmcbr_get_ro(sc->dev)) ivar->read_only = 1; ivar->bus_width = bus_width_1; ivar->timing = bus_timing_normal; ivar->mode = mmcbr_get_mode(sc->dev); if (ivar->mode == mode_sd) { mmc_decode_cid_sd(ivar->raw_cid, &ivar->cid); mmc_send_relative_addr(sc, &resp); ivar->rca = resp >> 16; /* Get card CSD. */ mmc_send_csd(sc, ivar->rca, ivar->raw_csd); if (bootverbose || mmc_debug) device_printf(sc->dev, "%sard detected (CSD %08x%08x%08x%08x)\n", newcard ? "New c" : "C", ivar->raw_csd[0], ivar->raw_csd[1], ivar->raw_csd[2], ivar->raw_csd[3]); mmc_decode_csd_sd(ivar->raw_csd, &ivar->csd); ivar->sec_count = ivar->csd.capacity / MMC_SECTOR_SIZE; if (ivar->csd.csd_structure > 0) ivar->high_cap = 1; ivar->tran_speed = ivar->csd.tran_speed; ivar->erase_sector = ivar->csd.erase_sector * ivar->csd.write_bl_len / MMC_SECTOR_SIZE; err = mmc_send_status(sc, ivar->rca, &status); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading card status %d\n", err); break; } if ((status & R1_CARD_IS_LOCKED) != 0) { device_printf(sc->dev, "Card is password protected, skipping.\n"); break; } /* Get card SCR. Card must be selected to fetch it. */ mmc_select_card(sc, ivar->rca); mmc_app_send_scr(sc, ivar->rca, ivar->raw_scr); mmc_app_decode_scr(ivar->raw_scr, &ivar->scr); /* Get card switch capabilities (command class 10). */ if ((ivar->scr.sda_vsn >= 1) && (ivar->csd.ccc & (1<<10))) { mmc_sd_switch(sc, SD_SWITCH_MODE_CHECK, SD_SWITCH_GROUP1, SD_SWITCH_NOCHANGE, switch_res); if (switch_res[13] & 2) { ivar->timing = bus_timing_hs; ivar->hs_tran_speed = SD_MAX_HS; } } mmc_app_sd_status(sc, ivar->rca, ivar->raw_sd_status); mmc_app_decode_sd_status(ivar->raw_sd_status, &ivar->sd_status); if (ivar->sd_status.au_size != 0) { ivar->erase_sector = 16 << ivar->sd_status.au_size; } /* Find max supported bus width. */ if ((mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA) && (ivar->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) ivar->bus_width = bus_width_4; /* * Some cards that report maximum I/O block sizes * greater than 512 require the block length to be * set to 512, even though that is supposed to be * the default. Example: * * Transcend 2GB SDSC card, CID: * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 */ if (ivar->csd.read_bl_len != MMC_SECTOR_SIZE || ivar->csd.write_bl_len != MMC_SECTOR_SIZE) mmc_set_blocklen(sc, MMC_SECTOR_SIZE); mmc_format_card_id_string(ivar); if (bootverbose || mmc_debug) mmc_log_card(sc->dev, ivar, newcard); if (newcard) { /* Add device. */ child = device_add_child(sc->dev, NULL, -1); device_set_ivars(child, ivar); } mmc_select_card(sc, 0); return; } mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid); ivar->rca = rca++; mmc_set_relative_addr(sc, ivar->rca); /* Get card CSD. */ mmc_send_csd(sc, ivar->rca, ivar->raw_csd); if (bootverbose || mmc_debug) device_printf(sc->dev, "%sard detected (CSD %08x%08x%08x%08x)\n", newcard ? "New c" : "C", ivar->raw_csd[0], ivar->raw_csd[1], ivar->raw_csd[2], ivar->raw_csd[3]); mmc_decode_csd_mmc(ivar->raw_csd, &ivar->csd); ivar->sec_count = ivar->csd.capacity / MMC_SECTOR_SIZE; ivar->tran_speed = ivar->csd.tran_speed; ivar->erase_sector = ivar->csd.erase_sector * ivar->csd.write_bl_len / MMC_SECTOR_SIZE; err = mmc_send_status(sc, ivar->rca, &status); if (err != MMC_ERR_NONE) { device_printf(sc->dev, "Error reading card status %d\n", err); break; } if ((status & R1_CARD_IS_LOCKED) != 0) { device_printf(sc->dev, "Card is password protected, skipping.\n"); break; } mmc_select_card(sc, ivar->rca); /* Only MMC >= 4.x cards support EXT_CSD. */ if (ivar->csd.spec_vers >= 4) { mmc_send_ext_csd(sc, ivar->raw_ext_csd); /* Handle extended capacity from EXT_CSD */ sec_count = ivar->raw_ext_csd[EXT_CSD_SEC_CNT] + (ivar->raw_ext_csd[EXT_CSD_SEC_CNT + 1] << 8) + (ivar->raw_ext_csd[EXT_CSD_SEC_CNT + 2] << 16) + (ivar->raw_ext_csd[EXT_CSD_SEC_CNT + 3] << 24); if (sec_count != 0) { ivar->sec_count = sec_count; ivar->high_cap = 1; } /* Get card speed in high speed mode. */ ivar->timing = bus_timing_hs; if (ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_52) ivar->hs_tran_speed = MMC_TYPE_52_MAX_HS; else if (ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_26) ivar->hs_tran_speed = MMC_TYPE_26_MAX_HS; else ivar->hs_tran_speed = ivar->tran_speed; /* Find max supported bus width. */ ivar->bus_width = mmc_test_bus_width(sc); /* Handle HC erase sector size. */ if (ivar->raw_ext_csd[EXT_CSD_ERASE_GRP_SIZE] != 0) { ivar->erase_sector = 1024 * ivar->raw_ext_csd[EXT_CSD_ERASE_GRP_SIZE]; mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_ERASE_GRP_DEF, 1); } } else { ivar->bus_width = bus_width_1; ivar->timing = bus_timing_normal; } /* * Some cards that report maximum I/O block sizes greater * than 512 require the block length to be set to 512, even * though that is supposed to be the default. Example: * * Transcend 2GB SDSC card, CID: * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 */ if (ivar->csd.read_bl_len != MMC_SECTOR_SIZE || ivar->csd.write_bl_len != MMC_SECTOR_SIZE) mmc_set_blocklen(sc, MMC_SECTOR_SIZE); mmc_format_card_id_string(ivar); if (bootverbose || mmc_debug) mmc_log_card(sc->dev, ivar, newcard); if (newcard) { /* Add device. */ child = device_add_child(sc->dev, NULL, -1); device_set_ivars(child, ivar); } mmc_select_card(sc, 0); } } static void mmc_rescan_cards(struct mmc_softc *sc) { struct mmc_ivars *ivar = NULL; device_t *devlist; int err, i, devcount; if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) return; for (i = 0; i < devcount; i++) { ivar = device_get_ivars(devlist[i]); if (mmc_select_card(sc, ivar->rca)) { if (bootverbose || mmc_debug) device_printf(sc->dev, "Card at relative address %d lost.\n", ivar->rca); device_delete_child(sc->dev, devlist[i]); free(ivar, M_DEVBUF); } } free(devlist, M_TEMP); mmc_select_card(sc, 0); } static int mmc_delete_cards(struct mmc_softc *sc) { struct mmc_ivars *ivar; device_t *devlist; int err, i, devcount; if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) return (err); for (i = 0; i < devcount; i++) { ivar = device_get_ivars(devlist[i]); if (bootverbose || mmc_debug) device_printf(sc->dev, "Card at relative address %d deleted.\n", ivar->rca); device_delete_child(sc->dev, devlist[i]); free(ivar, M_DEVBUF); } free(devlist, M_TEMP); return (0); } static void mmc_go_discovery(struct mmc_softc *sc) { uint32_t ocr; device_t dev; int err; dev = sc->dev; if (mmcbr_get_power_mode(dev) != power_on) { /* * First, try SD modes */ sc->squelched++; /* Errors are expected, squelch reporting. */ mmcbr_set_mode(dev, mode_sd); mmc_power_up(sc); mmcbr_set_bus_mode(dev, pushpull); if (bootverbose || mmc_debug) device_printf(sc->dev, "Probing bus\n"); mmc_idle_cards(sc); err = mmc_send_if_cond(sc, 1); if ((bootverbose || mmc_debug) && err == 0) device_printf(sc->dev, "SD 2.0 interface conditions: OK\n"); if (mmc_send_app_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) { if (bootverbose || mmc_debug) device_printf(sc->dev, "SD probe: failed\n"); /* * Failed, try MMC */ mmcbr_set_mode(dev, mode_mmc); if (mmc_send_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) { if (bootverbose || mmc_debug) device_printf(sc->dev, "MMC probe: failed\n"); ocr = 0; /* Failed both, powerdown. */ } else if (bootverbose || mmc_debug) device_printf(sc->dev, "MMC probe: OK (OCR: 0x%08x)\n", ocr); } else if (bootverbose || mmc_debug) device_printf(sc->dev, "SD probe: OK (OCR: 0x%08x)\n", ocr); sc->squelched--; mmcbr_set_ocr(dev, mmc_select_vdd(sc, ocr)); if (mmcbr_get_ocr(dev) != 0) mmc_idle_cards(sc); } else { mmcbr_set_bus_mode(dev, opendrain); mmcbr_set_clock(dev, CARD_ID_FREQUENCY); mmcbr_update_ios(dev); /* XXX recompute vdd based on new cards? */ } /* * Make sure that we have a mutually agreeable voltage to at least * one card on the bus. */ if (bootverbose || mmc_debug) device_printf(sc->dev, "Current OCR: 0x%08x\n", mmcbr_get_ocr(dev)); if (mmcbr_get_ocr(dev) == 0) { device_printf(sc->dev, "No compatible cards found on bus\n"); mmc_delete_cards(sc); mmc_power_down(sc); return; } /* * Reselect the cards after we've idled them above. */ if (mmcbr_get_mode(dev) == mode_sd) { err = mmc_send_if_cond(sc, 1); mmc_send_app_op_cond(sc, (err ? 0 : MMC_OCR_CCS) | mmcbr_get_ocr(dev), NULL); } else mmc_send_op_cond(sc, MMC_OCR_CCS | mmcbr_get_ocr(dev), NULL); mmc_discover_cards(sc); mmc_rescan_cards(sc); mmcbr_set_bus_mode(dev, pushpull); mmcbr_update_ios(dev); mmc_calculate_clock(sc); bus_generic_attach(dev); /* mmc_update_children_sysctl(dev);*/ } static int mmc_calculate_clock(struct mmc_softc *sc) { int max_dtr, max_hs_dtr, max_timing; int nkid, i, f_max; device_t *kids; struct mmc_ivars *ivar; f_max = mmcbr_get_f_max(sc->dev); max_dtr = max_hs_dtr = f_max; if ((mmcbr_get_caps(sc->dev) & MMC_CAP_HSPEED)) max_timing = bus_timing_hs; else max_timing = bus_timing_normal; if (device_get_children(sc->dev, &kids, &nkid) != 0) panic("can't get children"); for (i = 0; i < nkid; i++) { ivar = device_get_ivars(kids[i]); if (ivar->timing < max_timing) max_timing = ivar->timing; if (ivar->tran_speed < max_dtr) max_dtr = ivar->tran_speed; if (ivar->hs_tran_speed < max_hs_dtr) max_hs_dtr = ivar->hs_tran_speed; } for (i = 0; i < nkid; i++) { ivar = device_get_ivars(kids[i]); if (ivar->timing == bus_timing_normal) continue; mmc_select_card(sc, ivar->rca); mmc_set_timing(sc, max_timing); } mmc_select_card(sc, 0); free(kids, M_TEMP); if (max_timing == bus_timing_hs) max_dtr = max_hs_dtr; if (bootverbose || mmc_debug) { device_printf(sc->dev, "setting transfer rate to %d.%03dMHz%s\n", max_dtr / 1000000, (max_dtr / 1000) % 1000, max_timing == bus_timing_hs ? " (high speed timing)" : ""); } mmcbr_set_timing(sc->dev, max_timing); mmcbr_set_clock(sc->dev, max_dtr); mmcbr_update_ios(sc->dev); return max_dtr; } static void mmc_scan(struct mmc_softc *sc) { device_t dev = sc->dev; mmc_acquire_bus(dev, dev); mmc_go_discovery(sc); mmc_release_bus(dev, dev); } static int mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) { struct mmc_ivars *ivar = device_get_ivars(child); switch (which) { default: return (EINVAL); case MMC_IVAR_DSR_IMP: *result = ivar->csd.dsr_imp; break; case MMC_IVAR_MEDIA_SIZE: *result = ivar->sec_count; break; case MMC_IVAR_RCA: *result = ivar->rca; break; case MMC_IVAR_SECTOR_SIZE: *result = MMC_SECTOR_SIZE; break; case MMC_IVAR_TRAN_SPEED: *result = mmcbr_get_clock(bus); break; case MMC_IVAR_READ_ONLY: *result = ivar->read_only; break; case MMC_IVAR_HIGH_CAP: *result = ivar->high_cap; break; case MMC_IVAR_CARD_TYPE: *result = ivar->mode; break; case MMC_IVAR_BUS_WIDTH: *result = ivar->bus_width; break; case MMC_IVAR_ERASE_SECTOR: *result = ivar->erase_sector; break; case MMC_IVAR_MAX_DATA: *result = mmcbr_get_max_data(bus); break; case MMC_IVAR_CARD_ID_STRING: *(char **)result = ivar->card_id_string; break; case MMC_IVAR_CARD_SN_STRING: *(char **)result = ivar->card_sn_string; break; } return (0); } static int mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) { /* * None are writable ATM */ return (EINVAL); } static void mmc_delayed_attach(void *xsc) { struct mmc_softc *sc = xsc; mmc_scan(sc); config_intrhook_disestablish(&sc->config_intrhook); } static int mmc_child_location_str(device_t dev, device_t child, char *buf, size_t buflen) { snprintf(buf, buflen, "rca=0x%04x", mmc_get_rca(child)); return (0); } static device_method_t mmc_methods[] = { /* device_if */ DEVMETHOD(device_probe, mmc_probe), DEVMETHOD(device_attach, mmc_attach), DEVMETHOD(device_detach, mmc_detach), DEVMETHOD(device_suspend, mmc_suspend), DEVMETHOD(device_resume, mmc_resume), /* Bus interface */ DEVMETHOD(bus_read_ivar, mmc_read_ivar), DEVMETHOD(bus_write_ivar, mmc_write_ivar), DEVMETHOD(bus_child_location_str, mmc_child_location_str), /* MMC Bus interface */ DEVMETHOD(mmcbus_wait_for_request, mmc_wait_for_request), DEVMETHOD(mmcbus_acquire_bus, mmc_acquire_bus), DEVMETHOD(mmcbus_release_bus, mmc_release_bus), DEVMETHOD_END }; static driver_t mmc_driver = { "mmc", mmc_methods, sizeof(struct mmc_softc), }; static devclass_t mmc_devclass; +DRIVER_MODULE(mmc, aml8726_mmc, mmc_driver, mmc_devclass, NULL, NULL); +DRIVER_MODULE(mmc, aml8726_sdxc, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, sdhci_bcm, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, sdhci_fdt, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, sdhci_imx, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, sdhci_pci, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, sdhci_ti, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, ti_mmchs, mmc_driver, mmc_devclass, NULL, NULL); DRIVER_MODULE(mmc, dwmmc, mmc_driver, mmc_devclass, NULL, NULL);