diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2015-03-16 22:10:36 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2015-03-26 13:53:24 +0100 |
commit | fc9e83bb68ff851be89a4b24097b0948ef160c4f (patch) | |
tree | 011b08058ff03d0c130c742a9f82e462b285a26e /freebsd/sys/dev/mmc | |
parent | nexus-devices: Fix for QorIQ T variants (diff) | |
download | rtems-libbsd-fc9e83bb68ff851be89a4b24097b0948ef160c4f.tar.bz2 |
mmc: Import MMC/SDCard support from libusb
Diffstat (limited to 'freebsd/sys/dev/mmc')
-rw-r--r-- | freebsd/sys/dev/mmc/bridge.h | 138 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmc.c | 1758 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmcbrvar.h | 112 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmcreg.h | 443 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmcsd.c | 801 | ||||
-rw-r--r-- | freebsd/sys/dev/mmc/mmcvar.h | 102 |
6 files changed, 3354 insertions, 0 deletions
diff --git a/freebsd/sys/dev/mmc/bridge.h b/freebsd/sys/dev/mmc/bridge.h new file mode 100644 index 00000000..bd61c15a --- /dev/null +++ b/freebsd/sys/dev/mmc/bridge.h @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * $FreeBSD$ + */ + +#ifndef DEV_MMC_BRIDGE_H +#define DEV_MMC_BRIDGE_H + +/* + * This file defines interfaces for the mmc bridge. The names chosen + * are similar to or the same as the names used in Linux to allow for + * easy porting of what Linux calls mmc host drivers. I use the + * FreeBSD terminology of bridge and bus for consistancy with other + * drivers in the system. This file corresponds roughly to the Linux + * linux/mmc/host.h file. + * + * A mmc bridge is a chipset that can have one or more mmc and/or sd + * cards attached to it. mmc cards are attached on a bus topology, + * while sd and sdio cards are attached using a star topology (meaning + * in practice each sd card has its own, independent slot). Each + * mmcbr is assumed to be derived from the mmcbr. This is done to + * allow for easier addition of bridges (as each bridge does not need + * to be added to the mmcbus file). + * + * Attached to the mmc bridge is an mmcbus. The mmcbus is described + * in dev/mmc/bus.h. + */ + + +/* + * mmc_ios is a structure that is used to store the state of the mmc/sd + * bus configuration. This include the bus' clock speed, its voltage, + * the bus mode for command output, the SPI chip select, some power + * states and the bus width. + */ +enum mmc_vdd { + vdd_150 = 0, vdd_155, vdd_160, vdd_165, vdd_170, vdd_180, + vdd_190, vdd_200, vdd_210, vdd_220, vdd_230, vdd_240, vdd_250, + vdd_260, vdd_270, vdd_280, vdd_290, vdd_300, vdd_310, vdd_320, + vdd_330, vdd_340, vdd_350, vdd_360 +}; + +enum mmc_power_mode { + power_off = 0, power_up, power_on +}; + +enum mmc_bus_mode { + opendrain = 1, pushpull +}; + +enum mmc_chip_select { + cs_dontcare = 0, cs_high, cs_low +}; + +enum mmc_bus_width { + bus_width_1 = 0, bus_width_4 = 2, bus_width_8 = 3 +}; + +enum mmc_bus_timing { + bus_timing_normal = 0, bus_timing_hs +}; + +struct mmc_ios { + uint32_t clock; /* Speed of the clock in Hz to move data */ + enum mmc_vdd vdd; /* Voltage to apply to the power pins/ */ + enum mmc_bus_mode bus_mode; + enum mmc_chip_select chip_select; + enum mmc_bus_width bus_width; + enum mmc_power_mode power_mode; + enum mmc_bus_timing timing; +}; + +enum mmc_card_mode { + mode_mmc, mode_sd +}; + +struct mmc_host { + int f_min; + int f_max; + uint32_t host_ocr; + uint32_t ocr; + uint32_t caps; +#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can do 4-bit data transfers */ +#define MMC_CAP_8_BIT_DATA (1 << 1) /* Can do 8-bit data transfers */ +#define MMC_CAP_HSPEED (1 << 2) /* Can do High Speed transfers */ + enum mmc_card_mode mode; + struct mmc_ios ios; /* Current state of the host */ +}; + +#endif /* DEV_MMC_BRIDGE_H */ diff --git a/freebsd/sys/dev/mmc/mmc.c b/freebsd/sys/dev/mmc/mmc.c new file mode 100644 index 00000000..15175ba8 --- /dev/null +++ b/freebsd/sys/dev/mmc/mmc.c @@ -0,0 +1,1758 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2006 Bernd Walter. All rights reserved. + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/sysctl.h> +#ifdef __rtems__ +#include <sys/condvar.h> +#endif /* __rtems__ */ + +#include <dev/mmc/mmcreg.h> +#include <dev/mmc/mmcbrvar.h> +#include <dev/mmc/mmcvar.h> +#include <rtems/bsd/local/mmcbr_if.h> +#include <rtems/bsd/local/mmcbus_if.h> + +struct mmc_softc { + device_t dev; + struct mtx sc_mtx; + struct intr_config_hook config_intrhook; + device_t owner; + uint32_t last_rca; +#ifdef __rtems__ + struct cv req_done; +#endif /* __rtems__ */ +}; + +/* + * Per-card data + */ +struct mmc_ivars { + uint32_t raw_cid[4]; /* Raw bits of the CID */ + uint32_t raw_csd[4]; /* Raw bits of the CSD */ + uint32_t raw_scr[2]; /* Raw bits of the SCR */ + uint8_t raw_ext_csd[512]; /* Raw bits of the EXT_CSD */ + uint32_t raw_sd_status[16]; /* Raw bits of the SD_STATUS */ + uint16_t rca; + enum mmc_card_mode mode; + struct mmc_cid cid; /* cid decoded */ + struct mmc_csd csd; /* csd decoded */ + struct mmc_scr scr; /* scr decoded */ + struct mmc_sd_status sd_status; /* SD_STATUS decoded */ + u_char read_only; /* True when the device is read-only */ + u_char bus_width; /* Bus width to use */ + u_char timing; /* Bus timing support */ + u_char high_cap; /* High Capacity card (block addressed) */ + uint32_t sec_count; /* Card capacity in 512byte blocks */ + uint32_t tran_speed; /* Max speed in normal mode */ + uint32_t hs_tran_speed; /* Max speed in high speed mode */ + uint32_t erase_sector; /* Card native erase sector size */ + char card_id_string[64];/* Formatted CID info (serial, MFG, etc) */ +}; + +#define CMD_RETRIES 3 + +static SYSCTL_NODE(_hw, OID_AUTO, mmc, CTLFLAG_RD, NULL, "mmc driver"); + +static int mmc_debug; +SYSCTL_INT(_hw_mmc, OID_AUTO, debug, CTLFLAG_RW, &mmc_debug, 0, "Debug level"); + +/* bus entry points */ +static int mmc_acquire_bus(device_t busdev, device_t dev); +static int mmc_attach(device_t dev); +static int mmc_child_location_str(device_t dev, device_t child, char *buf, + size_t buflen); +static int mmc_detach(device_t dev); +static int mmc_probe(device_t dev); +static int mmc_read_ivar(device_t bus, device_t child, int which, + uintptr_t *result); +static int mmc_release_bus(device_t busdev, device_t dev); +static int mmc_resume(device_t dev); +static int mmc_suspend(device_t dev); +static int mmc_wait_for_request(device_t brdev, device_t reqdev, + struct mmc_request *req); +static int mmc_write_ivar(device_t bus, device_t child, int which, + uintptr_t value); + +#define MMC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define MMC_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ + "mmc", MTX_DEF) +#define MMC_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define MMC_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define MMC_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +static int mmc_all_send_cid(struct mmc_softc *sc, uint32_t *rawcid); +static void mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr); +static void mmc_app_decode_sd_status(uint32_t *raw_sd_status, + struct mmc_sd_status *sd_status); +static int mmc_app_sd_status(struct mmc_softc *sc, uint16_t rca, + uint32_t *rawsdstatus); +static int mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, + uint32_t *rawscr); +static int mmc_calculate_clock(struct mmc_softc *sc); +static void mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid); +static void mmc_decode_cid_sd(uint32_t *raw_cid, struct mmc_cid *cid); +static void mmc_decode_csd_mmc(uint32_t *raw_csd, struct mmc_csd *csd); +static void mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd); +static void mmc_delayed_attach(void *xsc); +static int mmc_delete_cards(struct mmc_softc *sc); +static void mmc_discover_cards(struct mmc_softc *sc); +static void mmc_format_card_id_string(struct mmc_ivars *ivar); +static void mmc_go_discovery(struct mmc_softc *sc); +static uint32_t mmc_get_bits(uint32_t *bits, int bit_len, int start, + int size); +static int mmc_highest_voltage(uint32_t ocr); +static void mmc_idle_cards(struct mmc_softc *sc); +static void mmc_ms_delay(int ms); +static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard); +static void mmc_power_down(struct mmc_softc *sc); +static void mmc_power_up(struct mmc_softc *sc); +static void mmc_rescan_cards(struct mmc_softc *sc); +static void mmc_scan(struct mmc_softc *sc); +static int mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, + uint8_t value, uint8_t *res); +static int mmc_select_card(struct mmc_softc *sc, uint16_t rca); +static uint32_t mmc_select_vdd(struct mmc_softc *sc, uint32_t ocr); +static int mmc_send_app_op_cond(struct mmc_softc *sc, uint32_t ocr, + uint32_t *rocr); +static int mmc_send_csd(struct mmc_softc *sc, uint16_t rca, uint32_t *rawcsd); +static int mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd); +static int mmc_send_if_cond(struct mmc_softc *sc, uint8_t vhs); +static int mmc_send_op_cond(struct mmc_softc *sc, uint32_t ocr, + uint32_t *rocr); +static int mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp); +static int mmc_send_status(struct mmc_softc *sc, uint16_t rca, + uint32_t *status); +static int mmc_set_blocklen(struct mmc_softc *sc, uint32_t len); +static int mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, + int width); +static int mmc_set_relative_addr(struct mmc_softc *sc, uint16_t resp); +static int mmc_set_timing(struct mmc_softc *sc, int timing); +static int mmc_switch(struct mmc_softc *sc, uint8_t set, uint8_t index, + uint8_t value); +static int mmc_test_bus_width(struct mmc_softc *sc); +static int mmc_wait_for_app_cmd(struct mmc_softc *sc, uint32_t rca, + struct mmc_command *cmd, int retries); +static int mmc_wait_for_cmd(struct mmc_softc *sc, struct mmc_command *cmd, + int retries); +static int mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode, + uint32_t arg, uint32_t flags, uint32_t *resp, int retries); +static int mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req); +static void mmc_wakeup(struct mmc_request *req); + +static void +mmc_ms_delay(int ms) +{ + + DELAY(1000 * ms); /* XXX BAD */ +} + +static int +mmc_probe(device_t dev) +{ + + device_set_desc(dev, "MMC/SD bus"); + return (0); +} + +static int +mmc_attach(device_t dev) +{ + struct mmc_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + MMC_LOCK_INIT(sc); +#ifdef __rtems__ + cv_init(&sc->req_done, "MMC request done"); +#endif /* __rtems__ */ + + /* We'll probe and attach our children later, but before / mount */ + sc->config_intrhook.ich_func = mmc_delayed_attach; + sc->config_intrhook.ich_arg = sc; + if (config_intrhook_establish(&sc->config_intrhook) != 0) + device_printf(dev, "config_intrhook_establish failed\n"); + return (0); +} + +static int +mmc_detach(device_t dev) +{ + struct mmc_softc *sc = device_get_softc(dev); + int err; + + if ((err = mmc_delete_cards(sc)) != 0) + return (err); + mmc_power_down(sc); +#ifdef __rtems__ + cv_destroy(&sc->req_done); +#endif /* __rtems__ */ + MMC_LOCK_DESTROY(sc); + + return (0); +} + +static int +mmc_suspend(device_t dev) +{ + struct mmc_softc *sc = device_get_softc(dev); + int err; + + err = bus_generic_suspend(dev); + if (err) + return (err); + mmc_power_down(sc); + return (0); +} + +static int +mmc_resume(device_t dev) +{ + struct mmc_softc *sc = device_get_softc(dev); + + mmc_scan(sc); + return (bus_generic_resume(dev)); +} + +static int +mmc_acquire_bus(device_t busdev, device_t dev) +{ + struct mmc_softc *sc; + struct mmc_ivars *ivar; + int err; + int rca; + + err = MMCBR_ACQUIRE_HOST(device_get_parent(busdev), busdev); + if (err) + return (err); + sc = device_get_softc(busdev); + MMC_LOCK(sc); + if (sc->owner) + panic("mmc: host bridge didn't serialize us."); + sc->owner = dev; + MMC_UNLOCK(sc); + + if (busdev != dev) { + /* + * Keep track of the last rca that we've selected. If + * we're asked to do it again, don't. We never + * unselect unless the bus code itself wants the mmc + * bus, and constantly reselecting causes problems. + */ + rca = mmc_get_rca(dev); + if (sc->last_rca != rca) { + mmc_select_card(sc, rca); + sc->last_rca = rca; + /* Prepare bus width for the new card. */ + ivar = device_get_ivars(dev); + if (bootverbose || mmc_debug) { + device_printf(busdev, + "setting bus width to %d bits\n", + (ivar->bus_width == bus_width_4) ? 4 : + (ivar->bus_width == bus_width_8) ? 8 : 1); + } + mmc_set_card_bus_width(sc, rca, ivar->bus_width); + mmcbr_set_bus_width(busdev, ivar->bus_width); + mmcbr_update_ios(busdev); + } + } else { + /* + * If there's a card selected, stand down. + */ + if (sc->last_rca != 0) { + mmc_select_card(sc, 0); + sc->last_rca = 0; + } + } + + return (0); +} + +static int +mmc_release_bus(device_t busdev, device_t dev) +{ + struct mmc_softc *sc; + int err; + + sc = device_get_softc(busdev); + + MMC_LOCK(sc); + if (!sc->owner) + panic("mmc: releasing unowned bus."); + if (sc->owner != dev) + panic("mmc: you don't own the bus. game over."); + MMC_UNLOCK(sc); + err = MMCBR_RELEASE_HOST(device_get_parent(busdev), busdev); + if (err) + return (err); + MMC_LOCK(sc); + sc->owner = NULL; + MMC_UNLOCK(sc); + return (0); +} + +static uint32_t +mmc_select_vdd(struct mmc_softc *sc, uint32_t ocr) +{ + + return (ocr & MMC_OCR_VOLTAGE); +} + +static int +mmc_highest_voltage(uint32_t ocr) +{ + int i; + + for (i = 30; i >= 0; i--) + if (ocr & (1 << i)) + return (i); + return (-1); +} + +static void +mmc_wakeup(struct mmc_request *req) +{ + struct mmc_softc *sc; + + sc = (struct mmc_softc *)req->done_data; + MMC_LOCK(sc); + req->flags |= MMC_REQ_DONE; +#ifdef __rtems__ + cv_broadcast(&sc->req_done); +#endif /* __rtems__ */ + MMC_UNLOCK(sc); +#ifndef __rtems__ + wakeup(req); +#endif /* __rtems__ */ +} + +static int +mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req) +{ + + req->done = mmc_wakeup; + req->done_data = sc; + if (mmc_debug > 1) { + device_printf(sc->dev, "REQUEST: CMD%d arg %#x flags %#x", + req->cmd->opcode, req->cmd->arg, req->cmd->flags); + if (req->cmd->data) { + printf(" data %d\n", (int)req->cmd->data->len); + } else + printf("\n"); + } + MMCBR_REQUEST(device_get_parent(sc->dev), sc->dev, req); + MMC_LOCK(sc); + while ((req->flags & MMC_REQ_DONE) == 0) +#ifndef __rtems__ + msleep(req, &sc->sc_mtx, 0, "mmcreq", 0); +#else /* __rtems__ */ + cv_wait(&sc->req_done, &sc->sc_mtx); +#endif /* __rtems__ */ + MMC_UNLOCK(sc); + if (mmc_debug > 2 || (mmc_debug > 1 && req->cmd->error)) + device_printf(sc->dev, "RESULT: %d\n", req->cmd->error); + return (0); +} + +static int +mmc_wait_for_request(device_t brdev, device_t reqdev, struct mmc_request *req) +{ + struct mmc_softc *sc = device_get_softc(brdev); + + return (mmc_wait_for_req(sc, req)); +} + +static int +mmc_wait_for_cmd(struct mmc_softc *sc, struct mmc_command *cmd, int retries) +{ + struct mmc_request mreq; + + memset(&mreq, 0, sizeof(mreq)); + memset(cmd->resp, 0, sizeof(cmd->resp)); + cmd->retries = retries; + mreq.cmd = cmd; + mmc_wait_for_req(sc, &mreq); + return (cmd->error); +} + +static int +mmc_wait_for_app_cmd(struct mmc_softc *sc, uint32_t rca, + struct mmc_command *cmd, int retries) +{ + struct mmc_command appcmd; + int err = MMC_ERR_NONE, i; + + for (i = 0; i <= retries; i++) { + appcmd.opcode = MMC_APP_CMD; + appcmd.arg = rca << 16; + appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + appcmd.data = NULL; + mmc_wait_for_cmd(sc, &appcmd, 0); + err = appcmd.error; + if (err != MMC_ERR_NONE) + continue; + if (!(appcmd.resp[0] & R1_APP_CMD)) + return MMC_ERR_FAILED; + mmc_wait_for_cmd(sc, cmd, 0); + err = cmd->error; + if (err == MMC_ERR_NONE) + break; + } + return (err); +} + +static int +mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode, + uint32_t arg, uint32_t flags, uint32_t *resp, int retries) +{ + struct mmc_command cmd; + int err; + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = opcode; + cmd.arg = arg; + cmd.flags = flags; + cmd.data = NULL; + err = mmc_wait_for_cmd(sc, &cmd, retries); + if (err) + return (err); + if (cmd.error) + return (cmd.error); + if (resp) { + if (flags & MMC_RSP_136) + memcpy(resp, cmd.resp, 4 * sizeof(uint32_t)); + else + *resp = cmd.resp[0]; + } + return (0); +} + +static void +mmc_idle_cards(struct mmc_softc *sc) +{ + device_t dev; + struct mmc_command cmd; + + dev = sc->dev; + mmcbr_set_chip_select(dev, cs_high); + mmcbr_update_ios(dev); + mmc_ms_delay(1); + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = MMC_GO_IDLE_STATE; + cmd.arg = 0; + cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; + cmd.data = NULL; + mmc_wait_for_cmd(sc, &cmd, 0); + mmc_ms_delay(1); + + mmcbr_set_chip_select(dev, cs_dontcare); + mmcbr_update_ios(dev); + mmc_ms_delay(1); +} + +static int +mmc_send_app_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr) +{ + struct mmc_command cmd; + int err = MMC_ERR_NONE, i; + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = ACMD_SD_SEND_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; + cmd.data = NULL; + + for (i = 0; i < 1000; i++) { + err = mmc_wait_for_app_cmd(sc, 0, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + break; + if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) || + (ocr & MMC_OCR_VOLTAGE) == 0) + break; + err = MMC_ERR_TIMEOUT; + mmc_ms_delay(10); + } + if (rocr && err == MMC_ERR_NONE) + *rocr = cmd.resp[0]; + return (err); +} + +static int +mmc_send_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr) +{ + struct mmc_command cmd; + int err = MMC_ERR_NONE, i; + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = MMC_SEND_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; + cmd.data = NULL; + + for (i = 0; i < 1000; i++) { + err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + break; + if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) || + (ocr & MMC_OCR_VOLTAGE) == 0) + break; + err = MMC_ERR_TIMEOUT; + mmc_ms_delay(10); + } + if (rocr && err == MMC_ERR_NONE) + *rocr = cmd.resp[0]; + return (err); +} + +static int +mmc_send_if_cond(struct mmc_softc *sc, uint8_t vhs) +{ + struct mmc_command cmd; + int err; + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = SD_SEND_IF_COND; + cmd.arg = (vhs << 8) + 0xAA; + cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; + cmd.data = NULL; + + err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + return (err); +} + +static void +mmc_power_up(struct mmc_softc *sc) +{ + device_t dev; + + dev = sc->dev; + mmcbr_set_vdd(dev, mmc_highest_voltage(mmcbr_get_host_ocr(dev))); + mmcbr_set_bus_mode(dev, opendrain); + mmcbr_set_chip_select(dev, cs_dontcare); + mmcbr_set_bus_width(dev, bus_width_1); + mmcbr_set_power_mode(dev, power_up); + mmcbr_set_clock(dev, 0); + mmcbr_update_ios(dev); + mmc_ms_delay(1); + + mmcbr_set_clock(dev, mmcbr_get_f_min(sc->dev)); + mmcbr_set_timing(dev, bus_timing_normal); + mmcbr_set_power_mode(dev, power_on); + mmcbr_update_ios(dev); + mmc_ms_delay(2); +} + +static void +mmc_power_down(struct mmc_softc *sc) +{ + device_t dev = sc->dev; + + mmcbr_set_bus_mode(dev, opendrain); + mmcbr_set_chip_select(dev, cs_dontcare); + mmcbr_set_bus_width(dev, bus_width_1); + mmcbr_set_power_mode(dev, power_off); + mmcbr_set_clock(dev, 0); + mmcbr_set_timing(dev, bus_timing_normal); + mmcbr_update_ios(dev); +} + +static int +mmc_select_card(struct mmc_softc *sc, uint16_t rca) +{ + int flags; + + flags = (rca ? MMC_RSP_R1B : MMC_RSP_NONE) | MMC_CMD_AC; + return (mmc_wait_for_command(sc, MMC_SELECT_CARD, (uint32_t)rca << 16, + flags, NULL, CMD_RETRIES)); +} + +static int +mmc_switch(struct mmc_softc *sc, uint8_t set, uint8_t index, uint8_t value) +{ + struct mmc_command cmd; + int err; + + cmd.opcode = MMC_SWITCH_FUNC; + cmd.arg = (MMC_SWITCH_FUNC_WR << 24) | + (index << 16) | + (value << 8) | + set; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + cmd.data = NULL; + err = mmc_wait_for_cmd(sc, &cmd, 0); + return (err); +} + +static int +mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp, uint8_t value, + uint8_t *res) +{ + int err; + struct mmc_command cmd; + struct mmc_data data; + + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + memset(res, 0, 64); + + cmd.opcode = SD_SWITCH_FUNC; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.arg = mode << 31; /* 0 - check, 1 - set */ + cmd.arg |= 0x00FFFFFF; + cmd.arg &= ~(0xF << (grp * 4)); + cmd.arg |= value << (grp * 4); + cmd.data = &data; + + data.data = res; + data.len = 64; + data.flags = MMC_DATA_READ; + + err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + return (err); +} + +static int +mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, int width) +{ + struct mmc_command cmd; + int err; + uint8_t value; + + if (mmcbr_get_mode(sc->dev) == mode_sd) { + memset(&cmd, 0, sizeof(struct mmc_command)); + cmd.opcode = ACMD_SET_CLR_CARD_DETECT; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + cmd.arg = SD_CLR_CARD_DETECT; + err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); + if (err != 0) + return (err); + memset(&cmd, 0, sizeof(struct mmc_command)); + cmd.opcode = ACMD_SET_BUS_WIDTH; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + switch (width) { + case bus_width_1: + cmd.arg = SD_BUS_WIDTH_1; + break; + case bus_width_4: + cmd.arg = SD_BUS_WIDTH_4; + break; + default: + return (MMC_ERR_INVALID); + } + err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); + } else { + switch (width) { + case bus_width_1: + value = EXT_CSD_BUS_WIDTH_1; + break; + case bus_width_4: + value = EXT_CSD_BUS_WIDTH_4; + break; + case bus_width_8: + value = EXT_CSD_BUS_WIDTH_8; + break; + default: + return (MMC_ERR_INVALID); + } + err = mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, + value); + } + return (err); +} + +static int +mmc_set_timing(struct mmc_softc *sc, int timing) +{ + int err; + uint8_t value; + u_char switch_res[64]; + + switch (timing) { + case bus_timing_normal: + value = 0; + break; + case bus_timing_hs: + value = 1; + break; + default: + return (MMC_ERR_INVALID); + } + if (mmcbr_get_mode(sc->dev) == mode_sd) + err = mmc_sd_switch(sc, SD_SWITCH_MODE_SET, SD_SWITCH_GROUP1, + value, switch_res); + else + err = mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, value); + return (err); +} + +static int +mmc_test_bus_width(struct mmc_softc *sc) +{ + struct mmc_command cmd; + struct mmc_data data; + int err; + uint8_t buf[8]; + uint8_t p8[8] = { 0x55, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t p8ok[8] = { 0xAA, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t p4[4] = { 0x5A, 0x00, 0x00, 0x00, }; + uint8_t p4ok[4] = { 0xA5, 0x00, 0x00, 0x00, }; + + if (mmcbr_get_caps(sc->dev) & MMC_CAP_8_BIT_DATA) { + mmcbr_set_bus_width(sc->dev, bus_width_8); + mmcbr_update_ios(sc->dev); + + cmd.opcode = MMC_BUSTEST_W; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.data = &data; + + data.data = p8; + data.len = 8; + data.flags = MMC_DATA_WRITE; + mmc_wait_for_cmd(sc, &cmd, 0); + + cmd.opcode = MMC_BUSTEST_R; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.data = &data; + + data.data = buf; + data.len = 8; + data.flags = MMC_DATA_READ; + err = mmc_wait_for_cmd(sc, &cmd, 0); + + mmcbr_set_bus_width(sc->dev, bus_width_1); + mmcbr_update_ios(sc->dev); + + if (err == MMC_ERR_NONE && memcmp(buf, p8ok, 8) == 0) + return (bus_width_8); + } + + if (mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA) { + mmcbr_set_bus_width(sc->dev, bus_width_4); + mmcbr_update_ios(sc->dev); + + cmd.opcode = MMC_BUSTEST_W; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.data = &data; + + data.data = p4; + data.len = 4; + data.flags = MMC_DATA_WRITE; + mmc_wait_for_cmd(sc, &cmd, 0); + + cmd.opcode = MMC_BUSTEST_R; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.data = &data; + + data.data = buf; + data.len = 4; + data.flags = MMC_DATA_READ; + err = mmc_wait_for_cmd(sc, &cmd, 0); + + mmcbr_set_bus_width(sc->dev, bus_width_1); + mmcbr_update_ios(sc->dev); + + if (err == MMC_ERR_NONE && memcmp(buf, p4ok, 4) == 0) + return (bus_width_4); + } + return (bus_width_1); +} + +static uint32_t +mmc_get_bits(uint32_t *bits, int bit_len, int start, int size) +{ + const int i = (bit_len / 32) - (start / 32) - 1; + const int shift = start & 31; + uint32_t retval = bits[i] >> shift; + if (size + shift > 32) + retval |= bits[i - 1] << (32 - shift); + return (retval & ((1llu << size) - 1)); +} + +static void +mmc_decode_cid_sd(uint32_t *raw_cid, struct mmc_cid *cid) +{ + int i; + + /* There's no version info, so we take it on faith */ + memset(cid, 0, sizeof(*cid)); + cid->mid = mmc_get_bits(raw_cid, 128, 120, 8); + cid->oid = mmc_get_bits(raw_cid, 128, 104, 16); + for (i = 0; i < 5; i++) + cid->pnm[i] = mmc_get_bits(raw_cid, 128, 96 - i * 8, 8); + cid->pnm[5] = 0; + cid->prv = mmc_get_bits(raw_cid, 128, 56, 8); + cid->psn = mmc_get_bits(raw_cid, 128, 24, 32); + cid->mdt_year = mmc_get_bits(raw_cid, 128, 12, 8) + 2000; + cid->mdt_month = mmc_get_bits(raw_cid, 128, 8, 4); +} + +static void +mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid) +{ + int i; + + /* There's no version info, so we take it on faith */ + memset(cid, 0, sizeof(*cid)); + cid->mid = mmc_get_bits(raw_cid, 128, 120, 8); + cid->oid = mmc_get_bits(raw_cid, 128, 104, 8); + for (i = 0; i < 6; i++) + cid->pnm[i] = mmc_get_bits(raw_cid, 128, 96 - i * 8, 8); + cid->pnm[6] = 0; + cid->prv = mmc_get_bits(raw_cid, 128, 48, 8); + cid->psn = mmc_get_bits(raw_cid, 128, 16, 32); + cid->mdt_month = mmc_get_bits(raw_cid, 128, 12, 4); + cid->mdt_year = mmc_get_bits(raw_cid, 128, 8, 4) + 1997; +} + +static void +mmc_format_card_id_string(struct mmc_ivars *ivar) +{ + char oidstr[8]; + uint8_t c1; + uint8_t c2; + + /* + * Format a card ID string for use by the mmcsd driver, it's what + * appears between the <> in the following: + * mmcsd0: 968MB <SD SD01G 8.0 SN 2686905 Mfg 08/2008 by 3 TN> at mmc0 + * 22.5MHz/4bit/128-block + * + * The card_id_string in mmc_ivars is currently allocated as 64 bytes, + * and our max formatted length is currently 55 bytes if every field + * contains the largest value. + * + * Sometimes the oid is two printable ascii chars; when it's not, + * format it as 0xnnnn instead. + */ + c1 = (ivar->cid.oid >> 8) & 0x0ff; + c2 = ivar->cid.oid & 0x0ff; + if (c1 > 0x1f && c1 < 0x7f && c2 > 0x1f && c2 < 0x7f) + snprintf(oidstr, sizeof(oidstr), "%c%c", c1, c2); + else + snprintf(oidstr, sizeof(oidstr), "0x%04x", ivar->cid.oid); + snprintf(ivar->card_id_string, sizeof(ivar->card_id_string), + "%s%s %s %d.%d SN %u MFG %02d/%04d by %d %s", + ivar->mode == mode_sd ? "SD" : "MMC", ivar->high_cap ? "HC" : "", + ivar->cid.pnm, ivar->cid.prv >> 4, ivar->cid.prv & 0x0f, + ivar->cid.psn, ivar->cid.mdt_month, ivar->cid.mdt_year, + ivar->cid.mid, oidstr); +} + +static const int exp[8] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 +}; + +static const int mant[16] = { + 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 +}; + +static const int cur_min[8] = { + 500, 1000, 5000, 10000, 25000, 35000, 60000, 100000 +}; + +static const int cur_max[8] = { + 1000, 5000, 10000, 25000, 35000, 45000, 800000, 200000 +}; + +static void +mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd) +{ + int v; + int m; + int e; + + memset(csd, 0, sizeof(*csd)); + csd->csd_structure = v = mmc_get_bits(raw_csd, 128, 126, 2); + if (v == 0) { + m = mmc_get_bits(raw_csd, 128, 115, 4); + e = mmc_get_bits(raw_csd, 128, 112, 3); + csd->tacc = (exp[e] * mant[m] + 9) / 10; + csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100; + m = mmc_get_bits(raw_csd, 128, 99, 4); + e = mmc_get_bits(raw_csd, 128, 96, 3); + csd->tran_speed = exp[e] * 10000 * mant[m]; + csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12); + csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4); + csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1); + csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1); + csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1); + csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1); + csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 59, 3)]; + csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 56, 3)]; + csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 53, 3)]; + csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 50, 3)]; + m = mmc_get_bits(raw_csd, 128, 62, 12); + e = mmc_get_bits(raw_csd, 128, 47, 3); + csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len; + csd->erase_blk_en = mmc_get_bits(raw_csd, 128, 46, 1); + csd->erase_sector = mmc_get_bits(raw_csd, 128, 39, 7) + 1; + csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 7); + csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1); + csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3); + csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4); + csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1); + } else if (v == 1) { + m = mmc_get_bits(raw_csd, 128, 115, 4); + e = mmc_get_bits(raw_csd, 128, 112, 3); + csd->tacc = (exp[e] * mant[m] + 9) / 10; + csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100; + m = mmc_get_bits(raw_csd, 128, 99, 4); + e = mmc_get_bits(raw_csd, 128, 96, 3); + csd->tran_speed = exp[e] * 10000 * mant[m]; + csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12); + csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4); + csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1); + csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1); + csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1); + csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1); + csd->capacity = ((uint64_t)mmc_get_bits(raw_csd, 128, 48, 22) + 1) * + 512 * 1024; + csd->erase_blk_en = mmc_get_bits(raw_csd, 128, 46, 1); + csd->erase_sector = mmc_get_bits(raw_csd, 128, 39, 7) + 1; + csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 7); + csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1); + csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3); + csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4); + csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1); + } else + panic("unknown SD CSD version"); +} + +static void +mmc_decode_csd_mmc(uint32_t *raw_csd, struct mmc_csd *csd) +{ + int m; + int e; + + memset(csd, 0, sizeof(*csd)); + csd->csd_structure = mmc_get_bits(raw_csd, 128, 126, 2); + csd->spec_vers = mmc_get_bits(raw_csd, 128, 122, 4); + m = mmc_get_bits(raw_csd, 128, 115, 4); + e = mmc_get_bits(raw_csd, 128, 112, 3); + csd->tacc = exp[e] * mant[m] + 9 / 10; + csd->nsac = mmc_get_bits(raw_csd, 128, 104, 8) * 100; + m = mmc_get_bits(raw_csd, 128, 99, 4); + e = mmc_get_bits(raw_csd, 128, 96, 3); + csd->tran_speed = exp[e] * 10000 * mant[m]; + csd->ccc = mmc_get_bits(raw_csd, 128, 84, 12); + csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 128, 80, 4); + csd->read_bl_partial = mmc_get_bits(raw_csd, 128, 79, 1); + csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1); + csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1); + csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1); + csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 59, 3)]; + csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 56, 3)]; + csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 53, 3)]; + csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 50, 3)]; + m = mmc_get_bits(raw_csd, 128, 62, 12); + e = mmc_get_bits(raw_csd, 128, 47, 3); + csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len; + csd->erase_blk_en = 0; + csd->erase_sector = (mmc_get_bits(raw_csd, 128, 42, 5) + 1) * + (mmc_get_bits(raw_csd, 128, 37, 5) + 1); + csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 5); + csd->wp_grp_enable = mmc_get_bits(raw_csd, 128, 31, 1); + csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3); + csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4); + csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1); +} + +static void +mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr) +{ + unsigned int scr_struct; + + memset(scr, 0, sizeof(*scr)); + + scr_struct = mmc_get_bits(raw_scr, 64, 60, 4); + if (scr_struct != 0) { + printf("Unrecognised SCR structure version %d\n", + scr_struct); + return; + } + scr->sda_vsn = mmc_get_bits(raw_scr, 64, 56, 4); + scr->bus_widths = mmc_get_bits(raw_scr, 64, 48, 4); +} + +static void +mmc_app_decode_sd_status(uint32_t *raw_sd_status, + struct mmc_sd_status *sd_status) +{ + + memset(sd_status, 0, sizeof(*sd_status)); + + sd_status->bus_width = mmc_get_bits(raw_sd_status, 512, 510, 2); + sd_status->secured_mode = mmc_get_bits(raw_sd_status, 512, 509, 1); + sd_status->card_type = mmc_get_bits(raw_sd_status, 512, 480, 16); + sd_status->prot_area = mmc_get_bits(raw_sd_status, 512, 448, 12); + sd_status->speed_class = mmc_get_bits(raw_sd_status, 512, 440, 8); + sd_status->perf_move = mmc_get_bits(raw_sd_status, 512, 432, 8); + sd_status->au_size = mmc_get_bits(raw_sd_status, 512, 428, 4); + sd_status->erase_size = mmc_get_bits(raw_sd_status, 512, 408, 16); + sd_status->erase_timeout = mmc_get_bits(raw_sd_status, 512, 402, 6); + sd_status->erase_offset = mmc_get_bits(raw_sd_status, 512, 400, 2); +} + +static int +mmc_all_send_cid(struct mmc_softc *sc, uint32_t *rawcid) +{ + struct mmc_command cmd; + int err; + + cmd.opcode = MMC_ALL_SEND_CID; + cmd.arg = 0; + cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + cmd.data = NULL; + err = mmc_wait_for_cmd(sc, &cmd, 0); + memcpy(rawcid, cmd.resp, 4 * sizeof(uint32_t)); + return (err); +} + +static int +mmc_send_csd(struct mmc_softc *sc, uint16_t rca, uint32_t *rawcsd) +{ + struct mmc_command cmd; + int err; + + cmd.opcode = MMC_SEND_CSD; + cmd.arg = rca << 16; + cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + cmd.data = NULL; + err = mmc_wait_for_cmd(sc, &cmd, 0); + memcpy(rawcsd, cmd.resp, 4 * sizeof(uint32_t)); + return (err); +} + +static int +mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, uint32_t *rawscr) +{ + int err; + struct mmc_command cmd; + struct mmc_data data; + + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + memset(rawscr, 0, 8); + cmd.opcode = ACMD_SEND_SCR; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.arg = 0; + cmd.data = &data; + + data.data = rawscr; + data.len = 8; + data.flags = MMC_DATA_READ; + + err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); + rawscr[0] = be32toh(rawscr[0]); + rawscr[1] = be32toh(rawscr[1]); + return (err); +} + +static int +mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd) +{ + int err; + struct mmc_command cmd; + struct mmc_data data; + + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + memset(rawextcsd, 0, 512); + cmd.opcode = MMC_SEND_EXT_CSD; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.arg = 0; + cmd.data = &data; + + data.data = rawextcsd; + data.len = 512; + data.flags = MMC_DATA_READ; + + err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); + return (err); +} + +static int +mmc_app_sd_status(struct mmc_softc *sc, uint16_t rca, uint32_t *rawsdstatus) +{ + int err, i; + struct mmc_command cmd; + struct mmc_data data; + + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + memset(rawsdstatus, 0, 64); + cmd.opcode = ACMD_SD_STATUS; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.arg = 0; + cmd.data = &data; + + data.data = rawsdstatus; + data.len = 64; + data.flags = MMC_DATA_READ; + + err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); + for (i = 0; i < 16; i++) + rawsdstatus[i] = be32toh(rawsdstatus[i]); + return (err); +} + +static int +mmc_set_relative_addr(struct mmc_softc *sc, uint16_t resp) +{ + struct mmc_command cmd; + int err; + + cmd.opcode = MMC_SET_RELATIVE_ADDR; + cmd.arg = resp << 16; + cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; + cmd.data = NULL; + err = mmc_wait_for_cmd(sc, &cmd, 0); + return (err); +} + +static int +mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp) +{ + struct mmc_command cmd; + int err; + + cmd.opcode = SD_SEND_RELATIVE_ADDR; + cmd.arg = 0; + cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; + cmd.data = NULL; + err = mmc_wait_for_cmd(sc, &cmd, 0); + *resp = cmd.resp[0]; + return (err); +} + +static int +mmc_send_status(struct mmc_softc *sc, uint16_t rca, uint32_t *status) +{ + struct mmc_command cmd; + int err; + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + cmd.data = NULL; + err = mmc_wait_for_cmd(sc, &cmd, 0); + *status = cmd.resp[0]; + return (err); +} + +static int +mmc_set_blocklen(struct mmc_softc *sc, uint32_t len) +{ + struct mmc_command cmd; + int err; + + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.arg = len; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + cmd.data = NULL; + err = mmc_wait_for_cmd(sc, &cmd, 0); + return (err); +} + +static void +mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard) +{ + device_printf(dev, "Card at relative address %d%s:\n", + ivar->rca, newcard ? " added" : ""); + device_printf(dev, " card: %s\n", ivar->card_id_string); + device_printf(dev, " bus: %ubit, %uMHz%s\n", + (ivar->bus_width == bus_width_1 ? 1 : + (ivar->bus_width == bus_width_4 ? 4 : 8)), + (ivar->timing == bus_timing_hs ? + ivar->hs_tran_speed : ivar->tran_speed) / 1000000, + ivar->timing == bus_timing_hs ? ", high speed timing" : ""); + device_printf(dev, " memory: %u blocks, erase sector %u blocks%s\n", + ivar->sec_count, ivar->erase_sector, + ivar->read_only ? ", read-only" : ""); +} + +static void +mmc_discover_cards(struct mmc_softc *sc) +{ + struct mmc_ivars *ivar = NULL; + device_t *devlist; + int err, i, devcount, newcard; + uint32_t raw_cid[4], resp, sec_count, status; + device_t child; + uint16_t rca = 2; + u_char switch_res[64]; + + if (bootverbose || mmc_debug) + device_printf(sc->dev, "Probing cards\n"); + while (1) { + err = mmc_all_send_cid(sc, raw_cid); + if (err == MMC_ERR_TIMEOUT) + break; + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, "Error reading CID %d\n", err); + break; + } + newcard = 1; + if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) + return; + for (i = 0; i < devcount; i++) { + ivar = device_get_ivars(devlist[i]); + if (memcmp(ivar->raw_cid, raw_cid, sizeof(raw_cid)) == 0) { + newcard = 0; + break; + } + } + free(devlist, M_TEMP); + if (bootverbose || mmc_debug) { + device_printf(sc->dev, "%sard detected (CID %08x%08x%08x%08x)\n", + newcard ? "New c" : "C", + raw_cid[0], raw_cid[1], raw_cid[2], raw_cid[3]); + } + if (newcard) { + ivar = malloc(sizeof(struct mmc_ivars), M_DEVBUF, + M_WAITOK | M_ZERO); + if (!ivar) + return; + memcpy(ivar->raw_cid, raw_cid, sizeof(raw_cid)); + } + if (mmcbr_get_ro(sc->dev)) + ivar->read_only = 1; + ivar->bus_width = bus_width_1; + ivar->timing = bus_timing_normal; + ivar->mode = mmcbr_get_mode(sc->dev); + if (ivar->mode == mode_sd) { + mmc_decode_cid_sd(ivar->raw_cid, &ivar->cid); + mmc_send_relative_addr(sc, &resp); + ivar->rca = resp >> 16; + /* Get card CSD. */ + mmc_send_csd(sc, ivar->rca, ivar->raw_csd); + if (bootverbose || mmc_debug) + device_printf(sc->dev, + "%sard detected (CSD %08x%08x%08x%08x)\n", + newcard ? "New c" : "C", ivar->raw_csd[0], + ivar->raw_csd[1], ivar->raw_csd[2], + ivar->raw_csd[3]); + mmc_decode_csd_sd(ivar->raw_csd, &ivar->csd); + ivar->sec_count = ivar->csd.capacity / MMC_SECTOR_SIZE; + if (ivar->csd.csd_structure > 0) + ivar->high_cap = 1; + ivar->tran_speed = ivar->csd.tran_speed; + ivar->erase_sector = ivar->csd.erase_sector * + ivar->csd.write_bl_len / MMC_SECTOR_SIZE; + + err = mmc_send_status(sc, ivar->rca, &status); + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, + "Error reading card status %d\n", err); + break; + } + if ((status & R1_CARD_IS_LOCKED) != 0) { + device_printf(sc->dev, + "Card is password protected, skipping.\n"); + break; + } + + /* Get card SCR. Card must be selected to fetch it. */ + mmc_select_card(sc, ivar->rca); + mmc_app_send_scr(sc, ivar->rca, ivar->raw_scr); + mmc_app_decode_scr(ivar->raw_scr, &ivar->scr); + /* Get card switch capabilities (command class 10). */ + if ((ivar->scr.sda_vsn >= 1) && + (ivar->csd.ccc & (1<<10))) { + mmc_sd_switch(sc, SD_SWITCH_MODE_CHECK, + SD_SWITCH_GROUP1, SD_SWITCH_NOCHANGE, + switch_res); + if (switch_res[13] & 2) { + ivar->timing = bus_timing_hs; + ivar->hs_tran_speed = SD_MAX_HS; + } + } + mmc_app_sd_status(sc, ivar->rca, ivar->raw_sd_status); + mmc_app_decode_sd_status(ivar->raw_sd_status, + &ivar->sd_status); + if (ivar->sd_status.au_size != 0) { + ivar->erase_sector = + 16 << ivar->sd_status.au_size; + } + mmc_select_card(sc, 0); + /* Find max supported bus width. */ + if ((mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA) && + (ivar->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) + ivar->bus_width = bus_width_4; + + /* + * Some cards that report maximum I/O block sizes + * greater than 512 require the block length to be + * set to 512, even though that is supposed to be + * the default. Example: + * + * Transcend 2GB SDSC card, CID: + * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 + */ + if (ivar->csd.read_bl_len != MMC_SECTOR_SIZE || + ivar->csd.write_bl_len != MMC_SECTOR_SIZE) + mmc_set_blocklen(sc, MMC_SECTOR_SIZE); + + mmc_format_card_id_string(ivar); + + if (bootverbose || mmc_debug) + mmc_log_card(sc->dev, ivar, newcard); + if (newcard) { + /* Add device. */ + child = device_add_child(sc->dev, NULL, -1); + device_set_ivars(child, ivar); + } + return; + } + mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid); + ivar->rca = rca++; + mmc_set_relative_addr(sc, ivar->rca); + /* Get card CSD. */ + mmc_send_csd(sc, ivar->rca, ivar->raw_csd); + if (bootverbose || mmc_debug) + device_printf(sc->dev, + "%sard detected (CSD %08x%08x%08x%08x)\n", + newcard ? "New c" : "C", ivar->raw_csd[0], + ivar->raw_csd[1], ivar->raw_csd[2], + ivar->raw_csd[3]); + + mmc_decode_csd_mmc(ivar->raw_csd, &ivar->csd); + ivar->sec_count = ivar->csd.capacity / MMC_SECTOR_SIZE; + ivar->tran_speed = ivar->csd.tran_speed; + ivar->erase_sector = ivar->csd.erase_sector * + ivar->csd.write_bl_len / MMC_SECTOR_SIZE; + + err = mmc_send_status(sc, ivar->rca, &status); + if (err != MMC_ERR_NONE) { + device_printf(sc->dev, + "Error reading card status %d\n", err); + break; + } + if ((status & R1_CARD_IS_LOCKED) != 0) { + device_printf(sc->dev, + "Card is password protected, skipping.\n"); + break; + } + + /* Only MMC >= 4.x cards support EXT_CSD. */ + if (ivar->csd.spec_vers >= 4) { + /* Card must be selected to fetch EXT_CSD. */ + mmc_select_card(sc, ivar->rca); + mmc_send_ext_csd(sc, ivar->raw_ext_csd); + /* Handle extended capacity from EXT_CSD */ + sec_count = ivar->raw_ext_csd[EXT_CSD_SEC_CNT] + + (ivar->raw_ext_csd[EXT_CSD_SEC_CNT + 1] << 8) + + (ivar->raw_ext_csd[EXT_CSD_SEC_CNT + 2] << 16) + + (ivar->raw_ext_csd[EXT_CSD_SEC_CNT + 3] << 24); + if (sec_count != 0) { + ivar->sec_count = sec_count; + ivar->high_cap = 1; + } + /* Get card speed in high speed mode. */ + ivar->timing = bus_timing_hs; + if (ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] + & EXT_CSD_CARD_TYPE_52) + ivar->hs_tran_speed = MMC_TYPE_52_MAX_HS; + else if (ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] + & EXT_CSD_CARD_TYPE_26) + ivar->hs_tran_speed = MMC_TYPE_26_MAX_HS; + else + ivar->hs_tran_speed = ivar->tran_speed; + /* Find max supported bus width. */ + ivar->bus_width = mmc_test_bus_width(sc); + mmc_select_card(sc, 0); + /* Handle HC erase sector size. */ + if (ivar->raw_ext_csd[EXT_CSD_ERASE_GRP_SIZE] != 0) { + ivar->erase_sector = 1024 * + ivar->raw_ext_csd[EXT_CSD_ERASE_GRP_SIZE]; + mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_ERASE_GRP_DEF, 1); + } + } else { + ivar->bus_width = bus_width_1; + ivar->timing = bus_timing_normal; + } + + /* + * Some cards that report maximum I/O block sizes greater + * than 512 require the block length to be set to 512, even + * though that is supposed to be the default. Example: + * + * Transcend 2GB SDSC card, CID: + * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000 + */ + if (ivar->csd.read_bl_len != MMC_SECTOR_SIZE || + ivar->csd.write_bl_len != MMC_SECTOR_SIZE) + mmc_set_blocklen(sc, MMC_SECTOR_SIZE); + + mmc_format_card_id_string(ivar); + + if (bootverbose || mmc_debug) + mmc_log_card(sc->dev, ivar, newcard); + if (newcard) { + /* Add device. */ + child = device_add_child(sc->dev, NULL, -1); + device_set_ivars(child, ivar); + } + } +} + +static void +mmc_rescan_cards(struct mmc_softc *sc) +{ + struct mmc_ivars *ivar = NULL; + device_t *devlist; + int err, i, devcount; + + if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) + return; + for (i = 0; i < devcount; i++) { + ivar = device_get_ivars(devlist[i]); + if (mmc_select_card(sc, ivar->rca)) { + if (bootverbose || mmc_debug) + device_printf(sc->dev, "Card at relative address %d lost.\n", + ivar->rca); + device_delete_child(sc->dev, devlist[i]); + free(ivar, M_DEVBUF); + } + } + free(devlist, M_TEMP); + mmc_select_card(sc, 0); +} + +static int +mmc_delete_cards(struct mmc_softc *sc) +{ + struct mmc_ivars *ivar; + device_t *devlist; + int err, i, devcount; + + if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) + return (err); + for (i = 0; i < devcount; i++) { + ivar = device_get_ivars(devlist[i]); + if (bootverbose || mmc_debug) + device_printf(sc->dev, "Card at relative address %d deleted.\n", + ivar->rca); + device_delete_child(sc->dev, devlist[i]); + free(ivar, M_DEVBUF); + } + free(devlist, M_TEMP); + return (0); +} + +static void +mmc_go_discovery(struct mmc_softc *sc) +{ + uint32_t ocr; + device_t dev; + int err; + + dev = sc->dev; + if (mmcbr_get_power_mode(dev) != power_on) { + /* + * First, try SD modes + */ + mmcbr_set_mode(dev, mode_sd); + mmc_power_up(sc); + mmcbr_set_bus_mode(dev, pushpull); + if (bootverbose || mmc_debug) + device_printf(sc->dev, "Probing bus\n"); + mmc_idle_cards(sc); + err = mmc_send_if_cond(sc, 1); + if ((bootverbose || mmc_debug) && err == 0) + device_printf(sc->dev, "SD 2.0 interface conditions: OK\n"); + if (mmc_send_app_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) { + if (bootverbose || mmc_debug) + device_printf(sc->dev, "SD probe: failed\n"); + /* + * Failed, try MMC + */ + mmcbr_set_mode(dev, mode_mmc); + if (mmc_send_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) { + if (bootverbose || mmc_debug) + device_printf(sc->dev, "MMC probe: failed\n"); + ocr = 0; /* Failed both, powerdown. */ + } else if (bootverbose || mmc_debug) + device_printf(sc->dev, + "MMC probe: OK (OCR: 0x%08x)\n", ocr); + } else if (bootverbose || mmc_debug) + device_printf(sc->dev, "SD probe: OK (OCR: 0x%08x)\n", ocr); + + mmcbr_set_ocr(dev, mmc_select_vdd(sc, ocr)); + if (mmcbr_get_ocr(dev) != 0) + mmc_idle_cards(sc); + } else { + mmcbr_set_bus_mode(dev, opendrain); + mmcbr_set_clock(dev, mmcbr_get_f_min(dev)); + mmcbr_update_ios(dev); + /* XXX recompute vdd based on new cards? */ + } + /* + * Make sure that we have a mutually agreeable voltage to at least + * one card on the bus. + */ + if (bootverbose || mmc_debug) + device_printf(sc->dev, "Current OCR: 0x%08x\n", mmcbr_get_ocr(dev)); + if (mmcbr_get_ocr(dev) == 0) { + mmc_delete_cards(sc); + mmc_power_down(sc); + return; + } + /* + * Reselect the cards after we've idled them above. + */ + if (mmcbr_get_mode(dev) == mode_sd) { + err = mmc_send_if_cond(sc, 1); + mmc_send_app_op_cond(sc, + (err ? 0 : MMC_OCR_CCS) | mmcbr_get_ocr(dev), NULL); + } else + mmc_send_op_cond(sc, mmcbr_get_ocr(dev), NULL); + mmc_discover_cards(sc); + mmc_rescan_cards(sc); + + mmcbr_set_bus_mode(dev, pushpull); + mmcbr_update_ios(dev); + mmc_calculate_clock(sc); + bus_generic_attach(dev); +/* mmc_update_children_sysctl(dev);*/ +} + +static int +mmc_calculate_clock(struct mmc_softc *sc) +{ + int max_dtr, max_hs_dtr, max_timing; + int nkid, i, f_min, f_max; + device_t *kids; + struct mmc_ivars *ivar; + + f_min = mmcbr_get_f_min(sc->dev); + f_max = mmcbr_get_f_max(sc->dev); + max_dtr = max_hs_dtr = f_max; + if ((mmcbr_get_caps(sc->dev) & MMC_CAP_HSPEED)) + max_timing = bus_timing_hs; + else + max_timing = bus_timing_normal; + if (device_get_children(sc->dev, &kids, &nkid) != 0) + panic("can't get children"); + for (i = 0; i < nkid; i++) { + ivar = device_get_ivars(kids[i]); + if (ivar->timing < max_timing) + max_timing = ivar->timing; + if (ivar->tran_speed < max_dtr) + max_dtr = ivar->tran_speed; + if (ivar->hs_tran_speed < max_hs_dtr) + max_hs_dtr = ivar->hs_tran_speed; + } + for (i = 0; i < nkid; i++) { + ivar = device_get_ivars(kids[i]); + if (ivar->timing == bus_timing_normal) + continue; + mmc_select_card(sc, ivar->rca); + mmc_set_timing(sc, max_timing); + } + mmc_select_card(sc, 0); + free(kids, M_TEMP); + if (max_timing == bus_timing_hs) + max_dtr = max_hs_dtr; + if (bootverbose || mmc_debug) { + device_printf(sc->dev, + "setting transfer rate to %d.%03dMHz%s\n", + max_dtr / 1000000, (max_dtr / 1000) % 1000, + max_timing == bus_timing_hs ? " (high speed timing)" : ""); + } + mmcbr_set_timing(sc->dev, max_timing); + mmcbr_set_clock(sc->dev, max_dtr); + mmcbr_update_ios(sc->dev); + return max_dtr; +} + +static void +mmc_scan(struct mmc_softc *sc) +{ + device_t dev = sc->dev; + + mmc_acquire_bus(dev, dev); + mmc_go_discovery(sc); + mmc_release_bus(dev, dev); +} + +static int +mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) +{ + struct mmc_ivars *ivar = device_get_ivars(child); + + switch (which) { + default: + return (EINVAL); + case MMC_IVAR_DSR_IMP: + *result = ivar->csd.dsr_imp; + break; + case MMC_IVAR_MEDIA_SIZE: + *result = ivar->sec_count; + break; + case MMC_IVAR_RCA: + *result = ivar->rca; + break; + case MMC_IVAR_SECTOR_SIZE: + *result = MMC_SECTOR_SIZE; + break; + case MMC_IVAR_TRAN_SPEED: + *result = mmcbr_get_clock(bus); + break; + case MMC_IVAR_READ_ONLY: + *result = ivar->read_only; + break; + case MMC_IVAR_HIGH_CAP: + *result = ivar->high_cap; + break; + case MMC_IVAR_CARD_TYPE: + *result = ivar->mode; + break; + case MMC_IVAR_BUS_WIDTH: + *result = ivar->bus_width; + break; + case MMC_IVAR_ERASE_SECTOR: + *result = ivar->erase_sector; + break; + case MMC_IVAR_MAX_DATA: + *result = mmcbr_get_max_data(bus); + break; + case MMC_IVAR_CARD_ID_STRING: + *(char **)result = ivar->card_id_string; + break; + } + return (0); +} + +static int +mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) +{ + /* + * None are writable ATM + */ + return (EINVAL); +} + +static void +mmc_delayed_attach(void *xsc) +{ + struct mmc_softc *sc = xsc; + + mmc_scan(sc); + config_intrhook_disestablish(&sc->config_intrhook); +} + +static int +mmc_child_location_str(device_t dev, device_t child, char *buf, + size_t buflen) +{ + + snprintf(buf, buflen, "rca=0x%04x", mmc_get_rca(child)); + return (0); +} + +static device_method_t mmc_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, mmc_probe), + DEVMETHOD(device_attach, mmc_attach), + DEVMETHOD(device_detach, mmc_detach), + DEVMETHOD(device_suspend, mmc_suspend), + DEVMETHOD(device_resume, mmc_resume), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, mmc_read_ivar), + DEVMETHOD(bus_write_ivar, mmc_write_ivar), + DEVMETHOD(bus_child_location_str, mmc_child_location_str), + + /* MMC Bus interface */ + DEVMETHOD(mmcbus_wait_for_request, mmc_wait_for_request), + DEVMETHOD(mmcbus_acquire_bus, mmc_acquire_bus), + DEVMETHOD(mmcbus_release_bus, mmc_release_bus), + + DEVMETHOD_END +}; + +static driver_t mmc_driver = { + "mmc", + mmc_methods, + sizeof(struct mmc_softc), +}; +static devclass_t mmc_devclass; + +DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, NULL, NULL); +DRIVER_MODULE(mmc, dw_mmc, mmc_driver, mmc_devclass, NULL, NULL); +DRIVER_MODULE(mmc, sdhci, mmc_driver, mmc_devclass, NULL, NULL); diff --git a/freebsd/sys/dev/mmc/mmcbrvar.h b/freebsd/sys/dev/mmc/mmcbrvar.h new file mode 100644 index 00000000..1f0a5714 --- /dev/null +++ b/freebsd/sys/dev/mmc/mmcbrvar.h @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 2006 Bernd Walter. All rights reserved. + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * "$FreeBSD$" + */ + +#ifndef DEV_MMC_MMCBRVAR_H +#define DEV_MMC_MMCBRVAR_H + +#include <dev/mmc/bridge.h> +#include <dev/mmc/mmcreg.h> +#include <rtems/bsd/local/mmcbr_if.h> + +enum mmcbr_device_ivars { + MMCBR_IVAR_BUS_MODE, + MMCBR_IVAR_BUS_WIDTH, + MMCBR_IVAR_CHIP_SELECT, + MMCBR_IVAR_CLOCK, + MMCBR_IVAR_F_MIN, + MMCBR_IVAR_F_MAX, + MMCBR_IVAR_HOST_OCR, + MMCBR_IVAR_MODE, + MMCBR_IVAR_OCR, + MMCBR_IVAR_POWER_MODE, + MMCBR_IVAR_VDD, + MMCBR_IVAR_CAPS, + MMCBR_IVAR_TIMING, + MMCBR_IVAR_MAX_DATA +}; + +/* + * Simplified accessors for pci devices + */ +#define MMCBR_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(mmcbr, var, MMCBR, ivar, type) + +MMCBR_ACCESSOR(bus_mode, BUS_MODE, int) +MMCBR_ACCESSOR(bus_width, BUS_WIDTH, int) +MMCBR_ACCESSOR(chip_select, CHIP_SELECT, int) +MMCBR_ACCESSOR(clock, CLOCK, int) +MMCBR_ACCESSOR(f_max, F_MAX, int) +MMCBR_ACCESSOR(f_min, F_MIN, int) +MMCBR_ACCESSOR(host_ocr, HOST_OCR, int) +MMCBR_ACCESSOR(mode, MODE, int) +MMCBR_ACCESSOR(ocr, OCR, int) +MMCBR_ACCESSOR(power_mode, POWER_MODE, int) +MMCBR_ACCESSOR(vdd, VDD, int) +MMCBR_ACCESSOR(caps, CAPS, int) +MMCBR_ACCESSOR(timing, TIMING, int) +MMCBR_ACCESSOR(max_data, MAX_DATA, int) + +static int __inline +mmcbr_update_ios(device_t dev) +{ + return (MMCBR_UPDATE_IOS(device_get_parent(dev), dev)); +} + +static int __inline +mmcbr_get_ro(device_t dev) +{ + return (MMCBR_GET_RO(device_get_parent(dev), dev)); +} + +#endif /* DEV_MMC_MMCBRVAR_H */ diff --git a/freebsd/sys/dev/mmc/mmcreg.h b/freebsd/sys/dev/mmc/mmcreg.h new file mode 100644 index 00000000..3d2b5691 --- /dev/null +++ b/freebsd/sys/dev/mmc/mmcreg.h @@ -0,0 +1,443 @@ +/*- + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * $FreeBSD$ + */ + +#ifndef DEV_MMC_MMCREG_H +#define DEV_MMC_MMCREG_H + +/* + * This file contains the register definitions for the mmc and sd busses. + * They are taken from publicly available sources. + */ + +struct mmc_data; +struct mmc_request; + +struct mmc_command { + uint32_t opcode; + uint32_t arg; + uint32_t resp[4]; + uint32_t flags; /* Expected responses */ +#define MMC_RSP_PRESENT (1ul << 0) /* Response */ +#define MMC_RSP_136 (1ul << 1) /* 136 bit response */ +#define MMC_RSP_CRC (1ul << 2) /* Expect valid crc */ +#define MMC_RSP_BUSY (1ul << 3) /* Card may send busy */ +#define MMC_RSP_OPCODE (1ul << 4) /* Response include opcode */ +#define MMC_RSP_MASK 0x1ful +#define MMC_CMD_AC (0ul << 5) /* Addressed Command, no data */ +#define MMC_CMD_ADTC (1ul << 5) /* Addressed Data transfer cmd */ +#define MMC_CMD_BC (2ul << 5) /* Broadcast command, no response */ +#define MMC_CMD_BCR (3ul << 5) /* Broadcast command with response */ +#define MMC_CMD_MASK (3ul << 5) + +/* Possible response types defined in the standard: */ +#define MMC_RSP_NONE (0) +#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE) +#define MMC_RSP_R1B (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY) +#define MMC_RSP_R2 (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC) +#define MMC_RSP_R3 (MMC_RSP_PRESENT) +#define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC) +#define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC) +#define MMC_RSP(x) ((x) & MMC_RSP_MASK) + uint32_t retries; + uint32_t error; +#define MMC_ERR_NONE 0 +#define MMC_ERR_TIMEOUT 1 +#define MMC_ERR_BADCRC 2 +#define MMC_ERR_FIFO 3 +#define MMC_ERR_FAILED 4 +#define MMC_ERR_INVALID 5 +#define MMC_ERR_NO_MEMORY 6 +#define MMC_ERR_MAX 6 + struct mmc_data *data; /* Data segment with cmd */ + struct mmc_request *mrq; /* backpointer to request */ +}; + +/* + * R1 responses + * + * Types (per SD 2.0 standard) + * e : error bit + * s : status bit + * r : detected and set for the actual command response + * x : Detected and set during command execution. The host can get + * the status by issuing a command with R1 response. + * + * Clear Condition (per SD 2.0 standard) + * a : according to the card current state. + * b : always related to the previous command. reception of a valid + * command will clear it (with a delay of one command). + * c : clear by read + */ +#define R1_OUT_OF_RANGE (1u << 31) /* erx, c */ +#define R1_ADDRESS_ERROR (1u << 30) /* erx, c */ +#define R1_BLOCK_LEN_ERROR (1u << 29) /* erx, c */ +#define R1_ERASE_SEQ_ERROR (1u << 28) /* er, c */ +#define R1_ERASE_PARAM (1u << 27) /* erx, c */ +#define R1_WP_VIOLATION (1u << 26) /* erx, c */ +#define R1_CARD_IS_LOCKED (1u << 25) /* sx, a */ +#define R1_LOCK_UNLOCK_FAILED (1u << 24) /* erx, c */ +#define R1_COM_CRC_ERROR (1u << 23) /* er, b */ +#define R1_ILLEGAL_COMMAND (1u << 22) /* er, b */ +#define R1_CARD_ECC_FAILED (1u << 21) /* erx, c */ +#define R1_CC_ERROR (1u << 20) /* erx, c */ +#define R1_ERROR (1u << 19) /* erx, c */ +#define R1_CSD_OVERWRITE (1u << 16) /* erx, c */ +#define R1_WP_ERASE_SKIP (1u << 15) /* erx, c */ +#define R1_CARD_ECC_DISABLED (1u << 14) /* sx, a */ +#define R1_ERASE_RESET (1u << 13) /* sr, c */ +#define R1_CURRENT_STATE_MASK (0xfu << 9) /* sx, b */ +#define R1_READY_FOR_DATA (1u << 8) /* sx, a */ +#define R1_APP_CMD (1u << 5) /* sr, c */ +#define R1_AKE_SEQ_ERROR (1u << 3) /* er, c */ +#define R1_STATUS(x) ((x) & 0xFFFFE000) +#define R1_CURRENT_STATE(x) (((x) & R1_CURRENT_STATE_MASK) >> 9) +#define R1_STATE_IDLE 0 +#define R1_STATE_READY 1 +#define R1_STATE_IDENT 2 +#define R1_STATE_STBY 3 +#define R1_STATE_TRAN 4 +#define R1_STATE_DATA 5 +#define R1_STATE_RCV 6 +#define R1_STATE_PRG 7 +#define R1_STATE_DIS 8 + +struct mmc_data { + size_t len; /* size of the data */ + size_t xfer_len; + void *data; /* data buffer */ + uint32_t flags; +#define MMC_DATA_WRITE (1UL << 0) +#define MMC_DATA_READ (1UL << 1) +#define MMC_DATA_STREAM (1UL << 2) +#define MMC_DATA_MULTI (1UL << 3) + struct mmc_request *mrq; +}; + +struct mmc_request { + struct mmc_command *cmd; + struct mmc_command *stop; + void (*done)(struct mmc_request *); /* Completion function */ + void *done_data; /* requestor set data */ + uint32_t flags; +#define MMC_REQ_DONE 1 +}; + +/* Command definitions */ + +/* Class 0 and 1: Basic commands & read stream commands */ +#define MMC_GO_IDLE_STATE 0 +#define MMC_SEND_OP_COND 1 +#define MMC_ALL_SEND_CID 2 +#define MMC_SET_RELATIVE_ADDR 3 +#define SD_SEND_RELATIVE_ADDR 3 +#define MMC_SET_DSR 4 + /* reserved: 5 */ +#define MMC_SWITCH_FUNC 6 +#define MMC_SWITCH_FUNC_CMDS 0 +#define MMC_SWITCH_FUNC_SET 1 +#define MMC_SWITCH_FUNC_CLR 2 +#define MMC_SWITCH_FUNC_WR 3 +#define MMC_SELECT_CARD 7 +#define MMC_DESELECT_CARD 7 +#define MMC_SEND_EXT_CSD 8 +#define SD_SEND_IF_COND 8 +#define MMC_SEND_CSD 9 +#define MMC_SEND_CID 10 +#define MMC_READ_DAT_UNTIL_STOP 11 +#define MMC_STOP_TRANSMISSION 12 +#define MMC_SEND_STATUS 13 +#define MMC_BUSTEST_R 14 +#define MMC_GO_INACTIVE_STATE 15 +#define MMC_BUSTEST_W 19 + +/* Class 2: Block oriented read commands */ +#define MMC_SET_BLOCKLEN 16 +#define MMC_READ_SINGLE_BLOCK 17 +#define MMC_READ_MULTIPLE_BLOCK 18 + /* reserved: 19 */ + +/* Class 3: Stream write commands */ +#define MMC_WRITE_DAT_UNTIL_STOP 20 + /* reserved: 21 */ + /* reserved: 22 */ + +/* Class 4: Block oriented write commands */ +#define MMC_SET_BLOCK_COUNT 23 +#define MMC_WRITE_BLOCK 24 +#define MMC_WRITE_MULTIPLE_BLOCK 25 +#define MMC_PROGARM_CID 26 +#define MMC_PROGRAM_CSD 27 + +/* Class 6: Block oriented write protection commands */ +#define MMC_SET_WRITE_PROT 28 +#define MMC_CLR_WRITE_PROT 29 +#define MMC_SEND_WRITE_PROT 30 + /* reserved: 31 */ + +/* Class 5: Erase commands */ +#define SD_ERASE_WR_BLK_START 32 +#define SD_ERASE_WR_BLK_END 33 + /* 34 -- reserved old command */ +#define MMC_ERASE_GROUP_START 35 +#define MMC_ERASE_GROUP_END 36 + /* 37 -- reserved old command */ +#define MMC_ERASE 38 + +/* Class 9: I/O mode commands */ +#define MMC_FAST_IO 39 +#define MMC_GO_IRQ_STATE 40 + /* reserved: 41 */ + +/* Class 7: Lock card */ +#define MMC_LOCK_UNLOCK 42 + /* reserved: 43 */ + /* reserved: 44 */ + /* reserved: 45 */ + /* reserved: 46 */ + /* reserved: 47 */ + /* reserved: 48 */ + /* reserved: 49 */ + /* reserved: 50 */ + /* reserved: 51 */ + /* reserved: 54 */ + +/* Class 8: Application specific commands */ +#define MMC_APP_CMD 55 +#define MMC_GEN_CMD 56 + /* reserved: 57 */ + /* reserved: 58 */ + /* reserved: 59 */ + /* reserved for mfg: 60 */ + /* reserved for mfg: 61 */ + /* reserved for mfg: 62 */ + /* reserved for mfg: 63 */ + +/* Class 9: I/O cards (sd) */ +#define SD_IO_RW_DIRECT 52 +#define SD_IO_RW_EXTENDED 53 + +/* Class 10: Switch function commands */ +#define SD_SWITCH_FUNC 6 + /* reserved: 34 */ + /* reserved: 35 */ + /* reserved: 36 */ + /* reserved: 37 */ + /* reserved: 50 */ + /* reserved: 57 */ + + +/* Application specific commands for SD */ +#define ACMD_SET_BUS_WIDTH 6 +#define ACMD_SD_STATUS 13 +#define ACMD_SEND_NUM_WR_BLOCKS 22 +#define ACMD_SET_WR_BLK_ERASE_COUNT 23 +#define ACMD_SD_SEND_OP_COND 41 +#define ACMD_SET_CLR_CARD_DETECT 42 +#define ACMD_SEND_SCR 51 + +/* + * EXT_CSD fields + */ +#define EXT_CSD_ERASE_GRP_DEF 175 /* R/W */ +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_REV 192 /* RO */ +#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ +#define EXT_CSD_ERASE_TO_MULT 223 /* RO */ +#define EXT_CSD_ERASE_GRP_SIZE 224 /* RO */ + +/* + * EXT_CSD field definitions + */ +#define EXT_CSD_CMD_SET_NORMAL 1 +#define EXT_CSD_CMD_SET_SECURE 2 +#define EXT_CSD_CMD_SET_CPSECURE 4 + +#define EXT_CSD_CARD_TYPE_26 1 +#define EXT_CSD_CARD_TYPE_52 2 + +#define EXT_CSD_BUS_WIDTH_1 0 +#define EXT_CSD_BUS_WIDTH_4 1 +#define EXT_CSD_BUS_WIDTH_8 2 + +#define MMC_TYPE_26_MAX_HS 26000000 +#define MMC_TYPE_52_MAX_HS 52000000 + +/* + * SD bus widths + */ +#define SD_BUS_WIDTH_1 0 +#define SD_BUS_WIDTH_4 2 + +/* + * SD Switch + */ +#define SD_SWITCH_MODE_CHECK 0 +#define SD_SWITCH_MODE_SET 1 +#define SD_SWITCH_GROUP1 0 +#define SD_SWITCH_NORMAL_MODE 0 +#define SD_SWITCH_HS_MODE 1 +#define SD_SWITCH_NOCHANGE 0xF + +#define SD_CLR_CARD_DETECT 0 +#define SD_SET_CARD_DETECT 1 + +#define SD_MAX_HS 50000000 + +/* OCR bits */ + +/* + * in SD 2.0 spec, bits 8-14 are now marked reserved + * Low voltage in SD2.0 spec is bit 7, TBD voltage + * Low voltage in MC 3.31 spec is bit 7, 1.65-1.95V + * Specs prior to MMC 3.31 defined bits 0-7 as voltages down to 1.5V. + * 3.31 redefined them to be reserved and also said that cards had to + * support the 2.7-3.6V and fixed the OCR to be 0xfff8000 for high voltage + * cards. MMC 4.0 says that a dual voltage card responds with 0xfff8080. + * Looks like the fine-grained control of the voltage tolerance ranges + * was abandoned. + * + * The MMC_OCR_CCS appears to be valid for only SD cards. + */ +#define MMC_OCR_VOLTAGE 0x3fffffffU /* Vdd Voltage mask */ +#define MMC_OCR_LOW_VOLTAGE (1u << 7) /* Low Voltage Range -- tbd */ +#define MMC_OCR_200_210 (1U << 8) /* Vdd voltage 2.00 ~ 2.10 */ +#define MMC_OCR_210_220 (1U << 9) /* Vdd voltage 2.10 ~ 2.20 */ +#define MMC_OCR_220_230 (1U << 10) /* Vdd voltage 2.20 ~ 2.30 */ +#define MMC_OCR_230_240 (1U << 11) /* Vdd voltage 2.30 ~ 2.40 */ +#define MMC_OCR_240_250 (1U << 12) /* Vdd voltage 2.40 ~ 2.50 */ +#define MMC_OCR_250_260 (1U << 13) /* Vdd voltage 2.50 ~ 2.60 */ +#define MMC_OCR_260_270 (1U << 14) /* Vdd voltage 2.60 ~ 2.70 */ +#define MMC_OCR_270_280 (1U << 15) /* Vdd voltage 2.70 ~ 2.80 */ +#define MMC_OCR_280_290 (1U << 16) /* Vdd voltage 2.80 ~ 2.90 */ +#define MMC_OCR_290_300 (1U << 17) /* Vdd voltage 2.90 ~ 3.00 */ +#define MMC_OCR_300_310 (1U << 18) /* Vdd voltage 3.00 ~ 3.10 */ +#define MMC_OCR_310_320 (1U << 19) /* Vdd voltage 3.10 ~ 3.20 */ +#define MMC_OCR_320_330 (1U << 20) /* Vdd voltage 3.20 ~ 3.30 */ +#define MMC_OCR_330_340 (1U << 21) /* Vdd voltage 3.30 ~ 3.40 */ +#define MMC_OCR_340_350 (1U << 22) /* Vdd voltage 3.40 ~ 3.50 */ +#define MMC_OCR_350_360 (1U << 23) /* Vdd voltage 3.50 ~ 3.60 */ +#define MMC_OCR_CCS (1u << 30) /* Card Capacity status (SD vs SDHC) */ +#define MMC_OCR_CARD_BUSY (1U << 31) /* Card Power up status */ + +/* CSD -- decoded structure */ +struct mmc_cid { + uint32_t mid; + char pnm[8]; + uint32_t psn; + uint16_t oid; + uint16_t mdt_year; + uint8_t mdt_month; + uint8_t prv; + uint8_t fwrev; +}; + +struct mmc_csd +{ + uint8_t csd_structure; + uint8_t spec_vers; + uint16_t ccc; + uint16_t tacc; + uint32_t nsac; + uint32_t r2w_factor; + uint32_t tran_speed; + uint32_t read_bl_len; + uint32_t write_bl_len; + uint32_t vdd_r_curr_min; + uint32_t vdd_r_curr_max; + uint32_t vdd_w_curr_min; + uint32_t vdd_w_curr_max; + uint32_t wp_grp_size; + uint32_t erase_sector; + uint64_t capacity; + unsigned int read_bl_partial:1, + read_blk_misalign:1, + write_bl_partial:1, + write_blk_misalign:1, + dsr_imp:1, + erase_blk_en:1, + wp_grp_enable:1; +}; + +struct mmc_scr +{ + unsigned char sda_vsn; + unsigned char bus_widths; +#define SD_SCR_BUS_WIDTH_1 (1<<0) +#define SD_SCR_BUS_WIDTH_4 (1<<2) +}; + +struct mmc_sd_status +{ + uint8_t bus_width; + uint8_t secured_mode; + uint16_t card_type; + uint16_t prot_area; + uint8_t speed_class; + uint8_t perf_move; + uint8_t au_size; + uint16_t erase_size; + uint8_t erase_timeout; + uint8_t erase_offset; +}; + +/* + * Older versions of the MMC standard had a variable sector size. However, + * I've been able to find no old MMC or SD cards that have a non 512 + * byte sector size anywhere, so we assume that such cards are very rare + * and only note their existance in passing here... + */ +#define MMC_SECTOR_SIZE 512 + +#endif /* DEV_MMCREG_H */ diff --git a/freebsd/sys/dev/mmc/mmcsd.c b/freebsd/sys/dev/mmc/mmcsd.c new file mode 100644 index 00000000..42c45cf7 --- /dev/null +++ b/freebsd/sys/dev/mmc/mmcsd.c @@ -0,0 +1,801 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2006 Bernd Walter. All rights reserved. + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/bio.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <geom/geom_disk.h> + +#include <dev/mmc/mmcbrvar.h> +#include <dev/mmc/mmcvar.h> +#include <dev/mmc/mmcreg.h> + +#include <rtems/bsd/local/mmcbus_if.h> +#ifdef __rtems__ +#include <machine/rtems-bsd-support.h> +#include <rtems/bdbuf.h> +#include <rtems/diskdevs.h> +#include <rtems/libio.h> +#include <rtems/media.h> +#endif /* __rtems__ */ + +#if __FreeBSD_version < 800002 +#define kproc_create kthread_create +#define kproc_exit kthread_exit +#endif + +struct mmcsd_softc { + device_t dev; + struct mtx sc_mtx; +#ifndef __rtems__ + struct disk *disk; + struct proc *p; + struct bio_queue_head bio_queue; + daddr_t eblock, eend; /* Range remaining after the last erase. */ + int running; + int suspend; +#endif /* __rtems__ */ +}; + +/* bus entry points */ +static int mmcsd_attach(device_t dev); +static int mmcsd_detach(device_t dev); +static int mmcsd_probe(device_t dev); + +#ifndef __rtems__ +/* disk routines */ +static int mmcsd_close(struct disk *dp); +static int mmcsd_dump(void *arg, void *virtual, vm_offset_t physical, + off_t offset, size_t length); +static int mmcsd_open(struct disk *dp); +static void mmcsd_strategy(struct bio *bp); +static void mmcsd_task(void *arg); +#endif /* __rtems__ */ + +static int mmcsd_bus_bit_width(device_t dev); +#ifndef __rtems__ +static daddr_t mmcsd_delete(struct mmcsd_softc *sc, struct bio *bp); +static daddr_t mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp); +#endif /* __rtems__ */ + +#define MMCSD_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define MMCSD_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define MMCSD_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ + "mmcsd", MTX_DEF) +#define MMCSD_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define MMCSD_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define MMCSD_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +static int +mmcsd_probe(device_t dev) +{ + + device_quiet(dev); + device_set_desc(dev, "MMC/SD Memory Card"); + return (0); +} + +#ifdef __rtems__ +static rtems_status_code +rtems_bsd_mmcsd_set_block_size(struct mmcsd_softc *self, uint32_t block_size) +{ + rtems_status_code status_code = RTEMS_SUCCESSFUL; + device_t dev = self->dev; + struct mmc_command cmd; + struct mmc_request req; + + memset(&req, 0, sizeof(req)); + memset(&cmd, 0, sizeof(cmd)); + + req.cmd = &cmd; + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + cmd.arg = block_size; + MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, + &req); + if (req.cmd->error != MMC_ERR_NONE) { + status_code = RTEMS_IO_ERROR; + } + + return status_code; +} + +static int +rtems_bsd_mmcsd_disk_read_write(struct mmcsd_softc *self, rtems_blkdev_request *blkreq) +{ + rtems_status_code status_code = RTEMS_SUCCESSFUL; + device_t dev = self->dev; + int shift = mmc_get_high_cap(dev) ? 0 : 9; + int rca = mmc_get_rca(dev); + uint32_t buffer_count = blkreq->bufnum; + uint32_t transfer_bytes = blkreq->bufs[0].length; + uint32_t block_count = transfer_bytes / MMC_SECTOR_SIZE; + uint32_t opcode; + uint32_t data_flags; + uint32_t i; + + if (blkreq->req == RTEMS_BLKDEV_REQ_WRITE) { + if (block_count > 1) { + opcode = MMC_WRITE_MULTIPLE_BLOCK; + } else { + opcode = MMC_WRITE_BLOCK; + } + + data_flags = MMC_DATA_WRITE; + } else { + BSD_ASSERT(blkreq->req == RTEMS_BLKDEV_REQ_READ); + + if (block_count > 1) { + opcode = MMC_READ_MULTIPLE_BLOCK; + } else { + opcode = MMC_READ_SINGLE_BLOCK; + } + + data_flags = MMC_DATA_READ; + } + + MMCSD_LOCK(self); + + for (i = 0; i < buffer_count; ++i) { + rtems_blkdev_sg_buffer *sg = &blkreq->bufs [i]; + struct mmc_request req; + struct mmc_command cmd; + struct mmc_command stop; + struct mmc_data data; + rtems_interval timeout; + + memset(&req, 0, sizeof(req)); + memset(&cmd, 0, sizeof(cmd)); + memset(&stop, 0, sizeof(stop)); + + req.cmd = &cmd; + + cmd.opcode = opcode; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.data = &data; + cmd.arg = sg->block << shift; + + if (block_count > 1) { + data_flags |= MMC_DATA_MULTI; + stop.opcode = MMC_STOP_TRANSMISSION; + stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + req.stop = &stop; + } + + data.flags = data_flags;; + data.data = sg->buffer; + data.mrq = &req; + data.len = transfer_bytes; + + MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, + &req); + if (req.cmd->error != MMC_ERR_NONE) { + status_code = RTEMS_IO_ERROR; + goto error; + } + + timeout = rtems_clock_tick_later_usec(250000); + while (1) { + struct mmc_request req2; + struct mmc_command cmd2; + uint32_t status; + + memset(&req2, 0, sizeof(req2)); + memset(&cmd2, 0, sizeof(cmd2)); + + req2.cmd = &cmd2; + + cmd2.opcode = MMC_SEND_STATUS; + cmd2.arg = rca << 16; + cmd2.flags = MMC_RSP_R1 | MMC_CMD_AC; + + MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, + &req2); + if (req2.cmd->error != MMC_ERR_NONE) { + status_code = RTEMS_IO_ERROR; + goto error; + } + + status = cmd2.resp[0]; + if ((status & R1_READY_FOR_DATA) != 0 + && R1_CURRENT_STATE(status) != R1_STATE_PRG) { + break; + } + + if (!rtems_clock_tick_before(timeout)) { + status_code = RTEMS_IO_ERROR; + goto error; + } + } + } + +error: + + MMCSD_UNLOCK(self); + + rtems_blkdev_request_done(blkreq, status_code); + + return 0; +} + +static int +rtems_bsd_mmcsd_disk_ioctl(rtems_disk_device *dd, uint32_t req, void *arg) +{ + struct mmcsd_softc *self = rtems_disk_get_driver_data(dd); + + if (req == RTEMS_BLKIO_REQUEST) { + rtems_blkdev_request *blkreq = arg; + + return rtems_bsd_mmcsd_disk_read_write(self, blkreq); + } else if (req == RTEMS_BLKIO_CAPABILITIES) { + *(uint32_t *) arg = RTEMS_BLKDEV_CAP_MULTISECTOR_CONT; + return 0; + } else { + return rtems_blkdev_ioctl(dd, req, arg); + } +} + +static rtems_status_code +rtems_bsd_mmcsd_attach_worker(rtems_media_state state, const char *src, char **dest, void *arg) +{ + rtems_status_code status_code = RTEMS_SUCCESSFUL; + struct mmcsd_softc *self = arg; + char *disk = NULL; + + if (state == RTEMS_MEDIA_STATE_READY) { + device_t dev = self->dev; + uint32_t block_count = mmc_get_media_size(dev); + uint32_t block_size = MMC_SECTOR_SIZE; + + disk = rtems_media_create_path("/dev", src, device_get_unit(dev)); + if (disk == NULL) { + printf("OOPS: create path failed\n"); + goto error; + } + + MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev); + + status_code = rtems_bsd_mmcsd_set_block_size(self, block_size); + if (status_code != RTEMS_SUCCESSFUL) { + printf("OOPS: set block size failed\n"); + goto error; + } + + status_code = rtems_blkdev_create(disk, block_size, + block_count, rtems_bsd_mmcsd_disk_ioctl, self); + if (status_code != RTEMS_SUCCESSFUL) { + goto error; + } + + *dest = strdup(disk, M_RTEMS_HEAP); + } + + return RTEMS_SUCCESSFUL; + +error: + free(disk, M_RTEMS_HEAP); + + return RTEMS_IO_ERROR; +} +#endif /* __rtems__ */ +static int +mmcsd_attach(device_t dev) +{ + struct mmcsd_softc *sc; +#ifndef __rtems__ + struct disk *d; +#endif /* __rtems__ */ + intmax_t mb; + uint32_t speed; + uint32_t maxblocks; + char unit; + + sc = device_get_softc(dev); + sc->dev = dev; + MMCSD_LOCK_INIT(sc); + +#ifndef __rtems__ + d = sc->disk = disk_alloc(); + d->d_open = mmcsd_open; + d->d_close = mmcsd_close; + d->d_strategy = mmcsd_strategy; + d->d_dump = mmcsd_dump; + d->d_name = "mmcsd"; + d->d_drv1 = sc; + d->d_maxsize = 4*1024*1024; /* Maximum defined SD card AU size. */ + d->d_sectorsize = mmc_get_sector_size(dev); + d->d_mediasize = (off_t)mmc_get_media_size(dev) * d->d_sectorsize; + d->d_stripeoffset = 0; + d->d_stripesize = mmc_get_erase_sector(dev) * d->d_sectorsize; + d->d_unit = device_get_unit(dev); + d->d_flags = DISKFLAG_CANDELETE; + /* + * Display in most natural units. There's no cards < 1MB. + * The SD standard goes to 2GiB, but the data format supports + * up to 4GiB and some card makers push it up to this limit. + * The SDHC standard only goes to 32GiB (the data format in + * SDHC is good to 2TiB however, which isn't too ugly at + * 2048GiBm, so we note it in passing here and don't add the + * code to print TiB). + */ + mb = d->d_mediasize >> 20; /* 1MiB == 1 << 20 */ +#else /* __rtems__ */ + mb = mmc_get_media_size(dev); + mb *= mmc_get_sector_size(dev); + mb >>= 20; +#endif /* __rtems__ */ + unit = 'M'; + if (mb >= 10240) { /* 1GiB = 1024 MiB */ + unit = 'G'; + mb /= 1024; + } + /* + * Report the clock speed of the underlying hardware, which might be + * different than what the card reports due to hardware limitations. + * Report how many blocks the hardware transfers at once, but clip the + * number to MAXPHYS since the system won't initiate larger transfers. + */ + speed = mmcbr_get_clock(device_get_parent(dev)); + maxblocks = mmc_get_max_data(dev); + if (maxblocks > MAXPHYS) + maxblocks = MAXPHYS; + device_printf(dev, "%ju%cB <%s>%s at %s %d.%01dMHz/%dbit/%d-block\n", + mb, unit, mmc_get_card_id_string(dev), + mmc_get_read_only(dev) ? " (read-only)" : "", + device_get_nameunit(device_get_parent(dev)), + speed / 1000000, (speed / 100000) % 10, + mmcsd_bus_bit_width(dev), maxblocks); +#ifndef __rtems__ + disk_create(d, DISK_VERSION); + bioq_init(&sc->bio_queue); + + sc->running = 1; + sc->suspend = 0; + sc->eblock = sc->eend = 0; + kproc_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card"); +#else /* __rtems__ */ + rtems_status_code status_code = rtems_media_server_disk_attach( + device_get_name(dev), + rtems_bsd_mmcsd_attach_worker, + sc + ); + BSD_ASSERT(status_code == RTEMS_SUCCESSFUL); +#endif /* __rtems__ */ + + return (0); +} + +static int +mmcsd_detach(device_t dev) +{ + struct mmcsd_softc *sc = device_get_softc(dev); + +#ifndef __rtems__ + MMCSD_LOCK(sc); + sc->suspend = 0; + if (sc->running > 0) { + /* kill thread */ + sc->running = 0; + wakeup(sc); + /* wait for thread to finish. */ + while (sc->running != -1) + msleep(sc, &sc->sc_mtx, 0, "detach", 0); + } + MMCSD_UNLOCK(sc); + + /* Flush the request queue. */ + bioq_flush(&sc->bio_queue, NULL, ENXIO); + /* kill disk */ + disk_destroy(sc->disk); +#else /* __rtems__ */ + BSD_PANIC("FIXME"); +#endif /* __rtems__ */ + + MMCSD_LOCK_DESTROY(sc); + + return (0); +} + +static int +mmcsd_suspend(device_t dev) +{ +#ifndef __rtems__ + struct mmcsd_softc *sc = device_get_softc(dev); + + MMCSD_LOCK(sc); + sc->suspend = 1; + if (sc->running > 0) { + /* kill thread */ + sc->running = 0; + wakeup(sc); + /* wait for thread to finish. */ + while (sc->running != -1) + msleep(sc, &sc->sc_mtx, 0, "detach", 0); + } + MMCSD_UNLOCK(sc); +#else /* __rtems__ */ + BSD_PANIC("FIXME"); +#endif /* __rtems__ */ + return (0); +} + +static int +mmcsd_resume(device_t dev) +{ +#ifndef __rtems__ + struct mmcsd_softc *sc = device_get_softc(dev); + + MMCSD_LOCK(sc); + sc->suspend = 0; + if (sc->running <= 0) { + sc->running = 1; + MMCSD_UNLOCK(sc); + kproc_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card"); + } else + MMCSD_UNLOCK(sc); +#else /* __rtems__ */ + BSD_PANIC("FIXME"); +#endif /* __rtems__ */ + return (0); +} + +#ifndef __rtems__ +static int +mmcsd_open(struct disk *dp) +{ + + return (0); +} + +static int +mmcsd_close(struct disk *dp) +{ + + return (0); +} + +static void +mmcsd_strategy(struct bio *bp) +{ + struct mmcsd_softc *sc; + + sc = (struct mmcsd_softc *)bp->bio_disk->d_drv1; + MMCSD_LOCK(sc); + if (sc->running > 0 || sc->suspend > 0) { + bioq_disksort(&sc->bio_queue, bp); + MMCSD_UNLOCK(sc); + wakeup(sc); + } else { + MMCSD_UNLOCK(sc); + biofinish(bp, NULL, ENXIO); + } +} + +static daddr_t +mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp) +{ + daddr_t block, end; + struct mmc_command cmd; + struct mmc_command stop; + struct mmc_request req; + struct mmc_data data; + device_t dev = sc->dev; + int sz = sc->disk->d_sectorsize; + + block = bp->bio_pblkno; + end = bp->bio_pblkno + (bp->bio_bcount / sz); + while (block < end) { + char *vaddr = bp->bio_data + + (block - bp->bio_pblkno) * sz; + int numblocks = min(end - block, mmc_get_max_data(dev)); + memset(&req, 0, sizeof(req)); + memset(&cmd, 0, sizeof(cmd)); + memset(&stop, 0, sizeof(stop)); + req.cmd = &cmd; + cmd.data = &data; + if (bp->bio_cmd == BIO_READ) { + if (numblocks > 1) + cmd.opcode = MMC_READ_MULTIPLE_BLOCK; + else + cmd.opcode = MMC_READ_SINGLE_BLOCK; + } else { + if (numblocks > 1) + cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK; + else + cmd.opcode = MMC_WRITE_BLOCK; + } + cmd.arg = block; + if (!mmc_get_high_cap(dev)) + cmd.arg <<= 9; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + data.data = vaddr; + data.mrq = &req; + if (bp->bio_cmd == BIO_READ) + data.flags = MMC_DATA_READ; + else + data.flags = MMC_DATA_WRITE; + data.len = numblocks * sz; + if (numblocks > 1) { + data.flags |= MMC_DATA_MULTI; + stop.opcode = MMC_STOP_TRANSMISSION; + stop.arg = 0; + stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + req.stop = &stop; + } +// printf("Len %d %lld-%lld flags %#x sz %d\n", +// (int)data.len, (long long)block, (long long)end, data.flags, sz); + MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, + &req); + if (req.cmd->error != MMC_ERR_NONE) + break; + block += numblocks; + } + return (block); +} + +static daddr_t +mmcsd_delete(struct mmcsd_softc *sc, struct bio *bp) +{ + daddr_t block, end, start, stop; + struct mmc_command cmd; + struct mmc_request req; + device_t dev = sc->dev; + int sz = sc->disk->d_sectorsize; + int erase_sector; + + block = bp->bio_pblkno; + end = bp->bio_pblkno + (bp->bio_bcount / sz); + /* Coalesce with part remaining from previous request. */ + if (block > sc->eblock && block <= sc->eend) + block = sc->eblock; + if (end >= sc->eblock && end < sc->eend) + end = sc->eend; + /* Safe round to the erase sector boundaries. */ + erase_sector = mmc_get_erase_sector(dev); + start = block + erase_sector - 1; /* Round up. */ + start -= start % erase_sector; + stop = end; /* Round down. */ + stop -= end % erase_sector; + /* We can't erase area smaller then sector, store it for later. */ + if (start >= stop) { + sc->eblock = block; + sc->eend = end; + return (end); + } + + /* Set erase start position. */ + memset(&req, 0, sizeof(req)); + memset(&cmd, 0, sizeof(cmd)); + req.cmd = &cmd; + if (mmc_get_card_type(dev) == mode_sd) + cmd.opcode = SD_ERASE_WR_BLK_START; + else + cmd.opcode = MMC_ERASE_GROUP_START; + cmd.arg = start; + if (!mmc_get_high_cap(dev)) + cmd.arg <<= 9; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, + &req); + if (req.cmd->error != MMC_ERR_NONE) { + printf("erase err1: %d\n", req.cmd->error); + return (block); + } + /* Set erase stop position. */ + memset(&req, 0, sizeof(req)); + memset(&cmd, 0, sizeof(cmd)); + req.cmd = &cmd; + if (mmc_get_card_type(dev) == mode_sd) + cmd.opcode = SD_ERASE_WR_BLK_END; + else + cmd.opcode = MMC_ERASE_GROUP_END; + cmd.arg = stop; + if (!mmc_get_high_cap(dev)) + cmd.arg <<= 9; + cmd.arg--; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, + &req); + if (req.cmd->error != MMC_ERR_NONE) { + printf("erase err2: %d\n", req.cmd->error); + return (block); + } + /* Erase range. */ + memset(&req, 0, sizeof(req)); + memset(&cmd, 0, sizeof(cmd)); + req.cmd = &cmd; + cmd.opcode = MMC_ERASE; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, + &req); + if (req.cmd->error != MMC_ERR_NONE) { + printf("erase err3 %d\n", req.cmd->error); + return (block); + } + /* Store one of remaining parts for the next call. */ + if (bp->bio_pblkno >= sc->eblock || block == start) { + sc->eblock = stop; /* Predict next forward. */ + sc->eend = end; + } else { + sc->eblock = block; /* Predict next backward. */ + sc->eend = start; + } + return (end); +} + +static int +mmcsd_dump(void *arg, void *virtual, vm_offset_t physical, + off_t offset, size_t length) +{ + struct disk *disk = arg; + struct mmcsd_softc *sc = (struct mmcsd_softc *)disk->d_drv1; + device_t dev = sc->dev; + struct bio bp; + daddr_t block, end; + + /* length zero is special and really means flush buffers to media */ + if (!length) + return (0); + + bzero(&bp, sizeof(struct bio)); + bp.bio_disk = disk; + bp.bio_pblkno = offset / disk->d_sectorsize; + bp.bio_bcount = length; + bp.bio_data = virtual; + bp.bio_cmd = BIO_WRITE; + end = bp.bio_pblkno + bp.bio_bcount / sc->disk->d_sectorsize; + MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev); + block = mmcsd_rw(sc, &bp); + MMCBUS_RELEASE_BUS(device_get_parent(dev), dev); + return ((end < block) ? EIO : 0); +} + +static void +mmcsd_task(void *arg) +{ + struct mmcsd_softc *sc = (struct mmcsd_softc*)arg; + struct bio *bp; + int sz; + daddr_t block, end; + device_t dev; + + dev = sc->dev; + while (1) { + MMCSD_LOCK(sc); + do { + if (sc->running == 0) + goto out; + bp = bioq_takefirst(&sc->bio_queue); + if (bp == NULL) + msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0); + } while (bp == NULL); + MMCSD_UNLOCK(sc); + if (bp->bio_cmd != BIO_READ && mmc_get_read_only(dev)) { + bp->bio_error = EROFS; + bp->bio_resid = bp->bio_bcount; + bp->bio_flags |= BIO_ERROR; + biodone(bp); + continue; + } + MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev); + sz = sc->disk->d_sectorsize; + block = bp->bio_pblkno; + end = bp->bio_pblkno + (bp->bio_bcount / sz); + if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) { + /* Access to the remaining erase block obsoletes it. */ + if (block < sc->eend && end > sc->eblock) + sc->eblock = sc->eend = 0; + block = mmcsd_rw(sc, bp); + } else if (bp->bio_cmd == BIO_DELETE) { + block = mmcsd_delete(sc, bp); + } + MMCBUS_RELEASE_BUS(device_get_parent(dev), dev); + if (block < end) { + bp->bio_error = EIO; + bp->bio_resid = (end - block) * sz; + bp->bio_flags |= BIO_ERROR; + } + biodone(bp); + } +out: + /* tell parent we're done */ + sc->running = -1; + MMCSD_UNLOCK(sc); + wakeup(sc); + + kproc_exit(0); +} +#endif /* __rtems__ */ + +static int +mmcsd_bus_bit_width(device_t dev) +{ + + if (mmc_get_bus_width(dev) == bus_width_1) + return (1); + if (mmc_get_bus_width(dev) == bus_width_4) + return (4); + return (8); +} + +static device_method_t mmcsd_methods[] = { + DEVMETHOD(device_probe, mmcsd_probe), + DEVMETHOD(device_attach, mmcsd_attach), + DEVMETHOD(device_detach, mmcsd_detach), + DEVMETHOD(device_suspend, mmcsd_suspend), + DEVMETHOD(device_resume, mmcsd_resume), + DEVMETHOD_END +}; + +static driver_t mmcsd_driver = { + "mmcsd", + mmcsd_methods, + sizeof(struct mmcsd_softc), +}; +static devclass_t mmcsd_devclass; + +DRIVER_MODULE(mmcsd, mmc, mmcsd_driver, mmcsd_devclass, NULL, NULL); diff --git a/freebsd/sys/dev/mmc/mmcvar.h b/freebsd/sys/dev/mmc/mmcvar.h new file mode 100644 index 00000000..99b8a6a5 --- /dev/null +++ b/freebsd/sys/dev/mmc/mmcvar.h @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 2006 Bernd Walter. All rights reserved. + * Copyright (c) 2006 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Portions of this software may have been developed with reference to + * the SD Simplified Specification. The following disclaimer may apply: + * + * The following conditions apply to the release of the simplified + * specification ("Simplified Specification") by the SD Card Association and + * the SD Group. The Simplified Specification is a subset of the complete SD + * Specification which is owned by the SD Card Association and the SD + * Group. This Simplified Specification is provided on a non-confidential + * basis subject to the disclaimers below. Any implementation of the + * Simplified Specification may require a license from the SD Card + * Association, SD Group, SD-3C LLC or other third parties. + * + * Disclaimers: + * + * The information contained in the Simplified Specification is presented only + * as a standard specification for SD Cards and SD Host/Ancillary products and + * is provided "AS-IS" without any representations or warranties of any + * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD + * Card Association for any damages, any infringements of patents or other + * right of the SD Group, SD-3C LLC, the SD Card Association or any third + * parties, which may result from its use. No license is granted by + * implication, estoppel or otherwise under any patent or other rights of the + * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing + * herein shall be construed as an obligation by the SD Group, the SD-3C LLC + * or the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * "$FreeBSD$" + */ + +#ifndef DEV_MMC_MMCVAR_H +#define DEV_MMC_MMCVAR_H + +#include <dev/mmc/bridge.h> + +enum mmc_device_ivars { + MMC_IVAR_DSR_IMP, + MMC_IVAR_MEDIA_SIZE, + MMC_IVAR_RCA, + MMC_IVAR_SECTOR_SIZE, + MMC_IVAR_TRAN_SPEED, + MMC_IVAR_READ_ONLY, + MMC_IVAR_HIGH_CAP, + MMC_IVAR_CARD_TYPE, + MMC_IVAR_BUS_WIDTH, + MMC_IVAR_ERASE_SECTOR, + MMC_IVAR_MAX_DATA, + MMC_IVAR_CARD_ID_STRING +}; + +/* + * Simplified accessors for pci devices + */ +#define MMC_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(mmc, var, MMC, ivar, type) + +MMC_ACCESSOR(dsr_imp, DSR_IMP, int) +#ifndef __rtems__ +MMC_ACCESSOR(media_size, MEDIA_SIZE, off_t) +#else /* __rtems__ */ +/* + * The instance variable value storage is limited by the uintptr_t type. Since + * off_t has more bits than uintptr_t on most RTEMS targets, we need this hack. + */ +MMC_ACCESSOR(media_size, MEDIA_SIZE, uintptr_t) +#endif /* __rtems__ */ +MMC_ACCESSOR(rca, RCA, int) +MMC_ACCESSOR(sector_size, SECTOR_SIZE, int) +MMC_ACCESSOR(tran_speed, TRAN_SPEED, int) +MMC_ACCESSOR(read_only, READ_ONLY, int) +MMC_ACCESSOR(high_cap, HIGH_CAP, int) +MMC_ACCESSOR(card_type, CARD_TYPE, int) +MMC_ACCESSOR(bus_width, BUS_WIDTH, int) +MMC_ACCESSOR(erase_sector, ERASE_SECTOR, int) +MMC_ACCESSOR(max_data, MAX_DATA, int) +MMC_ACCESSOR(card_id_string, CARD_ID_STRING, const char *) + +#endif /* DEV_MMC_MMCVAR_H */ |