Index: head/sys/arm/allwinner/a10_timer.c =================================================================== --- head/sys/arm/allwinner/a10_timer.c (revision 348884) +++ head/sys/arm/allwinner/a10_timer.c (revision 348885) @@ -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: head/sys/arm/broadcom/bcm2835/bcm2835_sdhost.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_sdhost.c (revision 348884) +++ head/sys/arm/broadcom/bcm2835/bcm2835_sdhost.c (revision 348885) @@ -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: head/sys/arm64/rockchip/if_dwc_rk.c =================================================================== --- head/sys/arm64/rockchip/if_dwc_rk.c (revision 348884) +++ head/sys/arm64/rockchip/if_dwc_rk.c (revision 348885) @@ -1,182 +1,186 @@ /*- * 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 +#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"))) 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);