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 */