Path: blob/main/sys/arm/broadcom/bcm2835/bcm2835_sdhost.c
39566 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2018 Klaus P. Ohrhallinger <[email protected]>4* All rights reserved.5*6* Based on bcm2835_sdhci.c:7* Copyright (c) 2012 Oleksandr Tymoshenko <[email protected]>8* All rights reserved.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18*19* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND20* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE21* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE22* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE23* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL24* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS25* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT27* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY28* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF29* SUCH DAMAGE.30*31*/32#include <sys/cdefs.h>33/*34* pin 48-53 - card slot35* pin 34-39 - radio module36*37* alt-0 - rubbish SDHCI (0x7e202000) aka sdhost38* alt-3 - advanced SDHCI (0x7e300000) aka sdhci/mmc/sdio39*40* driving card slot with mmc:41*42* sdhost_pins {43* brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>;44* brcm,function = <0x7>;45* brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>;46* phandle = <0x17>;47* };48* sdio_pins {49* brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>;50* brcm,function = <0x4>;51* brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>;52* phandle = <0x18>;53* };54*55* driving card slot with sdhost:56*57* sdhost_pins {58* brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>;59* brcm,function = <0x4>;60* brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>;61* phandle = <0x17>;62* };63* sdio_pins {64* brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>;65* brcm,function = <0x7>;66* brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>;67* phandle = <0x18>;68* };69*70*/7172#include <sys/param.h>73#include <sys/systm.h>74#include <sys/kobj.h>75#include <sys/bus.h>76#include <sys/kernel.h>77#include <sys/lock.h>78#include <sys/malloc.h>79#include <sys/module.h>80#include <sys/mutex.h>81#include <sys/rman.h>82#include <sys/sysctl.h>83#include <sys/taskqueue.h>84#include <sys/gpio.h>8586#include <machine/bus.h>8788#include <dev/ofw/ofw_bus.h>89#include <dev/ofw/ofw_bus_subr.h>9091#include <dev/mmc/bridge.h>92#include <dev/mmc/mmcreg.h>9394#include <dev/sdhci/sdhci.h>9596#include "mmcbr_if.h"97#include "sdhci_if.h"9899#include "opt_mmccam.h"100101#include "bcm2835_dma.h"102#include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h>103#include "bcm2835_vcbus.h"104105/* #define SDHOST_DEBUG */106107/* Registers */108#define HC_COMMAND 0x00 /* Command and flags */109#define HC_ARGUMENT 0x04110#define HC_TIMEOUTCOUNTER 0x08111#define HC_CLOCKDIVISOR 0x0c112#define HC_RESPONSE_0 0x10113#define HC_RESPONSE_1 0x14114#define HC_RESPONSE_2 0x18115#define HC_RESPONSE_3 0x1c116#define HC_HOSTSTATUS 0x20117#define HC_POWER 0x30118#define HC_DEBUG 0x34119#define HC_HOSTCONFIG 0x38120#define HC_BLOCKSIZE 0x3c121#define HC_DATAPORT 0x40122#define HC_BLOCKCOUNT 0x50123124/* Flags for HC_COMMAND register */125#define HC_CMD_ENABLE 0x8000126#define HC_CMD_FAILED 0x4000127#define HC_CMD_BUSY 0x0800128#define HC_CMD_RESPONSE_NONE 0x0400129#define HC_CMD_RESPONSE_LONG 0x0200130#define HC_CMD_WRITE 0x0080131#define HC_CMD_READ 0x0040132#define HC_CMD_COMMAND_MASK 0x003f133134#define HC_CLOCKDIVISOR_MAXVAL 0x07ff135136/* Flags for HC_HOSTSTATUS register */137#define HC_HSTST_HAVEDATA 0x0001138#define HC_HSTST_ERROR_FIFO 0x0008139#define HC_HSTST_ERROR_CRC7 0x0010140#define HC_HSTST_ERROR_CRC16 0x0020141#define HC_HSTST_TIMEOUT_CMD 0x0040142#define HC_HSTST_TIMEOUT_DATA 0x0080143#define HC_HSTST_INT_BLOCK 0x0200144#define HC_HSTST_INT_BUSY 0x0400145146#define HC_HSTST_RESET 0xffff147148#define HC_HSTST_MASK_ERROR_DATA (HC_HSTST_ERROR_FIFO | \149HC_HSTST_ERROR_CRC7 | HC_HSTST_ERROR_CRC16 | HC_HSTST_TIMEOUT_DATA)150151#define HC_HSTST_MASK_ERROR_ALL (HC_HSTST_MASK_ERROR_DATA | \152HC_HSTST_TIMEOUT_CMD)153154/* Flags for HC_HOSTCONFIG register */155#define HC_HSTCF_INTBUS_WIDE 0x0002156#define HC_HSTCF_EXTBUS_4BIT 0x0004157#define HC_HSTCF_SLOW_CARD 0x0008158#define HC_HSTCF_INT_DATA 0x0010159#define HC_HSTCF_INT_BLOCK 0x0100160#define HC_HSTCF_INT_BUSY 0x0400161162/* Flags for HC_DEBUG register */163#define HC_DBG_FIFO_THRESH_WRITE_SHIFT 9164#define HC_DBG_FIFO_THRESH_READ_SHIFT 14165#define HC_DBG_FIFO_THRESH_MASK 0x001f166167/* Settings */168#define HC_FIFO_SIZE 16169#define HC_FIFO_THRESH_READ 4170#define HC_FIFO_THRESH_WRITE 4171172#define HC_TIMEOUT_DEFAULT 0x00f00000173174#define BCM2835_DEFAULT_SDHCI_FREQ 50175176static int bcm2835_sdhost_debug = 0;177178#ifdef SDHOST_DEBUG179180TUNABLE_INT("hw.bcm2835.sdhost.debug", &bcm2835_sdhost_debug);181SYSCTL_INT(_hw_sdhci, OID_AUTO, bcm2835_sdhost_debug, CTLFLAG_RWTUN,182&bcm2835_sdhost_debug, 0, "bcm2835-sdhost Debug level");183184#define dprintf(fmt, args...) \185do { \186if (bcm2835_sdhost_debug > 0) \187printf(fmt,##args); \188} while (0)189#else190191#define dprintf(fmt, args...)192193#endif /* ! SDHOST_DEBUG */194195static struct ofw_compat_data compat_data[] = {196{"brcm,bcm2835-sdhost", 1},197{NULL, 0}198};199200struct bcm_sdhost_softc {201device_t sc_dev;202struct resource * sc_mem_res;203struct resource * sc_irq_res;204bus_space_tag_t sc_bst;205bus_space_handle_t sc_bsh;206void * sc_intrhand;207struct mmc_request * sc_req;208struct sdhci_slot sc_slot;209210struct mtx mtx;211212char cmdbusy;213char mmc_app_cmd;214215u_int32_t sdhci_int_status;216u_int32_t sdhci_signal_enable;217u_int32_t sdhci_present_state;218u_int32_t sdhci_blocksize;219u_int32_t sdhci_blockcount;220221u_int32_t sdcard_rca;222};223224static int bcm_sdhost_probe(device_t);225static int bcm_sdhost_attach(device_t);226static int bcm_sdhost_detach(device_t);227static void bcm_sdhost_intr(void *);228229static int bcm_sdhost_get_ro(device_t, device_t);230231static inline uint32_t232RD4(struct bcm_sdhost_softc *sc, bus_size_t off)233{234uint32_t val;235236val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off);237238return (val);239}240241static inline void242WR4(struct bcm_sdhost_softc *sc, bus_size_t off, uint32_t val)243{244245bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val);246}247248#ifdef notyet249static inline uint16_t250RD2(struct bcm_sdhost_softc *sc, bus_size_t off)251{252uint32_t val;253254val = RD4(sc, off & ~3);255256return ((val >> (off & 3)*8) & 0xffff);257}258#endif259260static inline uint8_t261RD1(struct bcm_sdhost_softc *sc, bus_size_t off)262{263uint32_t val;264265val = RD4(sc, off & ~3);266267return ((val >> (off & 3)*8) & 0xff);268}269270static inline void271WR2(struct bcm_sdhost_softc *sc, bus_size_t off, uint16_t val)272{273uint32_t val32;274275val32 = RD4(sc, off & ~3);276val32 &= ~(0xffff << (off & 3)*8);277val32 |= (val << (off & 3)*8);278WR4(sc, off & ~3, val32);279}280281static inline void282WR1(struct bcm_sdhost_softc *sc, bus_size_t off, uint8_t val)283{284uint32_t val32;285286val32 = RD4(sc, off & ~3);287val32 &= ~(0xff << (off & 3)*8);288val32 |= (val << (off & 3)*8);289WR4(sc, off & ~3, val32);290}291292static void293bcm_sdhost_print_regs(struct bcm_sdhost_softc *sc, struct sdhci_slot *slot,294int line, int error)295{296297if (bcm2835_sdhost_debug > 0 || error > 0) {298printf("%s: sc=%p slot=%p\n",299__func__, sc, slot);300printf("HC_COMMAND: 0x%08x\n",301RD4(sc, HC_COMMAND));302printf("HC_ARGUMENT: 0x%08x\n",303RD4(sc, HC_ARGUMENT));304printf("HC_TIMEOUTCOUNTER: 0x%08x\n",305RD4(sc, HC_TIMEOUTCOUNTER));306printf("HC_CLOCKDIVISOR: 0x%08x\n",307RD4(sc, HC_CLOCKDIVISOR));308printf("HC_RESPONSE_0: 0x%08x\n",309RD4(sc, HC_RESPONSE_0));310printf("HC_RESPONSE_1: 0x%08x\n",311RD4(sc, HC_RESPONSE_1));312printf("HC_RESPONSE_2: 0x%08x\n",313RD4(sc, HC_RESPONSE_2));314printf("HC_RESPONSE_3: 0x%08x\n",315RD4(sc, HC_RESPONSE_3));316printf("HC_HOSTSTATUS: 0x%08x\n",317RD4(sc, HC_HOSTSTATUS));318printf("HC_POWER: 0x%08x\n",319RD4(sc, HC_POWER));320printf("HC_DEBUG: 0x%08x\n",321RD4(sc, HC_DEBUG));322printf("HC_HOSTCONFIG: 0x%08x\n",323RD4(sc, HC_HOSTCONFIG));324printf("HC_BLOCKSIZE: 0x%08x\n",325RD4(sc, HC_BLOCKSIZE));326printf("HC_BLOCKCOUNT: 0x%08x\n",327RD4(sc, HC_BLOCKCOUNT));328329} else {330/*331printf("%04d | HC_COMMAND: 0x%08x HC_ARGUMENT: 0x%08x "332"HC_HOSTSTATUS: 0x%08x HC_HOSTCONFIG: 0x%08x\n",333line, RD4(sc, HC_COMMAND), RD4(sc, HC_ARGUMENT),334RD4(sc, HC_HOSTSTATUS), RD4(sc, HC_HOSTCONFIG));335*/336}337}338339static void340bcm_sdhost_reset(device_t dev, struct sdhci_slot *slot)341{342struct bcm_sdhost_softc *sc = device_get_softc(dev);343u_int32_t dbg;344345WR4(sc, HC_POWER, 0);346347WR4(sc, HC_COMMAND, 0);348WR4(sc, HC_ARGUMENT, 0);349WR4(sc, HC_TIMEOUTCOUNTER, HC_TIMEOUT_DEFAULT);350WR4(sc, HC_CLOCKDIVISOR, 0);351WR4(sc, HC_HOSTSTATUS, HC_HSTST_RESET);352WR4(sc, HC_HOSTCONFIG, 0);353WR4(sc, HC_BLOCKSIZE, 0);354WR4(sc, HC_BLOCKCOUNT, 0);355356dbg = RD4(sc, HC_DEBUG);357dbg &= ~( (HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_READ_SHIFT) |358(HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_WRITE_SHIFT) );359dbg |= (HC_FIFO_THRESH_READ << HC_DBG_FIFO_THRESH_READ_SHIFT) |360(HC_FIFO_THRESH_WRITE << HC_DBG_FIFO_THRESH_WRITE_SHIFT);361WR4(sc, HC_DEBUG, dbg);362363DELAY(250000);364365WR4(sc, HC_POWER, 1);366367DELAY(250000);368369sc->sdhci_present_state = SDHCI_CARD_PRESENT | SDHCI_CARD_STABLE |370SDHCI_WRITE_PROTECT;371372WR4(sc, HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_MAXVAL);373WR4(sc, HC_HOSTCONFIG, HC_HSTCF_INT_BUSY);374}375376static int377bcm_sdhost_probe(device_t dev)378{379380dprintf("%s:\n", __func__);381382if (!ofw_bus_status_okay(dev))383return (ENXIO);384385if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)386return (ENXIO);387388device_set_desc(dev, "Broadcom 2708 SDHOST controller");389390return (BUS_PROBE_DEFAULT);391}392393static int394bcm_sdhost_attach(device_t dev)395{396struct bcm_sdhost_softc *sc = device_get_softc(dev);397int rid, err;398u_int default_freq;399400dprintf("%s: dev=%p sc=%p unit=%d\n",401__func__, dev, sc, device_get_unit(dev));402403mtx_init(&sc->mtx, "BCM SDHOST mtx", "bcm_sdhost",404MTX_DEF | MTX_RECURSE);405406sc->sc_dev = dev;407sc->sc_req = NULL;408409sc->cmdbusy = 0;410sc->mmc_app_cmd = 0;411sc->sdhci_int_status = 0;412sc->sdhci_signal_enable = 0;413sc->sdhci_present_state = 0;414sc->sdhci_blocksize = 0;415sc->sdhci_blockcount = 0;416417sc->sdcard_rca = 0;418419default_freq = 50;420err = 0;421422if (bootverbose)423device_printf(dev, "SDHCI frequency: %dMHz\n", default_freq);424425rid = 0;426sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,427RF_ACTIVE);428if (!sc->sc_mem_res) {429device_printf(dev, "cannot allocate memory window\n");430err = ENXIO;431goto fail;432}433434sc->sc_bst = rman_get_bustag(sc->sc_mem_res);435sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);436437bcm_sdhost_reset(dev, &sc->sc_slot);438439bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 0);440441rid = 0;442sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,443RF_ACTIVE);444if (!sc->sc_irq_res) {445device_printf(dev, "cannot allocate interrupt\n");446err = ENXIO;447goto fail;448}449450if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,451NULL, bcm_sdhost_intr, sc, &sc->sc_intrhand)) {452device_printf(dev, "cannot setup interrupt handler\n");453err = ENXIO;454goto fail;455}456457sc->sc_slot.caps = 0;458sc->sc_slot.caps |= SDHCI_CAN_VDD_330;459sc->sc_slot.caps |= SDHCI_CAN_DO_HISPD;460sc->sc_slot.caps |= (default_freq << SDHCI_CLOCK_BASE_SHIFT);461462sc->sc_slot.quirks = 0;463sc->sc_slot.quirks |= SDHCI_QUIRK_MISSING_CAPS;464sc->sc_slot.quirks |= SDHCI_QUIRK_DONT_SHIFT_RESPONSE;465466sc->sc_slot.opt = 0;467468/* XXX ?469sc->slot->timeout_clk = ...;470*/471472sdhci_init_slot(dev, &sc->sc_slot, 0);473474bus_identify_children(dev);475bus_attach_children(dev);476477sdhci_start_slot(&sc->sc_slot);478479return (0);480481fail:482if (sc->sc_intrhand)483bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);484if (sc->sc_irq_res)485bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);486if (sc->sc_mem_res)487bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);488489return (err);490}491492static int493bcm_sdhost_detach(device_t dev)494{495496dprintf("%s:\n", __func__);497498return (EBUSY);499}500501/*502* rv 0 --> command finished503* rv 1 --> command timed out504*/505static inline int506bcm_sdhost_waitcommand(struct bcm_sdhost_softc *sc)507{508int timeout = 1000;509510mtx_assert(&sc->mtx, MA_OWNED);511512while ((RD4(sc, HC_COMMAND) & HC_CMD_ENABLE) && --timeout > 0) {513DELAY(100);514}515516return ((timeout > 0) ? 0 : 1);517}518519static int520bcm_sdhost_waitcommand_status(struct bcm_sdhost_softc *sc)521{522u_int32_t cdst;523int i;524525/* wait for card to change status from526* ''prg'' to ''trn''527* card status: sd specs p. 103528*/529i = 0;530do {531DELAY(1000);532WR4(sc, HC_ARGUMENT, sc->sdcard_rca << 16);533WR4(sc, HC_COMMAND,534MMC_SEND_STATUS | HC_CMD_ENABLE);535bcm_sdhost_waitcommand(sc);536cdst = RD4(sc, HC_RESPONSE_0);537dprintf("%s: card status %08x (cs %d)\n",538__func__, cdst, (cdst & 0x0e00) >> 9);539if (i++ > 100) {540printf("%s: giving up, "541"card status %08x (cs %d)\n",542__func__, cdst,543(cdst & 0x0e00) >> 9);544return (1);545break;546}547} while (((cdst & 0x0e00) >> 9) != 4);548549return (0);550}551552static void553bcm_sdhost_intr(void *arg)554{555struct bcm_sdhost_softc *sc = arg;556struct sdhci_slot *slot = &sc->sc_slot;557uint32_t hstst;558uint32_t cmd;559560mtx_lock(&sc->mtx);561562hstst = RD4(sc, HC_HOSTSTATUS);563cmd = RD4(sc, HC_COMMAND);564if (hstst & HC_HSTST_HAVEDATA) {565if (cmd & HC_CMD_READ) {566sc->sdhci_present_state |= SDHCI_DATA_AVAILABLE;567sc->sdhci_int_status |= SDHCI_INT_DATA_AVAIL;568} else if (cmd & HC_CMD_WRITE) {569sc->sdhci_present_state |= SDHCI_SPACE_AVAILABLE;570sc->sdhci_int_status |= SDHCI_INT_SPACE_AVAIL;571} else {572panic("%s: hstst & HC_HSTST_HAVEDATA but no "573"HC_CMD_READ or HC_CMD_WRITE: cmd=%0x8 "574"hstst=%08x\n", __func__, cmd, hstst);575}576} else {577sc->sdhci_present_state &=578~(SDHCI_DATA_AVAILABLE|SDHCI_SPACE_AVAILABLE);579sc->sdhci_int_status &=580~(SDHCI_INT_DATA_AVAIL|SDHCI_INT_SPACE_AVAIL);581}582583if (hstst & HC_HSTST_MASK_ERROR_ALL) {584printf("%s: ERROR: HC_HOSTSTATUS: %08x\n", __func__, hstst);585bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);586sc->sdhci_int_status |= SDHCI_INT_ERROR;587} else {588sc->sdhci_int_status &= ~SDHCI_INT_ERROR;589}590591dprintf("%s: hstst=%08x offset=%08lx sdhci_present_state=%08x "592"sdhci_int_status=%08x\n", __func__, hstst, slot->offset,593sc->sdhci_present_state, sc->sdhci_int_status);594595sdhci_generic_intr(&sc->sc_slot);596597sc->sdhci_int_status &=598~(SDHCI_INT_ERROR|SDHCI_INT_DATA_AVAIL|SDHCI_INT_DATA_END);599sc->sdhci_present_state &= ~SDHCI_DATA_AVAILABLE;600601if ((hstst & HC_HSTST_HAVEDATA) &&602(sc->sdhci_blocksize * sc->sdhci_blockcount == slot->offset)) {603dprintf("%s: offset=%08lx sdhci_blocksize=%08x "604"sdhci_blockcount=%08x\n", __func__, slot->offset,605sc->sdhci_blocksize, sc->sdhci_blockcount);606sc->sdhci_int_status &=607~(SDHCI_INT_DATA_AVAIL|SDHCI_INT_SPACE_AVAIL);608sc->sdhci_int_status |= SDHCI_INT_DATA_END;609sdhci_generic_intr(&sc->sc_slot);610sc->sdhci_int_status &= ~SDHCI_INT_DATA_END;611612if ((cmd & HC_CMD_COMMAND_MASK) == MMC_READ_MULTIPLE_BLOCK ||613(cmd & HC_CMD_COMMAND_MASK) == MMC_WRITE_MULTIPLE_BLOCK) {614WR4(sc, HC_ARGUMENT, 0x00000000);615WR4(sc, HC_COMMAND,616MMC_STOP_TRANSMISSION | HC_CMD_ENABLE);617618if (bcm_sdhost_waitcommand(sc)) {619printf("%s: timeout #1\n", __func__);620bcm_sdhost_print_regs(sc, &sc->sc_slot,621__LINE__, 1);622}623}624625if (cmd & HC_CMD_WRITE) {626if (bcm_sdhost_waitcommand_status(sc) != 0)627sc->sdhci_int_status |= SDHCI_INT_ERROR;628}629630slot->data_done = 1;631632sc->sdhci_int_status |= SDHCI_INT_RESPONSE;633sdhci_generic_intr(&sc->sc_slot);634sc->sdhci_int_status &= ~(SDHCI_INT_RESPONSE|SDHCI_INT_ERROR);635}636637/* this resets the interrupt */638WR4(sc, HC_HOSTSTATUS,639(HC_HSTST_INT_BUSY|HC_HSTST_INT_BLOCK|HC_HSTST_HAVEDATA));640641mtx_unlock(&sc->mtx);642}643644static int645bcm_sdhost_get_ro(device_t bus, device_t child)646{647648dprintf("%s:\n", __func__);649650return (0);651}652653static bool654bcm_sdhost_get_card_present(device_t dev, struct sdhci_slot *slot)655{656657dprintf("%s:\n", __func__);658659return (1);660}661662static void663bcm_sdhost_command(device_t dev, struct sdhci_slot *slot, uint16_t val)664{665struct bcm_sdhost_softc *sc = device_get_softc(dev);666struct mmc_data *data = slot->curcmd->data;667uint16_t val2;668uint8_t opcode;669uint8_t flags;670671mtx_assert(&sc->mtx, MA_OWNED);672673if (RD4(sc, HC_COMMAND) & HC_CMD_ENABLE) {674panic("%s: HC_CMD_ENABLE on entry\n", __func__);675}676677if (sc->cmdbusy == 1)678panic("%s: cmdbusy\n", __func__);679680sc->cmdbusy = 1;681682val2 = ((val >> 8) & HC_CMD_COMMAND_MASK) | HC_CMD_ENABLE;683684opcode = val >> 8;685flags = val & 0xff;686687if (opcode == MMC_APP_CMD)688sc->mmc_app_cmd = 1;689690if ((flags & SDHCI_CMD_RESP_MASK) == SDHCI_CMD_RESP_LONG)691val2 |= HC_CMD_RESPONSE_LONG;692else if ((flags & SDHCI_CMD_RESP_MASK) == SDHCI_CMD_RESP_SHORT_BUSY)693/* XXX XXX when enabled, cmd 7 (select card) blocks forever */694;/*val2 |= HC_CMD_BUSY; */695else if ((flags & SDHCI_CMD_RESP_MASK) == SDHCI_CMD_RESP_SHORT)696;697else698val2 |= HC_CMD_RESPONSE_NONE;699700if (val2 & HC_CMD_BUSY)701sc->sdhci_present_state |=702SDHCI_CMD_INHIBIT | SDHCI_DAT_INHIBIT;703704if (data != NULL && data->flags & MMC_DATA_READ)705val2 |= HC_CMD_READ;706else if (data != NULL && data->flags & MMC_DATA_WRITE)707val2 |= HC_CMD_WRITE;708709dprintf("%s: SDHCI_COMMAND_FLAGS --> HC_COMMAND %04x --> %04x\n",710__func__, val, val2);711712if (opcode == MMC_READ_MULTIPLE_BLOCK ||713opcode == MMC_WRITE_MULTIPLE_BLOCK) {714u_int32_t save_sdarg;715716dprintf("%s: issuing MMC_SET_BLOCK_COUNT: CMD %08x ARG %08x\n",717__func__, MMC_SET_BLOCK_COUNT | HC_CMD_ENABLE,718sc->sdhci_blockcount);719720save_sdarg = RD4(sc, HC_ARGUMENT);721WR4(sc, HC_ARGUMENT, sc->sdhci_blockcount);722WR4(sc, HC_COMMAND, MMC_SET_BLOCK_COUNT | HC_CMD_ENABLE);723724/* Seems to always return timeout */725726if (bcm_sdhost_waitcommand(sc)) {727printf("%s: timeout #2\n", __func__);728bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);729} else {730bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 0);731}732WR4(sc, HC_ARGUMENT, save_sdarg);733734} else if (opcode == MMC_SELECT_CARD) {735sc->sdcard_rca = (RD4(sc, HC_ARGUMENT) >> 16);736}737738/* actually issuing the command */739WR4(sc, HC_COMMAND, val2);740741if (val2 & HC_CMD_READ || val2 & HC_CMD_WRITE) {742u_int8_t hstcfg;743744hstcfg = RD4(sc, HC_HOSTCONFIG);745hstcfg |= (HC_HSTCF_INT_BUSY | HC_HSTCF_INT_DATA);746WR4(sc, HC_HOSTCONFIG, hstcfg);747slot->data_done = 0;748749if (bcm_sdhost_waitcommand(sc)) {750printf("%s: timeout #3\n", __func__);751bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);752}753754} else if (opcode == MMC_ERASE) {755if (bcm_sdhost_waitcommand_status(sc) != 0) {756printf("%s: timeout #4\n", __func__);757bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);758}759slot->data_done = 1;760sc->sdhci_present_state &=761~(SDHCI_CMD_INHIBIT | SDHCI_DAT_INHIBIT);762763} else {764if (bcm_sdhost_waitcommand(sc)) {765printf("%s: timeout #5\n", __func__);766bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);767}768slot->data_done = 1;769sc->sdhci_present_state &=770~(SDHCI_CMD_INHIBIT | SDHCI_DAT_INHIBIT);771}772773bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 0);774775if (RD4(sc, HC_HOSTSTATUS) & HC_HSTST_TIMEOUT_CMD)776slot->curcmd->error = MMC_ERR_TIMEOUT;777else if (RD4(sc, HC_COMMAND) & HC_CMD_FAILED)778slot->curcmd->error = MMC_ERR_FAILED;779780dprintf("%s: curcmd->flags=%d data_done=%d\n",781__func__, slot->curcmd->flags, slot->data_done);782783if (val2 & HC_CMD_RESPONSE_NONE)784slot->curcmd->error = 0;785786if (sc->mmc_app_cmd == 1 && opcode != MMC_APP_CMD)787sc->mmc_app_cmd = 0;788789if (RD4(sc, HC_COMMAND) & HC_CMD_ENABLE) {790bcm_sdhost_print_regs(sc, &sc->sc_slot, __LINE__, 1);791panic("%s: still HC_CMD_ENABLE on exit\n", __func__);792}793794sc->cmdbusy = 0;795796if (!(val2 & HC_CMD_READ || val2 & HC_CMD_WRITE))797sc->sdhci_int_status |= SDHCI_INT_RESPONSE;798799/* HACK, so sdhci_finish_command() does not800* have to be exported801*/802mtx_unlock(&slot->mtx);803sdhci_generic_intr(slot);804mtx_lock(&slot->mtx);805sc->sdhci_int_status &= ~SDHCI_INT_RESPONSE;806}807808static uint8_t809bcm_sdhost_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)810{811struct bcm_sdhost_softc *sc = device_get_softc(dev);812uint32_t val1, val2;813814mtx_lock(&sc->mtx);815816switch (off) {817case SDHCI_HOST_CONTROL:818val1 = RD4(sc, HC_HOSTCONFIG);819val2 = 0;820if (val1 & HC_HSTCF_EXTBUS_4BIT)821val2 |= SDHCI_CTRL_4BITBUS;822dprintf("%s: SDHCI_HOST_CONTROL --> HC_HOSTCONFIG val2 %02x\n",823__func__, val2);824break;825case SDHCI_POWER_CONTROL:826val1 = RD1(sc, HC_POWER);827val2 = (val1 == 1) ? 0x0f : 0;828dprintf("%s: SDHCI_POWER_CONTROL --> HC_POWER val2 %02x\n",829__func__, val2);830break;831case SDHCI_BLOCK_GAP_CONTROL:832dprintf("%s: SDHCI_BLOCK_GAP_CONTROL\n", __func__);833val2 = 0;834break;835case SDHCI_WAKE_UP_CONTROL:836dprintf("%s: SDHCI_WAKE_UP_CONTROL\n", __func__);837val2 = 0;838break;839case SDHCI_TIMEOUT_CONTROL:840dprintf("%s: SDHCI_TIMEOUT_CONTROL\n", __func__);841val2 = 0;842break;843case SDHCI_SOFTWARE_RESET:844dprintf("%s: SDHCI_SOFTWARE_RESET\n", __func__);845val2 = 0;846break;847case SDHCI_ADMA_ERR:848dprintf("%s: SDHCI_ADMA_ERR\n", __func__);849val2 = 0;850break;851default:852dprintf("%s: UNKNOWN off=%08lx\n", __func__, off);853val2 = 0;854break;855}856857mtx_unlock(&sc->mtx);858859return (val2);860}861862static uint16_t863bcm_sdhost_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)864{865struct bcm_sdhost_softc *sc = device_get_softc(dev);866uint32_t val2, val; /* = RD4(sc, off & ~3); */867868mtx_lock(&sc->mtx);869870switch (off) {871case SDHCI_BLOCK_SIZE:872val2 = sc->sdhci_blocksize;873dprintf("%s: SDHCI_BLOCK_SIZE --> HC_BLOCKSIZE %08x\n",874__func__, val2);875break;876case SDHCI_BLOCK_COUNT:877val2 = sc->sdhci_blockcount;878dprintf("%s: SDHCI_BLOCK_COUNT --> HC_BLOCKCOUNT %08x\n",879__func__, val2);880break;881case SDHCI_TRANSFER_MODE:882dprintf("%s: SDHCI_TRANSFER_MODE\n", __func__);883val2 = 0;884break;885case SDHCI_CLOCK_CONTROL:886val = RD4(sc, HC_CLOCKDIVISOR);887val2 = (val << SDHCI_DIVIDER_SHIFT) |888SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN |889SDHCI_CLOCK_INT_STABLE;890dprintf("%s: SDHCI_CLOCK_CONTROL %04x --> %04x\n",891__func__, val, val2);892break;893case SDHCI_ACMD12_ERR:894dprintf("%s: SDHCI_ACMD12_ERR\n", __func__);895val2 = 0;896break;897case SDHCI_HOST_CONTROL2:898dprintf("%s: SDHCI_HOST_CONTROL2\n", __func__);899val2 = 0;900break;901case SDHCI_SLOT_INT_STATUS:902dprintf("%s: SDHCI_SLOT_INT_STATUS\n", __func__);903val2 = 0;904break;905case SDHCI_HOST_VERSION:906dprintf("%s: SDHCI_HOST_VERSION\n", __func__);907val2 = 0;908break;909default:910dprintf("%s: UNKNOWN off=%08lx\n", __func__, off);911val2 = 0;912break;913}914915mtx_unlock(&sc->mtx);916917return (val2);918}919920static uint32_t921bcm_sdhost_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)922{923struct bcm_sdhost_softc *sc = device_get_softc(dev);924uint32_t val2;925926mtx_lock(&sc->mtx);927928switch (off) {929case SDHCI_DMA_ADDRESS:930dprintf("%s: SDHCI_DMA_ADDRESS\n", __func__);931val2 = 0;932break;933case SDHCI_ARGUMENT:934dprintf("%s: SDHCI_ARGUMENT\n", __func__);935val2 = (RD4(sc, HC_COMMAND) << 16) |936(RD4(sc, HC_ARGUMENT) & 0x0000ffff);937break;938case SDHCI_RESPONSE + 0:939val2 = RD4(sc, HC_RESPONSE_0);940dprintf("%s: SDHCI_RESPONSE+0 %08x\n", __func__, val2);941break;942case SDHCI_RESPONSE + 4:943val2 = RD4(sc, HC_RESPONSE_1);944dprintf("%s: SDHCI_RESPONSE+4 %08x\n", __func__, val2);945break;946case SDHCI_RESPONSE + 8:947val2 = RD4(sc, HC_RESPONSE_2);948dprintf("%s: SDHCI_RESPONSE+8 %08x\n", __func__, val2);949break;950case SDHCI_RESPONSE + 12:951val2 = RD4(sc, HC_RESPONSE_3);952dprintf("%s: SDHCI_RESPONSE+12 %08x\n", __func__, val2);953break;954case SDHCI_BUFFER:955dprintf("%s: SDHCI_BUFFER\n", __func__);956val2 = 0;957break;958case SDHCI_PRESENT_STATE:959dprintf("%s: SDHCI_PRESENT_STATE %08x\n",960__func__, sc->sdhci_present_state);961val2 = sc->sdhci_present_state;962break;963case SDHCI_INT_STATUS:964dprintf("%s: SDHCI_INT_STATUS %08x\n",965__func__, sc->sdhci_int_status);966val2 = sc->sdhci_int_status;967break;968case SDHCI_INT_ENABLE:969dprintf("%s: SDHCI_INT_ENABLE\n", __func__);970val2 = 0;971break;972case SDHCI_SIGNAL_ENABLE:973dprintf("%s: SDHCI_SIGNAL_ENABLE %08x\n",974__func__, sc->sdhci_signal_enable);975val2 = sc->sdhci_signal_enable;976break;977case SDHCI_CAPABILITIES:978val2 = 0;979break;980case SDHCI_CAPABILITIES2:981dprintf("%s: SDHCI_CAPABILITIES2\n", __func__);982val2 = 0;983break;984case SDHCI_MAX_CURRENT:985dprintf("%s: SDHCI_MAX_CURRENT\n", __func__);986val2 = 0;987break;988case SDHCI_ADMA_ADDRESS_LO:989dprintf("%s: SDHCI_ADMA_ADDRESS_LO\n", __func__);990val2 = 0;991break;992default:993dprintf("%s: UNKNOWN off=%08lx\n", __func__, off);994val2 = 0;995break;996}997998mtx_unlock(&sc->mtx);9991000return (val2);1001}10021003static void1004bcm_sdhost_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,1005uint32_t *data, bus_size_t count)1006{1007struct bcm_sdhost_softc *sc = device_get_softc(dev);1008bus_size_t i;1009bus_size_t avail;1010uint32_t edm;10111012mtx_lock(&sc->mtx);10131014dprintf("%s: off=%08lx count=%08lx\n", __func__, off, count);10151016for (i = 0; i < count;) {1017edm = RD4(sc, HC_DEBUG);1018avail = ((edm >> 4) & 0x1f);1019if (i + avail > count)1020avail = count - i;1021if (avail > 0)1022bus_space_read_multi_4(sc->sc_bst, sc->sc_bsh,1023HC_DATAPORT, data + i, avail);1024i += avail;1025DELAY(1);1026}10271028mtx_unlock(&sc->mtx);1029}10301031static void1032bcm_sdhost_write_1(device_t dev, struct sdhci_slot *slot,1033bus_size_t off, uint8_t val)1034{1035struct bcm_sdhost_softc *sc = device_get_softc(dev);1036uint32_t val2;10371038mtx_lock(&sc->mtx);10391040switch (off) {1041case SDHCI_HOST_CONTROL:1042val2 = RD4(sc, HC_HOSTCONFIG);1043val2 |= HC_HSTCF_INT_BUSY;1044val2 |= HC_HSTCF_INTBUS_WIDE | HC_HSTCF_SLOW_CARD;1045if (val & SDHCI_CTRL_4BITBUS)1046val2 |= HC_HSTCF_EXTBUS_4BIT;1047dprintf("%s: SDHCI_HOST_CONTROL --> HC_HOSTC %04x --> %04x\n",1048__func__, val, val2);1049WR4(sc, HC_HOSTCONFIG, val2);1050break;1051case SDHCI_POWER_CONTROL:1052val2 = (val != 0) ? 1 : 0;1053dprintf("%s: SDHCI_POWER_CONTROL --> HC_POWER %02x --> %02x\n",1054__func__, val, val2);1055WR1(sc, HC_POWER, val2);1056break;1057case SDHCI_BLOCK_GAP_CONTROL:1058dprintf("%s: SDHCI_BLOCK_GAP_CONTROL val=%02x\n",1059__func__, val);1060break;1061case SDHCI_TIMEOUT_CONTROL:1062dprintf("%s: SDHCI_TIMEOUT_CONTROL val=%02x\n",1063__func__, val);1064break;1065case SDHCI_SOFTWARE_RESET:1066dprintf("%s: SDHCI_SOFTWARE_RESET val=%02x\n",1067__func__, val);1068break;1069case SDHCI_ADMA_ERR:1070dprintf("%s: SDHCI_ADMA_ERR val=%02x\n",1071__func__, val);1072break;1073default:1074dprintf("%s: UNKNOWN off=%08lx val=%08x\n",1075__func__, off, val);1076break;1077}10781079mtx_unlock(&sc->mtx);1080}10811082static void1083bcm_sdhost_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val)1084{1085struct bcm_sdhost_softc *sc = device_get_softc(dev);1086uint16_t val2;10871088mtx_lock(&sc->mtx);10891090switch (off) {1091case SDHCI_BLOCK_SIZE:1092dprintf("%s: SDHCI_BLOCK_SIZE val=%04x\n" ,1093__func__, val);1094sc->sdhci_blocksize = val;1095WR2(sc, HC_BLOCKSIZE, val);1096break;10971098case SDHCI_BLOCK_COUNT:1099dprintf("%s: SDHCI_BLOCK_COUNT val=%04x\n" ,1100__func__, val);1101sc->sdhci_blockcount = val;1102WR2(sc, HC_BLOCKCOUNT, val);1103break;11041105case SDHCI_TRANSFER_MODE:1106dprintf("%s: SDHCI_TRANSFER_MODE val=%04x\n" ,1107__func__, val);1108break;11091110case SDHCI_COMMAND_FLAGS:1111bcm_sdhost_command(dev, slot, val);1112break;11131114case SDHCI_CLOCK_CONTROL:1115val2 = (val & ~SDHCI_DIVIDER_MASK) >> SDHCI_DIVIDER_SHIFT;1116/* get crc16 errors with cdiv=0 */1117if (val2 == 0)1118val2 = 1;1119dprintf("%s: SDHCI_CLOCK_CONTROL %04x --> SCDIV %04x\n",1120__func__, val, val2);1121WR4(sc, HC_CLOCKDIVISOR, val2);1122break;11231124case SDHCI_ACMD12_ERR:1125dprintf("%s: SDHCI_ACMD12_ERR val=%04x\n" ,1126__func__, val);1127break;11281129case SDHCI_HOST_CONTROL2:1130dprintf("%s: SDHCI_HOST_CONTROL2 val=%04x\n" ,1131__func__, val);1132break;11331134case SDHCI_SLOT_INT_STATUS:1135dprintf("%s: SDHCI_SLOT_INT_STATUS val=%04x\n" ,1136__func__, val);1137break;11381139default:1140dprintf("%s: UNKNOWN off=%08lx val=%04x\n",1141__func__, off, val);1142break;1143}11441145mtx_unlock(&sc->mtx);1146}11471148static void1149bcm_sdhost_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val)1150{1151struct bcm_sdhost_softc *sc = device_get_softc(dev);1152uint32_t val2;1153uint32_t hstcfg;11541155mtx_lock(&sc->mtx);11561157switch (off) {1158case SDHCI_ARGUMENT:1159val2 = val;1160dprintf("%s: SDHCI_ARGUMENT --> HC_ARGUMENT val=%08x\n",1161__func__, val);1162WR4(sc, HC_ARGUMENT, val2);1163break;1164case SDHCI_INT_STATUS:1165dprintf("%s: SDHCI_INT_STATUS val=%08x\n",1166__func__, val);1167sc->sdhci_int_status = val;1168break;1169case SDHCI_INT_ENABLE:1170dprintf("%s: SDHCI_INT_ENABLE val=%08x\n" ,1171__func__, val);1172break;1173case SDHCI_SIGNAL_ENABLE:1174sc->sdhci_signal_enable = val;1175hstcfg = RD4(sc, HC_HOSTCONFIG);1176if (val != 0)1177hstcfg &= ~(HC_HSTCF_INT_BLOCK | HC_HSTCF_INT_DATA);1178else1179hstcfg |= (HC_HSTCF_INT_BUSY|HC_HSTCF_INT_BLOCK|1180HC_HSTCF_INT_DATA);1181hstcfg |= HC_HSTCF_INT_BUSY;1182dprintf("%s: SDHCI_SIGNAL_ENABLE --> HC_HOSTC %08x --> %08x\n" ,1183__func__, val, hstcfg);1184WR4(sc, HC_HOSTCONFIG, hstcfg);1185break;1186case SDHCI_CAPABILITIES:1187dprintf("%s: SDHCI_CAPABILITIES val=%08x\n",1188__func__, val);1189break;1190case SDHCI_CAPABILITIES2:1191dprintf("%s: SDHCI_CAPABILITIES2 val=%08x\n",1192__func__, val);1193break;1194case SDHCI_MAX_CURRENT:1195dprintf("%s: SDHCI_MAX_CURRENT val=%08x\n",1196__func__, val);1197break;1198case SDHCI_ADMA_ADDRESS_LO:1199dprintf("%s: SDHCI_ADMA_ADDRESS_LO val=%08x\n",1200__func__, val);1201break;1202default:1203dprintf("%s: UNKNOWN off=%08lx val=%08x\n",1204__func__, off, val);1205break;1206}12071208mtx_unlock(&sc->mtx);1209}12101211static void1212bcm_sdhost_write_multi_4(device_t dev, struct sdhci_slot *slot,1213bus_size_t off, uint32_t *data, bus_size_t count)1214{1215struct bcm_sdhost_softc *sc = device_get_softc(dev);1216bus_size_t i;1217bus_size_t space;1218uint32_t edm;12191220mtx_lock(&sc->mtx);12211222dprintf("%s: off=%08lx count=%02lx\n", __func__, off, count);12231224for (i = 0; i < count;) {1225edm = RD4(sc, HC_DEBUG);1226space = HC_FIFO_SIZE - ((edm >> 4) & 0x1f);1227if (i + space > count)1228space = count - i;1229if (space > 0)1230bus_space_write_multi_4(sc->sc_bst, sc->sc_bsh,1231HC_DATAPORT, data + i, space);1232i += space;1233DELAY(1);1234}12351236/* wait until FIFO is really empty */1237while (((RD4(sc, HC_DEBUG) >> 4) & 0x1f) > 0)1238DELAY(1);12391240mtx_unlock(&sc->mtx);1241}12421243static device_method_t bcm_sdhost_methods[] = {1244/* Device interface */1245DEVMETHOD(device_probe, bcm_sdhost_probe),1246DEVMETHOD(device_attach, bcm_sdhost_attach),1247DEVMETHOD(device_detach, bcm_sdhost_detach),12481249/* Bus interface */1250DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar),1251DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar),12521253/* MMC bridge interface */1254DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios),1255DEVMETHOD(mmcbr_request, sdhci_generic_request),1256DEVMETHOD(mmcbr_get_ro, bcm_sdhost_get_ro),1257DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),1258DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),12591260/* SDHCI registers accessors */1261DEVMETHOD(sdhci_read_1, bcm_sdhost_read_1),1262DEVMETHOD(sdhci_read_2, bcm_sdhost_read_2),1263DEVMETHOD(sdhci_read_4, bcm_sdhost_read_4),1264DEVMETHOD(sdhci_read_multi_4, bcm_sdhost_read_multi_4),1265DEVMETHOD(sdhci_write_1, bcm_sdhost_write_1),1266DEVMETHOD(sdhci_write_2, bcm_sdhost_write_2),1267DEVMETHOD(sdhci_write_4, bcm_sdhost_write_4),1268DEVMETHOD(sdhci_write_multi_4, bcm_sdhost_write_multi_4),1269DEVMETHOD(sdhci_get_card_present,bcm_sdhost_get_card_present),12701271DEVMETHOD_END1272};12731274static driver_t bcm_sdhost_driver = {1275"sdhost_bcm",1276bcm_sdhost_methods,1277sizeof(struct bcm_sdhost_softc),1278};12791280DRIVER_MODULE(sdhost_bcm, simplebus, bcm_sdhost_driver, NULL, NULL);1281SDHCI_DEPEND(sdhost_bcm);1282#ifndef MMCCAM1283MMC_DECLARE_BRIDGE(sdhost_bcm);1284#endif128512861287