Index: stable/12/share/man/man9/fdt_pinctrl.9 =================================================================== --- stable/12/share/man/man9/fdt_pinctrl.9 (revision 358652) +++ stable/12/share/man/man9/fdt_pinctrl.9 (revision 358653) @@ -1,158 +1,191 @@ .\" -*- nroff -*- .\" .\" Copyright (c) 2018 Oleksandr Tymoshenko .\" .\" All rights reserved. .\" .\" This program is free software. .\" .\" 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 DEVELOPERS ``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 DEVELOPERS 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$ .\" .Dd June 23, 2018 .Dt fdt_pinctrl 9 .Os .Sh NAME .Nm fdt_pinctrl .Nd helper functions for FDT pinmux controller drivers .Sh SYNOPSIS .In dev/fdt/fdt_pinctrl.h .Ft int .Fn fdt_pinctrl_configure "device_t client" "u_int index" .Ft int .Fn fdt_pinctrl_configure_by_name "device_t client" "const char * name" .Ft int .Fn fdt_pinctrl_register "device_t pinctrl" "const char *pinprop" .Ft int .Fn fdt_pinctrl_configure_tree "device_t pinctrl" .Sh DESCRIPTION .Xr fdt_pinctrl 4 provides an API for manipulating I/O pin configurations on pinmux controllers and pinmux clients. On the controller side, the standard newbus probe and attach methods are implemented. As part of handling attach, it calls the .Fn fdt_pinctrl_register function to register itself as a pinmux controller. Then .Fn fdt_pinctrl_configure_tree is used to walk the device tree and configure pins specified by the pinctrl-0 property for all active devices. The driver also implements the .Fn fdt_pinctrl_configure method, which allows client devices to change their pin configurations after startup. If a client device requires a pin configuration change at some point of its lifecycle, it uses the .Fn fdt_pinctrl_configure or .Fn fdt_pinctrl_configure_by_name functions. .Pp .Fn fdt_pinctrl_configure is used by client device .Fa client to request a pin configuration described by the pinctrl-N property with index .Fa index . .Pp .Fn fdt_pinctrl_configure_by_name is used by client device .Fa client to request the pin configuration with name .Fa name . .Pp .Fn fdt_pinctrl_register registers a pinctrl driver so that it can be used by other devices which call .Fn fdt_pinctrl_configure or .Fn fdt_pinctrl_configure_by_name . It also registers each child node of the pinctrl driver's node which contains a property with the name given in .Fa pinprop . If .Fa pinprop is .Dv NULL , every descendant node is registered. It is possible for the driver to register itself as a pinmux controller for more than one pin property type by calling .Fn fdt_pinctrl_register multiple types. .Pp .Fn fdt_pinctrl_configure_tree walks through enabled devices in the device tree. If the pinctrl-0 property contains references to child nodes of the specified pinctrl device, their pins are configured. .Sh EXAMPLES .Bd -literal static int foo_configure_pins(device_t dev, phandle_t cfgxref) { phandle_t cfgnode; uint32_t *pins, *functions; int npins, nfunctions; cfgnode = OF_node_from_xref(cfgxref); pins = NULL; npins = OF_getencprop_alloc_multi(cfgnode, "foo,pins", sizeof(*pins), (void **)&pins); functions = NULL; nfunctions = OF_getencprop_alloc_multi(cfgnode, "foo,functions", sizeof(*functions), (void **)&functions); ... } static int +foo_is_gpio(device_t dev, device_t gpiodev, bool *is_gpio) +{ + return (foo_is_pin_func_gpio(is_gpio)); +} + +static int +foo_set_flags(device_t dev, device_t gpiodev, uint32_t pin, uint32_t flags) +{ + int rv; + + rv = foo_is_pin_func_gpio(is_gpio); + if (rv != 0) + return (rv); + foo_set_flags(pin, flags); + return (0); +} + +static int +foo_get_flags(device_t dev, device_t gpiodev, uint32_t pin, uint32_t *flags) +{ + int rv; + + rv = foo_is_pin_func_gpio(is_gpio); + if (rv != 0) + return (rv); + foo_get_flags(pin, flags); + return (0); +} + +static int foo_attach(device_t dev) { ... fdt_pinctrl_register(dev, "foo,pins"); /* * It is possible to register more than one pinprop handler */ fdt_pinctrl_register(dev, "bar,pins"); fdt_pinctrl_configure_tree(dev); return (0); } static device_method_t foo_methods[] = { ... /* fdt_pinctrl interface */ DEVMETHOD(fdt_pinctrl_configure, foo_configure_pins), + DEVMETHOD(fdt_pinctrl_is_gpio, foo_is_gpio), + DEVMETHOD(fdt_pinctrl_set_flags, foo_set_flags), + DEVMETHOD(fdt_pinctrl_get_flags, foo_get_flags), /* Terminate method list */ DEVMETHOD_END }; DRIVER_MODULE(foo, simplebus, foo_driver, foo_devclass, NULL, NULL); .Ed .Sh SEE ALSO .Xr fdt_pinctrl 4 , .Sh AUTHORS This manual page was written by .An Oleksandr Tymoshenko . Index: stable/12/sys/arm/allwinner/a10_timer.c =================================================================== --- stable/12/sys/arm/allwinner/a10_timer.c (revision 358652) +++ stable/12/sys/arm/allwinner/a10_timer.c (revision 358653) @@ -1,474 +1,480 @@ /*- * Copyright (c) 2012 Ganbold Tsagaankhuu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__aarch64__) #include "opt_soc.h" #else #include #endif /** * Timer registers addr * */ #define TIMER_IRQ_EN_REG 0x00 #define TIMER_IRQ_ENABLE(x) (1 << x) #define TIMER_IRQ_STA_REG 0x04 #define TIMER_IRQ_PENDING(x) (1 << x) /* * On A10, A13, A20 and A31/A31s 6 timers are available */ #define TIMER_CTRL_REG(x) (0x10 + 0x10 * x) #define TIMER_CTRL_START (1 << 0) #define TIMER_CTRL_AUTORELOAD (1 << 1) #define TIMER_CTRL_CLKSRC_MASK (3 << 2) #define TIMER_CTRL_OSC24M (1 << 2) #define TIMER_CTRL_PRESCALAR_MASK (0x7 << 4) #define TIMER_CTRL_PRESCALAR(x) ((x - 1) << 4) #define TIMER_CTRL_MODE_MASK (1 << 7) #define TIMER_CTRL_MODE_SINGLE (1 << 7) #define TIMER_CTRL_MODE_CONTINUOUS (0 << 7) #define TIMER_INTV_REG(x) (0x14 + 0x10 * x) #define TIMER_CURV_REG(x) (0x18 + 0x10 * x) /* 64 bit counter, available in A10 and A13 */ #define CNT64_CTRL_REG 0xa0 #define CNT64_CTRL_RL_EN 0x02 /* read latch enable */ #define CNT64_LO_REG 0xa4 #define CNT64_HI_REG 0xa8 #define SYS_TIMER_CLKSRC 24000000 /* clock source */ enum a10_timer_type { A10_TIMER = 1, A23_TIMER, }; struct a10_timer_softc { device_t sc_dev; struct resource *res[2]; void *sc_ih; /* interrupt handler */ uint32_t sc_period; uint64_t timer0_freq; struct eventtimer et; enum a10_timer_type type; }; #define timer_read_4(sc, reg) \ bus_read_4(sc->res[A10_TIMER_MEMRES], reg) #define timer_write_4(sc, reg, val) \ bus_write_4(sc->res[A10_TIMER_MEMRES], reg, val) static u_int a10_timer_get_timecount(struct timecounter *); +#if defined(__arm__) static int a10_timer_timer_start(struct eventtimer *, sbintime_t first, sbintime_t period); static int a10_timer_timer_stop(struct eventtimer *); +#endif static uint64_t timer_read_counter64(struct a10_timer_softc *sc); +#if defined(__arm__) static void a10_timer_eventtimer_setup(struct a10_timer_softc *sc); +#endif static void a23_timer_timecounter_setup(struct a10_timer_softc *sc); static u_int a23_timer_get_timecount(struct timecounter *tc); static int a10_timer_irq(void *); static int a10_timer_probe(device_t); static int a10_timer_attach(device_t); #if defined(__arm__) static delay_func a10_timer_delay; #endif static struct timecounter a10_timer_timecounter = { .tc_name = "a10_timer timer0", .tc_get_timecount = a10_timer_get_timecount, .tc_counter_mask = ~0u, .tc_frequency = 0, .tc_quality = 1000, }; static struct timecounter a23_timer_timecounter = { .tc_name = "a10_timer timer0", .tc_get_timecount = a23_timer_get_timecount, .tc_counter_mask = ~0u, .tc_frequency = 0, /* We want it to be selected over the arm generic timecounter */ .tc_quality = 2000, }; #define A10_TIMER_MEMRES 0 #define A10_TIMER_IRQRES 1 static struct resource_spec a10_timer_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static struct ofw_compat_data compat_data[] = { {"allwinner,sun4i-a10-timer", A10_TIMER}, {"allwinner,sun8i-a23-timer", A23_TIMER}, {NULL, 0}, }; static int a10_timer_probe(device_t dev) { struct a10_timer_softc *sc; #if defined(__arm__) u_int soc_family; #endif sc = device_get_softc(dev); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); #if defined(__arm__) /* For SoC >= A10 we have the ARM Timecounter/Eventtimer */ soc_family = allwinner_soc_family(); if (soc_family != ALLWINNERSOC_SUN4I && soc_family != ALLWINNERSOC_SUN5I) return (ENXIO); #endif device_set_desc(dev, "Allwinner timer"); return (BUS_PROBE_DEFAULT); } static int a10_timer_attach(device_t dev) { struct a10_timer_softc *sc; clk_t clk; int err; sc = device_get_softc(dev); sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; if (bus_alloc_resources(dev, a10_timer_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } sc->sc_dev = dev; /* Setup and enable the timer interrupt */ err = bus_setup_intr(dev, sc->res[A10_TIMER_IRQRES], INTR_TYPE_CLK, a10_timer_irq, NULL, sc, &sc->sc_ih); if (err != 0) { bus_release_resources(dev, a10_timer_spec, sc->res); device_printf(dev, "Unable to setup the clock irq handler, " "err = %d\n", err); return (ENXIO); } if (clk_get_by_ofw_index(dev, 0, 0, &clk) != 0) sc->timer0_freq = SYS_TIMER_CLKSRC; else { if (clk_get_freq(clk, &sc->timer0_freq) != 0) { device_printf(dev, "Cannot get clock source frequency\n"); return (ENXIO); } } #if defined(__arm__) a10_timer_eventtimer_setup(sc); arm_set_delay(a10_timer_delay, sc); a10_timer_timecounter.tc_priv = sc; a10_timer_timecounter.tc_frequency = sc->timer0_freq; tc_init(&a10_timer_timecounter); #elif defined(__aarch64__) a23_timer_timecounter_setup(sc); #endif if (bootverbose) { device_printf(sc->sc_dev, "clock: hz=%d stathz = %d\n", hz, stathz); device_printf(sc->sc_dev, "event timer clock frequency %ju\n", sc->timer0_freq); device_printf(sc->sc_dev, "timecounter clock frequency %jd\n", a10_timer_timecounter.tc_frequency); } return (0); } static int a10_timer_irq(void *arg) { struct a10_timer_softc *sc; uint32_t val; sc = (struct a10_timer_softc *)arg; /* Clear interrupt pending bit. */ timer_write_4(sc, TIMER_IRQ_STA_REG, TIMER_IRQ_PENDING(0)); val = timer_read_4(sc, TIMER_CTRL_REG(0)); /* * Disabled autoreload and sc_period > 0 means * timer_start was called with non NULL first value. * Now we will set periodic timer with the given period * value. */ if ((val & (1<<1)) == 0 && sc->sc_period > 0) { /* Update timer */ timer_write_4(sc, TIMER_CURV_REG(0), sc->sc_period); /* Make periodic and enable */ val |= TIMER_CTRL_AUTORELOAD | TIMER_CTRL_START; timer_write_4(sc, TIMER_CTRL_REG(0), val); } if (sc->et.et_active) sc->et.et_event_cb(&sc->et, sc->et.et_arg); return (FILTER_HANDLED); } /* * Event timer function for A10 and A13 */ +#if defined(__arm__) static void a10_timer_eventtimer_setup(struct a10_timer_softc *sc) { uint32_t val; /* Set clock source to OSC24M, 1 pre-division, continuous mode */ val = timer_read_4(sc, TIMER_CTRL_REG(0)); val &= ~TIMER_CTRL_PRESCALAR_MASK | ~TIMER_CTRL_MODE_MASK | ~TIMER_CTRL_CLKSRC_MASK; val |= TIMER_CTRL_PRESCALAR(1) | TIMER_CTRL_OSC24M; timer_write_4(sc, TIMER_CTRL_REG(0), val); /* Enable timer0 */ val = timer_read_4(sc, TIMER_IRQ_EN_REG); val |= TIMER_IRQ_ENABLE(0); timer_write_4(sc, TIMER_IRQ_EN_REG, val); /* Set desired frequency in event timer and timecounter */ sc->et.et_frequency = sc->timer0_freq; sc->et.et_name = "a10_timer Eventtimer"; sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC; sc->et.et_quality = 1000; sc->et.et_min_period = (0x00000005LLU << 32) / sc->et.et_frequency; sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; sc->et.et_start = a10_timer_timer_start; sc->et.et_stop = a10_timer_timer_stop; sc->et.et_priv = sc; et_register(&sc->et); } static int a10_timer_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period) { struct a10_timer_softc *sc; uint32_t count; uint32_t val; sc = (struct a10_timer_softc *)et->et_priv; if (period != 0) sc->sc_period = ((uint32_t)et->et_frequency * period) >> 32; else sc->sc_period = 0; if (first != 0) count = ((uint32_t)et->et_frequency * first) >> 32; else count = sc->sc_period; /* Update timer values */ timer_write_4(sc, TIMER_INTV_REG(0), sc->sc_period); timer_write_4(sc, TIMER_CURV_REG(0), count); val = timer_read_4(sc, TIMER_CTRL_REG(0)); if (period != 0) { /* periodic */ val |= TIMER_CTRL_AUTORELOAD; } else { /* oneshot */ val &= ~TIMER_CTRL_AUTORELOAD; } /* Enable timer0 */ val |= TIMER_IRQ_ENABLE(0); timer_write_4(sc, TIMER_CTRL_REG(0), val); return (0); } static int a10_timer_timer_stop(struct eventtimer *et) { struct a10_timer_softc *sc; uint32_t val; sc = (struct a10_timer_softc *)et->et_priv; /* Disable timer0 */ val = timer_read_4(sc, TIMER_CTRL_REG(0)); val &= ~TIMER_CTRL_START; timer_write_4(sc, TIMER_CTRL_REG(0), val); sc->sc_period = 0; return (0); } +#endif /* * Timecounter functions for A23 and above */ static void a23_timer_timecounter_setup(struct a10_timer_softc *sc) { uint32_t val; /* Set clock source to OSC24M, 1 pre-division, continuous mode */ val = timer_read_4(sc, TIMER_CTRL_REG(0)); val &= ~TIMER_CTRL_PRESCALAR_MASK | ~TIMER_CTRL_MODE_MASK | ~TIMER_CTRL_CLKSRC_MASK; val |= TIMER_CTRL_PRESCALAR(1) | TIMER_CTRL_OSC24M; timer_write_4(sc, TIMER_CTRL_REG(0), val); /* Set reload value */ timer_write_4(sc, TIMER_INTV_REG(0), ~0); val = timer_read_4(sc, TIMER_INTV_REG(0)); /* Enable timer0 */ val = timer_read_4(sc, TIMER_CTRL_REG(0)); val |= TIMER_CTRL_AUTORELOAD | TIMER_CTRL_START; timer_write_4(sc, TIMER_CTRL_REG(0), val); val = timer_read_4(sc, TIMER_CURV_REG(0)); a23_timer_timecounter.tc_priv = sc; a23_timer_timecounter.tc_frequency = sc->timer0_freq; tc_init(&a23_timer_timecounter); } static u_int a23_timer_get_timecount(struct timecounter *tc) { struct a10_timer_softc *sc; uint32_t val; sc = (struct a10_timer_softc *)tc->tc_priv; if (sc == NULL) return (0); val = timer_read_4(sc, TIMER_CURV_REG(0)); /* Counter count backwards */ return (~0u - val); } /* * Timecounter functions for A10 and A13, using the 64 bits counter */ static uint64_t timer_read_counter64(struct a10_timer_softc *sc) { uint32_t lo, hi; /* Latch counter, wait for it to be ready to read. */ timer_write_4(sc, CNT64_CTRL_REG, CNT64_CTRL_RL_EN); while (timer_read_4(sc, CNT64_CTRL_REG) & CNT64_CTRL_RL_EN) continue; hi = timer_read_4(sc, CNT64_HI_REG); lo = timer_read_4(sc, CNT64_LO_REG); return (((uint64_t)hi << 32) | lo); } #if defined(__arm__) static void a10_timer_delay(int usec, void *arg) { struct a10_timer_softc *sc = arg; uint64_t end, now; now = timer_read_counter64(sc); end = now + (sc->timer0_freq / 1000000) * (usec + 1); while (now < end) now = timer_read_counter64(sc); } #endif static u_int a10_timer_get_timecount(struct timecounter *tc) { if (tc->tc_priv == NULL) return (0); return ((u_int)timer_read_counter64(tc->tc_priv)); } static device_method_t a10_timer_methods[] = { DEVMETHOD(device_probe, a10_timer_probe), DEVMETHOD(device_attach, a10_timer_attach), DEVMETHOD_END }; static driver_t a10_timer_driver = { "a10_timer", a10_timer_methods, sizeof(struct a10_timer_softc), }; static devclass_t a10_timer_devclass; EARLY_DRIVER_MODULE(a10_timer, simplebus, a10_timer_driver, a10_timer_devclass, 0, 0, BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); Index: stable/12/sys/arm/broadcom/bcm2835/bcm2835_sdhost.c =================================================================== --- stable/12/sys/arm/broadcom/bcm2835/bcm2835_sdhost.c (revision 358652) +++ stable/12/sys/arm/broadcom/bcm2835/bcm2835_sdhost.c (revision 358653) @@ -1,1293 +1,1295 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Klaus P. Ohrhallinger * All rights reserved. * * Based on bcm2835_sdhci.c: * Copyright (c) 2012 Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); /* * pin 48-53 - card slot * pin 34-39 - radio module * * alt-0 - rubbish SDHCI (0x7e202000) aka sdhost * alt-3 - advanced SDHCI (0x7e300000) aka sdhci/mmc/sdio * * driving card slot with mmc: * * sdhost_pins { * brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>; * brcm,function = <0x7>; * brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>; * phandle = <0x17>; * }; * sdio_pins { * brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>; * brcm,function = <0x4>; * brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>; * phandle = <0x18>; * }; * * driving card slot with sdhost: * * sdhost_pins { * brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>; * brcm,function = <0x4>; * brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>; * phandle = <0x17>; * }; * sdio_pins { * brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>; * brcm,function = <0x7>; * brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>; * phandle = <0x18>; * }; * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mmcbr_if.h" #include "sdhci_if.h" #include "opt_mmccam.h" #include "bcm2835_dma.h" #include #include "bcm2835_vcbus.h" /* #define SDHOST_DEBUG */ /* Registers */ #define HC_COMMAND 0x00 /* Command and flags */ #define HC_ARGUMENT 0x04 #define HC_TIMEOUTCOUNTER 0x08 #define HC_CLOCKDIVISOR 0x0c #define HC_RESPONSE_0 0x10 #define HC_RESPONSE_1 0x14 #define HC_RESPONSE_2 0x18 #define HC_RESPONSE_3 0x1c #define HC_HOSTSTATUS 0x20 #define HC_POWER 0x30 #define HC_DEBUG 0x34 #define HC_HOSTCONFIG 0x38 #define HC_BLOCKSIZE 0x3c #define HC_DATAPORT 0x40 #define HC_BLOCKCOUNT 0x50 /* Flags for HC_COMMAND register */ #define HC_CMD_ENABLE 0x8000 #define HC_CMD_FAILED 0x4000 #define HC_CMD_BUSY 0x0800 #define HC_CMD_RESPONSE_NONE 0x0400 #define HC_CMD_RESPONSE_LONG 0x0200 #define HC_CMD_WRITE 0x0080 #define HC_CMD_READ 0x0040 #define HC_CMD_COMMAND_MASK 0x003f #define HC_CLOCKDIVISOR_MAXVAL 0x07ff /* Flags for HC_HOSTSTATUS register */ #define HC_HSTST_HAVEDATA 0x0001 #define HC_HSTST_ERROR_FIFO 0x0008 #define HC_HSTST_ERROR_CRC7 0x0010 #define HC_HSTST_ERROR_CRC16 0x0020 #define HC_HSTST_TIMEOUT_CMD 0x0040 #define HC_HSTST_TIMEOUT_DATA 0x0080 #define HC_HSTST_INT_BLOCK 0x0200 #define HC_HSTST_INT_BUSY 0x0400 #define HC_HSTST_RESET 0xffff #define HC_HSTST_MASK_ERROR_DATA (HC_HSTST_ERROR_FIFO | \ HC_HSTST_ERROR_CRC7 | HC_HSTST_ERROR_CRC16 | HC_HSTST_TIMEOUT_DATA) #define HC_HSTST_MASK_ERROR_ALL (HC_HSTST_MASK_ERROR_DATA | \ HC_HSTST_TIMEOUT_CMD) /* Flags for HC_HOSTCONFIG register */ #define HC_HSTCF_INTBUS_WIDE 0x0002 #define HC_HSTCF_EXTBUS_4BIT 0x0004 #define HC_HSTCF_SLOW_CARD 0x0008 #define HC_HSTCF_INT_DATA 0x0010 #define HC_HSTCF_INT_BLOCK 0x0100 #define HC_HSTCF_INT_BUSY 0x0400 /* Flags for HC_DEBUG register */ #define HC_DBG_FIFO_THRESH_WRITE_SHIFT 9 #define HC_DBG_FIFO_THRESH_READ_SHIFT 14 #define HC_DBG_FIFO_THRESH_MASK 0x001f /* Settings */ #define HC_FIFO_SIZE 16 #define HC_FIFO_THRESH_READ 4 #define HC_FIFO_THRESH_WRITE 4 #define HC_TIMEOUT_DEFAULT 0x00f00000 #define BCM2835_DEFAULT_SDHCI_FREQ 50 static int bcm2835_sdhost_debug = 0; #ifdef SDHOST_DEBUG TUNABLE_INT("hw.bcm2835.sdhost.debug", &bcm2835_sdhost_debug); SYSCTL_INT(_hw_sdhci, OID_AUTO, bcm2835_sdhost_debug, CTLFLAG_RWTUN, &bcm2835_sdhost_debug, 0, "bcm2835-sdhost Debug level"); #define dprintf(fmt, args...) \ do { \ if (bcm2835_sdhost_debug > 0) \ printf(fmt,##args); \ } while (0) #else #define dprintf(fmt, args...) #endif /* ! SDHOST_DEBUG */ static struct ofw_compat_data compat_data[] = { {"brcm,bcm2835-sdhost", 1}, {NULL, 0} }; struct bcm_sdhost_softc { device_t sc_dev; struct resource * sc_mem_res; struct resource * sc_irq_res; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_intrhand; struct mmc_request * sc_req; struct sdhci_slot sc_slot; struct mtx mtx; char cmdbusy; char mmc_app_cmd; u_int32_t sdhci_int_status; u_int32_t sdhci_signal_enable; u_int32_t sdhci_present_state; u_int32_t sdhci_blocksize; u_int32_t sdhci_blockcount; u_int32_t sdcard_rca; }; static int bcm_sdhost_probe(device_t); static int bcm_sdhost_attach(device_t); static int bcm_sdhost_detach(device_t); static void bcm_sdhost_intr(void *); static int bcm_sdhost_get_ro(device_t, device_t); static inline uint32_t RD4(struct bcm_sdhost_softc *sc, bus_size_t off) { uint32_t val; val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off); return (val); } static inline void WR4(struct bcm_sdhost_softc *sc, bus_size_t off, uint32_t val) { bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val); } +#ifdef notyet static inline uint16_t RD2(struct bcm_sdhost_softc *sc, bus_size_t off) { uint32_t val; val = RD4(sc, off & ~3); return ((val >> (off & 3)*8) & 0xffff); } +#endif static inline uint8_t RD1(struct bcm_sdhost_softc *sc, bus_size_t off) { uint32_t val; val = RD4(sc, off & ~3); return ((val >> (off & 3)*8) & 0xff); } static inline void WR2(struct bcm_sdhost_softc *sc, bus_size_t off, uint16_t val) { uint32_t val32; val32 = RD4(sc, off & ~3); val32 &= ~(0xffff << (off & 3)*8); val32 |= (val << (off & 3)*8); WR4(sc, off & ~3, val32); } static inline void WR1(struct bcm_sdhost_softc *sc, bus_size_t off, uint8_t val) { uint32_t val32; val32 = RD4(sc, off & ~3); val32 &= ~(0xff << (off & 3)*8); val32 |= (val << (off & 3)*8); WR4(sc, off & ~3, val32); } static void bcm_sdhost_print_regs(struct bcm_sdhost_softc *sc, struct sdhci_slot *slot, int line, int error) { if (bcm2835_sdhost_debug > 0 || error > 0) { printf("%s: sc=%p slot=%p\n", __func__, sc, slot); printf("HC_COMMAND: 0x%08x\n", RD4(sc, HC_COMMAND)); printf("HC_ARGUMENT: 0x%08x\n", RD4(sc, HC_ARGUMENT)); printf("HC_TIMEOUTCOUNTER: 0x%08x\n", RD4(sc, HC_TIMEOUTCOUNTER)); printf("HC_CLOCKDIVISOR: 0x%08x\n", RD4(sc, HC_CLOCKDIVISOR)); printf("HC_RESPONSE_0: 0x%08x\n", RD4(sc, HC_RESPONSE_0)); printf("HC_RESPONSE_1: 0x%08x\n", RD4(sc, HC_RESPONSE_1)); printf("HC_RESPONSE_2: 0x%08x\n", RD4(sc, HC_RESPONSE_2)); printf("HC_RESPONSE_3: 0x%08x\n", RD4(sc, HC_RESPONSE_3)); printf("HC_HOSTSTATUS: 0x%08x\n", RD4(sc, HC_HOSTSTATUS)); printf("HC_POWER: 0x%08x\n", RD4(sc, HC_POWER)); printf("HC_DEBUG: 0x%08x\n", RD4(sc, HC_DEBUG)); printf("HC_HOSTCONFIG: 0x%08x\n", RD4(sc, HC_HOSTCONFIG)); printf("HC_BLOCKSIZE: 0x%08x\n", RD4(sc, HC_BLOCKSIZE)); printf("HC_BLOCKCOUNT: 0x%08x\n", RD4(sc, HC_BLOCKCOUNT)); } else { /* printf("%04d | HC_COMMAND: 0x%08x HC_ARGUMENT: 0x%08x " "HC_HOSTSTATUS: 0x%08x HC_HOSTCONFIG: 0x%08x\n", line, RD4(sc, HC_COMMAND), RD4(sc, HC_ARGUMENT), RD4(sc, HC_HOSTSTATUS), RD4(sc, HC_HOSTCONFIG)); */ } } static void bcm_sdhost_reset(device_t dev, struct sdhci_slot *slot) { struct bcm_sdhost_softc *sc = device_get_softc(dev); u_int32_t dbg; WR4(sc, HC_POWER, 0); WR4(sc, HC_COMMAND, 0); WR4(sc, HC_ARGUMENT, 0); WR4(sc, HC_TIMEOUTCOUNTER, HC_TIMEOUT_DEFAULT); WR4(sc, HC_CLOCKDIVISOR, 0); WR4(sc, HC_HOSTSTATUS, HC_HSTST_RESET); WR4(sc, HC_HOSTCONFIG, 0); WR4(sc, HC_BLOCKSIZE, 0); WR4(sc, HC_BLOCKCOUNT, 0); dbg = RD4(sc, HC_DEBUG); dbg &= ~( (HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_READ_SHIFT) | (HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_WRITE_SHIFT) ); dbg |= (HC_FIFO_THRESH_READ << HC_DBG_FIFO_THRESH_READ_SHIFT) | (HC_FIFO_THRESH_WRITE << HC_DBG_FIFO_THRESH_WRITE_SHIFT); WR4(sc, HC_DEBUG, dbg); DELAY(250000); WR4(sc, HC_POWER, 1); DELAY(250000); sc->sdhci_present_state = SDHCI_CARD_PRESENT | SDHCI_CARD_STABLE | SDHCI_WRITE_PROTECT; WR4(sc, HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_MAXVAL); WR4(sc, HC_HOSTCONFIG, HC_HSTCF_INT_BUSY); } static int bcm_sdhost_probe(device_t dev) { dprintf("%s:\n", __func__); if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Broadcom 2708 SDHOST controller"); return (BUS_PROBE_DEFAULT); } static int bcm_sdhost_attach(device_t dev) { struct bcm_sdhost_softc *sc = device_get_softc(dev); int rid, err; u_int default_freq; dprintf("%s: dev=%p sc=%p unit=%d\n", __func__, dev, sc, device_get_unit(dev)); mtx_init(&sc->mtx, "BCM SDHOST mtx", "bcm_sdhost", MTX_DEF | MTX_RECURSE); sc->sc_dev = dev; sc->sc_req = NULL; sc->cmdbusy = 0; sc->mmc_app_cmd = 0; sc->sdhci_int_status = 0; sc->sdhci_signal_enable = 0; sc->sdhci_present_state = 0; sc->sdhci_blocksize = 0; sc->sdhci_blockcount = 0; sc->sdcard_rca = 0; default_freq = 50; err = 0; if (bootverbose) device_printf(dev, "SDHCI frequency: %dMHz\n", default_freq); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); err = ENXIO; goto fail; } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); bcm_sdhost_reset(dev, &sc->sc_slot); bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 0); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { device_printf(dev, "cannot allocate interrupt\n"); err = ENXIO; goto fail; } if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, bcm_sdhost_intr, sc, &sc->sc_intrhand)) { device_printf(dev, "cannot setup interrupt handler\n"); err = ENXIO; goto fail; } sc->sc_slot.caps = 0; sc->sc_slot.caps |= SDHCI_CAN_VDD_330; sc->sc_slot.caps |= SDHCI_CAN_DO_HISPD; sc->sc_slot.caps |= (default_freq << SDHCI_CLOCK_BASE_SHIFT); sc->sc_slot.quirks = 0; sc->sc_slot.quirks |= SDHCI_QUIRK_MISSING_CAPS; sc->sc_slot.quirks |= SDHCI_QUIRK_DONT_SHIFT_RESPONSE; sc->sc_slot.opt = 0; /* XXX ? sc->slot->timeout_clk = ...; */ sdhci_init_slot(dev, &sc->sc_slot, 0); bus_generic_probe(dev); bus_generic_attach(dev); sdhci_start_slot(&sc->sc_slot); return (0); fail: if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (err); } static int bcm_sdhost_detach(device_t dev) { dprintf("%s:\n", __func__); return (EBUSY); } /* * rv 0 --> command finished * rv 1 --> command timed out */ static inline int bcm_sdhost_waitcommand(struct bcm_sdhost_softc *sc) { int timeout = 1000; mtx_assert(&sc->mtx, MA_OWNED); while ((RD4(sc, HC_COMMAND) & HC_CMD_ENABLE) && --timeout > 0) { DELAY(100); } return ((timeout > 0) ? 0 : 1); } static int bcm_sdhost_waitcommand_status(struct bcm_sdhost_softc *sc) { u_int32_t cdst; int i; /* wait for card to change status from * ''prg'' to ''trn'' * card status: sd specs p. 103 */ i = 0; do { DELAY(1000); WR4(sc, HC_ARGUMENT, sc->sdcard_rca << 16); WR4(sc, HC_COMMAND, MMC_SEND_STATUS | HC_CMD_ENABLE); bcm_sdhost_waitcommand(sc); cdst = RD4(sc, HC_RESPONSE_0); dprintf("%s: card status %08x (cs %d)\n", __func__, cdst, (cdst & 0x0e00) >> 9); if (i++ > 100) { printf("%s: giving up, " "card status %08x (cs %d)\n", __func__, cdst, (cdst & 0x0e00) >> 9); return (1); break; } } while (((cdst & 0x0e00) >> 9) != 4); return (0); } static void bcm_sdhost_intr(void *arg) { struct bcm_sdhost_softc *sc = arg; struct sdhci_slot *slot = &sc->sc_slot; uint32_t hstst; uint32_t cmd; mtx_lock(&sc->mtx); hstst = RD4(sc, HC_HOSTSTATUS); cmd = RD4(sc, HC_COMMAND); if (hstst & HC_HSTST_HAVEDATA) { if (cmd & HC_CMD_READ) { sc->sdhci_present_state |= SDHCI_DATA_AVAILABLE; sc->sdhci_int_status |= SDHCI_INT_DATA_AVAIL; } else if (cmd & HC_CMD_WRITE) { sc->sdhci_present_state |= SDHCI_SPACE_AVAILABLE; sc->sdhci_int_status |= SDHCI_INT_SPACE_AVAIL; } else { panic("%s: hstst & HC_HSTST_HAVEDATA but no " "HC_CMD_READ or HC_CMD_WRITE: cmd=%0x8 " "hstst=%08x\n", __func__, cmd, hstst); } } else { sc->sdhci_present_state &= ~(SDHCI_DATA_AVAILABLE|SDHCI_SPACE_AVAILABLE); sc->sdhci_int_status &= ~(SDHCI_INT_DATA_AVAIL|SDHCI_INT_SPACE_AVAIL); } if (hstst & HC_HSTST_MASK_ERROR_ALL) { printf("%s: ERROR: HC_HOSTSTATUS: %08x\n", __func__, hstst); bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1); sc->sdhci_int_status |= SDHCI_INT_ERROR; } else { sc->sdhci_int_status &= ~SDHCI_INT_ERROR; } dprintf("%s: hstst=%08x offset=%08lx sdhci_present_state=%08x " "sdhci_int_status=%08x\n", __func__, hstst, slot->offset, sc->sdhci_present_state, sc->sdhci_int_status); sdhci_generic_intr(&sc->sc_slot); sc->sdhci_int_status &= ~(SDHCI_INT_ERROR|SDHCI_INT_DATA_AVAIL|SDHCI_INT_DATA_END); sc->sdhci_present_state &= ~SDHCI_DATA_AVAILABLE; if ((hstst & HC_HSTST_HAVEDATA) && (sc->sdhci_blocksize * sc->sdhci_blockcount == slot->offset)) { dprintf("%s: offset=%08lx sdhci_blocksize=%08x " "sdhci_blockcount=%08x\n", __func__, slot->offset, sc->sdhci_blocksize, sc->sdhci_blockcount); sc->sdhci_int_status &= ~(SDHCI_INT_DATA_AVAIL|SDHCI_INT_SPACE_AVAIL); sc->sdhci_int_status |= SDHCI_INT_DATA_END; sdhci_generic_intr(&sc->sc_slot); sc->sdhci_int_status &= ~SDHCI_INT_DATA_END; if ((cmd & HC_CMD_COMMAND_MASK) == MMC_READ_MULTIPLE_BLOCK || (cmd & HC_CMD_COMMAND_MASK) == MMC_WRITE_MULTIPLE_BLOCK) { WR4(sc, HC_ARGUMENT, 0x00000000); WR4(sc, HC_COMMAND, MMC_STOP_TRANSMISSION | HC_CMD_ENABLE); if (bcm_sdhost_waitcommand(sc)) { printf("%s: timeout #1\n", __func__); bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1); } } if (cmd & HC_CMD_WRITE) { if (bcm_sdhost_waitcommand_status(sc) != 0) sc->sdhci_int_status |= SDHCI_INT_ERROR; } slot->data_done = 1; sc->sdhci_int_status |= SDHCI_INT_RESPONSE; sdhci_generic_intr(&sc->sc_slot); sc->sdhci_int_status &= ~(SDHCI_INT_RESPONSE|SDHCI_INT_ERROR); } /* this resets the interrupt */ WR4(sc, HC_HOSTSTATUS, (HC_HSTST_INT_BUSY|HC_HSTST_INT_BLOCK|HC_HSTST_HAVEDATA)); mtx_unlock(&sc->mtx); } static int bcm_sdhost_get_ro(device_t bus, device_t child) { dprintf("%s:\n", __func__); return (0); } static bool bcm_sdhost_get_card_present(device_t dev, struct sdhci_slot *slot) { dprintf("%s:\n", __func__); return (1); } static void bcm_sdhost_command(device_t dev, struct sdhci_slot *slot, uint16_t val) { struct bcm_sdhost_softc *sc = device_get_softc(dev); struct mmc_data *data = slot->curcmd->data; uint16_t val2; uint8_t opcode; uint8_t flags; mtx_assert(&sc->mtx, MA_OWNED); if (RD4(sc, HC_COMMAND) & HC_CMD_ENABLE) { panic("%s: HC_CMD_ENABLE on entry\n", __func__); } if (sc->cmdbusy == 1) panic("%s: cmdbusy\n", __func__); sc->cmdbusy = 1; val2 = ((val >> 8) & HC_CMD_COMMAND_MASK) | HC_CMD_ENABLE; opcode = val >> 8; flags = val & 0xff; if (opcode == MMC_APP_CMD) sc->mmc_app_cmd = 1; if ((flags & SDHCI_CMD_RESP_MASK) == SDHCI_CMD_RESP_LONG) val2 |= HC_CMD_RESPONSE_LONG; else if ((flags & SDHCI_CMD_RESP_MASK) == SDHCI_CMD_RESP_SHORT_BUSY) /* XXX XXX when enabled, cmd 7 (select card) blocks forever */ ;/*val2 |= HC_CMD_BUSY; */ else if ((flags & SDHCI_CMD_RESP_MASK) == SDHCI_CMD_RESP_SHORT) ; else val2 |= HC_CMD_RESPONSE_NONE; if (val2 & HC_CMD_BUSY) sc->sdhci_present_state |= SDHCI_CMD_INHIBIT | SDHCI_DAT_INHIBIT; if (data != NULL && data->flags & MMC_DATA_READ) val2 |= HC_CMD_READ; else if (data != NULL && data->flags & MMC_DATA_WRITE) val2 |= HC_CMD_WRITE; dprintf("%s: SDHCI_COMMAND_FLAGS --> HC_COMMAND %04x --> %04x\n", __func__, val, val2); if (opcode == MMC_READ_MULTIPLE_BLOCK || opcode == MMC_WRITE_MULTIPLE_BLOCK) { u_int32_t save_sdarg; dprintf("%s: issuing MMC_SET_BLOCK_COUNT: CMD %08x ARG %08x\n", __func__, MMC_SET_BLOCK_COUNT | HC_CMD_ENABLE, sc->sdhci_blockcount); save_sdarg = RD4(sc, HC_ARGUMENT); WR4(sc, HC_ARGUMENT, sc->sdhci_blockcount); WR4(sc, HC_COMMAND, MMC_SET_BLOCK_COUNT | HC_CMD_ENABLE); /* Seems to always return timeout */ if (bcm_sdhost_waitcommand(sc)) { printf("%s: timeout #2\n", __func__); bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1); } else { bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 0); } WR4(sc, HC_ARGUMENT, save_sdarg); } else if (opcode == MMC_SELECT_CARD) { sc->sdcard_rca = (RD4(sc, HC_ARGUMENT) >> 16); } /* actually issuing the command */ WR4(sc, HC_COMMAND, val2); if (val2 & HC_CMD_READ || val2 & HC_CMD_WRITE) { u_int8_t hstcfg; hstcfg = RD4(sc, HC_HOSTCONFIG); hstcfg |= (HC_HSTCF_INT_BUSY | HC_HSTCF_INT_DATA); WR4(sc, HC_HOSTCONFIG, hstcfg); slot->data_done = 0; if (bcm_sdhost_waitcommand(sc)) { printf("%s: timeout #3\n", __func__); bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1); } } else if (opcode == MMC_ERASE) { if (bcm_sdhost_waitcommand_status(sc) != 0) { printf("%s: timeout #4\n", __func__); bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1); } slot->data_done = 1; sc->sdhci_present_state &= ~(SDHCI_CMD_INHIBIT | SDHCI_DAT_INHIBIT); } else { if (bcm_sdhost_waitcommand(sc)) { printf("%s: timeout #5\n", __func__); bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1); } slot->data_done = 1; sc->sdhci_present_state &= ~(SDHCI_CMD_INHIBIT | SDHCI_DAT_INHIBIT); } bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 0); if (RD4(sc, HC_HOSTSTATUS) & HC_HSTST_TIMEOUT_CMD) slot->curcmd->error = MMC_ERR_TIMEOUT; else if (RD4(sc, HC_COMMAND) & HC_CMD_FAILED) slot->curcmd->error = MMC_ERR_FAILED; dprintf("%s: curcmd->flags=%d data_done=%d\n", __func__, slot->curcmd->flags, slot->data_done); if (val2 & HC_CMD_RESPONSE_NONE) slot->curcmd->error = 0; if (sc->mmc_app_cmd == 1 && opcode != MMC_APP_CMD) sc->mmc_app_cmd = 0; if (RD4(sc, HC_COMMAND) & HC_CMD_ENABLE) { bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1); panic("%s: still HC_CMD_ENABLE on exit\n", __func__); } sc->cmdbusy = 0; if (!(val2 & HC_CMD_READ || val2 & HC_CMD_WRITE)) sc->sdhci_int_status |= SDHCI_INT_RESPONSE; /* HACK, so sdhci_finish_command() does not * have to be exported */ mtx_unlock(&slot->mtx); sdhci_generic_intr(slot); mtx_lock(&slot->mtx); sc->sdhci_int_status &= ~SDHCI_INT_RESPONSE; } static uint8_t bcm_sdhost_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhost_softc *sc = device_get_softc(dev); uint32_t val1, val2; mtx_lock(&sc->mtx); switch (off) { case SDHCI_HOST_CONTROL: val1 = RD4(sc, HC_HOSTCONFIG); val2 = 0; if (val1 & HC_HSTCF_EXTBUS_4BIT) val2 |= SDHCI_CTRL_4BITBUS; dprintf("%s: SDHCI_HOST_CONTROL --> HC_HOSTCONFIG val2 %02x\n", __func__, val2); break; case SDHCI_POWER_CONTROL: val1 = RD1(sc, HC_POWER); val2 = (val1 == 1) ? 0x0f : 0; dprintf("%s: SDHCI_POWER_CONTROL --> HC_POWER val2 %02x\n", __func__, val2); break; case SDHCI_BLOCK_GAP_CONTROL: dprintf("%s: SDHCI_BLOCK_GAP_CONTROL\n", __func__); val2 = 0; break; case SDHCI_WAKE_UP_CONTROL: dprintf("%s: SDHCI_WAKE_UP_CONTROL\n", __func__); val2 = 0; break; case SDHCI_TIMEOUT_CONTROL: dprintf("%s: SDHCI_TIMEOUT_CONTROL\n", __func__); val2 = 0; break; case SDHCI_SOFTWARE_RESET: dprintf("%s: SDHCI_SOFTWARE_RESET\n", __func__); val2 = 0; break; case SDHCI_ADMA_ERR: dprintf("%s: SDHCI_ADMA_ERR\n", __func__); val2 = 0; break; default: dprintf("%s: UNKNOWN off=%08lx\n", __func__, off); val2 = 0; break; } mtx_unlock(&sc->mtx); return (val2); } static uint16_t bcm_sdhost_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhost_softc *sc = device_get_softc(dev); uint32_t val2, val; /* = RD4(sc, off & ~3); */ mtx_lock(&sc->mtx); switch (off) { case SDHCI_BLOCK_SIZE: val2 = sc->sdhci_blocksize; dprintf("%s: SDHCI_BLOCK_SIZE --> HC_BLOCKSIZE %08x\n", __func__, val2); break; case SDHCI_BLOCK_COUNT: val2 = sc->sdhci_blockcount; dprintf("%s: SDHCI_BLOCK_COUNT --> HC_BLOCKCOUNT %08x\n", __func__, val2); break; case SDHCI_TRANSFER_MODE: dprintf("%s: SDHCI_TRANSFER_MODE\n", __func__); val2 = 0; break; case SDHCI_CLOCK_CONTROL: val = RD4(sc, HC_CLOCKDIVISOR); val2 = (val << SDHCI_DIVIDER_SHIFT) | SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN | SDHCI_CLOCK_INT_STABLE; dprintf("%s: SDHCI_CLOCK_CONTROL %04x --> %04x\n", __func__, val, val2); break; case SDHCI_ACMD12_ERR: dprintf("%s: SDHCI_ACMD12_ERR\n", __func__); val2 = 0; break; case SDHCI_HOST_CONTROL2: dprintf("%s: SDHCI_HOST_CONTROL2\n", __func__); val2 = 0; break; case SDHCI_SLOT_INT_STATUS: dprintf("%s: SDHCI_SLOT_INT_STATUS\n", __func__); val2 = 0; break; case SDHCI_HOST_VERSION: dprintf("%s: SDHCI_HOST_VERSION\n", __func__); val2 = 0; break; default: dprintf("%s: UNKNOWN off=%08lx\n", __func__, off); val2 = 0; break; } mtx_unlock(&sc->mtx); return (val2); } static uint32_t bcm_sdhost_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhost_softc *sc = device_get_softc(dev); uint32_t val2; mtx_lock(&sc->mtx); switch (off) { case SDHCI_DMA_ADDRESS: dprintf("%s: SDHCI_DMA_ADDRESS\n", __func__); val2 = 0; break; case SDHCI_ARGUMENT: dprintf("%s: SDHCI_ARGUMENT\n", __func__); val2 = (RD4(sc, HC_COMMAND) << 16) | (RD4(sc, HC_ARGUMENT) & 0x0000ffff); break; case SDHCI_RESPONSE + 0: val2 = RD4(sc, HC_RESPONSE_0); dprintf("%s: SDHCI_RESPONSE+0 %08x\n", __func__, val2); break; case SDHCI_RESPONSE + 4: val2 = RD4(sc, HC_RESPONSE_1); dprintf("%s: SDHCI_RESPONSE+4 %08x\n", __func__, val2); break; case SDHCI_RESPONSE + 8: val2 = RD4(sc, HC_RESPONSE_2); dprintf("%s: SDHCI_RESPONSE+8 %08x\n", __func__, val2); break; case SDHCI_RESPONSE + 12: val2 = RD4(sc, HC_RESPONSE_3); dprintf("%s: SDHCI_RESPONSE+12 %08x\n", __func__, val2); break; case SDHCI_BUFFER: dprintf("%s: SDHCI_BUFFER\n", __func__); val2 = 0; break; case SDHCI_PRESENT_STATE: dprintf("%s: SDHCI_PRESENT_STATE %08x\n", __func__, sc->sdhci_present_state); val2 = sc->sdhci_present_state; break; case SDHCI_INT_STATUS: dprintf("%s: SDHCI_INT_STATUS %08x\n", __func__, sc->sdhci_int_status); val2 = sc->sdhci_int_status; break; case SDHCI_INT_ENABLE: dprintf("%s: SDHCI_INT_ENABLE\n", __func__); val2 = 0; break; case SDHCI_SIGNAL_ENABLE: dprintf("%s: SDHCI_SIGNAL_ENABLE %08x\n", __func__, sc->sdhci_signal_enable); val2 = sc->sdhci_signal_enable; break; case SDHCI_CAPABILITIES: val2 = 0; break; case SDHCI_CAPABILITIES2: dprintf("%s: SDHCI_CAPABILITIES2\n", __func__); val2 = 0; break; case SDHCI_MAX_CURRENT: dprintf("%s: SDHCI_MAX_CURRENT\n", __func__); val2 = 0; break; case SDHCI_ADMA_ADDRESS_LO: dprintf("%s: SDHCI_ADMA_ADDRESS_LO\n", __func__); val2 = 0; break; default: dprintf("%s: UNKNOWN off=%08lx\n", __func__, off); val2 = 0; break; } mtx_unlock(&sc->mtx); return (val2); } static void bcm_sdhost_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct bcm_sdhost_softc *sc = device_get_softc(dev); bus_size_t i; bus_size_t avail; uint32_t edm; mtx_lock(&sc->mtx); dprintf("%s: off=%08lx count=%08lx\n", __func__, off, count); for (i = 0; i < count;) { edm = RD4(sc, HC_DEBUG); avail = ((edm >> 4) & 0x1f); if (i + avail > count) avail = count - i; if (avail > 0) bus_space_read_multi_4(sc->sc_bst, sc->sc_bsh, HC_DATAPORT, data + i, avail); i += avail; DELAY(1); } mtx_unlock(&sc->mtx); } static void bcm_sdhost_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val) { struct bcm_sdhost_softc *sc = device_get_softc(dev); uint32_t val2; mtx_lock(&sc->mtx); switch (off) { case SDHCI_HOST_CONTROL: val2 = RD4(sc, HC_HOSTCONFIG); val2 |= HC_HSTCF_INT_BUSY; val2 |= HC_HSTCF_INTBUS_WIDE | HC_HSTCF_SLOW_CARD; if (val & SDHCI_CTRL_4BITBUS) val2 |= HC_HSTCF_EXTBUS_4BIT; dprintf("%s: SDHCI_HOST_CONTROL --> HC_HOSTC %04x --> %04x\n", __func__, val, val2); WR4(sc, HC_HOSTCONFIG, val2); break; case SDHCI_POWER_CONTROL: val2 = (val != 0) ? 1 : 0; dprintf("%s: SDHCI_POWER_CONTROL --> HC_POWER %02x --> %02x\n", __func__, val, val2); WR1(sc, HC_POWER, val2); break; case SDHCI_BLOCK_GAP_CONTROL: dprintf("%s: SDHCI_BLOCK_GAP_CONTROL val=%02x\n", __func__, val); break; case SDHCI_TIMEOUT_CONTROL: dprintf("%s: SDHCI_TIMEOUT_CONTROL val=%02x\n", __func__, val); break; case SDHCI_SOFTWARE_RESET: dprintf("%s: SDHCI_SOFTWARE_RESET val=%02x\n", __func__, val); break; case SDHCI_ADMA_ERR: dprintf("%s: SDHCI_ADMA_ERR val=%02x\n", __func__, val); break; default: dprintf("%s: UNKNOWN off=%08lx val=%08x\n", __func__, off, val); break; } mtx_unlock(&sc->mtx); } static void bcm_sdhost_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val) { struct bcm_sdhost_softc *sc = device_get_softc(dev); uint16_t val2; mtx_lock(&sc->mtx); switch (off) { case SDHCI_BLOCK_SIZE: dprintf("%s: SDHCI_BLOCK_SIZE val=%04x\n" , __func__, val); sc->sdhci_blocksize = val; WR2(sc, HC_BLOCKSIZE, val); break; case SDHCI_BLOCK_COUNT: dprintf("%s: SDHCI_BLOCK_COUNT val=%04x\n" , __func__, val); sc->sdhci_blockcount = val; WR2(sc, HC_BLOCKCOUNT, val); break; case SDHCI_TRANSFER_MODE: dprintf("%s: SDHCI_TRANSFER_MODE val=%04x\n" , __func__, val); break; case SDHCI_COMMAND_FLAGS: bcm_sdhost_command(dev, slot, val); break; case SDHCI_CLOCK_CONTROL: val2 = (val & ~SDHCI_DIVIDER_MASK) >> SDHCI_DIVIDER_SHIFT; /* get crc16 errors with cdiv=0 */ if (val2 == 0) val2 = 1; dprintf("%s: SDHCI_CLOCK_CONTROL %04x --> SCDIV %04x\n", __func__, val, val2); WR4(sc, HC_CLOCKDIVISOR, val2); break; case SDHCI_ACMD12_ERR: dprintf("%s: SDHCI_ACMD12_ERR val=%04x\n" , __func__, val); break; case SDHCI_HOST_CONTROL2: dprintf("%s: SDHCI_HOST_CONTROL2 val=%04x\n" , __func__, val); break; case SDHCI_SLOT_INT_STATUS: dprintf("%s: SDHCI_SLOT_INT_STATUS val=%04x\n" , __func__, val); break; default: dprintf("%s: UNKNOWN off=%08lx val=%04x\n", __func__, off, val); break; } mtx_unlock(&sc->mtx); } static void bcm_sdhost_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val) { struct bcm_sdhost_softc *sc = device_get_softc(dev); uint32_t val2; uint32_t hstcfg; mtx_lock(&sc->mtx); switch (off) { case SDHCI_ARGUMENT: val2 = val; dprintf("%s: SDHCI_ARGUMENT --> HC_ARGUMENT val=%08x\n", __func__, val); WR4(sc, HC_ARGUMENT, val2); break; case SDHCI_INT_STATUS: dprintf("%s: SDHCI_INT_STATUS val=%08x\n", __func__, val); sc->sdhci_int_status = val; break; case SDHCI_INT_ENABLE: dprintf("%s: SDHCI_INT_ENABLE val=%08x\n" , __func__, val); break; case SDHCI_SIGNAL_ENABLE: sc->sdhci_signal_enable = val; hstcfg = RD4(sc, HC_HOSTCONFIG); if (val != 0) hstcfg &= ~(HC_HSTCF_INT_BLOCK | HC_HSTCF_INT_DATA); else hstcfg |= (HC_HSTCF_INT_BUSY|HC_HSTCF_INT_BLOCK| HC_HSTCF_INT_DATA); hstcfg |= HC_HSTCF_INT_BUSY; dprintf("%s: SDHCI_SIGNAL_ENABLE --> HC_HOSTC %08x --> %08x\n" , __func__, val, hstcfg); WR4(sc, HC_HOSTCONFIG, hstcfg); break; case SDHCI_CAPABILITIES: dprintf("%s: SDHCI_CAPABILITIES val=%08x\n", __func__, val); break; case SDHCI_CAPABILITIES2: dprintf("%s: SDHCI_CAPABILITIES2 val=%08x\n", __func__, val); break; case SDHCI_MAX_CURRENT: dprintf("%s: SDHCI_MAX_CURRENT val=%08x\n", __func__, val); break; case SDHCI_ADMA_ADDRESS_LO: dprintf("%s: SDHCI_ADMA_ADDRESS_LO val=%08x\n", __func__, val); break; default: dprintf("%s: UNKNOWN off=%08lx val=%08x\n", __func__, off, val); break; } mtx_unlock(&sc->mtx); } static void bcm_sdhost_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct bcm_sdhost_softc *sc = device_get_softc(dev); bus_size_t i; bus_size_t space; uint32_t edm; mtx_lock(&sc->mtx); dprintf("%s: off=%08lx count=%02lx\n", __func__, off, count); for (i = 0; i < count;) { edm = RD4(sc, HC_DEBUG); space = HC_FIFO_SIZE - ((edm >> 4) & 0x1f); if (i + space > count) space = count - i; if (space > 0) bus_space_write_multi_4(sc->sc_bst, sc->sc_bsh, HC_DATAPORT, data + i, space); i += space; DELAY(1); } /* wait until FIFO is really empty */ while (((RD4(sc, HC_DEBUG) >> 4) & 0x1f) > 0) DELAY(1); mtx_unlock(&sc->mtx); } static device_method_t bcm_sdhost_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_sdhost_probe), DEVMETHOD(device_attach, bcm_sdhost_attach), DEVMETHOD(device_detach, bcm_sdhost_detach), /* Bus interface */ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), /* MMC bridge interface */ DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios), DEVMETHOD(mmcbr_request, sdhci_generic_request), DEVMETHOD(mmcbr_get_ro, bcm_sdhost_get_ro), DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), /* SDHCI registers accessors */ DEVMETHOD(sdhci_read_1, bcm_sdhost_read_1), DEVMETHOD(sdhci_read_2, bcm_sdhost_read_2), DEVMETHOD(sdhci_read_4, bcm_sdhost_read_4), DEVMETHOD(sdhci_read_multi_4, bcm_sdhost_read_multi_4), DEVMETHOD(sdhci_write_1, bcm_sdhost_write_1), DEVMETHOD(sdhci_write_2, bcm_sdhost_write_2), DEVMETHOD(sdhci_write_4, bcm_sdhost_write_4), DEVMETHOD(sdhci_write_multi_4, bcm_sdhost_write_multi_4), DEVMETHOD(sdhci_get_card_present,bcm_sdhost_get_card_present), DEVMETHOD_END }; static devclass_t bcm_sdhost_devclass; static driver_t bcm_sdhost_driver = { "sdhost_bcm", bcm_sdhost_methods, sizeof(struct bcm_sdhost_softc), }; DRIVER_MODULE(sdhost_bcm, simplebus, bcm_sdhost_driver, bcm_sdhost_devclass, NULL, NULL); SDHCI_DEPEND(sdhost_bcm); #ifndef MMCCAM MMC_DECLARE_BRIDGE(sdhost_bcm); #endif Index: stable/12/sys/arm64/rockchip/if_dwc_rk.c =================================================================== --- stable/12/sys/arm64/rockchip/if_dwc_rk.c (revision 358652) +++ stable/12/sys/arm64/rockchip/if_dwc_rk.c (revision 358653) @@ -1,182 +1,192 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "syscon_if.h" #include "if_dwc_if.h" #define RK3328_GRF_MAC_CON0 0x0900 #define RK3328_GRF_MAC_CON0_TX_MASK 0x7F #define RK3328_GRF_MAC_CON0_TX_SHIFT 0 #define RK3328_GRF_MAC_CON0_RX_MASK 0x7F #define RK3328_GRF_MAC_CON0_RX_SHIFT 7 #define RK3328_GRF_MAC_CON1 0x0904 #define RK3328_GRF_MAC_CON2 0x0908 #define RK3328_GRF_MACPHY_CON0 0x0B00 #define RK3328_GRF_MACPHY_CON1 0x0B04 #define RK3328_GRF_MACPHY_CON2 0x0B08 #define RK3328_GRF_MACPHY_CON3 0x0B0C #define RK3328_GRF_MACPHY_STATUS 0x0B10 +static struct ofw_compat_data compat_data[] = { + {"rockchip,rk3288-gmac", 1}, + {"rockchip,rk3328-gmac", 1}, + {"rockchip,rk3399-gmac", 1}, + {NULL, 0} +}; + +#ifdef notyet static void rk3328_set_delays(struct syscon *grf, phandle_t node) { uint32_t tx, rx; if (OF_getencprop(node, "tx_delay", &tx, sizeof(tx)) <= 0) tx = 0x30; if (OF_getencprop(node, "rx_delay", &rx, sizeof(rx)) <= 0) rx = 0x10; tx = ((tx & RK3328_GRF_MAC_CON0_TX_MASK) << RK3328_GRF_MAC_CON0_TX_SHIFT); rx = ((rx & RK3328_GRF_MAC_CON0_TX_MASK) << RK3328_GRF_MAC_CON0_RX_SHIFT); SYSCON_WRITE_4(grf, RK3328_GRF_MAC_CON0, tx | rx | 0xFFFF0000); } +#endif #define RK3399_GRF_SOC_CON6 0xc218 #define RK3399_GRF_SOC_CON6_TX_MASK 0x7F #define RK3399_GRF_SOC_CON6_TX_SHIFT 0 #define RK3399_GRF_SOC_CON6_RX_MASK 0x7F #define RK3399_GRF_SOC_CON6_RX_SHIFT 8 +#ifdef notyet static void rk3399_set_delays(struct syscon *grf, phandle_t node) { uint32_t tx, rx; if (OF_getencprop(node, "tx_delay", &tx, sizeof(tx)) <= 0) tx = 0x30; if (OF_getencprop(node, "rx_delay", &rx, sizeof(rx)) <= 0) rx = 0x10; tx = ((tx & RK3399_GRF_SOC_CON6_TX_MASK) << RK3399_GRF_SOC_CON6_TX_SHIFT); rx = ((rx & RK3399_GRF_SOC_CON6_TX_MASK) << RK3399_GRF_SOC_CON6_RX_SHIFT); SYSCON_WRITE_4(grf, RK3399_GRF_SOC_CON6, tx | rx | 0xFFFF0000); } +#endif static int if_dwc_rk_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!(ofw_bus_is_compatible(dev, "rockchip,rk3328-gmac") || - ofw_bus_is_compatible(dev, "rockchip,rk3399-gmac"))) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Rockchip Gigabit Ethernet Controller"); return (BUS_PROBE_DEFAULT); } static int if_dwc_rk_init(device_t dev) { phandle_t node; struct syscon *grf = NULL; node = ofw_bus_get_node(dev); if (OF_hasprop(node, "rockchip,grf") && syscon_get_by_ofw_property(dev, node, "rockchip,grf", &grf) != 0) { device_printf(dev, "cannot get grf driver handle\n"); return (ENXIO); } #ifdef notyet if (ofw_bus_is_compatible(dev, "rockchip,rk3399-gmac")) rk3399_set_delays(grf, node); else if (ofw_bus_is_compatible(dev, "rockchip,rk3328-gmac")) rk3328_set_delays(grf, node); #endif /* Mode should be set according to dtb property */ return (0); } static int if_dwc_rk_mac_type(device_t dev) { return (DWC_GMAC_ALT_DESC); } static int if_dwc_rk_mii_clk(device_t dev) { /* Should be calculated from the clock */ return (GMAC_MII_CLK_150_250M_DIV102); } static device_method_t if_dwc_rk_methods[] = { DEVMETHOD(device_probe, if_dwc_rk_probe), DEVMETHOD(if_dwc_init, if_dwc_rk_init), DEVMETHOD(if_dwc_mac_type, if_dwc_rk_mac_type), DEVMETHOD(if_dwc_mii_clk, if_dwc_rk_mii_clk), DEVMETHOD_END }; static devclass_t dwc_rk_devclass; extern driver_t dwc_driver; DEFINE_CLASS_1(dwc, dwc_rk_driver, if_dwc_rk_methods, sizeof(struct dwc_softc), dwc_driver); DRIVER_MODULE(dwc_rk, simplebus, dwc_rk_driver, dwc_rk_devclass, 0, 0); MODULE_DEPEND(dwc_rk, dwc, 1, 1, 1); Index: stable/12/sys/arm64/rockchip/rk_gpio.c =================================================================== --- stable/12/sys/arm64/rockchip/rk_gpio.c (revision 358652) +++ stable/12/sys/arm64/rockchip/rk_gpio.c (revision 358653) @@ -1,429 +1,475 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include "opt_soc.h" - #include "gpio_if.h" +#include "fdt_pinctrl_if.h" + #define RK_GPIO_SWPORTA_DR 0x00 /* Data register */ #define RK_GPIO_SWPORTA_DDR 0x04 /* Data direction register */ #define RK_GPIO_INTEN 0x30 /* Interrupt enable register */ #define RK_GPIO_INTMASK 0x34 /* Interrupt mask register */ #define RK_GPIO_INTTYPE_LEVEL 0x38 /* Interrupt level register */ #define RK_GPIO_INT_POLARITY 0x3C /* Interrupt polarity register */ #define RK_GPIO_INT_STATUS 0x40 /* Interrupt status register */ #define RK_GPIO_INT_RAWSTATUS 0x44 /* Raw Interrupt status register */ #define RK_GPIO_DEBOUNCE 0x48 /* Debounce enable register */ #define RK_GPIO_PORTA_EOI 0x4C /* Clear interrupt register */ #define RK_GPIO_EXT_PORTA 0x50 /* External port register */ #define RK_GPIO_LS_SYNC 0x60 /* Level sensitive syncronization enable register */ +#define RK_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ + GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) + struct rk_gpio_softc { device_t sc_dev; device_t sc_busdev; struct mtx sc_mtx; struct resource *sc_res[2]; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; clk_t clk; + device_t pinctrl; }; static struct ofw_compat_data compat_data[] = { {"rockchip,gpio-bank", 1}, {NULL, 0} }; static struct resource_spec rk_gpio_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static int rk_gpio_detach(device_t dev); #define RK_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) #define RK_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) #define RK_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define RK_GPIO_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) #define RK_GPIO_READ(_sc, _off) \ bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) static int rk_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "RockChip GPIO Bank controller"); return (BUS_PROBE_DEFAULT); } static int rk_gpio_attach(device_t dev) { struct rk_gpio_softc *sc; phandle_t node; int err; sc = device_get_softc(dev); sc->sc_dev = dev; + sc->pinctrl = device_get_parent(dev); node = ofw_bus_get_node(sc->sc_dev); if (!OF_hasprop(node, "gpio-controller")) return (ENXIO); mtx_init(&sc->sc_mtx, "rk gpio", "gpio", MTX_SPIN); if (bus_alloc_resources(dev, rk_gpio_spec, sc->sc_res)) { device_printf(dev, "could not allocate resources\n"); bus_release_resources(dev, rk_gpio_spec, sc->sc_res); mtx_destroy(&sc->sc_mtx); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_res[0]); sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); if (clk_get_by_ofw_index(dev, 0, 0, &sc->clk) != 0) { device_printf(dev, "Cannot get clock\n"); rk_gpio_detach(dev); return (ENXIO); } err = clk_enable(sc->clk); if (err != 0) { device_printf(dev, "Could not enable clock %s\n", clk_get_name(sc->clk)); rk_gpio_detach(dev); return (ENXIO); } sc->sc_busdev = gpiobus_attach_bus(dev); if (sc->sc_busdev == NULL) { rk_gpio_detach(dev); return (ENXIO); } return (0); } static int rk_gpio_detach(device_t dev) { struct rk_gpio_softc *sc; sc = device_get_softc(dev); if (sc->sc_busdev) gpiobus_detach_bus(dev); bus_release_resources(dev, rk_gpio_spec, sc->sc_res); mtx_destroy(&sc->sc_mtx); clk_disable(sc->clk); return(0); } static device_t rk_gpio_get_bus(device_t dev) { struct rk_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } static int rk_gpio_pin_max(device_t dev, int *maxpin) { /* Each bank have always 32 pins */ + /* XXX not true*/ *maxpin = 31; return (0); } static int rk_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct rk_gpio_softc *sc; sc = device_get_softc(dev); if (pin >= 32) return (EINVAL); RK_GPIO_LOCK(sc); snprintf(name, GPIOMAXNAME, "gpio%d", pin); RK_GPIO_UNLOCK(sc); return (0); } static int rk_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct rk_gpio_softc *sc; uint32_t reg; + int rv; + bool is_gpio; sc = device_get_softc(dev); + rv = FDT_PINCTRL_IS_GPIO(sc->pinctrl, dev, pin, &is_gpio); + if (rv != 0) + return (rv); + if (!is_gpio) + return (EINVAL); + + *flags = 0; + rv = FDT_PINCTRL_GET_FLAGS(sc->pinctrl, dev, pin, flags); + if (rv != 0) + return (rv); + RK_GPIO_LOCK(sc); reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR); RK_GPIO_UNLOCK(sc); if (reg & (1 << pin)) - *flags = GPIO_PIN_OUTPUT; + *flags |= GPIO_PIN_OUTPUT; else - *flags = GPIO_PIN_INPUT; + *flags |= GPIO_PIN_INPUT; return (0); } static int rk_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { - /* Caps are managed by the pinctrl device */ - *caps = 0; + *caps = RK_GPIO_DEFAULT_CAPS; return (0); } static int rk_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct rk_gpio_softc *sc; uint32_t reg; + int rv; + bool is_gpio; sc = device_get_softc(dev); + rv = FDT_PINCTRL_IS_GPIO(sc->pinctrl, dev, pin, &is_gpio); + if (rv != 0) + return (rv); + if (!is_gpio) + return (EINVAL); + + rv = FDT_PINCTRL_SET_FLAGS(sc->pinctrl, dev, pin, flags); + if (rv != 0) + return (rv); + RK_GPIO_LOCK(sc); reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR); if (flags & GPIO_PIN_INPUT) reg &= ~(1 << pin); else if (flags & GPIO_PIN_OUTPUT) reg |= (1 << pin); RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DDR, reg); RK_GPIO_UNLOCK(sc); return (0); } static int rk_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct rk_gpio_softc *sc; uint32_t reg; sc = device_get_softc(dev); RK_GPIO_LOCK(sc); reg = RK_GPIO_READ(sc, RK_GPIO_EXT_PORTA); RK_GPIO_UNLOCK(sc); *val = reg & (1 << pin) ? 1 : 0; return (0); } static int rk_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct rk_gpio_softc *sc; uint32_t reg; sc = device_get_softc(dev); RK_GPIO_LOCK(sc); reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DR); if (value) reg |= (1 << pin); else reg &= ~(1 << pin); RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DR, reg); RK_GPIO_UNLOCK(sc); return (0); } static int rk_gpio_pin_toggle(device_t dev, uint32_t pin) { struct rk_gpio_softc *sc; uint32_t reg; sc = device_get_softc(dev); RK_GPIO_LOCK(sc); reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DR); if (reg & (1 << pin)) reg &= ~(1 << pin); else reg |= (1 << pin); RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DR, reg); RK_GPIO_UNLOCK(sc); return (0); } static int rk_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins, uint32_t change_pins, uint32_t *orig_pins) { struct rk_gpio_softc *sc; uint32_t reg; sc = device_get_softc(dev); RK_GPIO_LOCK(sc); reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DR); if (orig_pins) *orig_pins = reg; if ((clear_pins | change_pins) != 0) { reg = (reg & ~clear_pins) ^ change_pins; RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DR, reg); } RK_GPIO_UNLOCK(sc); return (0); } static int rk_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins, uint32_t *pin_flags) { struct rk_gpio_softc *sc; uint32_t reg, set, mask, flags; int i; sc = device_get_softc(dev); if (first_pin != 0 || num_pins > 32) return (EINVAL); set = 0; mask = 0; for (i = 0; i < num_pins; i++) { mask = (mask << 1) | 1; flags = pin_flags[i]; if (flags & GPIO_PIN_INPUT) { set &= ~(1 << i); } else if (flags & GPIO_PIN_OUTPUT) { set |= (1 << i); } } RK_GPIO_LOCK(sc); reg = RK_GPIO_READ(sc, RK_GPIO_SWPORTA_DDR); reg &= ~mask; reg |= set; RK_GPIO_WRITE(sc, RK_GPIO_SWPORTA_DDR, reg); RK_GPIO_UNLOCK(sc); return (0); } static int rk_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) { /* The gpios are mapped as */ *pin = gpios[0]; *flags = gpios[1]; return (0); } +static phandle_t +rk_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 rk_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_gpio_probe), DEVMETHOD(device_attach, rk_gpio_attach), DEVMETHOD(device_detach, rk_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_get_bus, rk_gpio_get_bus), DEVMETHOD(gpio_pin_max, rk_gpio_pin_max), DEVMETHOD(gpio_pin_getname, rk_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, rk_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, rk_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, rk_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, rk_gpio_pin_get), DEVMETHOD(gpio_pin_set, rk_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, rk_gpio_pin_toggle), DEVMETHOD(gpio_pin_access_32, rk_gpio_pin_access_32), DEVMETHOD(gpio_pin_config_32, rk_gpio_pin_config_32), DEVMETHOD(gpio_map_gpios, rk_gpio_map_gpios), + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_node, rk_gpio_get_node), + DEVMETHOD_END }; static driver_t rk_gpio_driver = { "gpio", rk_gpio_methods, sizeof(struct rk_gpio_softc), }; static devclass_t rk_gpio_devclass; +/* + * GPIO driver is always a child of rk_pinctrl driver and should be probed + * and attached within rk_pinctrl_attach function. Due to this, bus pass order + * must be same as bus pass order of rk_pinctrl driver. + */ EARLY_DRIVER_MODULE(rk_gpio, simplebus, rk_gpio_driver, - rk_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); + rk_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); Index: stable/12/sys/arm64/rockchip/rk_grf.c =================================================================== --- stable/12/sys/arm64/rockchip/rk_grf.c (revision 358652) +++ stable/12/sys/arm64/rockchip/rk_grf.c (revision 358653) @@ -1,85 +1,80 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include -#include "opt_soc.h" - static struct ofw_compat_data compat_data[] = { -#ifdef SOC_ROCKCHIP_RK3328 + {"rockchip,rk3288-grf", 1}, {"rockchip,rk3328-grf", 1}, -#endif -#ifdef SOC_ROCKCHIP_RK3399 {"rockchip,rk3399-grf", 1}, {"rockchip,rk3399-pmugrf", 1}, -#endif {NULL, 0} }; static int rk_grf_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "RockChip General Register Files"); return (BUS_PROBE_DEFAULT); } static device_method_t rk_grf_methods[] = { DEVMETHOD(device_probe, rk_grf_probe), DEVMETHOD_END }; DEFINE_CLASS_1(rk_grf, rk_grf_driver, rk_grf_methods, sizeof(struct simple_mfd_softc), simple_mfd_driver); static devclass_t rk_grf_devclass; EARLY_DRIVER_MODULE(rk_grf, simplebus, rk_grf_driver, rk_grf_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(rk_grf, 1); Index: stable/12/sys/arm64/rockchip/rk_pinctrl.c =================================================================== --- stable/12/sys/arm64/rockchip/rk_pinctrl.c (revision 358652) +++ stable/12/sys/arm64/rockchip/rk_pinctrl.c (revision 358653) @@ -1,1074 +1,1352 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "syscon_if.h" +#include "fdt_pinctrl_if.h" struct rk_pinctrl_pin_drive { uint32_t bank; uint32_t subbank; uint32_t offset; uint32_t value; uint32_t ma; }; struct rk_pinctrl_bank { uint32_t bank; uint32_t subbank; uint32_t offset; uint32_t nbits; }; struct rk_pinctrl_pin_fixup { uint32_t bank; uint32_t subbank; uint32_t pin; uint32_t reg; uint32_t bit; uint32_t mask; }; struct rk_pinctrl_gpio { uint32_t bank; char *gpio_name; device_t gpio_dev; }; struct rk_pinctrl_softc; struct rk_pinctrl_conf { struct rk_pinctrl_bank *iomux_conf; uint32_t iomux_nbanks; struct rk_pinctrl_pin_fixup *pin_fixup; uint32_t npin_fixup; struct rk_pinctrl_pin_drive *pin_drive; uint32_t npin_drive; struct rk_pinctrl_gpio *gpio_bank; uint32_t ngpio_bank; uint32_t (*get_pd_offset)(struct rk_pinctrl_softc *, uint32_t); struct syscon *(*get_syscon)(struct rk_pinctrl_softc *, uint32_t); int (*parse_bias)(phandle_t, int); + int (*resolv_bias_value)(int, int); + int (*get_bias_value)(int, int); }; struct rk_pinctrl_softc { struct simplebus_softc simplebus_sc; device_t dev; struct syscon *grf; struct syscon *pmu; struct rk_pinctrl_conf *conf; + struct mtx mtx; }; +#define RK_PINCTRL_LOCK(_sc) mtx_lock_spin(&(_sc)->mtx) +#define RK_PINCTRL_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->mtx) +#define RK_PINCTRL_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED) + #define RK_IOMUX(_bank, _subbank, _offset, _nbits) \ { \ .bank = _bank, \ .subbank = _subbank, \ .offset = _offset, \ .nbits = _nbits, \ } #define RK_PINFIX(_bank, _pin, _reg, _bit, _mask) \ { \ .bank = _bank, \ .pin = _pin, \ .reg = _reg, \ .bit = _bit, \ .mask = _mask, \ } #define RK_PINDRIVE(_bank, _subbank, _offset, _value, _ma) \ { \ .bank = _bank, \ .subbank = _subbank, \ .offset = _offset, \ .value = _value, \ .ma = _ma, \ } #define RK_GPIO(_bank, _name) \ { \ .bank = _bank, \ .gpio_name = _name, \ } static struct rk_pinctrl_gpio rk3288_gpio_bank[] = { RK_GPIO(0, "gpio0"), RK_GPIO(1, "gpio1"), RK_GPIO(2, "gpio2"), RK_GPIO(3, "gpio3"), RK_GPIO(4, "gpio4"), RK_GPIO(5, "gpio5"), RK_GPIO(6, "gpio6"), RK_GPIO(7, "gpio7"), RK_GPIO(8, "gpio8"), }; static struct rk_pinctrl_bank rk3288_iomux_bank[] = { /* bank sub offs nbits */ /* PMU */ RK_IOMUX(0, 0, 0x0084, 2), RK_IOMUX(0, 1, 0x0088, 2), RK_IOMUX(0, 2, 0x008C, 2), /* GFR */ RK_IOMUX(1, 3, 0x000C, 2), RK_IOMUX(2, 0, 0x0010, 2), RK_IOMUX(2, 1, 0x0014, 2), RK_IOMUX(2, 2, 0x0018, 2), RK_IOMUX(2, 3, 0x001C, 2), RK_IOMUX(3, 0, 0x0020, 2), RK_IOMUX(3, 1, 0x0024, 2), RK_IOMUX(3, 2, 0x0028, 2), RK_IOMUX(3, 3, 0x002C, 4), RK_IOMUX(4, 0, 0x0034, 4), RK_IOMUX(4, 1, 0x003C, 4), RK_IOMUX(4, 2, 0x0044, 2), RK_IOMUX(4, 3, 0x0048, 2), /* 5,0 - Empty */ RK_IOMUX(5, 1, 0x0050, 2), RK_IOMUX(5, 2, 0x0054, 2), /* 5,3 - Empty */ RK_IOMUX(6, 0, 0x005C, 2), RK_IOMUX(6, 1, 0x0060, 2), RK_IOMUX(6, 2, 0x0064, 2), /* 6,3 - Empty */ RK_IOMUX(7, 0, 0x006C, 2), RK_IOMUX(7, 1, 0x0070, 2), RK_IOMUX(7, 2, 0x0074, 4), /* 7,3 - Empty */ RK_IOMUX(8, 0, 0x0080, 2), RK_IOMUX(8, 1, 0x0084, 2), /* 8,2 - Empty */ /* 8,3 - Empty */ }; static struct rk_pinctrl_pin_fixup rk3288_pin_fixup[] = { }; static struct rk_pinctrl_pin_drive rk3288_pin_drive[] = { /* bank sub offs val ma */ /* GPIO0A (PMU)*/ RK_PINDRIVE(0, 0, 0x070, 0, 2), RK_PINDRIVE(0, 0, 0x070, 1, 4), RK_PINDRIVE(0, 0, 0x070, 2, 8), RK_PINDRIVE(0, 0, 0x070, 3, 12), /* GPIO0B (PMU)*/ RK_PINDRIVE(0, 1, 0x074, 0, 2), RK_PINDRIVE(0, 1, 0x074, 1, 4), RK_PINDRIVE(0, 1, 0x074, 2, 8), RK_PINDRIVE(0, 1, 0x074, 3, 12), /* GPIO0C (PMU)*/ RK_PINDRIVE(0, 2, 0x078, 0, 2), RK_PINDRIVE(0, 2, 0x078, 1, 4), RK_PINDRIVE(0, 2, 0x078, 2, 8), RK_PINDRIVE(0, 2, 0x078, 3, 12), /* GPIO1D */ RK_PINDRIVE(1, 3, 0x1CC, 0, 2), RK_PINDRIVE(1, 3, 0x1CC, 1, 4), RK_PINDRIVE(1, 3, 0x1CC, 2, 8), RK_PINDRIVE(1, 3, 0x1CC, 3, 12), /* GPIO2A */ RK_PINDRIVE(2, 0, 0x1D0, 0, 2), RK_PINDRIVE(2, 0, 0x1D0, 1, 4), RK_PINDRIVE(2, 0, 0x1D0, 2, 8), RK_PINDRIVE(2, 0, 0x1D0, 3, 12), /* GPIO2B */ RK_PINDRIVE(2, 1, 0x1D4, 0, 2), RK_PINDRIVE(2, 1, 0x1D4, 1, 4), RK_PINDRIVE(2, 1, 0x1D4, 2, 8), RK_PINDRIVE(2, 1, 0x1D4, 3, 12), /* GPIO2C */ RK_PINDRIVE(2, 2, 0x1D8, 0, 2), RK_PINDRIVE(2, 2, 0x1D8, 1, 4), RK_PINDRIVE(2, 2, 0x1D8, 2, 8), RK_PINDRIVE(2, 2, 0x1D8, 3, 12), /* GPIO2D */ RK_PINDRIVE(2, 3, 0x1DC, 0, 2), RK_PINDRIVE(2, 3, 0x1DC, 1, 4), RK_PINDRIVE(2, 3, 0x1DC, 2, 8), RK_PINDRIVE(2, 3, 0x1DC, 3, 12), /* GPIO3A */ RK_PINDRIVE(3, 0, 0x1E0, 0, 2), RK_PINDRIVE(3, 0, 0x1E0, 1, 4), RK_PINDRIVE(3, 0, 0x1E0, 2, 8), RK_PINDRIVE(3, 0, 0x1E0, 3, 12), /* GPIO3B */ RK_PINDRIVE(3, 1, 0x1E4, 0, 2), RK_PINDRIVE(3, 1, 0x1E4, 1, 4), RK_PINDRIVE(3, 1, 0x1E4, 2, 8), RK_PINDRIVE(3, 1, 0x1E4, 3, 12), /* GPIO3C */ RK_PINDRIVE(3, 2, 0x1E8, 0, 2), RK_PINDRIVE(3, 2, 0x1E8, 1, 4), RK_PINDRIVE(3, 2, 0x1E8, 2, 8), RK_PINDRIVE(3, 2, 0x1E8, 3, 12), /* GPIO3D */ RK_PINDRIVE(3, 3, 0x1EC, 0, 2), RK_PINDRIVE(3, 3, 0x1EC, 1, 4), RK_PINDRIVE(3, 3, 0x1EC, 2, 8), RK_PINDRIVE(3, 3, 0x1EC, 3, 12), /* GPIO4A */ RK_PINDRIVE(4, 0, 0x1F0, 0, 2), RK_PINDRIVE(4, 0, 0x1F0, 1, 4), RK_PINDRIVE(4, 0, 0x1F0, 2, 8), RK_PINDRIVE(4, 0, 0x1F0, 3, 12), /* GPIO4B */ RK_PINDRIVE(4, 1, 0x1F4, 0, 2), RK_PINDRIVE(4, 1, 0x1F4, 1, 4), RK_PINDRIVE(4, 1, 0x1F4, 2, 8), RK_PINDRIVE(4, 1, 0x1F4, 3, 12), /* GPIO4C */ RK_PINDRIVE(4, 2, 0x1F8, 0, 2), RK_PINDRIVE(4, 2, 0x1F8, 1, 4), RK_PINDRIVE(4, 2, 0x1F8, 2, 8), RK_PINDRIVE(4, 2, 0x1F8, 3, 12), /* GPIO4D */ RK_PINDRIVE(4, 3, 0x1FC, 0, 2), RK_PINDRIVE(4, 3, 0x1FC, 1, 4), RK_PINDRIVE(4, 3, 0x1FC, 2, 8), RK_PINDRIVE(4, 3, 0x1FC, 3, 12), /* GPIO5B */ RK_PINDRIVE(5, 1, 0x204, 0, 2), RK_PINDRIVE(5, 1, 0x204, 1, 4), RK_PINDRIVE(5, 1, 0x204, 2, 8), RK_PINDRIVE(5, 1, 0x204, 3, 12), /* GPIO5C */ RK_PINDRIVE(5, 2, 0x208, 0, 2), RK_PINDRIVE(5, 2, 0x208, 1, 4), RK_PINDRIVE(5, 2, 0x208, 2, 8), RK_PINDRIVE(5, 2, 0x208, 3, 12), /* GPIO6A */ RK_PINDRIVE(6, 0, 0x210, 0, 2), RK_PINDRIVE(6, 0, 0x210, 1, 4), RK_PINDRIVE(6, 0, 0x210, 2, 8), RK_PINDRIVE(6, 0, 0x210, 3, 12), /* GPIO6B */ RK_PINDRIVE(6, 1, 0x214, 0, 2), RK_PINDRIVE(6, 1, 0x214, 1, 4), RK_PINDRIVE(6, 1, 0x214, 2, 8), RK_PINDRIVE(6, 1, 0x214, 3, 12), /* GPIO6C */ RK_PINDRIVE(6, 2, 0x218, 0, 2), RK_PINDRIVE(6, 2, 0x218, 1, 4), RK_PINDRIVE(6, 2, 0x218, 2, 8), RK_PINDRIVE(6, 2, 0x218, 3, 12), /* GPIO7A */ RK_PINDRIVE(7, 0, 0x220, 0, 2), RK_PINDRIVE(7, 0, 0x220, 1, 4), RK_PINDRIVE(7, 0, 0x220, 2, 8), RK_PINDRIVE(7, 0, 0x220, 3, 12), /* GPIO7B */ RK_PINDRIVE(7, 1, 0x224, 0, 2), RK_PINDRIVE(7, 1, 0x224, 1, 4), RK_PINDRIVE(7, 1, 0x224, 2, 8), RK_PINDRIVE(7, 1, 0x224, 3, 12), /* GPIO7C */ RK_PINDRIVE(7, 2, 0x228, 0, 2), RK_PINDRIVE(7, 2, 0x228, 1, 4), RK_PINDRIVE(7, 2, 0x228, 2, 8), RK_PINDRIVE(7, 2, 0x228, 3, 12), /* GPIO8A */ RK_PINDRIVE(8, 0, 0x230, 0, 2), RK_PINDRIVE(8, 0, 0x230, 1, 4), RK_PINDRIVE(8, 0, 0x230, 2, 8), RK_PINDRIVE(8, 0, 0x230, 3, 12), /* GPIO8B */ RK_PINDRIVE(8, 1, 0x234, 0, 2), RK_PINDRIVE(8, 1, 0x234, 1, 4), RK_PINDRIVE(8, 1, 0x234, 2, 8), RK_PINDRIVE(8, 1, 0x234, 3, 12), }; static uint32_t rk3288_get_pd_offset(struct rk_pinctrl_softc *sc, uint32_t bank) { if (bank == 0) return (0x064); /* PMU */ return (0x130); } static struct syscon * rk3288_get_syscon(struct rk_pinctrl_softc *sc, uint32_t bank) { if (bank == 0) return (sc->pmu); return (sc->grf); } static int rk3288_parse_bias(phandle_t node, int bank) { if (OF_hasprop(node, "bias-disable")) return (0); if (OF_hasprop(node, "bias-pull-up")) return (1); if (OF_hasprop(node, "bias-pull-down")) return (2); return (-1); } +static int +rk3288_resolv_bias_value(int bank, int bias) +{ + int rv = 0; + + if (bias == 1) + rv = GPIO_PIN_PULLUP; + else if (bias == 2) + rv = GPIO_PIN_PULLDOWN; + + return (rv); +} + +static int +rk3288_get_bias_value(int bank, int bias) +{ + int rv = 0; + + if (bias & GPIO_PIN_PULLUP) + rv = 1; + else if (bias & GPIO_PIN_PULLDOWN) + rv = 2; + + return (rv); +} + struct rk_pinctrl_conf rk3288_conf = { .iomux_conf = rk3288_iomux_bank, .iomux_nbanks = nitems(rk3288_iomux_bank), .pin_fixup = rk3288_pin_fixup, .npin_fixup = nitems(rk3288_pin_fixup), .pin_drive = rk3288_pin_drive, .npin_drive = nitems(rk3288_pin_drive), .gpio_bank = rk3288_gpio_bank, .ngpio_bank = nitems(rk3288_gpio_bank), .get_pd_offset = rk3288_get_pd_offset, .get_syscon = rk3288_get_syscon, .parse_bias = rk3288_parse_bias, + .resolv_bias_value = rk3288_resolv_bias_value, + .get_bias_value = rk3288_get_bias_value, }; static struct rk_pinctrl_gpio rk3328_gpio_bank[] = { RK_GPIO(0, "gpio0"), RK_GPIO(1, "gpio1"), RK_GPIO(2, "gpio2"), RK_GPIO(3, "gpio3"), }; static struct rk_pinctrl_bank rk3328_iomux_bank[] = { /* bank sub offs nbits */ RK_IOMUX(0, 0, 0x0000, 2), RK_IOMUX(0, 1, 0x0004, 2), RK_IOMUX(0, 2, 0x0008, 2), RK_IOMUX(0, 3, 0x000C, 2), RK_IOMUX(1, 0, 0x0010, 2), RK_IOMUX(1, 1, 0x0014, 2), RK_IOMUX(1, 2, 0x0018, 2), RK_IOMUX(1, 3, 0x001C, 2), RK_IOMUX(2, 0, 0x0020, 2), RK_IOMUX(2, 1, 0x0024, 3), RK_IOMUX(2, 2, 0x002c, 3), RK_IOMUX(2, 3, 0x0034, 2), RK_IOMUX(3, 0, 0x0038, 3), RK_IOMUX(3, 1, 0x0040, 3), RK_IOMUX(3, 2, 0x0048, 2), RK_IOMUX(3, 3, 0x004c, 2), }; static struct rk_pinctrl_pin_fixup rk3328_pin_fixup[] = { /* bank pin reg bit mask */ RK_PINFIX(2, 12, 0x24, 8, 0x300), RK_PINFIX(2, 15, 0x28, 0, 0x7), RK_PINFIX(2, 23, 0x30, 14, 0x6000), }; static struct rk_pinctrl_pin_drive rk3328_pin_drive[] = { /* bank sub offs val ma */ RK_PINDRIVE(0, 0, 0x200, 0, 2), RK_PINDRIVE(0, 0, 0x200, 1, 4), RK_PINDRIVE(0, 0, 0x200, 2, 8), RK_PINDRIVE(0, 0, 0x200, 3, 12), RK_PINDRIVE(0, 1, 0x204, 0, 2), RK_PINDRIVE(0, 1, 0x204, 1, 4), RK_PINDRIVE(0, 1, 0x204, 2, 8), RK_PINDRIVE(0, 1, 0x204, 3, 12), RK_PINDRIVE(0, 2, 0x208, 0, 2), RK_PINDRIVE(0, 2, 0x208, 1, 4), RK_PINDRIVE(0, 2, 0x208, 2, 8), RK_PINDRIVE(0, 2, 0x208, 3, 12), RK_PINDRIVE(0, 3, 0x20C, 0, 2), RK_PINDRIVE(0, 3, 0x20C, 1, 4), RK_PINDRIVE(0, 3, 0x20C, 2, 8), RK_PINDRIVE(0, 3, 0x20C, 3, 12), RK_PINDRIVE(1, 0, 0x210, 0, 2), RK_PINDRIVE(1, 0, 0x210, 1, 4), RK_PINDRIVE(1, 0, 0x210, 2, 8), RK_PINDRIVE(1, 0, 0x210, 3, 12), RK_PINDRIVE(1, 1, 0x214, 0, 2), RK_PINDRIVE(1, 1, 0x214, 1, 4), RK_PINDRIVE(1, 1, 0x214, 2, 8), RK_PINDRIVE(1, 1, 0x214, 3, 12), RK_PINDRIVE(1, 2, 0x218, 0, 2), RK_PINDRIVE(1, 2, 0x218, 1, 4), RK_PINDRIVE(1, 2, 0x218, 2, 8), RK_PINDRIVE(1, 2, 0x218, 3, 12), RK_PINDRIVE(1, 3, 0x21C, 0, 2), RK_PINDRIVE(1, 3, 0x21C, 1, 4), RK_PINDRIVE(1, 3, 0x21C, 2, 8), RK_PINDRIVE(1, 3, 0x21C, 3, 12), RK_PINDRIVE(2, 0, 0x220, 0, 2), RK_PINDRIVE(2, 0, 0x220, 1, 4), RK_PINDRIVE(2, 0, 0x220, 2, 8), RK_PINDRIVE(2, 0, 0x220, 3, 12), RK_PINDRIVE(2, 1, 0x224, 0, 2), RK_PINDRIVE(2, 1, 0x224, 1, 4), RK_PINDRIVE(2, 1, 0x224, 2, 8), RK_PINDRIVE(2, 1, 0x224, 3, 12), RK_PINDRIVE(2, 2, 0x228, 0, 2), RK_PINDRIVE(2, 2, 0x228, 1, 4), RK_PINDRIVE(2, 2, 0x228, 2, 8), RK_PINDRIVE(2, 2, 0x228, 3, 12), RK_PINDRIVE(2, 3, 0x22C, 0, 2), RK_PINDRIVE(2, 3, 0x22C, 1, 4), RK_PINDRIVE(2, 3, 0x22C, 2, 8), RK_PINDRIVE(2, 3, 0x22C, 3, 12), RK_PINDRIVE(3, 0, 0x230, 0, 2), RK_PINDRIVE(3, 0, 0x230, 1, 4), RK_PINDRIVE(3, 0, 0x230, 2, 8), RK_PINDRIVE(3, 0, 0x230, 3, 12), RK_PINDRIVE(3, 1, 0x234, 0, 2), RK_PINDRIVE(3, 1, 0x234, 1, 4), RK_PINDRIVE(3, 1, 0x234, 2, 8), RK_PINDRIVE(3, 1, 0x234, 3, 12), RK_PINDRIVE(3, 2, 0x238, 0, 2), RK_PINDRIVE(3, 2, 0x238, 1, 4), RK_PINDRIVE(3, 2, 0x238, 2, 8), RK_PINDRIVE(3, 2, 0x238, 3, 12), RK_PINDRIVE(3, 3, 0x23C, 0, 2), RK_PINDRIVE(3, 3, 0x23C, 1, 4), RK_PINDRIVE(3, 3, 0x23C, 2, 8), RK_PINDRIVE(3, 3, 0x23C, 3, 12), }; static uint32_t rk3328_get_pd_offset(struct rk_pinctrl_softc *sc, uint32_t bank) { return (0x100); } static struct syscon * rk3328_get_syscon(struct rk_pinctrl_softc *sc, uint32_t bank) { return (sc->grf); } struct rk_pinctrl_conf rk3328_conf = { .iomux_conf = rk3328_iomux_bank, .iomux_nbanks = nitems(rk3328_iomux_bank), .pin_fixup = rk3328_pin_fixup, .npin_fixup = nitems(rk3328_pin_fixup), .pin_drive = rk3328_pin_drive, .npin_drive = nitems(rk3328_pin_drive), .gpio_bank = rk3328_gpio_bank, .ngpio_bank = nitems(rk3328_gpio_bank), .get_pd_offset = rk3328_get_pd_offset, .get_syscon = rk3328_get_syscon, .parse_bias = rk3288_parse_bias, + .resolv_bias_value = rk3288_resolv_bias_value, + .get_bias_value = rk3288_get_bias_value, }; static struct rk_pinctrl_gpio rk3399_gpio_bank[] = { RK_GPIO(0, "gpio0"), RK_GPIO(1, "gpio1"), RK_GPIO(2, "gpio2"), RK_GPIO(3, "gpio3"), RK_GPIO(4, "gpio4"), }; static struct rk_pinctrl_bank rk3399_iomux_bank[] = { /* bank sub offs nbits */ RK_IOMUX(0, 0, 0x0000, 2), RK_IOMUX(0, 1, 0x0004, 2), RK_IOMUX(0, 2, 0x0008, 2), RK_IOMUX(0, 3, 0x000C, 2), RK_IOMUX(1, 0, 0x0010, 2), RK_IOMUX(1, 1, 0x0014, 2), RK_IOMUX(1, 2, 0x0018, 2), RK_IOMUX(1, 3, 0x001C, 2), RK_IOMUX(2, 0, 0xE000, 2), RK_IOMUX(2, 1, 0xE004, 2), RK_IOMUX(2, 2, 0xE008, 2), RK_IOMUX(2, 3, 0xE00C, 2), RK_IOMUX(3, 0, 0xE010, 2), RK_IOMUX(3, 1, 0xE014, 2), RK_IOMUX(3, 2, 0xE018, 2), RK_IOMUX(3, 3, 0xE01C, 2), RK_IOMUX(4, 0, 0xE020, 2), RK_IOMUX(4, 1, 0xE024, 2), RK_IOMUX(4, 2, 0xE028, 2), RK_IOMUX(4, 3, 0xE02C, 2), }; static struct rk_pinctrl_pin_fixup rk3399_pin_fixup[] = {}; static struct rk_pinctrl_pin_drive rk3399_pin_drive[] = { /* bank sub offs val ma */ /* GPIO0A */ RK_PINDRIVE(0, 0, 0x80, 0, 5), RK_PINDRIVE(0, 0, 0x80, 1, 10), RK_PINDRIVE(0, 0, 0x80, 2, 15), RK_PINDRIVE(0, 0, 0x80, 3, 20), /* GPIOB */ RK_PINDRIVE(0, 1, 0x88, 0, 5), RK_PINDRIVE(0, 1, 0x88, 1, 10), RK_PINDRIVE(0, 1, 0x88, 2, 15), RK_PINDRIVE(0, 1, 0x88, 3, 20), /* GPIO1A */ RK_PINDRIVE(1, 0, 0xA0, 0, 3), RK_PINDRIVE(1, 0, 0xA0, 1, 6), RK_PINDRIVE(1, 0, 0xA0, 2, 9), RK_PINDRIVE(1, 0, 0xA0, 3, 12), /* GPIO1B */ RK_PINDRIVE(1, 1, 0xA8, 0, 3), RK_PINDRIVE(1, 1, 0xA8, 1, 6), RK_PINDRIVE(1, 1, 0xA8, 2, 9), RK_PINDRIVE(1, 1, 0xA8, 3, 12), /* GPIO1C */ RK_PINDRIVE(1, 2, 0xB0, 0, 3), RK_PINDRIVE(1, 2, 0xB0, 1, 6), RK_PINDRIVE(1, 2, 0xB0, 2, 9), RK_PINDRIVE(1, 2, 0xB0, 3, 12), /* GPIO1D */ RK_PINDRIVE(1, 3, 0xB8, 0, 3), RK_PINDRIVE(1, 3, 0xB8, 1, 6), RK_PINDRIVE(1, 3, 0xB8, 2, 9), RK_PINDRIVE(1, 3, 0xB8, 3, 12), }; static uint32_t rk3399_get_pd_offset(struct rk_pinctrl_softc *sc, uint32_t bank) { if (bank < 2) return (0x40); return (0xE040); } static struct syscon * rk3399_get_syscon(struct rk_pinctrl_softc *sc, uint32_t bank) { if (bank < 2) return (sc->pmu); return (sc->grf); } static int rk3399_parse_bias(phandle_t node, int bank) { int pullup, pulldown; if (OF_hasprop(node, "bias-disable")) return (0); switch (bank) { case 0: case 2: pullup = 3; pulldown = 1; break; case 1: case 3: case 4: pullup = 1; pulldown = 2; break; } if (OF_hasprop(node, "bias-pull-up")) return (pullup); if (OF_hasprop(node, "bias-pull-down")) return (pulldown); return (-1); } +static int +rk3399_resolv_bias_value(int bank, int bias) +{ + int rv = 0; + + switch (bank) { + case 0: + case 2: + if (bias == 3) + rv = GPIO_PIN_PULLUP; + else if (bias == 1) + rv = GPIO_PIN_PULLDOWN; + break; + case 1: + case 3: + case 4: + if (bias == 1) + rv = GPIO_PIN_PULLUP; + else if (bias == 2) + rv = GPIO_PIN_PULLDOWN; + break; + } + + return (rv); +} + +static int +rk3399_get_bias_value(int bank, int bias) +{ + int rv = 0; + + switch (bank) { + case 0: + case 2: + if (bias & GPIO_PIN_PULLUP) + rv = 3; + else if (bias & GPIO_PIN_PULLDOWN) + rv = 1; + break; + case 1: + case 3: + case 4: + if (bias & GPIO_PIN_PULLUP) + rv = 1; + else if (bias & GPIO_PIN_PULLDOWN) + rv = 2; + break; + } + + return (rv); +} + struct rk_pinctrl_conf rk3399_conf = { .iomux_conf = rk3399_iomux_bank, .iomux_nbanks = nitems(rk3399_iomux_bank), .pin_fixup = rk3399_pin_fixup, .npin_fixup = nitems(rk3399_pin_fixup), .pin_drive = rk3399_pin_drive, .npin_drive = nitems(rk3399_pin_drive), .gpio_bank = rk3399_gpio_bank, .ngpio_bank = nitems(rk3399_gpio_bank), .get_pd_offset = rk3399_get_pd_offset, .get_syscon = rk3399_get_syscon, .parse_bias = rk3399_parse_bias, + .resolv_bias_value = rk3399_resolv_bias_value, + .get_bias_value = rk3399_get_bias_value, }; static struct ofw_compat_data compat_data[] = { {"rockchip,rk3288-pinctrl", (uintptr_t)&rk3288_conf}, {"rockchip,rk3328-pinctrl", (uintptr_t)&rk3328_conf}, {"rockchip,rk3399-pinctrl", (uintptr_t)&rk3399_conf}, {NULL, 0} }; static int rk_pinctrl_parse_drive(struct rk_pinctrl_softc *sc, phandle_t node, uint32_t bank, uint32_t subbank, uint32_t *drive, uint32_t *offset) { uint32_t value; int i; if (OF_getencprop(node, "drive-strength", &value, sizeof(value)) != 0) return (-1); /* Map to the correct drive value */ for (i = 0; i < sc->conf->npin_drive; i++) { if (sc->conf->pin_drive[i].bank != bank && sc->conf->pin_drive[i].subbank != subbank) continue; if (sc->conf->pin_drive[i].ma == value) { *drive = sc->conf->pin_drive[i].value; return (0); } } return (-1); } static void rk_pinctrl_get_fixup(struct rk_pinctrl_softc *sc, uint32_t bank, uint32_t pin, uint32_t *reg, uint32_t *mask, uint32_t *bit) { int i; for (i = 0; i < sc->conf->npin_fixup; i++) if (sc->conf->pin_fixup[i].bank == bank && sc->conf->pin_fixup[i].pin == pin) { *reg = sc->conf->pin_fixup[i].reg; *mask = sc->conf->pin_fixup[i].mask; *bit = sc->conf->pin_fixup[i].bit; return; } } static int rk_pinctrl_handle_io(struct rk_pinctrl_softc *sc, phandle_t node, uint32_t bank, uint32_t pin) { bool have_cfg, have_direction, have_value; uint32_t direction_value, pin_value; struct rk_pinctrl_gpio *gpio; int i, rv; have_cfg = false; have_direction = false; have_value = false; /* Get (subset of) GPIO pin properties. */ if (OF_hasprop(node, "output-disable")) { have_cfg = true; have_direction = true; direction_value = GPIO_PIN_INPUT; } if (OF_hasprop(node, "output-enable")) { have_cfg = true; have_direction = true; direction_value = GPIO_PIN_OUTPUT; } if (OF_hasprop(node, "output-low")) { have_cfg = true; have_direction = true; direction_value = GPIO_PIN_OUTPUT; have_value = true; pin_value = 0; } if (OF_hasprop(node, "output-high")) { have_cfg = true; have_direction = true; direction_value = GPIO_PIN_OUTPUT; have_value = true; pin_value = 1; } if (!have_cfg) return (0); /* Find gpio */ gpio = NULL; for(i = 0; i < sc->conf->ngpio_bank; i++) { if (bank == sc->conf->gpio_bank[i].bank) { gpio = sc->conf->gpio_bank + i; break; } } if (gpio == NULL) { device_printf(sc->dev, "Cannot find GPIO bank %d\n", bank); return (ENXIO); } if (gpio->gpio_dev == NULL) { device_printf(sc->dev, "No GPIO subdevice found for bank %d\n", bank); return (ENXIO); } rv = 0; if (have_value) { rv = GPIO_PIN_SET(gpio->gpio_dev, pin, pin_value); if (rv != 0) { device_printf(sc->dev, "Cannot set GPIO value: %d\n", rv); return (rv); } } if (have_direction) { rv = GPIO_PIN_SETFLAGS(gpio->gpio_dev, pin, direction_value); if (rv != 0) { device_printf(sc->dev, "Cannot set GPIO direction: %d\n", rv); return (rv); } } return (0); } static void rk_pinctrl_configure_pin(struct rk_pinctrl_softc *sc, uint32_t *pindata) { phandle_t pin_conf; struct syscon *syscon; uint32_t bank, subbank, pin, function, bias; uint32_t bit, mask, reg, drive; int i, rv; bank = pindata[0]; pin = pindata[1]; function = pindata[2]; pin_conf = OF_node_from_xref(pindata[3]); subbank = pin / 8; for (i = 0; i < sc->conf->iomux_nbanks; i++) if (sc->conf->iomux_conf[i].bank == bank && sc->conf->iomux_conf[i].subbank == subbank) break; if (i == sc->conf->iomux_nbanks) { device_printf(sc->dev, "Unknown pin %d in bank %d\n", pin, bank); return; } /* Find syscon */ syscon = sc->conf->get_syscon(sc, bank); /* Parse pin function */ reg = sc->conf->iomux_conf[i].offset; switch (sc->conf->iomux_conf[i].nbits) { case 4: if ((pin % 8) >= 4) reg += 0x4; bit = (pin % 4) * 4; mask = (0xF << bit); break; case 3: if ((pin % 8) >= 5) reg += 4; bit = (pin % 8 % 5) * 3; mask = (0x7 << bit); break; case 2: bit = (pin % 8) * 2; mask = (0x3 << bit); break; default: device_printf(sc->dev, "Unknown pin stride width %d in bank %d\n", sc->conf->iomux_conf[i].nbits, bank); return; } rk_pinctrl_get_fixup(sc, bank, pin, ®, &mask, &bit); /* * NOTE: not all syscon registers uses hi-word write mask, thus * register modify method should be used. * XXXX We should not pass write mask to syscon register * without hi-word write mask. */ SYSCON_MODIFY_4(syscon, reg, mask, function << bit | (mask << 16)); /* Pull-Up/Down */ bias = sc->conf->parse_bias(pin_conf, bank); if (bias >= 0) { reg = sc->conf->get_pd_offset(sc, bank); reg += bank * 0x10 + ((pin / 8) * 0x4); bit = (pin % 8) * 2; mask = (0x3 << bit); SYSCON_MODIFY_4(syscon, reg, mask, bias << bit | (mask << 16)); } /* Drive Strength */ rv = rk_pinctrl_parse_drive(sc, pin_conf, bank, subbank, &drive, ®); if (rv == 0) { bit = (pin % 8) * 2; mask = (0x3 << bit); SYSCON_MODIFY_4(syscon, reg, mask, drive << bit | (mask << 16)); } /* Input/Outpot + default level */ rv = rk_pinctrl_handle_io(sc, pin_conf, bank, pin); } static int rk_pinctrl_configure_pins(device_t dev, phandle_t cfgxref) { struct rk_pinctrl_softc *sc; phandle_t node; uint32_t *pins; int i, npins; sc = device_get_softc(dev); node = OF_node_from_xref(cfgxref); npins = OF_getencprop_alloc_multi(node, "rockchip,pins", sizeof(*pins), (void **)&pins); if (npins <= 0) return (ENOENT); for (i = 0; i != npins; i += 4) rk_pinctrl_configure_pin(sc, pins + i); return (0); } +static int +rk_pinctrl_is_gpio_locked(struct rk_pinctrl_softc *sc, struct syscon *syscon, + int bank, uint32_t pin, bool *is_gpio) +{ + uint32_t subbank, bit, mask, reg; + uint32_t pinfunc; + int i; + RK_PINCTRL_LOCK_ASSERT(sc); + + subbank = pin / 8; + *is_gpio = false; + + for (i = 0; i < sc->conf->iomux_nbanks; i++) + if (sc->conf->iomux_conf[i].bank == bank && + sc->conf->iomux_conf[i].subbank == subbank) + break; + + if (i == sc->conf->iomux_nbanks) { + device_printf(sc->dev, "Unknown pin %d in bank %d\n", pin, + bank); + return (EINVAL); + } + + syscon = sc->conf->get_syscon(sc, bank); + + /* Parse pin function */ + reg = sc->conf->iomux_conf[i].offset; + switch (sc->conf->iomux_conf[i].nbits) { + case 4: + if ((pin % 8) >= 4) + reg += 0x4; + bit = (pin % 4) * 4; + mask = (0xF << bit); + break; + case 3: + if ((pin % 8) >= 5) + reg += 4; + bit = (pin % 8 % 5) * 3; + mask = (0x7 << bit); + break; + case 2: + bit = (pin % 8) * 2; + mask = (0x3 << bit); + break; + default: + device_printf(sc->dev, + "Unknown pin stride width %d in bank %d\n", + sc->conf->iomux_conf[i].nbits, bank); + return (EINVAL); + } + rk_pinctrl_get_fixup(sc, bank, pin, ®, &mask, &bit); + + reg = SYSCON_READ_4(syscon, reg); + pinfunc = (reg & mask) >> bit; + + /* Test if the pin is in gpio mode */ + if (pinfunc == 0) + *is_gpio = true; + + return (0); +} + static int +rk_pinctrl_get_bank(struct rk_pinctrl_softc *sc, device_t gpio, int *bank) +{ + int i; + + for (i = 0; i < sc->conf->ngpio_bank; i++) { + if (sc->conf->gpio_bank[i].gpio_dev == gpio) + break; + } + if (i == sc->conf->ngpio_bank) + return (EINVAL); + + *bank = i; + return (0); +} + +static int +rk_pinctrl_is_gpio(device_t pinctrl, device_t gpio, uint32_t pin, bool *is_gpio) +{ + struct rk_pinctrl_softc *sc; + struct syscon *syscon; + int bank; + int rv; + + sc = device_get_softc(pinctrl); + RK_PINCTRL_LOCK(sc); + + rv = rk_pinctrl_get_bank(sc, gpio, &bank); + if (rv != 0) + goto done; + syscon = sc->conf->get_syscon(sc, bank); + rv = rk_pinctrl_is_gpio_locked(sc, syscon, bank, pin, is_gpio); + +done: + RK_PINCTRL_UNLOCK(sc); + + return (rv); +} + +static int +rk_pinctrl_get_flags(device_t pinctrl, device_t gpio, uint32_t pin, + uint32_t *flags) +{ + struct rk_pinctrl_softc *sc; + struct syscon *syscon; + uint32_t reg, mask, bit; + uint32_t bias; + int bank; + int rv = 0; + bool is_gpio; + + sc = device_get_softc(pinctrl); + RK_PINCTRL_LOCK(sc); + + rv = rk_pinctrl_get_bank(sc, gpio, &bank); + if (rv != 0) + goto done; + syscon = sc->conf->get_syscon(sc, bank); + rv = rk_pinctrl_is_gpio_locked(sc, syscon, bank, pin, &is_gpio); + if (rv != 0) + goto done; + if (!is_gpio) { + rv = EINVAL; + goto done; + } + /* Get the pullup/pulldown configuration */ + reg = sc->conf->get_pd_offset(sc, bank); + reg += bank * 0x10 + ((pin / 8) * 0x4); + bit = (pin % 8) * 2; + mask = (0x3 << bit) << 16; + reg = SYSCON_READ_4(syscon, reg); + reg = (reg >> bit) & 0x3; + bias = sc->conf->resolv_bias_value(bank, reg); + *flags = bias; + +done: + RK_PINCTRL_UNLOCK(sc); + return (rv); +} + +static int +rk_pinctrl_set_flags(device_t pinctrl, device_t gpio, uint32_t pin, + uint32_t flags) +{ + struct rk_pinctrl_softc *sc; + struct syscon *syscon; + uint32_t bit, mask, reg; + uint32_t bias; + int bank; + int rv = 0; + bool is_gpio; + + sc = device_get_softc(pinctrl); + RK_PINCTRL_LOCK(sc); + + rv = rk_pinctrl_get_bank(sc, gpio, &bank); + if (rv != 0) + goto done; + syscon = sc->conf->get_syscon(sc, bank); + rv = rk_pinctrl_is_gpio_locked(sc, syscon, bank, pin, &is_gpio); + if (rv != 0) + goto done; + if (!is_gpio) { + rv = EINVAL; + goto done; + } + /* Get the pullup/pulldown configuration */ + reg = sc->conf->get_pd_offset(sc, bank); + reg += bank * 0x10 + ((pin / 8) * 0x4); + bit = (pin % 8) * 2; + mask = (0x3 << bit); + bias = sc->conf->get_bias_value(bank, flags); + SYSCON_MODIFY_4(syscon, reg, mask, bias << bit | (mask << 16)); + +done: + RK_PINCTRL_UNLOCK(sc); + return (rv); +} + +static int rk_pinctrl_register_gpio(struct rk_pinctrl_softc *sc, char *gpio_name, device_t gpio_dev) { int i; for(i = 0; i < sc->conf->ngpio_bank; i++) { if (strcmp(gpio_name, sc->conf->gpio_bank[i].gpio_name) != 0) continue; sc->conf->gpio_bank[i].gpio_dev = gpio_dev; return(0); } return (ENXIO); } static int rk_pinctrl_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "RockChip Pinctrl controller"); return (BUS_PROBE_DEFAULT); } static int rk_pinctrl_attach(device_t dev) { struct rk_pinctrl_softc *sc; phandle_t node; device_t cdev; char *gpio_name, *eptr; int rv; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); if (OF_hasprop(node, "rockchip,grf") && syscon_get_by_ofw_property(dev, node, "rockchip,grf", &sc->grf) != 0) { device_printf(dev, "cannot get grf driver handle\n"); return (ENXIO); } /* RK3399,RK3288 has banks in PMU. RK3328 does not have a PMU. */ if (ofw_bus_node_is_compatible(node, "rockchip,rk3399-pinctrl") || ofw_bus_node_is_compatible(node, "rockchip,rk3288-pinctrl")) { if (OF_hasprop(node, "rockchip,pmu") && syscon_get_by_ofw_property(dev, node, "rockchip,pmu", &sc->pmu) != 0) { device_printf(dev, "cannot get pmu driver handle\n"); return (ENXIO); } } + mtx_init(&sc->mtx, "rk pinctrl", "pinctrl", MTX_SPIN); + sc->conf = (struct rk_pinctrl_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; fdt_pinctrl_register(dev, "rockchip,pins"); simplebus_init(dev, node); bus_generic_probe(dev); /* Attach child devices */ for (node = OF_child(node); node > 0; node = OF_peer(node)) { if (!ofw_bus_node_is_compatible(node, "rockchip,gpio-bank")) continue; rv = OF_getprop_alloc(node, "name", (void **)&gpio_name); if (rv <= 0) { device_printf(sc->dev, "Cannot GPIO subdevice name.\n"); continue; } cdev = simplebus_add_device(dev, node, 0, NULL, -1, NULL); if (cdev == NULL) { device_printf(dev, " Cannot add GPIO subdevice: %s\n", gpio_name); OF_prop_free(gpio_name); continue; } rv = device_probe_and_attach(cdev); if (rv != 0) { device_printf(sc->dev, "Cannot attach GPIO subdevice: %s\n", gpio_name); OF_prop_free(gpio_name); continue; } /* Grep device name from name property */ eptr = gpio_name; strsep(&eptr, "@"); if (gpio_name == eptr) { device_printf(sc->dev, "Unrecognized format of GPIO subdevice name: %s\n", gpio_name); OF_prop_free(gpio_name); continue; } rv = rk_pinctrl_register_gpio(sc, gpio_name, cdev); if (rv != 0) { device_printf(sc->dev, "Cannot register GPIO subdevice %s: %d\n", gpio_name, rv); OF_prop_free(gpio_name); continue; } OF_prop_free(gpio_name); } fdt_pinctrl_configure_tree(dev); return (bus_generic_attach(dev)); } static int rk_pinctrl_detach(device_t dev) { return (EBUSY); } static device_method_t rk_pinctrl_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_pinctrl_probe), DEVMETHOD(device_attach, rk_pinctrl_attach), DEVMETHOD(device_detach, rk_pinctrl_detach), /* fdt_pinctrl interface */ DEVMETHOD(fdt_pinctrl_configure, rk_pinctrl_configure_pins), + DEVMETHOD(fdt_pinctrl_is_gpio, rk_pinctrl_is_gpio), + DEVMETHOD(fdt_pinctrl_get_flags, rk_pinctrl_get_flags), + DEVMETHOD(fdt_pinctrl_set_flags, rk_pinctrl_set_flags), DEVMETHOD_END }; static devclass_t rk_pinctrl_devclass; DEFINE_CLASS_1(rk_pinctrl, rk_pinctrl_driver, rk_pinctrl_methods, sizeof(struct rk_pinctrl_softc), simplebus_driver); EARLY_DRIVER_MODULE(rk_pinctrl, simplebus, rk_pinctrl_driver, rk_pinctrl_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(rk_pinctrl, 1); Index: stable/12/sys/dev/fdt/fdt_pinctrl_if.m =================================================================== --- stable/12/sys/dev/fdt/fdt_pinctrl_if.m (revision 358652) +++ stable/12/sys/dev/fdt/fdt_pinctrl_if.m (revision 358653) @@ -1,59 +1,117 @@ #- # Copyright (c) 2014 Ian Lepore # 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$ # #include #include #include # # This is the interface that fdt_pinctrl drivers provide to other drivers. # INTERFACE fdt_pinctrl; +CODE { + static int + fdt_pinctrl_default_is_gpio(device_t pinctrl, device_t gpio, bool *is_gpio) + { + + return (EOPNOTSUPP); + } + + static int + fdt_pinctrl_default_set_flags(device_t pinctrl, device_t gpio, uint32_t pin, + uint32_t flags) + { + + return (EOPNOTSUPP); + } + + static int + fdt_pinctrl_default_get_flags(device_t pinctrl, device_t gpio, uint32_t pin, + uint32_t *flags) + { + + return (EOPNOTSUPP); + } +}; + # Needed for timestamping device probe/attach calls HEADER { #include } # # Set pins to the specified configuration. The cfgxref arg is an xref phandle # to a descendent node (child, grandchild, ...) of the pinctrl device node. # Returns 0 on success or a standard errno value. # PROLOG { TSENTER2(device_get_name(pinctrl)); } EPILOG { TSEXIT2(device_get_name(pinctrl)); } METHOD int configure { device_t pinctrl; phandle_t cfgxref; }; + +# +# Test if the pin is in gpio mode +# Called from a gpio device +# +METHOD int is_gpio { + device_t pinctrl; + device_t gpio; + uint32_t pin; + bool *is_gpio; +} DEFAULT fdt_pinctrl_default_is_gpio; + +# +# Set the flags of a pin +# Called from a gpio device +# +METHOD int set_flags { + device_t pinctrl; + device_t gpio; + uint32_t pin; + uint32_t flags; +} DEFAULT fdt_pinctrl_default_set_flags; + +# +# Get the flags of a pin +# Called from a gpio device +# +METHOD int get_flags { + device_t pinctrl; + device_t gpio; + uint32_t pin; + uint32_t *flags; +} DEFAULT fdt_pinctrl_default_get_flags; Index: stable/12 =================================================================== --- stable/12 (revision 358652) +++ stable/12 (revision 358653) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r348885,351543,356806-356807