From fc9e83bb68ff851be89a4b24097b0948ef160c4f Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 16 Mar 2015 22:10:36 +0100 Subject: mmc: Import MMC/SDCard support from libusb --- rtemsbsd/include/bsp/nexus-devices.h | 7 + rtemsbsd/include/rtems/bsd/local/mmcbr_if.h | 79 ++ rtemsbsd/include/rtems/bsd/local/mmcbus_if.h | 55 ++ rtemsbsd/local/mmcbr_if.c | 64 ++ rtemsbsd/local/mmcbus_if.c | 47 ++ rtemsbsd/rtems/rtems-bsd-cam.c | 16 +- rtemsbsd/rtems/rtems-bsd-configintrhook.c | 58 ++ rtemsbsd/sys/dev/dw_mmc/dw_mmc.c | 1106 ++++++++++++++++++++++++++ rtemsbsd/sys/dev/dw_mmc/dw_mmcreg.h | 220 +++++ 9 files changed, 1646 insertions(+), 6 deletions(-) create mode 100644 rtemsbsd/include/rtems/bsd/local/mmcbr_if.h create mode 100644 rtemsbsd/include/rtems/bsd/local/mmcbus_if.h create mode 100644 rtemsbsd/local/mmcbr_if.c create mode 100644 rtemsbsd/local/mmcbus_if.c create mode 100644 rtemsbsd/rtems/rtems-bsd-configintrhook.c create mode 100644 rtemsbsd/sys/dev/dw_mmc/dw_mmc.c create mode 100644 rtemsbsd/sys/dev/dw_mmc/dw_mmcreg.h (limited to 'rtemsbsd') diff --git a/rtemsbsd/include/bsp/nexus-devices.h b/rtemsbsd/include/bsp/nexus-devices.h index 4d6e767e..f615e4e4 100644 --- a/rtemsbsd/include/bsp/nexus-devices.h +++ b/rtemsbsd/include/bsp/nexus-devices.h @@ -88,6 +88,13 @@ RTEMS_BSD_DEFINE_NEXUS_DEVICE(cgem, 0, RTEMS_ARRAY_SIZE(cgem0_res), SYSINIT_DRIVER_REFERENCE(e1000phy, miibus); +#elif defined(LIBBSP_ARM_ALTERA_CYCLONE_V_BSP_H) + +RTEMS_BSD_DEFINE_NEXUS_DEVICE(dw_mmc, 0, 0, NULL); + +SYSINIT_DRIVER_REFERENCE(mmc, dw_mmc); +SYSINIT_DRIVER_REFERENCE(mmcsd, mmc); + #elif defined(LIBBSP_POWERPC_QORIQ_BSP_H) #if !QORIQ_CHIP_IS_T_VARIANT(QORIQ_CHIP_VARIANT) diff --git a/rtemsbsd/include/rtems/bsd/local/mmcbr_if.h b/rtemsbsd/include/rtems/bsd/local/mmcbr_if.h new file mode 100644 index 00000000..f11e201d --- /dev/null +++ b/rtemsbsd/include/rtems/bsd/local/mmcbr_if.h @@ -0,0 +1,79 @@ +/* + * This file is produced automatically. + * Do not modify anything in here by hand. + * + * Created from source file + * dev/mmc/mmcbr_if.m + * with + * makeobjops.awk + * + * See the source file for legal information + */ + + +#ifndef _mmcbr_if_h_ +#define _mmcbr_if_h_ + +/** @brief Unique descriptor for the MMCBR_UPDATE_IOS() method */ +extern struct kobjop_desc mmcbr_update_ios_desc; +/** @brief A function implementing the MMCBR_UPDATE_IOS() method */ +typedef int mmcbr_update_ios_t(device_t brdev, device_t reqdev); + +static __inline int MMCBR_UPDATE_IOS(device_t brdev, device_t reqdev) +{ + kobjop_t _m; + KOBJOPLOOKUP(((kobj_t)brdev)->ops,mmcbr_update_ios); + return ((mmcbr_update_ios_t *) _m)(brdev, reqdev); +} + +/** @brief Unique descriptor for the MMCBR_REQUEST() method */ +extern struct kobjop_desc mmcbr_request_desc; +/** @brief A function implementing the MMCBR_REQUEST() method */ +typedef int mmcbr_request_t(device_t brdev, device_t reqdev, + struct mmc_request *req); + +static __inline int MMCBR_REQUEST(device_t brdev, device_t reqdev, + struct mmc_request *req) +{ + kobjop_t _m; + KOBJOPLOOKUP(((kobj_t)brdev)->ops,mmcbr_request); + return ((mmcbr_request_t *) _m)(brdev, reqdev, req); +} + +/** @brief Unique descriptor for the MMCBR_GET_RO() method */ +extern struct kobjop_desc mmcbr_get_ro_desc; +/** @brief A function implementing the MMCBR_GET_RO() method */ +typedef int mmcbr_get_ro_t(device_t brdev, device_t reqdev); + +static __inline int MMCBR_GET_RO(device_t brdev, device_t reqdev) +{ + kobjop_t _m; + KOBJOPLOOKUP(((kobj_t)brdev)->ops,mmcbr_get_ro); + return ((mmcbr_get_ro_t *) _m)(brdev, reqdev); +} + +/** @brief Unique descriptor for the MMCBR_ACQUIRE_HOST() method */ +extern struct kobjop_desc mmcbr_acquire_host_desc; +/** @brief A function implementing the MMCBR_ACQUIRE_HOST() method */ +typedef int mmcbr_acquire_host_t(device_t brdev, device_t reqdev); + +static __inline int MMCBR_ACQUIRE_HOST(device_t brdev, device_t reqdev) +{ + kobjop_t _m; + KOBJOPLOOKUP(((kobj_t)brdev)->ops,mmcbr_acquire_host); + return ((mmcbr_acquire_host_t *) _m)(brdev, reqdev); +} + +/** @brief Unique descriptor for the MMCBR_RELEASE_HOST() method */ +extern struct kobjop_desc mmcbr_release_host_desc; +/** @brief A function implementing the MMCBR_RELEASE_HOST() method */ +typedef int mmcbr_release_host_t(device_t brdev, device_t reqdev); + +static __inline int MMCBR_RELEASE_HOST(device_t brdev, device_t reqdev) +{ + kobjop_t _m; + KOBJOPLOOKUP(((kobj_t)brdev)->ops,mmcbr_release_host); + return ((mmcbr_release_host_t *) _m)(brdev, reqdev); +} + +#endif /* _mmcbr_if_h_ */ diff --git a/rtemsbsd/include/rtems/bsd/local/mmcbus_if.h b/rtemsbsd/include/rtems/bsd/local/mmcbus_if.h new file mode 100644 index 00000000..5e0e2da0 --- /dev/null +++ b/rtemsbsd/include/rtems/bsd/local/mmcbus_if.h @@ -0,0 +1,55 @@ +/* + * This file is produced automatically. + * Do not modify anything in here by hand. + * + * Created from source file + * dev/mmc/mmcbus_if.m + * with + * makeobjops.awk + * + * See the source file for legal information + */ + + +#ifndef _mmcbus_if_h_ +#define _mmcbus_if_h_ + +/** @brief Unique descriptor for the MMCBUS_WAIT_FOR_REQUEST() method */ +extern struct kobjop_desc mmcbus_wait_for_request_desc; +/** @brief A function implementing the MMCBUS_WAIT_FOR_REQUEST() method */ +typedef int mmcbus_wait_for_request_t(device_t brdev, device_t reqdev, + struct mmc_request *req); + +static __inline int MMCBUS_WAIT_FOR_REQUEST(device_t brdev, device_t reqdev, + struct mmc_request *req) +{ + kobjop_t _m; + KOBJOPLOOKUP(((kobj_t)brdev)->ops,mmcbus_wait_for_request); + return ((mmcbus_wait_for_request_t *) _m)(brdev, reqdev, req); +} + +/** @brief Unique descriptor for the MMCBUS_ACQUIRE_BUS() method */ +extern struct kobjop_desc mmcbus_acquire_bus_desc; +/** @brief A function implementing the MMCBUS_ACQUIRE_BUS() method */ +typedef int mmcbus_acquire_bus_t(device_t brdev, device_t reqdev); + +static __inline int MMCBUS_ACQUIRE_BUS(device_t brdev, device_t reqdev) +{ + kobjop_t _m; + KOBJOPLOOKUP(((kobj_t)brdev)->ops,mmcbus_acquire_bus); + return ((mmcbus_acquire_bus_t *) _m)(brdev, reqdev); +} + +/** @brief Unique descriptor for the MMCBUS_RELEASE_BUS() method */ +extern struct kobjop_desc mmcbus_release_bus_desc; +/** @brief A function implementing the MMCBUS_RELEASE_BUS() method */ +typedef int mmcbus_release_bus_t(device_t brdev, device_t reqdev); + +static __inline int MMCBUS_RELEASE_BUS(device_t brdev, device_t reqdev) +{ + kobjop_t _m; + KOBJOPLOOKUP(((kobj_t)brdev)->ops,mmcbus_release_bus); + return ((mmcbus_release_bus_t *) _m)(brdev, reqdev); +} + +#endif /* _mmcbus_if_h_ */ diff --git a/rtemsbsd/local/mmcbr_if.c b/rtemsbsd/local/mmcbr_if.c new file mode 100644 index 00000000..1ce2d263 --- /dev/null +++ b/rtemsbsd/local/mmcbr_if.c @@ -0,0 +1,64 @@ +#include + +/* + * This file is produced automatically. + * Do not modify anything in here by hand. + * + * Created from source file + * freebsd-org/sys/dev/mmc/mmcbr_if.m + * with + * makeobjops.awk + * + * See the source file for legal information + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct kobj_method mmcbr_update_ios_method_default = { + &mmcbr_update_ios_desc, (kobjop_t) kobj_error_method +}; + +struct kobjop_desc mmcbr_update_ios_desc = { + 0, &mmcbr_update_ios_method_default +}; + +struct kobj_method mmcbr_request_method_default = { + &mmcbr_request_desc, (kobjop_t) kobj_error_method +}; + +struct kobjop_desc mmcbr_request_desc = { + 0, &mmcbr_request_method_default +}; + +struct kobj_method mmcbr_get_ro_method_default = { + &mmcbr_get_ro_desc, (kobjop_t) kobj_error_method +}; + +struct kobjop_desc mmcbr_get_ro_desc = { + 0, &mmcbr_get_ro_method_default +}; + +struct kobj_method mmcbr_acquire_host_method_default = { + &mmcbr_acquire_host_desc, (kobjop_t) kobj_error_method +}; + +struct kobjop_desc mmcbr_acquire_host_desc = { + 0, &mmcbr_acquire_host_method_default +}; + +struct kobj_method mmcbr_release_host_method_default = { + &mmcbr_release_host_desc, (kobjop_t) kobj_error_method +}; + +struct kobjop_desc mmcbr_release_host_desc = { + 0, &mmcbr_release_host_method_default +}; + diff --git a/rtemsbsd/local/mmcbus_if.c b/rtemsbsd/local/mmcbus_if.c new file mode 100644 index 00000000..a757a561 --- /dev/null +++ b/rtemsbsd/local/mmcbus_if.c @@ -0,0 +1,47 @@ +#include + +/* + * This file is produced automatically. + * Do not modify anything in here by hand. + * + * Created from source file + * freebsd-org/sys/dev/mmc/mmcbus_if.m + * with + * makeobjops.awk + * + * See the source file for legal information + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct kobj_method mmcbus_wait_for_request_method_default = { + &mmcbus_wait_for_request_desc, (kobjop_t) kobj_error_method +}; + +struct kobjop_desc mmcbus_wait_for_request_desc = { + 0, &mmcbus_wait_for_request_method_default +}; + +struct kobj_method mmcbus_acquire_bus_method_default = { + &mmcbus_acquire_bus_desc, (kobjop_t) kobj_error_method +}; + +struct kobjop_desc mmcbus_acquire_bus_desc = { + 0, &mmcbus_acquire_bus_method_default +}; + +struct kobj_method mmcbus_release_bus_method_default = { + &mmcbus_release_bus_desc, (kobjop_t) kobj_error_method +}; + +struct kobjop_desc mmcbus_release_bus_desc = { + 0, &mmcbus_release_bus_method_default +}; + diff --git a/rtemsbsd/rtems/rtems-bsd-cam.c b/rtemsbsd/rtems/rtems-bsd-cam.c index ab15f426..0f518586 100644 --- a/rtemsbsd/rtems/rtems-bsd-cam.c +++ b/rtemsbsd/rtems/rtems-bsd-cam.c @@ -151,7 +151,7 @@ rtems_bsd_scsi_inquiry(union ccb *ccb, struct scsi_inquiry_data *inq_data) rtems_bsd_ccb_callback, BSD_SCSI_TAG, (u_int8_t *) inq_data, - sizeof(*inq_data) - 1, + SHORT_INQUIRY_LENGTH, FALSE, 0, SSD_MIN_SIZE, @@ -339,11 +339,11 @@ rtems_bsd_sim_attach_worker(rtems_media_state state, const char *src, char **des } sc = rtems_bsd_scsi_inquiry(&sim->ccb, &inq_data); - if (sc != RTEMS_SUCCESSFUL) { + if (sc == RTEMS_SUCCESSFUL) { + scsi_print_inquiry(&inq_data); + } else { BSD_PRINTF("OOPS: inquiry failed\n"); - goto error; } - scsi_print_inquiry(&inq_data); for (retries = 0; retries <= 3; ++retries) { sc = rtems_bsd_scsi_test_unit_ready(&sim->ccb); @@ -353,10 +353,14 @@ rtems_bsd_sim_attach_worker(rtems_media_state state, const char *src, char **des } if (sc != RTEMS_SUCCESSFUL) { BSD_PRINTF("OOPS: test unit ready failed\n"); - goto error; } - sc = rtems_bsd_scsi_read_capacity(&sim->ccb, &block_count, &block_size); + for (retries = 0; retries <= 3; ++retries) { + sc = rtems_bsd_scsi_read_capacity(&sim->ccb, &block_count, &block_size); + if (sc == RTEMS_SUCCESSFUL) { + break; + } + } if (sc != RTEMS_SUCCESSFUL) { BSD_PRINTF("OOPS: read capacity failed\n"); goto error; diff --git a/rtemsbsd/rtems/rtems-bsd-configintrhook.c b/rtemsbsd/rtems/rtems-bsd-configintrhook.c new file mode 100644 index 00000000..baf737e1 --- /dev/null +++ b/rtemsbsd/rtems/rtems-bsd-configintrhook.c @@ -0,0 +1,58 @@ +/** + * @file + * + * @ingroup rtems_bsd_rtems + * + * @brief TODO. + */ + +/* + * Copyright (c) 2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * 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 COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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 + +#include +#include +#include + +int +config_intrhook_establish(struct intr_config_hook *hook) +{ + (*hook->ich_func)(hook->ich_arg); + + return (0); +} + +void +config_intrhook_disestablish(struct intr_config_hook *hook) +{ + /* Do nothing */ +} diff --git a/rtemsbsd/sys/dev/dw_mmc/dw_mmc.c b/rtemsbsd/sys/dev/dw_mmc/dw_mmc.c new file mode 100644 index 00000000..681bf95a --- /dev/null +++ b/rtemsbsd/sys/dev/dw_mmc/dw_mmc.c @@ -0,0 +1,1106 @@ +#include + +/*- + * Copyright (c) 2006 Bernd Walter. All rights reserved. + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * Copyright (c) 2010 Greg Ansley. All rights reserved. + * Copyright (c) 2014 embedded brains GmbH. 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 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include + +#include + +#ifdef LIBBSP_ARM_ALTERA_CYCLONE_V_BSP_H + +#define DW_MMC_ALTERA_CYCLONE_V + +#include +#include +#include +#include +#include + +#endif /* DW_MMC_ALTERA_CYCLONE_V */ + +struct dw_mmc_softc { + device_t dev; + struct mtx sc_mtx; + struct mtx bus_mtx; + bus_space_handle_t bushandle; + uint32_t biu_clock; + uint32_t ciu_clock; + uint32_t card_clock; + struct mmc_host host; + uint32_t cmdr_flags; + volatile struct dw_mmc_des *des; + rtems_id task_id; +}; + +#define DW_MMC_MAX_DES_COUNT 32 + +#define DW_MMC_MAX_DMA_TRANSFER_BYTES \ + (DW_MMC_MAX_DES_COUNT * 2 * DW_MMC_DES1_MAX_BS) + +static inline uint32_t +RD4(struct dw_mmc_softc *sc, bus_size_t off) +{ + return (bus_space_read_4(0, sc->bushandle, off)); +} + +static inline void +WR4(struct dw_mmc_softc *sc, bus_size_t off, uint32_t val) +{ + bus_space_write_4(0, sc->bushandle, off, val); +} + +/* bus entry points */ +static int dw_mmc_probe(device_t dev); +static int dw_mmc_attach(device_t dev); +static int dw_mmc_detach(device_t dev); +static void dw_mmc_intr(void *); + +static void +DW_MMC_LOCK(struct dw_mmc_softc *sc) +{ + mtx_lock(&sc->sc_mtx); + sc->task_id = rtems_task_self(); +} + +#define DW_MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define DW_MMC_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ + "dw_mmc", MTX_DEF) + +#define DW_MMC_BUS_LOCK(_sc) mtx_lock(&(_sc)->bus_mtx) +#define DW_MMC_BUS_UNLOCK(_sc) mtx_unlock(&(_sc)->bus_mtx) +#define DW_MMC_BUS_LOCK_INIT(_sc) \ + mtx_init(&_sc->bus_mtx, device_get_nameunit(_sc->dev), \ + "dw_mmc", MTX_DEF) + +static int +dw_mmc_poll_reset_completion(struct dw_mmc_softc *sc, uint32_t ctrl_resets) +{ + rtems_interval timeout = rtems_clock_tick_later_usec(250000); + + do { + if ((RD4(sc, DW_MMC_CTRL) & ctrl_resets) == 0) { + return 0; + } + } while (rtems_clock_tick_before(timeout)); + + return EBUSY; +} + +static uint32_t +dw_mmc_poll_intsts(struct dw_mmc_softc *sc, uint32_t mask) +{ + uint32_t ret_intsts = 0; + + while (1) { + uint32_t intsts = RD4(sc, DW_MMC_RINTSTS); + + if ((intsts & DW_MMC_INT_ERROR) != 0) { + WR4(sc, DW_MMC_RINTSTS, intsts); + ret_intsts = intsts; + break; + } + + if ((intsts & mask) != 0) { + WR4(sc, DW_MMC_RINTSTS, intsts & mask); + break; + } + } + + return ret_intsts; +} + +static void +dw_mmc_wait_for_interrupt(struct dw_mmc_softc *sc, uint32_t intmask) +{ + rtems_status_code rs; + + WR4(sc, DW_MMC_INTMASK, intmask); + + rs = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT); + BSD_ASSERT(rs == RTEMS_SUCCESSFUL); +} + +static int +dw_mmc_init(struct dw_mmc_softc *sc) +{ + uint32_t ctrl; + uint32_t fifoth; + int err; + + err = dw_mmc_poll_reset_completion(sc, DW_MMC_CTRL_RESET); + if (err != 0) { + return err; + } + + sc->card_clock = UINT32_MAX; + + /* Clear interrupt status */ + WR4(sc, DW_MMC_RINTSTS, 0xffffffff); + + /* Disable all interrupts */ + WR4(sc, DW_MMC_INTMASK, 0x0); + + /* Enable interrupts in general */ + ctrl = RD4(sc, DW_MMC_CTRL); + ctrl |= DW_MMC_CTRL_INT_ENABLE; + WR4(sc, DW_MMC_CTRL, ctrl); + + /* Set data and response timeout to maximum values */ + WR4(sc, DW_MMC_TMOUT, 0xffffffff); + + /* Set debounce value to 25ms */ + WR4(sc, DW_MMC_DEBNCE, (sc->biu_clock / 1000) * 25); + + /* Set FIFO watermarks */ + fifoth = RD4(sc, DW_MMC_FIFOTH); + fifoth &= ~(DW_MMC_FIFOTH_RX_WMARK_MSK | DW_MMC_FIFOTH_TX_WMARK_MSK); + fifoth |= DW_MMC_FIFOTH_RX_WMARK(511) | DW_MMC_FIFOTH_TX_WMARK(512); + WR4(sc, DW_MMC_FIFOTH, fifoth); + + /* Set DMA descriptor */ + WR4(sc, DW_MMC_DBADDR, (uint32_t) sc->des); + + return 0; +} + +static void +dw_mmc_fini(struct dw_mmc_softc *sc) +{ + WR4(sc, DW_MMC_CTRL, DW_MMC_CTRL_FIFO_RESET | DW_MMC_CTRL_RESET); +} + +static int +dw_mmc_probe(device_t dev) +{ + + device_set_desc(dev, "DesignWare Mobile Storage Host"); + return (0); +} + +static int +dw_mmc_platform_init(struct dw_mmc_softc *sc) +{ +#ifdef DW_MMC_ALTERA_CYCLONE_V + size_t des_size = DW_MMC_MAX_DES_COUNT * sizeof(*sc->des); + ALT_STATUS_CODE as; + + /* Module base address */ + sc->bushandle = (bus_space_handle_t) ALT_SDMMC_ADDR; + + /* BIU clock */ + as = alt_clk_freq_get(ALT_CLK_L4_MP, &sc->biu_clock); + BSD_ASSERT(as == ALT_E_SUCCESS); + + /* CIU clock */ + as = alt_clk_clock_enable(ALT_CLK_SDMMC); + BSD_ASSERT(as == ALT_E_SUCCESS); + as = alt_clk_freq_get(ALT_CLK_SDMMC, &sc->ciu_clock); + BSD_ASSERT(as == ALT_E_SUCCESS); + sc->ciu_clock /= 4; + + sc->des = rtems_cache_coherent_allocate(des_size, 0, 0); + if (sc->des == NULL) { + return (ENOMEM); + } + memset(__DEVOLATILE(void *, sc->des), 0, des_size); +#endif + + return (0); +} + +static void +dw_mmc_platform_install_intr(struct dw_mmc_softc *sc) +{ + rtems_vector_number irq = +#ifdef DW_MMC_ALTERA_CYCLONE_V + ALT_INT_INTERRUPT_SDMMC_IRQ; +#else + UINT32_MAX; +#endif + rtems_status_code rs; + + /* + * Activate the interrupt + */ + rs = rtems_interrupt_handler_install(irq, "DW MMC", + RTEMS_INTERRUPT_SHARED, dw_mmc_intr, sc); + BSD_ASSERT(rs == RTEMS_SUCCESSFUL); +} + +static bool +dw_mmc_platform_set_clock(struct dw_mmc_softc *sc, uint32_t card_clock) +{ + bool use_hold_reg; + +#ifdef DW_MMC_ALTERA_CYCLONE_V + uint32_t drvsel; + uint32_t smplsel; + uint32_t ctl; + ALT_STATUS_CODE as; + + /* FIXME: Values taken from U-Boot, not clear how they are determined */ + if (card_clock > 25000000) { + drvsel = 3; + smplsel = 7; + } else { + drvsel = 3; + smplsel = 0; + } + + use_hold_reg = drvsel != 0; + + as = alt_clk_clock_disable(ALT_CLK_SDMMC); + BSD_ASSERT(as == ALT_E_SUCCESS); + + ctl = alt_read_word(ALT_SYSMGR_SDMMC_CTL_ADDR); + ctl &= ALT_SYSMGR_SDMMC_CTL_DRVSEL_CLR_MSK + & ALT_SYSMGR_SDMMC_CTL_SMPLSEL_CLR_MSK; + ctl |= ALT_SYSMGR_SDMMC_CTL_DRVSEL_SET(drvsel) + | ALT_SYSMGR_SDMMC_CTL_SMPLSEL_SET(smplsel); + alt_write_word(ALT_SYSMGR_SDMMC_CTL_ADDR, ctl); + + as = alt_clk_clock_enable(ALT_CLK_SDMMC); + BSD_ASSERT(as == ALT_E_SUCCESS); +#else + use_hold_reg = false; +#endif + + return use_hold_reg; +} + +static void +dw_mmc_platform_fini(struct dw_mmc_softc *sc) +{ +#ifdef DW_MMC_ALTERA_CYCLONE_V + rtems_cache_coherent_free(__DEVOLATILE(void *, sc->des)); +#endif +} + +static int +dw_mmc_attach(device_t dev) +{ + struct dw_mmc_softc *sc = device_get_softc(dev); + int err; + + sc->dev = dev; + + err = dw_mmc_platform_init(sc); + if (err != 0) { + return (err); + } + + dw_mmc_fini(sc); + err = dw_mmc_init(sc); + if (err != 0) { + dw_mmc_platform_fini(sc); + + return (err); + } + + DW_MMC_LOCK_INIT(sc); + DW_MMC_BUS_LOCK_INIT(sc); + + dw_mmc_platform_install_intr(sc); + + sc->host.f_min = 400000; + sc->host.f_max = (int) sc->ciu_clock; + if (sc->host.f_max > 50000000) + sc->host.f_max = 50000000; /* Limit to 50MHz */ + + sc->host.host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340; + + /* FIXME: MMC_CAP_8_BIT_DATA for eSDIO? */ + sc->host.caps = MMC_CAP_4_BIT_DATA | MMC_CAP_HSPEED; + + device_add_child(dev, "mmc", 0); + device_set_ivars(dev, &sc->host); + err = bus_generic_attach(dev); + + return (err); +} + +static int +dw_mmc_detach(device_t dev) +{ + struct dw_mmc_softc *sc = device_get_softc(dev); + + dw_mmc_fini(sc); + + /* FIXME: Implement */ + BSD_ASSERT(0); + + return (EBUSY); +} + +static int +dw_mmc_cmd_wait(struct dw_mmc_softc *sc) +{ + rtems_interval timeout = rtems_clock_tick_later_usec(250000); + + do { + if ((RD4(sc, DW_MMC_CMD) & DW_MMC_CMD_START) == 0) { + return 0; + } + } while (rtems_clock_tick_before(timeout)); + + return EBUSY; +} + +static void +dw_mmc_cmd_start(struct dw_mmc_softc *sc, uint32_t cmd, uint32_t cmdarg) +{ + WR4(sc, DW_MMC_CMDARG, cmdarg); + cmd |= DW_MMC_CMD_START; + WR4(sc, DW_MMC_CMD, cmd); +} + +static int +dw_mmc_cmd_update_clock(struct dw_mmc_softc *sc) +{ + dw_mmc_cmd_start(sc, + DW_MMC_CMD_UPDATE_CLK | DW_MMC_CMD_PRV_DATA_WAIT, 0); + + return dw_mmc_cmd_wait(sc); +} + +static int +dw_mmc_set_clock(struct dw_mmc_softc *sc, uint32_t card_clock) +{ + uint32_t clkdiv; + int err; + + if (sc->card_clock == card_clock) { + return 0; + } + + sc->card_clock = card_clock; + + /* Disable card clock */ + WR4(sc, DW_MMC_CLKENA, 0); + + err = dw_mmc_cmd_update_clock(sc); + if (err != 0) { + return err; + } + + if (card_clock == 0) { + return 0; + } + + if (dw_mmc_platform_set_clock(sc, card_clock)) { + sc->cmdr_flags |= DW_MMC_CMD_USE_HOLD_REG; + } else { + sc->cmdr_flags &= ~DW_MMC_CMD_USE_HOLD_REG; + } + + if (card_clock == sc->ciu_clock) { + clkdiv = 0; + } else { + uint32_t s = 2 * card_clock; + + clkdiv = (sc->ciu_clock + s - 1) / s; + } + + WR4(sc, DW_MMC_CLKDIV, clkdiv); + WR4(sc, DW_MMC_CLKSRC, 0); + + err = dw_mmc_cmd_update_clock(sc); + if (err != 0) { + return err; + } + + /* Enable card clock */ + WR4(sc, DW_MMC_CLKENA, DW_MMC_CLKEN_ENABLE); + + return dw_mmc_cmd_update_clock(sc); +} + +static int +dw_mmc_update_ios(device_t brdev, device_t reqdev) +{ + struct dw_mmc_softc *sc = device_get_softc(brdev); + struct mmc_host *host; + struct mmc_ios *ios; + uint32_t ctype; + int err; + + DW_MMC_LOCK(sc); + + host = &sc->host; + ios = &host->ios; + + err = dw_mmc_set_clock(sc, (uint32_t) ios->clock); + if (err != 0) { + return (err); + } + + if (ios->power_mode == power_off) { + WR4(sc, DW_MMC_PWREN, 0); + } else { + sc->cmdr_flags |= DW_MMC_CMD_SEND_INIT; + WR4(sc, DW_MMC_PWREN, DW_MMC_PWREN_ENABLE); + } + + switch (ios->bus_width) { + default: + BSD_ASSERT(ios->bus_width == bus_width_1); + ctype = DW_MMC_CTYPE_1BIT; + break; + case bus_width_4: + ctype = DW_MMC_CTYPE_4BIT; + break; + case bus_width_8: + ctype = DW_MMC_CTYPE_8BIT; + break; + } + + WR4(sc, DW_MMC_CTYPE, ctype); + + DW_MMC_UNLOCK(sc); + + return (0); +} + +static int +dw_mmc_fifo_and_dma_reset(struct dw_mmc_softc *sc) +{ + uint32_t ctrl_resets = DW_MMC_CTRL_FIFO_RESET | DW_MMC_CTRL_DMA_RESET; + uint32_t ctrl = RD4(sc, DW_MMC_CTRL); + + ctrl |= ctrl_resets; + + WR4(sc, DW_MMC_CTRL, ctrl); + + return dw_mmc_poll_reset_completion(sc, ctrl_resets); +} + +static int +dw_mmc_cmd_read_response(struct dw_mmc_softc *sc, struct mmc_command *cmd, + uint32_t intsts) +{ + if ((intsts & DW_MMC_INT_RTO) != 0) { + return MMC_ERR_TIMEOUT; + } else if ((intsts & DW_MMC_INT_RCRC) != 0 + && (cmd->flags & MMC_RSP_CRC) != 0) { + return MMC_ERR_BADCRC; + } else if ((intsts & DW_MMC_INT_RE) != 0) { + return MMC_ERR_FAILED; + } + + if ((cmd->flags & MMC_RSP_PRESENT) != 0) { + uint32_t *resp = &cmd->resp[0]; + + if ((cmd->flags & MMC_RSP_136) != 0) { + resp[3] = RD4(sc, DW_MMC_RESP0); + resp[2] = RD4(sc, DW_MMC_RESP1); + resp[1] = RD4(sc, DW_MMC_RESP2); + resp[0] = RD4(sc, DW_MMC_RESP3); + } else { + resp[0] = RD4(sc, DW_MMC_RESP0); + } + } + + return MMC_ERR_NONE; +} + +static uint32_t +dw_mmc_cmd_data_read(struct dw_mmc_softc *sc, struct mmc_data *data, + uint32_t *data32, size_t count_bytes) +{ + uint32_t intsts = 0; + + while (count_bytes > 0) { + uint32_t status; + size_t available_words; + size_t dangling_bytes = 0; + size_t i; + + intsts = dw_mmc_poll_intsts(sc, DW_MMC_INT_RXDR + | DW_MMC_INT_DTO | DW_MMC_INT_HTO); + + if (intsts != 0) { + return intsts; + } + + status = RD4(sc, DW_MMC_STATUS); + available_words = DW_MMC_STATUS_GET_FIFO_CNT(status); + + if (available_words * DW_MMC_FIFO_WIDTH > count_bytes) { + dangling_bytes = count_bytes % DW_MMC_FIFO_WIDTH; + --available_words; + } + + for (i = 0; i < available_words; i++) { + data32[i] = RD4(sc, DW_MMC_DATA); + } + + data32 += available_words; + count_bytes -= available_words * DW_MMC_FIFO_WIDTH; + + if (dangling_bytes != 0) { + uint32_t tmp = RD4(sc, DW_MMC_DATA); + + memcpy(data32, &tmp, dangling_bytes); + BSD_ASSERT(count_bytes == dangling_bytes); + count_bytes = 0; + } + } + + if ((data->flags & MMC_DATA_MULTI) != 0) { + intsts = dw_mmc_poll_intsts(sc, DW_MMC_INT_ACD); + } + + return intsts; +} + +static uint32_t +dw_mmc_cmd_data_write(struct dw_mmc_softc *sc, struct mmc_data *data, + uint32_t *data32, size_t count_bytes) +{ + uint32_t intsts; + + while (count_bytes > 0) { + uint32_t status; + size_t pending_words = count_bytes / DW_MMC_FIFO_WIDTH; + size_t free_words; + size_t dangling_bytes; + size_t words_to_write; + size_t i; + + intsts = dw_mmc_poll_intsts(sc, DW_MMC_INT_TXDR + | DW_MMC_INT_HTO); + + if (intsts != 0) { + return intsts; + } + + status = RD4(sc, DW_MMC_STATUS); + free_words = DW_MMC_FIFO_DEPTH - DW_MMC_STATUS_GET_FIFO_CNT(status); + + if (pending_words >= free_words) { + words_to_write = free_words; + dangling_bytes = 0; + } else { + words_to_write = pending_words; + dangling_bytes = count_bytes % DW_MMC_FIFO_WIDTH; + } + + for (i = 0; i < words_to_write; i++) { + WR4(sc, DW_MMC_DATA, data32[i]); + } + + data32 += words_to_write; + count_bytes -= words_to_write * DW_MMC_FIFO_WIDTH; + + if (dangling_bytes != 0) { + uint32_t tmp = 0; + + memcpy(&tmp, &data32[0], dangling_bytes); + WR4(sc, DW_MMC_DATA, tmp); + BSD_ASSERT(count_bytes == dangling_bytes); + count_bytes = 0; + } + } + + intsts = dw_mmc_poll_intsts(sc, DW_MMC_INT_DTO); + + if ((data->flags & MMC_DATA_MULTI) != 0 && intsts == 0) { + dw_mmc_poll_intsts(sc, DW_MMC_INT_ACD); + } + + return intsts; +} + +static uint32_t +dw_mmc_cmd_data_transfer(struct dw_mmc_softc *sc, struct mmc_data *data, + size_t done_bytes, bool use_dma) +{ + uint32_t *data32 = (uint32_t *) ((char *) data->data + done_bytes); + bool do_write = (data->flags & MMC_DATA_WRITE) != 0; + uint32_t intsts; + + if (use_dma) { + dw_mmc_wait_for_interrupt(sc, DW_MMC_INT_DTO); + intsts = dw_mmc_poll_intsts(sc, DW_MMC_INT_DTO); + + if ((data->flags & MMC_DATA_MULTI) != 0 && intsts == 0) { + dw_mmc_poll_intsts(sc, DW_MMC_INT_ACD); + } + + if (!do_write) { + rtems_cache_invalidate_multiple_data_lines(data->data, + data->len); + } + } else { + size_t count_bytes = data->len - done_bytes; + + if (do_write) { + intsts = dw_mmc_cmd_data_write(sc, data, data32, count_bytes); + } else { + intsts = dw_mmc_cmd_data_read(sc, data, data32, count_bytes); + } + } + + return intsts; +} + +static int +dw_mmc_cmd_data_finish(struct dw_mmc_softc *sc, uint32_t intsts) +{ + int mmc_err = MMC_ERR_NONE; + + if ((intsts & DW_MMC_INT_ERROR) != 0) { + if ((intsts & DW_MMC_INT_DCRC) != 0) { + mmc_err = MMC_ERR_BADCRC; + } else if ((intsts & DW_MMC_INT_EBE) != 0) { + mmc_err = MMC_ERR_FAILED; + } else if ((intsts & DW_MMC_INT_DRTO) != 0) { + mmc_err = MMC_ERR_TIMEOUT; + } else { + mmc_err = MMC_ERR_FAILED; + } + } + + return mmc_err; +} + +static int +dw_mmc_cmd_done(struct dw_mmc_softc *sc, struct mmc_command *cmd, + struct mmc_data *data, size_t done_bytes, bool use_dma) +{ + uint32_t intsts; + int mmc_err; + + dw_mmc_wait_for_interrupt(sc, DW_MMC_INT_CMD_DONE); + + intsts = RD4(sc, DW_MMC_RINTSTS); + WR4(sc, DW_MMC_RINTSTS, + intsts & (DW_MMC_INT_ERROR | DW_MMC_INT_CMD_DONE)); + + mmc_err = dw_mmc_cmd_read_response(sc, cmd, intsts); + if (mmc_err != 0) { + return mmc_err; + } + + if (data != NULL) { + intsts = dw_mmc_cmd_data_transfer(sc, data, done_bytes, use_dma); + mmc_err = dw_mmc_cmd_data_finish(sc, intsts); + } + + return mmc_err; +} + +static size_t +dw_mmc_fill_fifo(struct dw_mmc_softc *sc, struct mmc_data *data) +{ + uint32_t *data32 = data->data; + size_t count_bytes = data->len; + size_t count_words = 0; + size_t dangling_bytes; + size_t i; + + if (count_bytes >= DW_MMC_FIFO_DEPTH * DW_MMC_FIFO_WIDTH) { + count_words = DW_MMC_FIFO_DEPTH; + dangling_bytes = 0; + } else { + count_words = count_bytes / DW_MMC_FIFO_WIDTH; + dangling_bytes = count_bytes % DW_MMC_FIFO_WIDTH; + } + + for (i = 0; i < count_words; ++i) { + WR4(sc, DW_MMC_DATA, data32[i]); + } + + if (dangling_bytes) { + uint32_t tmp = 0; + + memcpy(&tmp, &data32[i], dangling_bytes); + WR4(sc, DW_MMC_DATA, tmp); + } + + return count_words * DW_MMC_FIFO_WIDTH + dangling_bytes; +} + +static bool dw_mmc_dma_can_use(const struct mmc_data *data) +{ + uintptr_t cache_line = 32; + + return data->len >= cache_line + && ((data->len | (uintptr_t) data->data) & (cache_line - 1)) == 0; +} + +static void +dw_mmc_dma_setup(struct dw_mmc_softc *sc, struct mmc_data *data) +{ + volatile struct dw_mmc_des *des = sc->des; + uint32_t buf = (uint32_t) data->data; + size_t count_bytes = data->len; + uint32_t fs = DW_MMC_DES0_FS; + size_t s = 2 * DW_MMC_DES1_MAX_BS; + size_t n = (count_bytes + s - 1) / s; + size_t m = count_bytes % s; + size_t i; + + for (i = 0; i < n - 1; ++i) { + des[i].des1 = DW_MMC_DES1_BS1(DW_MMC_DES1_MAX_BS) + | DW_MMC_DES1_BS2(DW_MMC_DES1_MAX_BS); + des[i].des2 = buf; + buf += DW_MMC_DES1_MAX_BS; + des[i].des3 = buf; + buf += DW_MMC_DES1_MAX_BS; + des[i].des0 = DW_MMC_DES0_OWN | fs; + fs = 0; + } + + if (m > DW_MMC_DES1_MAX_BS) { + des[i].des1 = DW_MMC_DES1_BS1(DW_MMC_DES1_MAX_BS) + | DW_MMC_DES1_BS2(m - DW_MMC_DES1_MAX_BS); + des[i].des2 = buf; + buf += DW_MMC_DES1_MAX_BS; + des[i].des3 = buf; + } else { + des[i].des1 = DW_MMC_DES1_BS1(m); + des[i].des2 = buf; + des[i].des3 = 0; + } + + des[i].des0 = DW_MMC_DES0_OWN | DW_MMC_DES0_ER | fs | DW_MMC_DES0_LD; + +#ifdef __arm__ + _ARM_Data_synchronization_barrier(); +#else + /* TODO */ +#endif +} + + +static void +dw_mmc_cmd_do(struct dw_mmc_softc *sc, struct mmc_request *req, + struct mmc_command *cmd) +{ + size_t done_bytes = 0; + bool use_dma = false; + struct mmc_data *data; + uint32_t cmdr; + + data = cmd->data; + cmdr = cmd->opcode; + + if (cmd->opcode == MMC_STOP_TRANSMISSION) { + cmdr |= DW_MMC_CMD_SEND_STOP; + } else { + cmdr |= DW_MMC_CMD_PRV_DATA_WAIT; + } + + cmdr |= sc->cmdr_flags; + sc->cmdr_flags &= ~DW_MMC_CMD_SEND_INIT; + + if (MMC_RSP(cmd->flags) != MMC_RSP_NONE) { + cmdr |= DW_MMC_CMD_RESP_EXP; + + if ((cmd->flags & MMC_RSP_136) != 0) { + cmdr |= DW_MMC_CMD_RESP_LONG; + } + } + + if ((cmd->flags & MMC_RSP_CRC) != 0) { + cmdr |= DW_MMC_CMD_RESP_CRC; + } + + if (data != NULL) { + size_t count_bytes = data->len; + uint32_t ctrl; + int mmc_err; + + cmdr |= DW_MMC_CMD_DATA_EXP; + + if ((data->flags & MMC_DATA_MULTI) != 0) { + cmdr |= DW_MMC_CMD_SEND_STOP; + } + + mmc_err = dw_mmc_fifo_and_dma_reset(sc); + if (mmc_err != 0) { + cmd->error = mmc_err; + return; + } + + use_dma = dw_mmc_dma_can_use(data); + + ctrl = RD4(sc, DW_MMC_CTRL); + + if (use_dma) { + ctrl |= DW_MMC_CTRL_DMA_ENABLE; + } else { + ctrl &= ~DW_MMC_CTRL_DMA_ENABLE; + } + + WR4(sc, DW_MMC_CTRL, ctrl); + WR4(sc, DW_MMC_BLKSIZ, MIN(count_bytes, MMC_SECTOR_SIZE)); + WR4(sc, DW_MMC_BYTCNT, count_bytes); + + if ((data->flags & MMC_DATA_WRITE) != 0) { + cmdr |= DW_MMC_CMD_DATA_WR; + + if (use_dma) { + rtems_cache_flush_multiple_data_lines(data->data, + count_bytes); + } else { + done_bytes = dw_mmc_fill_fifo(sc, data); + } + } else if (use_dma) { + rtems_cache_invalidate_multiple_data_lines(data->data, + count_bytes); + } + + if (use_dma) { + if (count_bytes > DW_MMC_MAX_DMA_TRANSFER_BYTES) { + cmd->error = MMC_ERR_INVALID; + return; + } + + dw_mmc_dma_setup(sc, data); + } + } + + dw_mmc_cmd_start(sc, cmdr, cmd->arg); + + if (use_dma) { + WR4(sc, DW_MMC_PLDMND, 0); + } + + cmd->error = dw_mmc_cmd_done(sc, cmd, data, done_bytes, use_dma); +} + +static int +dw_mmc_request(device_t brdev, device_t reqdev, struct mmc_request *req) +{ + struct dw_mmc_softc *sc = device_get_softc(brdev); + + DW_MMC_LOCK(sc); + dw_mmc_cmd_do(sc, req, req->cmd); + DW_MMC_UNLOCK(sc); + + (*req->done)(req); + + return (0); +} + +static int +dw_mmc_get_ro(device_t brdev, device_t reqdev) +{ + return (0); +} + +static int +dw_mmc_acquire_host(device_t brdev, device_t reqdev) +{ + struct dw_mmc_softc *sc = device_get_softc(brdev); + + DW_MMC_BUS_LOCK(sc); + + return (0); +} + +static int +dw_mmc_release_host(device_t brdev, device_t reqdev) +{ + struct dw_mmc_softc *sc = device_get_softc(brdev); + + DW_MMC_BUS_UNLOCK(sc); + + return (0); +} + +static void +dw_mmc_intr(void *arg) +{ + struct dw_mmc_softc *sc = (struct dw_mmc_softc *) arg; + rtems_status_code rs; + + WR4(sc, DW_MMC_INTMASK, 0); + + rs = rtems_event_transient_send(sc->task_id); + BSD_ASSERT(rs == RTEMS_SUCCESSFUL); +} + +static int +dw_mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) +{ + struct dw_mmc_softc *sc = device_get_softc(bus); + + switch (which) { + default: + return (EINVAL); + case MMCBR_IVAR_BUS_MODE: + *(int *)result = sc->host.ios.bus_mode; + break; + case MMCBR_IVAR_BUS_WIDTH: + *(int *)result = sc->host.ios.bus_width; + break; + case MMCBR_IVAR_CHIP_SELECT: + *(int *)result = sc->host.ios.chip_select; + break; + case MMCBR_IVAR_CLOCK: + *(int *)result = sc->host.ios.clock; + break; + case MMCBR_IVAR_F_MIN: + *(int *)result = sc->host.f_min; + break; + case MMCBR_IVAR_F_MAX: + *(int *)result = sc->host.f_max; + break; + case MMCBR_IVAR_HOST_OCR: + *(int *)result = sc->host.host_ocr; + break; + case MMCBR_IVAR_MODE: + *(int *)result = sc->host.mode; + break; + case MMCBR_IVAR_OCR: + *(int *)result = sc->host.ocr; + break; + case MMCBR_IVAR_POWER_MODE: + *(int *)result = sc->host.ios.power_mode; + break; + case MMCBR_IVAR_VDD: + *(int *)result = sc->host.ios.vdd; + break; + case MMCBR_IVAR_CAPS: + *(int *)result = sc->host.caps; + break; + case MMCBR_IVAR_MAX_DATA: + *(int *)result = 1; + break; + } + return (0); +} + +static int +dw_mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) +{ + struct dw_mmc_softc *sc = device_get_softc(bus); + + switch (which) { + default: + return (EINVAL); + case MMCBR_IVAR_BUS_MODE: + sc->host.ios.bus_mode = value; + break; + case MMCBR_IVAR_BUS_WIDTH: + sc->host.ios.bus_width = value; + break; + case MMCBR_IVAR_CHIP_SELECT: + sc->host.ios.chip_select = value; + break; + case MMCBR_IVAR_CLOCK: + sc->host.ios.clock = value; + break; + case MMCBR_IVAR_MODE: + sc->host.mode = value; + break; + case MMCBR_IVAR_OCR: + sc->host.ocr = value; + break; + case MMCBR_IVAR_POWER_MODE: + sc->host.ios.power_mode = value; + break; + case MMCBR_IVAR_VDD: + sc->host.ios.vdd = value; + break; + /* These are read-only */ + case MMCBR_IVAR_CAPS: + case MMCBR_IVAR_HOST_OCR: + case MMCBR_IVAR_F_MIN: + case MMCBR_IVAR_F_MAX: + case MMCBR_IVAR_MAX_DATA: + return (EINVAL); + } + return (0); +} + +static device_method_t dw_mmc_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, dw_mmc_probe), + DEVMETHOD(device_attach, dw_mmc_attach), + DEVMETHOD(device_detach, dw_mmc_detach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, dw_mmc_read_ivar), + DEVMETHOD(bus_write_ivar, dw_mmc_write_ivar), + + /* mmcbr_if */ + DEVMETHOD(mmcbr_update_ios, dw_mmc_update_ios), + DEVMETHOD(mmcbr_request, dw_mmc_request), + DEVMETHOD(mmcbr_get_ro, dw_mmc_get_ro), + DEVMETHOD(mmcbr_acquire_host, dw_mmc_acquire_host), + DEVMETHOD(mmcbr_release_host, dw_mmc_release_host), + + DEVMETHOD_END +}; + +static driver_t dw_mmc_driver = { + "dw_mmc", + dw_mmc_methods, + sizeof(struct dw_mmc_softc) +}; + +static devclass_t dw_mmc_devclass; + +DRIVER_MODULE(dw_mmc, nexus, dw_mmc_driver, dw_mmc_devclass, NULL, NULL); diff --git a/rtemsbsd/sys/dev/dw_mmc/dw_mmcreg.h b/rtemsbsd/sys/dev/dw_mmc/dw_mmcreg.h new file mode 100644 index 00000000..d6936a56 --- /dev/null +++ b/rtemsbsd/sys/dev/dw_mmc/dw_mmcreg.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * 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 COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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. + */ + +#ifndef DW_MMC_DW_MMCREG_H +#define DW_MMC_DW_MMCREG_H + +#define DW_MMC_CTRL 0x000 +#define DW_MMC_PWREN 0x004 +#define DW_MMC_CLKDIV 0x008 +#define DW_MMC_CLKSRC 0x00c +#define DW_MMC_CLKENA 0x010 +#define DW_MMC_TMOUT 0x014 +#define DW_MMC_CTYPE 0x018 +#define DW_MMC_BLKSIZ 0x01c +#define DW_MMC_BYTCNT 0x020 +#define DW_MMC_INTMASK 0x024 +#define DW_MMC_CMDARG 0x028 +#define DW_MMC_CMD 0x02c +#define DW_MMC_RESP0 0x030 +#define DW_MMC_RESP1 0x034 +#define DW_MMC_RESP2 0x038 +#define DW_MMC_RESP3 0x03c +#define DW_MMC_MINTSTS 0x040 +#define DW_MMC_RINTSTS 0x044 +#define DW_MMC_STATUS 0x048 +#define DW_MMC_FIFOTH 0x04c +#define DW_MMC_CDETECT 0x050 +#define DW_MMC_WRTPRT 0x054 +#define DW_MMC_GPIO 0x058 +#define DW_MMC_TCBCNT 0x05c +#define DW_MMC_TBBCNT 0x060 +#define DW_MMC_DEBNCE 0x064 +#define DW_MMC_USRID 0x068 +#define DW_MMC_VERID 0x06c +#define DW_MMC_HCON 0x070 +#define DW_MMC_UHS_REG 0x074 +#define DW_MMC_RST_N 0x078 +#define DW_MMC_BMOD 0x080 +#define DW_MMC_PLDMND 0x084 +#define DW_MMC_DBADDR 0x088 +#define DW_MMC_IDSTS 0x08c +#define DW_MMC_IDINTEN 0x090 +#define DW_MMC_DSCADDR 0x094 +#define DW_MMC_BUFADDR 0x098 +#define DW_MMC_DATA 0x200 + +/* Control Register */ +#define DW_MMC_CTRL_DMA_ENABLE (1u << 25) +#define DW_MMC_CTRL_CEATA_INT_EN (1u << 11) +#define DW_MMC_CTRL_SEND_AS_CCSD (1u << 10) +#define DW_MMC_CTRL_SEND_CCSD (1u << 9) +#define DW_MMC_CTRL_ABRT_READ_DATA (1u << 8) +#define DW_MMC_CTRL_SEND_IRQ_RESP (1u << 7) +#define DW_MMC_CTRL_READ_WAIT (1u << 6) +#define DW_MMC_CTRL_INT_ENABLE (1u << 4) +#define DW_MMC_CTRL_DMA_RESET (1u << 2) +#define DW_MMC_CTRL_FIFO_RESET (1u << 1) +#define DW_MMC_CTRL_RESET (1u << 0) + +/* Power Enable Register */ +#define DW_MMC_PWREN_ENABLE (1u << 0) + +/* Clock Enable Register */ +#define DW_MMC_CLKEN_LOW_PWR (1u << 16) +#define DW_MMC_CLKEN_ENABLE (1u << 0) + +/* Timeout Register */ +#define DW_MMC_TMOUT_DATA(x) ((x) << 8) +#define DW_MMC_TMOUT_DATA_MSK 0xffffff00 +#define DW_MMC_TMOUT_RESP(x) ((x) & 0xFF) +#define DW_MMC_TMOUT_RESP_MSK 0xff + +/* Card Type Register */ +#define DW_MMC_CTYPE_8BIT (1u << 16) +#define DW_MMC_CTYPE_4BIT (1u << 0) +#define DW_MMC_CTYPE_1BIT 0 + +/* Interrupt Status and Interrupt Mask Register */ +#define DW_MMC_INT_SDIO(x) (1u << (16 + (x))) +#define DW_MMC_INT_EBE (1u << 15) +#define DW_MMC_INT_ACD (1u << 14) +#define DW_MMC_INT_SBE (1u << 13) +#define DW_MMC_INT_HLE (1u << 12) +#define DW_MMC_INT_FRUN (1u << 11) +#define DW_MMC_INT_HTO (1u << 10) +#define DW_MMC_INT_DRTO (1u << 9) +#define DW_MMC_INT_RTO (1u << 8) +#define DW_MMC_INT_DCRC (1u << 7) +#define DW_MMC_INT_RCRC (1u << 6) +#define DW_MMC_INT_RXDR (1u << 5) +#define DW_MMC_INT_TXDR (1u << 4) +#define DW_MMC_INT_DTO (1u << 3) +#define DW_MMC_INT_CMD_DONE (1u << 2) +#define DW_MMC_INT_RE (1u << 1) +#define DW_MMC_INT_CD (1u << 0) +#define DW_MMC_INT_ERROR (DW_MMC_INT_RE | DW_MMC_INT_RCRC | DW_MMC_INT_DCRC \ + | DW_MMC_INT_DRTO | DW_MMC_INT_RTO | DW_MMC_INT_EBE) + +/* Command Register */ +#define DW_MMC_CMD_START (1u << 31) +#define DW_MMC_CMD_USE_HOLD_REG (1u << 29) +#define DW_MMC_CMD_VOLT_SWITCH (1u << 28) +#define DW_MMC_CMD_BOOT_MODE (1u << 27) +#define DW_MMC_CMD_DISABLE_BOOT (1u << 26) +#define DW_MMC_CMD_EXP_BOOT_ACK (1u << 25) +#define DW_MMC_CMD_ENABLE_BOOT (1u << 24) +#define DW_MMC_CMD_CCS_EXP (1u << 23) +#define DW_MMC_CMD_CEATA_RD (1u << 22) +#define DW_MMC_CMD_UPDATE_CLK (1u << 21) +#define DW_MMC_CMD_SEND_INIT (1u << 15) +#define DW_MMC_CMD_STOP_ABRT (1u << 14) +#define DW_MMC_CMD_PRV_DATA_WAIT (1u << 13) +#define DW_MMC_CMD_SEND_STOP (1u << 12) +#define DW_MMC_CMD_STREAM_MODE (1u << 11) +#define DW_MMC_CMD_DATA_WR (1u << 10) +#define DW_MMC_CMD_DATA_EXP (1u << 9) +#define DW_MMC_CMD_RESP_CRC (1u << 8) +#define DW_MMC_CMD_RESP_LONG (1u << 7) +#define DW_MMC_CMD_RESP_EXP (1u << 6) +#define DW_MMC_CMD_INDEX(x) ((x) & 0x1f) + +/* Status Register */ +#define DW_MMC_STATUS_GET_FIFO_CNT(x) (((x) >> 17) & 0x1fff) +#define DW_MMC_STATUS_GET_RESP_IDX(x) (((x) >> 11) & 0x3f) +#define DW_MMC_STATUS_DS_MC_BUSY (1u << 10) +#define DW_MMC_STATUS_CARD_DATA_BUSY (1u << 9) +#define DW_MMC_STATUS_CARD_PRESENT (1u << 8) +#define DW_MMC_STATUS_GET_FSM_STATE(x) (((x) >> 4) & 0xf) +#define DW_MMC_STATUS_FIFO_FULL (1u << 3) +#define DW_MMC_STATUS_FIFO_EMPTY (1u << 2) +#define DW_MMC_STATUS_FIFO_TX_WM (1u << 1) +#define DW_MMC_STATUS_FIFO_RX_WM (1u << 0) + +/* DMA and FIFO Control Register */ +#define DW_MMC_FIFOTH_BSZ(x) ((x) << 28) +#define DW_MMC_FIFOTH_BSZ_MSK DW_MMC_FIFOTH_BSZ(0x7) +#define DW_MMC_FIFOTH_RX_WMARK(x) ((x) << 16) +#define DW_MMC_FIFOTH_RX_WMARK_MSK DW_MMC_FIFOTH_RX_WMARK(0xfff) +#define DW_MMC_FIFOTH_TX_WMARK(x) ((x) << 0) +#define DW_MMC_FIFOTH_TX_WMARK_MSK DW_MMC_FIFOTH_TX_WMARK(0xfff) + +/* Card Detect Register */ +#define DW_MMC_CDETECT_NOT_DETECTED (1u << 0) + +/* Write Protect Register */ +#define DW_MMC_WRTPRT_ENABLED (1u << 0) + +/* Hardware Reset Register */ +#define DW_MMC_RST_N_ACTIVE_MODE (1u << 0) + +/* Bus Mode Register */ +#define DW_MMC_BMOD_DE (1u << 7) +#define DW_MMC_BMOD_FB (1u << 1) +#define DW_MMC_BMOD_SWR (1u << 0) + +/* Internal DMAC Status and Interrupt DMAC Interrupt Enable Register */ +#define DW_MMC_IDMAC_INT_GET_FSM(x) (((x) >> 13) & 0xf) +#define DW_MMC_IDMAC_INT_GET_EB(x) (((x) >> 10) & 0x7) +#define DW_MMC_IDMAC_INT_AIS (1u << 9) +#define DW_MMC_IDMAC_INT_NIS (1u << 8) +#define DW_MMC_IDMAC_INT_CES (1u << 5) +#define DW_MMC_IDMAC_INT_DU (1u << 4) +#define DW_MMC_IDMAC_INT_FBE (1u << 2) +#define DW_MMC_IDMAC_INT_RI (1u << 1) +#define DW_MMC_IDMAC_INT_TI (1u << 0) + +/* Internal DMA descriptor */ + +struct dw_mmc_des { + uint32_t des0; + uint32_t des1; + uint32_t des2; + uint32_t des3; +}; + +#define DW_MMC_DES0_OWN (1u << 31) +#define DW_MMC_DES0_CES (1u << 30) +#define DW_MMC_DES0_ER (1u << 5) +#define DW_MMC_DES0_CH (1u << 4) +#define DW_MMC_DES0_FS (1u << 3) +#define DW_MMC_DES0_LD (1u << 2) +#define DW_MMC_DES0_DIC (1u << 1) + +#define DW_MMC_DES1_BS2(x) ((x) << 13) +#define DW_MMC_DES1_BS1(x) ((x) << 0) +#define DW_MMC_DES1_MAX_BS 4096 + +/* FIFO dimensions */ +#define DW_MMC_FIFO_DEPTH 1024 +#define DW_MMC_FIFO_WIDTH 4 + +#endif /* DW_MMC_DW_MMCREG_H */ -- cgit v1.2.3