From 114b4164ddcb5c7b6b98fbe329d5544ece25ec9f Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Fri, 20 Oct 2006 06:39:59 +0000 Subject: [PATCH] Preliminary MMC stack. This stack supports SD 1.0 cards only, but should be easily adapted to SD 2.0 (aka SDHC), SDIO, MMC and MMCplus cards. At the present time, there's only one bridge driver for the ARM9 based Atmel AT91RM9200. --- sys/conf/files | 4 + sys/dev/mmc/bridge.h | 105 ++++++ sys/dev/mmc/mmc.c | 745 ++++++++++++++++++++++++++++++++++++++++ sys/dev/mmc/mmcbr_if.m | 84 +++++ sys/dev/mmc/mmcbrvar.h | 73 ++++ sys/dev/mmc/mmcbus_if.m | 63 ++++ sys/dev/mmc/mmcreg.h | 326 ++++++++++++++++++ sys/dev/mmc/mmcsd.c | 244 +++++++++++++ sys/dev/mmc/mmcvar.h | 54 +++ 9 files changed, 1698 insertions(+) create mode 100644 sys/dev/mmc/bridge.h create mode 100644 sys/dev/mmc/mmc.c create mode 100644 sys/dev/mmc/mmcbr_if.m create mode 100644 sys/dev/mmc/mmcbrvar.h create mode 100644 sys/dev/mmc/mmcbus_if.m create mode 100644 sys/dev/mmc/mmcreg.h create mode 100644 sys/dev/mmc/mmcsd.c create mode 100644 sys/dev/mmc/mmcvar.h diff --git a/sys/conf/files b/sys/conf/files index ff9bb4a266a8..e255f246a613 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -748,6 +748,10 @@ dev/mlx/mlx.c optional mlx dev/mlx/mlx_disk.c optional mlx dev/mlx/mlx_pci.c optional mlx pci dev/mly/mly.c optional mly +dev/mmc/mmc.c optional mmc +dev/mmc/mmcbr_if.m optional mmc +dev/mmc/mmcbus_if.m optional mmc +dev/mmc/mmcsd.c optional mmcsd dev/mpt/mpt.c optional mpt dev/mpt/mpt_cam.c optional mpt dev/mpt/mpt_debug.c optional mpt diff --git a/sys/dev/mmc/bridge.h b/sys/dev/mmc/bridge.h new file mode 100644 index 000000000000..ddaaed8dc51a --- /dev/null +++ b/sys/dev/mmc/bridge.h @@ -0,0 +1,105 @@ +/*- + * 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. + * + * $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 +}; + +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_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 */ + enum mmc_card_mode mode; + struct mmc_ios ios; /* Current state of the host */ +}; + +#endif /* DEV_MMC_BRIDGE_H */ diff --git a/sys/dev/mmc/mmc.c b/sys/dev/mmc/mmc.c new file mode 100644 index 000000000000..83c13c370494 --- /dev/null +++ b/sys/dev/mmc/mmc.c @@ -0,0 +1,745 @@ +/*- + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "mmcbr_if.h" +#include "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; +}; + +/* + * 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 */ + uint16_t rca; + enum mmc_card_mode mode; + struct mmc_cid cid; /* cid decoded */ + struct mmc_csd csd; /* csd decoded */ +}; + +#define CMD_RETRIES 3 + +/* bus entry points */ +static int mmc_probe(device_t dev); +static int mmc_attach(device_t dev); +static int mmc_detach(device_t dev); + +#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 void mmc_delayed_attach(void *); +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 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); + + /* 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) +{ + return (EBUSY); /* XXX */ +} + +static int +mmc_acquire_bus(device_t busdev, device_t dev) +{ + struct mmc_softc *sc; + int err; + int rca; + + err = MMCBR_ACQUIRE_HOST(device_get_parent(busdev), dev); + if (err) + return (err); + sc = device_get_softc(busdev); + MMC_LOCK(sc); + if (sc->owner) + panic("mmc: host bridge didn't seralize 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. + rca = mmc_get_rca(dev); + if (sc->last_rca != rca) { + mmc_wait_for_command(sc, MMC_SELECT_CARD, rca << 16, + MMC_RSP_R1 | MMC_CMD_AC, NULL, CMD_RETRIES); + sc->last_rca = rca; + } + // XXX should set bus width here? + } else { + // If there's a card selected, stand down. + if (sc->last_rca != 0) { + mmc_wait_for_command(sc, MMC_SELECT_CARD, 0, + MMC_RSP_R1 | MMC_CMD_AC, NULL, CMD_RETRIES); + sc->last_rca = 0; + } + // XXX should set bus width here? + } + + 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), dev); + if (err) + return (err); + MMC_LOCK(sc); + sc->owner = NULL; + MMC_UNLOCK(sc); + return (0); +} + +static void +mmc_rescan_cards(struct mmc_softc *sc) +{ + /* XXX: Look at the children and see if they respond to status */ +} + +static uint32_t +mmc_select_vdd(struct mmc_softc *sc, uint32_t ocr) +{ + // XXX + return ocr; +} + +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; + +// printf("Wakeup for req %p done_data %p\n", req, req->done_data); + sc = (struct mmc_softc *)req->done_data; + MMC_LOCK(sc); + req->flags |= MMC_REQ_DONE; + wakeup(req); + MMC_UNLOCK(sc); +} + +static int +mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req) +{ + int err; + + req->done = mmc_wakeup; + req->done_data = sc; +// printf("Submitting request %p sc %p\n", req, sc); + MMCBR_REQUEST(device_get_parent(sc->dev), sc->dev, req); + MMC_LOCK(sc); + do { + err = msleep(req, &sc->sc_mtx, PZERO | PCATCH, "mmcreq", + hz / 10); + } while (!(req->flags & MMC_REQ_DONE) && err == EAGAIN); +// printf("Request %p done with error %d\n", req, err); + MMC_UNLOCK(sc); + return (err); +} + +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; + cmd->data = NULL; + mreq.cmd = cmd; +// printf("CMD: %x ARG %x\n", cmd->opcode, cmd->arg); + 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; + 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; + 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; + 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; + + for (i = 0; i < 10; 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 == 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; + + for (i = 0; i < 100; 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 == 0) + break; + err = MMC_ERR_TIMEOUT; + mmc_ms_delay(10); + } + if (rocr && err == MMC_ERR_NONE) + *rocr = cmd.resp[0]; + 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_power_mode(dev, power_on); + mmcbr_update_ios(dev); + mmc_ms_delay(2); +} + +// I wonder if the following is endian safe. +static uint32_t +mmc_get_bits(uint32_t *bits, int start, int size) +{ + const int i = 3 - (start / 32); + const int shift = start & 31; + uint32_t retval = bits[i] >> shift; + if (size + shift > 32) + retval |= bits[i - 1] << (32 - shift); + return retval & ((1 << size) - 1); +} + +static void +mmc_decode_cid(int is_sd, uint32_t *raw_cid, struct mmc_cid *cid) +{ + int i; + + memset(cid, 0, sizeof(*cid)); + if (is_sd) { + /* There's no version info, so we take it on faith */ + cid->mid = mmc_get_bits(raw_cid, 120, 8); + cid->oid = mmc_get_bits(raw_cid, 104, 16); + for (i = 0; i < 5; i++) + cid->pnm[i] = mmc_get_bits(raw_cid, 96 - i * 8, 8); + cid->prv = mmc_get_bits(raw_cid, 56, 8); + cid->psn = mmc_get_bits(raw_cid, 24, 32); + cid->mdt_year = mmc_get_bits(raw_cid, 12, 8) + 2001; + cid->mdt_month = mmc_get_bits(raw_cid, 8, 4); + } else { + // XXX write me + panic("write mmc cid decoder"); + } +} + +static const int exp[8] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 +}; +static const int mant[16] = { + 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(int is_sd, uint32_t *raw_csd, struct mmc_csd *csd) +{ + int v; + int m; + int e; + + memset(csd, 0, sizeof(*csd)); + if (is_sd) { + csd->csd_structure = v = mmc_get_bits(raw_csd, 126, 2); + if (v == 0) { + m = mmc_get_bits(raw_csd, 115, 4); + e = mmc_get_bits(raw_csd, 112, 3); + csd->tacc = exp[e] * mant[m] + 9 / 10; + csd->nsac = mmc_get_bits(raw_csd, 104, 8) * 100; + m = mmc_get_bits(raw_csd, 99, 4); + e = mmc_get_bits(raw_csd, 96, 3); + csd->tran_speed = exp[e] * 10000 * mant[m]; + csd->ccc = mmc_get_bits(raw_csd, 84, 12); + csd->read_bl_len = 1 << mmc_get_bits(raw_csd, 80, 4); + csd->read_bl_partial = mmc_get_bits(raw_csd, 79, 1); + csd->write_blk_misalign = mmc_get_bits(raw_csd, 78, 1); + csd->read_blk_misalign = mmc_get_bits(raw_csd, 77, 1); + csd->dsr_imp = mmc_get_bits(raw_csd, 76, 1); + csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 59, 3)]; + csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 56, 3)]; + csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 53, 3)]; + csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 50, 3)]; + m = mmc_get_bits(raw_csd, 62, 12); + e = mmc_get_bits(raw_csd, 47, 3); + csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len; + csd->erase_blk_en = mmc_get_bits(raw_csd, 46, 1); + csd->sector_size = mmc_get_bits(raw_csd, 39, 7); + csd->wp_grp_size = mmc_get_bits(raw_csd, 32, 7); + csd->wp_grp_enable = mmc_get_bits(raw_csd, 31, 1); + csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 26, 3); + csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 22, 4); + csd->write_bl_partial = mmc_get_bits(raw_csd, 21, 1); + } else if (v == 1) { + panic("Write SDHC CSD parser"); + } else + panic("unknown SD CSD version"); + } else { + panic("Write a MMC CSD parser"); + } +} + +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; + 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 *rawcid) +{ + struct mmc_command cmd; + int err; + + cmd.opcode = MMC_SEND_CSD; + cmd.arg = rca << 16; + cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + err = mmc_wait_for_cmd(sc, &cmd, 0); + memcpy(rawcid, cmd.resp, 4 * sizeof(uint32_t)); + 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; + err = mmc_wait_for_cmd(sc, &cmd, 0); + *resp = cmd.resp[0]; + return (err); +} + +static void +mmc_discover_cards(struct mmc_softc *sc) +{ + struct mmc_ivars *ivar; + int err; + uint32_t resp; + device_t child; + + while (1) { + ivar = malloc(sizeof(struct mmc_ivars), M_DEVBUF, M_WAITOK); + err = mmc_all_send_cid(sc, ivar->raw_cid); + if (err == MMC_ERR_TIMEOUT) + break; + if (err != MMC_ERR_NONE) { + printf("Error reading CID %d\n", err); + break; + } + if (mmcbr_get_mode(sc->dev) == mode_sd) { + ivar->mode = mode_sd; + mmc_decode_cid(1, ivar->raw_cid, &ivar->cid); + mmc_send_relative_addr(sc, &resp); + ivar->rca = resp >> 16; + // RO check + mmc_send_csd(sc, ivar->rca, ivar->raw_csd); + mmc_decode_csd(1, ivar->raw_csd, &ivar->csd); + printf("SD CARD: %lld bytes\n", ivar->csd.capacity); + child = device_add_child(sc->dev, NULL, -1); + device_set_ivars(child, ivar); + break; + } + panic("Write MMC card code here"); + } +} + +static void +mmc_go_discovery(struct mmc_softc *sc) +{ + uint32_t ocr; + device_t dev; + + 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); + mmc_idle_cards(sc); + if (mmc_send_app_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) { + // Failed, try MMC + mmcbr_set_mode(dev, mode_mmc); + if (mmc_send_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) + return; // Failed both, punt! XXX power down? + } + 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 (mmcbr_get_ocr(dev) == 0) + return; + /* + * Reselect the cards after we've idled them above. + */ + if (mmcbr_get_mode(dev) == mode_sd) + mmc_send_app_op_cond(sc, mmcbr_get_ocr(dev), NULL); + else + mmc_send_op_cond(sc, mmcbr_get_ocr(dev), NULL); + mmc_discover_cards(sc); + + mmcbr_set_bus_mode(dev, pushpull); + mmcbr_update_ios(dev); + bus_generic_attach(dev); +// mmc_update_children_sysctl(dev); +} + +static int +mmc_calculate_clock(struct mmc_softc *sc) +{ + int max_dtr = 0; + int nkid, i, f_min, f_max; + device_t *kids; + + f_min = mmcbr_get_f_min(sc->dev); + f_max = mmcbr_get_f_max(sc->dev); + max_dtr = f_max; + if (device_get_children(sc->dev, &kids, &nkid) != 0) + panic("can't get children"); + for (i = 0; i < nkid; i++) + if (mmc_get_tran_speed(kids[i]) < max_dtr) + max_dtr = mmc_get_tran_speed(kids[i]); + free(kids, M_TEMP); + device_printf(sc->dev, "setting transfer rate to %d.%03dMHz\n", + max_dtr / 1000000, (max_dtr / 1000) % 1000); + return max_dtr; +} + +static void +mmc_scan(struct mmc_softc *sc) +{ + device_t dev; + + dev = sc->dev; + mmc_acquire_bus(dev, dev); + + if (mmcbr_get_power_mode(dev) == power_on) + mmc_rescan_cards(sc); + mmc_go_discovery(sc); + mmcbr_set_clock(dev, mmc_calculate_clock(sc)); + mmcbr_update_ios(dev); + + mmc_release_bus(dev, dev); + // XXX probe/attach/detach children? +} + +static int +mmc_read_ivar(device_t bus, device_t child, int which, u_char *result) +{ + struct mmc_ivars *ivar = device_get_ivars(child); + + switch (which) { + default: + return (EINVAL); + case MMC_IVAR_DSR_IMP: + *(int *)result = ivar->csd.dsr_imp; + break; + case MMC_IVAR_MEDIA_SIZE: + *(int *)result = ivar->csd.capacity; + break; + case MMC_IVAR_MODE: + *(int *)result = ivar->mode; + break; + case MMC_IVAR_RCA: + *(int *)result = ivar->rca; + break; + case MMC_IVAR_SECTOR_SIZE: + *(int *)result = 512; + break; + case MMC_IVAR_TRAN_SPEED: + *(int *)result = ivar->csd.tran_speed; + break; + } + return (0); +} + +static int +mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value) +{ + // None are writable ATM + switch (which) { + default: + return (EINVAL); + } + return (0); +} + + +static void +mmc_delayed_attach(void *xsc) +{ + struct mmc_softc *sc = xsc; + + mmc_scan(sc); + config_intrhook_disestablish(&sc->config_intrhook); +} + +static device_method_t mmc_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, mmc_probe), + DEVMETHOD(device_attach, mmc_attach), + DEVMETHOD(device_detach, mmc_detach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, mmc_read_ivar), + DEVMETHOD(bus_write_ivar, mmc_write_ivar), + + /* 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), + + {0, 0}, +}; + +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, 0, 0); diff --git a/sys/dev/mmc/mmcbr_if.m b/sys/dev/mmc/mmcbr_if.m new file mode 100644 index 000000000000..a73aa62e0e0d --- /dev/null +++ b/sys/dev/mmc/mmcbr_if.m @@ -0,0 +1,84 @@ +#- +# 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 AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include +#include +#include + +# +# This is the interface that a mmc bridge chip gives to the mmc bus +# that attaches to the mmc bridge. +# + +INTERFACE mmcbr; + +# +# Called by the mmcbus to setup the IO pins correctly, the voltage to use +# for the card, the type of selects, power modes and bus width. +# +METHOD int update_ios { + device_t brdev; + device_t reqdev; +}; + +# +# Called by the mmcbus or its children to schedule a mmc request. These +# requests are queued. Time passes. The bridge then gets notification +# of the status of request, who then notifies the requesting device via +# the xfer_done mmcbus method. +# +METHOD int request { + device_t brdev; + device_t reqdev; + struct mmc_request *req; +}; + +# +# Called by mmcbus to get the read only status bits. +# +METHOD int get_ro { + device_t brdev; + device_t reqdev; +}; + +# +# Claim the current bridge, blocking the current thread until the host +# is no longer busy. +# +METHOD int acquire_host { + device_t brdev; + device_t reqdev; +} + +# +# Release the current bridge. +# +METHOD int release_host { + device_t brdev; + device_t reqdev; +} diff --git a/sys/dev/mmc/mmcbrvar.h b/sys/dev/mmc/mmcbrvar.h new file mode 100644 index 000000000000..31e089712f5c --- /dev/null +++ b/sys/dev/mmc/mmcbrvar.h @@ -0,0 +1,73 @@ +/*- + * 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. + * + * "$FreeBSD$" + */ + +#ifndef DEV_MMC_MMCBRVAR_H +#define DEV_MMC_MMCBRVAR_H + +#include +#include "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_, +}; + +/* + * 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) + +static int __inline +mmcbr_update_ios(device_t dev) +{ + return (MMCBR_UPDATE_IOS(device_get_parent(dev), dev)); +} + +#endif /* DEV_MMC_MMCBRVAR_H */ diff --git a/sys/dev/mmc/mmcbus_if.m b/sys/dev/mmc/mmcbus_if.m new file mode 100644 index 000000000000..59aeca827893 --- /dev/null +++ b/sys/dev/mmc/mmcbus_if.m @@ -0,0 +1,63 @@ +#- +# 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 AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include +#include + +# +# This is the set of callbacks that mmc bridges call into the bus, or +# that mmc/sd card drivers call to make requests. +# + +INTERFACE mmcbus; + +# +# Queue and wait for a request. +# +METHOD int wait_for_request { + device_t brdev; + device_t reqdev; + struct mmc_request *req; +}; + +# +# Claim the current bridge, blocking the current thread until the host +# is no longer busy. +# +METHOD int acquire_bus { + device_t brdev; + device_t reqdev; +} + +# +# Release the current bridge. +# +METHOD int release_bus { + device_t brdev; + device_t reqdev; +} diff --git a/sys/dev/mmc/mmcreg.h b/sys/dev/mmc/mmcreg.h new file mode 100644 index 000000000000..471a04e5a295 --- /dev/null +++ b/sys/dev/mmc/mmcreg.h @@ -0,0 +1,326 @@ +/*- + * 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. + * + * $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) +/* R7 -- new in sd 2.0 */ +#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 + 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_SELECT_CARD 7 +#define MMC_DESELECT_CARD 7 +#define MMC_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 + /* reserved: 14 */ +#define MMC_GO_INACTIVE_STATE 15 + +/* 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 + +/* 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_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; + 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 sector_size; /* Erase sector size! */ + 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; +}; + +#endif /* DEV_MMCREG_H */ diff --git a/sys/dev/mmc/mmcsd.c b/sys/dev/mmc/mmcsd.c new file mode 100644 index 000000000000..314c8a0a43db --- /dev/null +++ b/sys/dev/mmc/mmcsd.c @@ -0,0 +1,244 @@ +/*- + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mmcbus_if.h" + +struct mmcsd_softc { + device_t dev; + struct mtx sc_mtx; + struct disk *disk; + struct proc *p; + struct bio_queue_head bio_queue; +}; + +#define MULTI_BLOCK_READ_BROKEN + +/* bus entry points */ +static int mmcsd_probe(device_t dev); +static int mmcsd_attach(device_t dev); +static int mmcsd_detach(device_t dev); + +/* disk routines */ +static int mmcsd_open(struct disk *dp); +static int mmcsd_close(struct disk *dp); +static void mmcsd_strategy(struct bio *bp); +static void mmcsd_task(void *arg); + +#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_set_desc(dev, "mmc or sd flash card"); + return (0); +} + +static int +mmcsd_attach(device_t dev) +{ + struct mmcsd_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + MMCSD_LOCK_INIT(sc); + + sc->disk = disk_alloc(); + sc->disk->d_open = mmcsd_open; + sc->disk->d_close = mmcsd_close; + sc->disk->d_strategy = mmcsd_strategy; + // sc->disk->d_dump = mmcsd_dump; Need polling mmc layer + sc->disk->d_name = "mmcsd"; + sc->disk->d_drv1 = sc; + sc->disk->d_maxsize = DFLTPHYS; + sc->disk->d_sectorsize = mmc_get_sector_size(dev); + sc->disk->d_mediasize = mmc_get_media_size(dev); + sc->disk->d_unit = device_get_unit(dev); + disk_create(sc->disk, DISK_VERSION); + bioq_init(&sc->bio_queue); + kthread_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card"); + + return (0); +} + +static int +mmcsd_detach(device_t dev) +{ + return (EBUSY); /* XXX */ +} + +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); + bioq_disksort(&sc->bio_queue, bp); + wakeup(sc); + MMCSD_UNLOCK(sc); +} + +static void +mmcsd_task(void *arg) +{ + struct mmcsd_softc *sc = (struct mmcsd_softc*)arg; + struct bio *bp; + int sz; + daddr_t block, end; + struct mmc_command cmd; + struct mmc_command stop; + struct mmc_request req; + struct mmc_data data; + device_t dev; + + dev = sc->dev; + for (;;) { + MMCSD_LOCK(sc); + do { + bp = bioq_first(&sc->bio_queue); + if (bp == NULL) + msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0); + } while (bp == NULL); + bioq_remove(&sc->bio_queue, bp); + MMCSD_UNLOCK(sc); + MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev); +// printf("mmc_task: request %p for block %lld\n", bp, bp->bio_pblkno); + sz = sc->disk->d_sectorsize; + end = bp->bio_pblkno + (bp->bio_bcount / sz); + // XXX should use read/write_mulit + for (block = bp->bio_pblkno; block < end;) { + char *vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz; + 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) { +#ifdef MULTI_BLOCK_READ + if (end - block > 1) + cmd.opcode = MMC_READ_MULTIPLE_BLOCK; + else + cmd.opcode = MMC_READ_SINGLE_BLOCK; +#else + cmd.opcode = MMC_READ_SINGLE_BLOCK; +#endif + } else + cmd.opcode = MMC_WRITE_BLOCK; + cmd.arg = block << 9; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + // data.timeout_ns = ; + // data.timeout_clks = ; + data.data = vaddr; + data.mrq = &req; + if (bp->bio_cmd == BIO_READ) { + data.flags = MMC_DATA_READ; +#ifdef MULTI_BLOCK_READ + data.len = bp->bio_bcount; + if (end - block > 1) { + req.stop = &stop; + data.flags |= MMC_DATA_MULTI; + } + printf("Len %d %lld-%lld flags %#x sz %d\n", + data.len, block, end, data.flags, sz); + block = end; +#else + data.len = sz; + block++; +#endif + } else { + data.flags = MMC_DATA_WRITE; + data.len = sz; + block++; + } + stop.opcode = MMC_STOP_TRANSMISSION; + stop.arg = 0; + stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, + &req); + // XXX error handling +//XXX while (!(mmc_send_status(dev) & R1_READY_FOR_DATA)) +// continue; + // XXX decode mmc status + } + MMCBUS_RELEASE_BUS(device_get_parent(dev), dev); + biodone(bp); + } +} + +static device_method_t mmcsd_methods[] = { + DEVMETHOD(device_probe, mmcsd_probe), + DEVMETHOD(device_attach, mmcsd_attach), + DEVMETHOD(device_detach, mmcsd_detach), + {0, 0}, +}; + +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, 0, 0); diff --git a/sys/dev/mmc/mmcvar.h b/sys/dev/mmc/mmcvar.h new file mode 100644 index 000000000000..03d0bfefaccf --- /dev/null +++ b/sys/dev/mmc/mmcvar.h @@ -0,0 +1,54 @@ +/*- + * 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. + * + * "$FreeBSD$" + */ + +#ifndef DEV_MMC_MMCVAR_H +#define DEV_MMC_MMCVAR_H + +enum mmc_device_ivars { + MMC_IVAR_DSR_IMP, + MMC_IVAR_MEDIA_SIZE, + MMC_IVAR_MODE, + MMC_IVAR_RCA, + MMC_IVAR_SECTOR_SIZE, + MMC_IVAR_TRAN_SPEED, +// MMC_IVAR_, +}; + +/* + * 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) +MMC_ACCESSOR(media_size, MEDIA_SIZE, int) +MMC_ACCESSOR(mode, MODE, int) +MMC_ACCESSOR(rca, RCA, int) +MMC_ACCESSOR(sector_size, SECTOR_SIZE, int) +MMC_ACCESSOR(tran_speed, TRAN_SPEED, int) + +#endif /* DEV_MMC_MMCVAR_H */