[Midnightbsd-cvs] src [10096] trunk/sys/dev/mmc: sync mmc
laffer1 at midnightbsd.org
laffer1 at midnightbsd.org
Sun May 27 19:36:35 EDT 2018
Revision: 10096
http://svnweb.midnightbsd.org/src/?rev=10096
Author: laffer1
Date: 2018-05-27 19:36:35 -0400 (Sun, 27 May 2018)
Log Message:
-----------
sync mmc
Modified Paths:
--------------
trunk/sys/dev/mmc/bridge.h
trunk/sys/dev/mmc/mmc.c
trunk/sys/dev/mmc/mmcbr_if.m
trunk/sys/dev/mmc/mmcbrvar.h
trunk/sys/dev/mmc/mmcbus_if.m
trunk/sys/dev/mmc/mmcreg.h
trunk/sys/dev/mmc/mmcsd.c
trunk/sys/dev/mmc/mmcvar.h
Added Paths:
-----------
trunk/sys/dev/mmc/mmc_ioctl.h
trunk/sys/dev/mmc/mmc_private.h
trunk/sys/dev/mmc/mmc_subr.c
trunk/sys/dev/mmc/mmc_subr.h
Property Changed:
----------------
trunk/sys/dev/mmc/mmcbr_if.m
trunk/sys/dev/mmc/mmcbus_if.m
Modified: trunk/sys/dev/mmc/bridge.h
===================================================================
--- trunk/sys/dev/mmc/bridge.h 2018-05-27 23:36:22 UTC (rev 10095)
+++ trunk/sys/dev/mmc/bridge.h 2018-05-27 23:36:35 UTC (rev 10096)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 2006 M. Warner Losh. All rights reserved.
*
@@ -48,33 +49,32 @@
* or the SD Card Association to disclose or distribute any technical
* information, know-how or other confidential information to any third party.
*
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/mmc/bridge.h 322389 2017-08-11 00:41:43Z marius $
*/
#ifndef DEV_MMC_BRIDGE_H
-#define DEV_MMC_BRIDGE_H
+#define DEV_MMC_BRIDGE_H
+#include <sys/bus.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
+ * FreeBSD terminology of bridge and bus for consistency 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).
+ * cards attached to it. mmc devices are attached on a bus topology,
+ * while sd and sdio cards usually are attached using a star topology
+ * (meaning in practice each sd card has its own, independent slot).
+ * Since SDHCI v3.00, buses for esd and esdio are possible, though.
*
* Attached to the mmc bridge is an mmcbus. The mmcbus is described
- * in dev/mmc/bus.h.
+ * in dev/mmc/mmcbus_if.m.
*/
-
/*
* 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,
@@ -88,6 +88,10 @@
vdd_330, vdd_340, vdd_350, vdd_360
};
+enum mmc_vccq {
+ vccq_120 = 0, vccq_180, vccq_330
+};
+
enum mmc_power_mode {
power_off = 0, power_up, power_on
};
@@ -104,18 +108,28 @@
bus_width_1 = 0, bus_width_4 = 2, bus_width_8 = 3
};
+enum mmc_drv_type {
+ drv_type_b = 0, drv_type_a, drv_type_c, drv_type_d
+};
+
enum mmc_bus_timing {
- bus_timing_normal = 0, bus_timing_hs
+ bus_timing_normal = 0, bus_timing_hs, bus_timing_uhs_sdr12,
+ bus_timing_uhs_sdr25, bus_timing_uhs_sdr50, bus_timing_uhs_ddr50,
+ bus_timing_uhs_sdr104, bus_timing_mmc_ddr52, bus_timing_mmc_hs200,
+ bus_timing_mmc_hs400, bus_timing_mmc_hs400es, bus_timing_max =
+ bus_timing_mmc_hs400es
};
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_vdd vdd; /* Voltage to apply to the power pins */
+ enum mmc_vccq vccq; /* Voltage to use for signaling */
enum mmc_bus_mode bus_mode;
enum mmc_chip_select chip_select;
enum mmc_bus_width bus_width;
enum mmc_power_mode power_mode;
enum mmc_bus_timing timing;
+ enum mmc_drv_type drv_type;
};
enum mmc_card_mode {
@@ -122,6 +136,10 @@
mode_mmc, mode_sd
};
+enum mmc_retune_req {
+ retune_req_none = 0, retune_req_normal, retune_req_reset
+};
+
struct mmc_host {
int f_min;
int f_max;
@@ -128,11 +146,46 @@
uint32_t host_ocr;
uint32_t ocr;
uint32_t caps;
-#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can do 4-bit data transfers */
-#define MMC_CAP_8_BIT_DATA (1 << 1) /* Can do 8-bit data transfers */
-#define MMC_CAP_HSPEED (1 << 2) /* Can do High Speed transfers */
+#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can do 4-bit data transfers */
+#define MMC_CAP_8_BIT_DATA (1 << 1) /* Can do 8-bit data transfers */
+#define MMC_CAP_HSPEED (1 << 2) /* Can do High Speed transfers */
+#define MMC_CAP_BOOT_NOACC (1 << 4) /* Cannot access boot partitions */
+#define MMC_CAP_WAIT_WHILE_BUSY (1 << 5) /* Host waits for busy responses */
+#define MMC_CAP_UHS_SDR12 (1 << 6) /* Can do UHS SDR12 */
+#define MMC_CAP_UHS_SDR25 (1 << 7) /* Can do UHS SDR25 */
+#define MMC_CAP_UHS_SDR50 (1 << 8) /* Can do UHS SDR50 */
+#define MMC_CAP_UHS_SDR104 (1 << 9) /* Can do UHS SDR104 */
+#define MMC_CAP_UHS_DDR50 (1 << 10) /* Can do UHS DDR50 */
+#define MMC_CAP_MMC_DDR52_120 (1 << 11) /* Can do eMMC DDR52 at 1.2 V */
+#define MMC_CAP_MMC_DDR52_180 (1 << 12) /* Can do eMMC DDR52 at 1.8 V */
+#define MMC_CAP_MMC_DDR52 (MMC_CAP_MMC_DDR52_120 | MMC_CAP_MMC_DDR52_180)
+#define MMC_CAP_MMC_HS200_120 (1 << 13) /* Can do eMMC HS200 at 1.2 V */
+#define MMC_CAP_MMC_HS200_180 (1 << 14) /* Can do eMMC HS200 at 1.8 V */
+#define MMC_CAP_MMC_HS200 (MMC_CAP_MMC_HS200_120| MMC_CAP_MMC_HS200_180)
+#define MMC_CAP_MMC_HS400_120 (1 << 15) /* Can do eMMC HS400 at 1.2 V */
+#define MMC_CAP_MMC_HS400_180 (1 << 16) /* Can do eMMC HS400 at 1.8 V */
+#define MMC_CAP_MMC_HS400 (MMC_CAP_MMC_HS400_120 | MMC_CAP_MMC_HS400_180)
+#define MMC_CAP_MMC_HSX00_120 (MMC_CAP_MMC_HS200_120 | MMC_CAP_MMC_HS400_120)
+#define MMC_CAP_MMC_ENH_STROBE (1 << 17) /* Can do eMMC Enhanced Strobe */
+#define MMC_CAP_SIGNALING_120 (1 << 18) /* Can do signaling at 1.2 V */
+#define MMC_CAP_SIGNALING_180 (1 << 19) /* Can do signaling at 1.8 V */
+#define MMC_CAP_SIGNALING_330 (1 << 20) /* Can do signaling at 3.3 V */
+#define MMC_CAP_DRIVER_TYPE_A (1 << 21) /* Can do Driver Type A */
+#define MMC_CAP_DRIVER_TYPE_C (1 << 22) /* Can do Driver Type C */
+#define MMC_CAP_DRIVER_TYPE_D (1 << 23) /* Can do Driver Type D */
enum mmc_card_mode mode;
struct mmc_ios ios; /* Current state of the host */
};
+extern driver_t mmc_driver;
+extern devclass_t mmc_devclass;
+
+#define MMC_VERSION 5
+
+#define MMC_DECLARE_BRIDGE(name) \
+ DRIVER_MODULE(mmc, name, mmc_driver, mmc_devclass, NULL, NULL); \
+ MODULE_DEPEND(name, mmc, MMC_VERSION, MMC_VERSION, MMC_VERSION);
+#define MMC_DEPEND(name) \
+ MODULE_DEPEND(name, mmc, MMC_VERSION, MMC_VERSION, MMC_VERSION);
+
#endif /* DEV_MMC_BRIDGE_H */
Modified: trunk/sys/dev/mmc/mmc.c
===================================================================
--- trunk/sys/dev/mmc/mmc.c 2018-05-27 23:36:22 UTC (rev 10095)
+++ trunk/sys/dev/mmc/mmc.c 2018-05-27 23:36:35 UTC (rev 10096)
@@ -1,6 +1,8 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 2006 Bernd Walter. All rights reserved.
* Copyright (c) 2006 M. Warner Losh. All rights reserved.
+ * Copyright (c) 2017 Marius Strobl <marius at FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -51,7 +53,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/mmc/mmc.c 331039 2018-03-15 23:02:52Z marius $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -63,20 +65,19 @@
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmc_private.h>
+#include <dev/mmc/mmc_subr.h>
#include <dev/mmc/mmcreg.h>
#include <dev/mmc/mmcbrvar.h>
#include <dev/mmc/mmcvar.h>
+
#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;
-};
+CTASSERT(bus_timing_max <= sizeof(uint32_t) * NBBY);
/*
* Per-card data
@@ -85,31 +86,59 @@
uint32_t raw_cid[4]; /* Raw bits of the CID */
uint32_t raw_csd[4]; /* Raw bits of the CSD */
uint32_t raw_scr[2]; /* Raw bits of the SCR */
- uint8_t raw_ext_csd[512]; /* Raw bits of the EXT_CSD */
+ uint8_t raw_ext_csd[MMC_EXTCSD_SIZE]; /* Raw bits of the EXT_CSD */
uint32_t raw_sd_status[16]; /* Raw bits of the SD_STATUS */
uint16_t rca;
+ u_char read_only; /* True when the device is read-only */
+ u_char high_cap; /* High Capacity device (block addressed) */
enum mmc_card_mode mode;
+ enum mmc_bus_width bus_width; /* Bus width to use */
struct mmc_cid cid; /* cid decoded */
struct mmc_csd csd; /* csd decoded */
struct mmc_scr scr; /* scr decoded */
struct mmc_sd_status sd_status; /* SD_STATUS decoded */
- u_char read_only; /* True when the device is read-only */
- u_char bus_width; /* Bus width to use */
- u_char timing; /* Bus timing support */
- u_char high_cap; /* High Capacity card (block addressed) */
uint32_t sec_count; /* Card capacity in 512byte blocks */
+ uint32_t timings; /* Mask of bus timings supported */
+ uint32_t vccq_120; /* Mask of bus timings at VCCQ of 1.2 V */
+ uint32_t vccq_180; /* Mask of bus timings at VCCQ of 1.8 V */
uint32_t tran_speed; /* Max speed in normal mode */
uint32_t hs_tran_speed; /* Max speed in high speed mode */
uint32_t erase_sector; /* Card native erase sector size */
+ uint32_t cmd6_time; /* Generic switch timeout [us] */
+ uint32_t quirks; /* Quirks as per mmc_quirk->quirks */
char card_id_string[64];/* Formatted CID info (serial, MFG, etc) */
+ char card_sn_string[16];/* Formatted serial # for disk->d_ident */
};
-#define CMD_RETRIES 3
+#define CMD_RETRIES 3
+static const struct mmc_quirk mmc_quirks[] = {
+ /*
+ * For some SanDisk iNAND devices, the CMD38 argument needs to be
+ * provided in EXT_CSD[113].
+ */
+ { 0x2, 0x100, "SEM02G", MMC_QUIRK_INAND_CMD38 },
+ { 0x2, 0x100, "SEM04G", MMC_QUIRK_INAND_CMD38 },
+ { 0x2, 0x100, "SEM08G", MMC_QUIRK_INAND_CMD38 },
+ { 0x2, 0x100, "SEM16G", MMC_QUIRK_INAND_CMD38 },
+ { 0x2, 0x100, "SEM32G", MMC_QUIRK_INAND_CMD38 },
+
+ /*
+ * Disable TRIM for Kingston eMMCs where a firmware bug can lead to
+ * unrecoverable data corruption.
+ */
+ { 0x70, MMC_QUIRK_OID_ANY, "V10008", MMC_QUIRK_BROKEN_TRIM },
+ { 0x70, MMC_QUIRK_OID_ANY, "V10016", MMC_QUIRK_BROKEN_TRIM },
+
+ { 0x0, 0x0, NULL, 0x0 }
+};
+
static SYSCTL_NODE(_hw, OID_AUTO, mmc, CTLFLAG_RD, NULL, "mmc driver");
static int mmc_debug;
-SYSCTL_INT(_hw_mmc, OID_AUTO, debug, CTLFLAG_RW, &mmc_debug, 0, "Debug level");
+TUNABLE_INT("hw.mmc.debug", &mmc_debug);
+SYSCTL_INT(_hw_mmc, OID_AUTO, debug, CTLFLAG_RWTUN, &mmc_debug, 0,
+ "Debug level");
/* bus entry points */
static int mmc_acquire_bus(device_t busdev, device_t dev);
@@ -122,20 +151,22 @@
uintptr_t *result);
static int mmc_release_bus(device_t busdev, device_t dev);
static int mmc_resume(device_t dev);
+static void mmc_retune_pause(device_t busdev, device_t dev, bool retune);
+static void mmc_retune_unpause(device_t busdev, device_t dev);
static int mmc_suspend(device_t dev);
-static int mmc_wait_for_request(device_t brdev, device_t reqdev,
+static int mmc_wait_for_request(device_t busdev, device_t dev,
struct mmc_request *req);
static int mmc_write_ivar(device_t bus, device_t child, int which,
uintptr_t value);
-#define MMC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define MMC_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), \
+#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);
+#define MMC_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx);
+#define MMC_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED);
+#define MMC_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED);
static int mmc_all_send_cid(struct mmc_softc *sc, uint32_t *rawcid);
static void mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr);
@@ -146,12 +177,13 @@
static int mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca,
uint32_t *rawscr);
static int mmc_calculate_clock(struct mmc_softc *sc);
-static void mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid);
+static void mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid,
+ bool is_4_41p);
static void mmc_decode_cid_sd(uint32_t *raw_cid, struct mmc_cid *cid);
static void mmc_decode_csd_mmc(uint32_t *raw_csd, struct mmc_csd *csd);
-static void mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd);
+static int mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd);
static void mmc_delayed_attach(void *xsc);
-static int mmc_delete_cards(struct mmc_softc *sc);
+static int mmc_delete_cards(struct mmc_softc *sc, bool final);
static void mmc_discover_cards(struct mmc_softc *sc);
static void mmc_format_card_id_string(struct mmc_ivars *ivar);
static void mmc_go_discovery(struct mmc_softc *sc);
@@ -158,6 +190,7 @@
static uint32_t mmc_get_bits(uint32_t *bits, int bit_len, int start,
int size);
static int mmc_highest_voltage(uint32_t ocr);
+static bool mmc_host_timing(device_t dev, enum mmc_bus_timing timing);
static void mmc_idle_cards(struct mmc_softc *sc);
static void mmc_ms_delay(int ms);
static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard);
@@ -164,6 +197,7 @@
static void mmc_power_down(struct mmc_softc *sc);
static void mmc_power_up(struct mmc_softc *sc);
static void mmc_rescan_cards(struct mmc_softc *sc);
+static int mmc_retune(device_t busdev, device_t dev, bool reset);
static void mmc_scan(struct mmc_softc *sc);
static int mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp,
uint8_t value, uint8_t *res);
@@ -172,25 +206,28 @@
static int mmc_send_app_op_cond(struct mmc_softc *sc, uint32_t ocr,
uint32_t *rocr);
static int mmc_send_csd(struct mmc_softc *sc, uint16_t rca, uint32_t *rawcsd);
-static int mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd);
static int mmc_send_if_cond(struct mmc_softc *sc, uint8_t vhs);
static int mmc_send_op_cond(struct mmc_softc *sc, uint32_t ocr,
uint32_t *rocr);
static int mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp);
-static int mmc_send_status(struct mmc_softc *sc, uint16_t rca,
- uint32_t *status);
static int mmc_set_blocklen(struct mmc_softc *sc, uint32_t len);
-static int mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca,
- int width);
+static int mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar,
+ enum mmc_bus_timing timing);
+static int mmc_set_power_class(struct mmc_softc *sc, struct mmc_ivars *ivar);
static int mmc_set_relative_addr(struct mmc_softc *sc, uint16_t resp);
-static int mmc_set_timing(struct mmc_softc *sc, int timing);
-static int mmc_switch(struct mmc_softc *sc, uint8_t set, uint8_t index,
- uint8_t value);
+static int mmc_set_timing(struct mmc_softc *sc, struct mmc_ivars *ivar,
+ enum mmc_bus_timing timing);
+static int mmc_set_vccq(struct mmc_softc *sc, struct mmc_ivars *ivar,
+ enum mmc_bus_timing timing);
+static int mmc_switch_to_hs200(struct mmc_softc *sc, struct mmc_ivars *ivar,
+ uint32_t clock);
+static int mmc_switch_to_hs400(struct mmc_softc *sc, struct mmc_ivars *ivar,
+ uint32_t max_dtr, enum mmc_bus_timing max_timing);
static int mmc_test_bus_width(struct mmc_softc *sc);
-static int mmc_wait_for_app_cmd(struct mmc_softc *sc, uint32_t rca,
- struct mmc_command *cmd, int retries);
-static int mmc_wait_for_cmd(struct mmc_softc *sc, struct mmc_command *cmd,
- int retries);
+static uint32_t mmc_timing_to_dtr(struct mmc_ivars *ivar,
+ enum mmc_bus_timing timing);
+static const char *mmc_timing_to_string(enum mmc_bus_timing timing);
+static void mmc_update_child_list(struct mmc_softc *sc);
static int mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode,
uint32_t arg, uint32_t flags, uint32_t *resp, int retries);
static int mmc_wait_for_req(struct mmc_softc *sc, struct mmc_request *req);
@@ -234,7 +271,8 @@
struct mmc_softc *sc = device_get_softc(dev);
int err;
- if ((err = mmc_delete_cards(sc)) != 0)
+ err = mmc_delete_cards(sc, true);
+ if (err != 0)
return (err);
mmc_power_down(sc);
MMC_LOCK_DESTROY(sc);
@@ -249,10 +287,21 @@
int err;
err = bus_generic_suspend(dev);
- if (err)
- return (err);
+ if (err != 0)
+ return (err);
+ /*
+ * We power down with the bus acquired here, mainly so that no device
+ * is selected any longer and sc->last_rca gets set to 0. Otherwise,
+ * the deselect as part of the bus acquisition in mmc_scan() may fail
+ * during resume, as the bus isn't powered up again before later in
+ * mmc_go_discovery().
+ */
+ err = mmc_acquire_bus(dev, dev);
+ if (err != 0)
+ return (err);
mmc_power_down(sc);
- return (0);
+ err = mmc_release_bus(dev, dev);
+ return (err);
}
static int
@@ -270,7 +319,8 @@
struct mmc_softc *sc;
struct mmc_ivars *ivar;
int err;
- int rca;
+ uint16_t rca;
+ enum mmc_bus_timing timing;
err = MMCBR_ACQUIRE_HOST(device_get_parent(busdev), busdev);
if (err)
@@ -289,21 +339,59 @@
* unselect unless the bus code itself wants the mmc
* bus, and constantly reselecting causes problems.
*/
- rca = mmc_get_rca(dev);
+ ivar = device_get_ivars(dev);
+ rca = ivar->rca;
if (sc->last_rca != rca) {
- mmc_select_card(sc, rca);
+ if (mmc_select_card(sc, rca) != MMC_ERR_NONE) {
+ device_printf(busdev, "Card at relative "
+ "address %d failed to select\n", rca);
+ return (ENXIO);
+ }
sc->last_rca = rca;
+ timing = mmcbr_get_timing(busdev);
+ /*
+ * For eMMC modes, setting/updating bus width and VCCQ
+ * only really is necessary if there actually is more
+ * than one device on the bus as generally that already
+ * had to be done by mmc_calculate_clock() or one of
+ * its calees. Moreover, setting the bus width anew
+ * can trigger re-tuning (via a CRC error on the next
+ * CMD), even if not switching between devices an the
+ * previously selected one is still tuned. Obviously,
+ * we need to re-tune the host controller if devices
+ * are actually switched, though.
+ */
+ if (timing >= bus_timing_mmc_ddr52 &&
+ sc->child_count == 1)
+ return (0);
/* Prepare bus width for the new card. */
- ivar = device_get_ivars(dev);
if (bootverbose || mmc_debug) {
device_printf(busdev,
- "setting bus width to %d bits\n",
+ "setting bus width to %d bits %s timing\n",
(ivar->bus_width == bus_width_4) ? 4 :
- (ivar->bus_width == bus_width_8) ? 8 : 1);
+ (ivar->bus_width == bus_width_8) ? 8 : 1,
+ mmc_timing_to_string(timing));
}
- mmc_set_card_bus_width(sc, rca, ivar->bus_width);
+ if (mmc_set_card_bus_width(sc, ivar, timing) !=
+ MMC_ERR_NONE) {
+ device_printf(busdev, "Card at relative "
+ "address %d failed to set bus width\n",
+ rca);
+ return (ENXIO);
+ }
mmcbr_set_bus_width(busdev, ivar->bus_width);
mmcbr_update_ios(busdev);
+ if (mmc_set_vccq(sc, ivar, timing) != MMC_ERR_NONE) {
+ device_printf(busdev, "Failed to set VCCQ "
+ "for card at relative address %d\n", rca);
+ return (ENXIO);
+ }
+ if (timing >= bus_timing_mmc_hs200 &&
+ mmc_retune(busdev, dev, true) != 0) {
+ device_printf(busdev, "Card at relative "
+ "address %d failed to re-tune\n", rca);
+ return (ENXIO);
+ }
}
} else {
/*
@@ -310,7 +398,8 @@
* If there's a card selected, stand down.
*/
if (sc->last_rca != 0) {
- mmc_select_card(sc, 0);
+ if (mmc_select_card(sc, 0) != MMC_ERR_NONE)
+ return (ENXIO);
sc->last_rca = 0;
}
}
@@ -353,7 +442,8 @@
{
int i;
- for (i = 30; i >= 0; i--)
+ for (i = MMC_OCR_MAX_VOLTAGE_SHIFT;
+ i >= MMC_OCR_MIN_VOLTAGE_SHIFT; i--)
if (ocr & (1 << i))
return (i);
return (-1);
@@ -377,7 +467,7 @@
req->done = mmc_wakeup;
req->done_data = sc;
- if (mmc_debug > 1) {
+ if (__predict_false(mmc_debug > 1)) {
device_printf(sc->dev, "REQUEST: CMD%d arg %#x flags %#x",
req->cmd->opcode, req->cmd->arg, req->cmd->flags);
if (req->cmd->data) {
@@ -390,56 +480,67 @@
while ((req->flags & MMC_REQ_DONE) == 0)
msleep(req, &sc->sc_mtx, 0, "mmcreq", 0);
MMC_UNLOCK(sc);
- if (mmc_debug > 2 || (mmc_debug > 1 && req->cmd->error))
- device_printf(sc->dev, "RESULT: %d\n", req->cmd->error);
+ if (__predict_false(mmc_debug > 2 || (mmc_debug > 0 &&
+ req->cmd->error != MMC_ERR_NONE)))
+ device_printf(sc->dev, "CMD%d RESULT: %d\n",
+ req->cmd->opcode, req->cmd->error);
return (0);
}
static int
-mmc_wait_for_request(device_t brdev, device_t reqdev, struct mmc_request *req)
+mmc_wait_for_request(device_t busdev, device_t dev, struct mmc_request *req)
{
- struct mmc_softc *sc = device_get_softc(brdev);
+ struct mmc_softc *sc;
+ struct mmc_ivars *ivar;
+ int err, i;
+ enum mmc_retune_req retune_req;
- return (mmc_wait_for_req(sc, req));
-}
+ sc = device_get_softc(busdev);
+ KASSERT(sc->owner != NULL,
+ ("%s: Request from %s without bus being acquired.", __func__,
+ device_get_nameunit(dev)));
-static int
-mmc_wait_for_cmd(struct mmc_softc *sc, struct mmc_command *cmd, int retries)
-{
- struct mmc_request mreq;
-
- memset(&mreq, 0, sizeof(mreq));
- memset(cmd->resp, 0, sizeof(cmd->resp));
- cmd->retries = retries;
- mreq.cmd = cmd;
- mmc_wait_for_req(sc, &mreq);
- return (cmd->error);
-}
-
-static int
-mmc_wait_for_app_cmd(struct mmc_softc *sc, uint32_t rca,
- struct mmc_command *cmd, int retries)
-{
- struct mmc_command appcmd;
- int err = MMC_ERR_NONE, i;
-
- for (i = 0; i <= retries; i++) {
- appcmd.opcode = MMC_APP_CMD;
- appcmd.arg = rca << 16;
- appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
- appcmd.data = NULL;
- mmc_wait_for_cmd(sc, &appcmd, 0);
- err = appcmd.error;
- if (err != MMC_ERR_NONE)
- continue;
- if (!(appcmd.resp[0] & R1_APP_CMD))
- return MMC_ERR_FAILED;
- mmc_wait_for_cmd(sc, cmd, 0);
- err = cmd->error;
- if (err == MMC_ERR_NONE)
+ /*
+ * Unless no device is selected or re-tuning is already ongoing,
+ * execute re-tuning if a) the bridge is requesting to do so and
+ * re-tuning hasn't been otherwise paused, or b) if a child asked
+ * to be re-tuned prior to pausing (see also mmc_retune_pause()).
+ */
+ if (__predict_false(sc->last_rca != 0 && sc->retune_ongoing == 0 &&
+ (((retune_req = mmcbr_get_retune_req(busdev)) != retune_req_none &&
+ sc->retune_paused == 0) || sc->retune_needed == 1))) {
+ if (__predict_false(mmc_debug > 1)) {
+ device_printf(busdev,
+ "Re-tuning with%s circuit reset required\n",
+ retune_req == retune_req_reset ? "" : "out");
+ }
+ if (device_get_parent(dev) == busdev)
+ ivar = device_get_ivars(dev);
+ else {
+ for (i = 0; i < sc->child_count; i++) {
+ ivar = device_get_ivars(sc->child_list[i]);
+ if (ivar->rca == sc->last_rca)
+ break;
+ }
+ if (ivar->rca != sc->last_rca)
+ return (EINVAL);
+ }
+ sc->retune_ongoing = 1;
+ err = mmc_retune(busdev, dev, retune_req == retune_req_reset);
+ sc->retune_ongoing = 0;
+ switch (err) {
+ case MMC_ERR_NONE:
+ case MMC_ERR_FAILED: /* Re-tune error but still might work */
break;
+ case MMC_ERR_BADCRC: /* Switch failure on HS400 recovery */
+ return (ENXIO);
+ case MMC_ERR_INVALID: /* Driver implementation b0rken */
+ default: /* Unknown error, should not happen */
+ return (EINVAL);
+ }
+ sc->retune_needed = 0;
}
- return (err);
+ return (mmc_wait_for_req(sc, req));
}
static int
@@ -454,11 +555,9 @@
cmd.arg = arg;
cmd.flags = flags;
cmd.data = NULL;
- err = mmc_wait_for_cmd(sc, &cmd, retries);
+ err = mmc_wait_for_cmd(sc->dev, sc->dev, &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));
@@ -473,7 +572,7 @@
{
device_t dev;
struct mmc_command cmd;
-
+
dev = sc->dev;
mmcbr_set_chip_select(dev, cs_high);
mmcbr_update_ios(dev);
@@ -484,7 +583,7 @@
cmd.arg = 0;
cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
cmd.data = NULL;
- mmc_wait_for_cmd(sc, &cmd, 0);
+ mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES);
mmc_ms_delay(1);
mmcbr_set_chip_select(dev, cs_dontcare);
@@ -505,7 +604,8 @@
cmd.data = NULL;
for (i = 0; i < 1000; i++) {
- err = mmc_wait_for_app_cmd(sc, 0, &cmd, CMD_RETRIES);
+ err = mmc_wait_for_app_cmd(sc->dev, sc->dev, 0, &cmd,
+ CMD_RETRIES);
if (err != MMC_ERR_NONE)
break;
if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) ||
@@ -532,7 +632,7 @@
cmd.data = NULL;
for (i = 0; i < 1000; i++) {
- err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
+ err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE)
break;
if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) ||
@@ -558,7 +658,7 @@
cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
cmd.data = NULL;
- err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
+ err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES);
return (err);
}
@@ -566,6 +666,7 @@
mmc_power_up(struct mmc_softc *sc)
{
device_t dev;
+ enum mmc_vccq vccq;
dev = sc->dev;
mmcbr_set_vdd(dev, mmc_highest_voltage(mmcbr_get_host_ocr(dev)));
@@ -575,9 +676,14 @@
mmcbr_set_power_mode(dev, power_up);
mmcbr_set_clock(dev, 0);
mmcbr_update_ios(dev);
+ for (vccq = vccq_330; ; vccq--) {
+ mmcbr_set_vccq(dev, vccq);
+ if (mmcbr_switch_vccq(dev) == 0 || vccq == vccq_120)
+ break;
+ }
mmc_ms_delay(1);
- mmcbr_set_clock(dev, mmcbr_get_f_min(sc->dev));
+ mmcbr_set_clock(dev, SD_MMC_CARD_ID_FREQUENCY);
mmcbr_set_timing(dev, bus_timing_normal);
mmcbr_set_power_mode(dev, power_on);
mmcbr_update_ios(dev);
@@ -601,27 +707,13 @@
static int
mmc_select_card(struct mmc_softc *sc, uint16_t rca)
{
- int flags;
+ int err, flags;
flags = (rca ? MMC_RSP_R1B : MMC_RSP_NONE) | MMC_CMD_AC;
- return (mmc_wait_for_command(sc, MMC_SELECT_CARD, (uint32_t)rca << 16,
- flags, NULL, CMD_RETRIES));
-}
-
-static int
-mmc_switch(struct mmc_softc *sc, uint8_t set, uint8_t index, uint8_t value)
-{
- struct mmc_command cmd;
- int err;
-
- cmd.opcode = MMC_SWITCH_FUNC;
- cmd.arg = (MMC_SWITCH_FUNC_WR << 24) |
- (index << 16) |
- (value << 8) |
- set;
- cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
- cmd.data = NULL;
- err = mmc_wait_for_cmd(sc, &cmd, 0);
+ sc->retune_paused++;
+ err = mmc_wait_for_command(sc, MMC_SELECT_CARD, (uint32_t)rca << 16,
+ flags, NULL, CMD_RETRIES);
+ sc->retune_paused--;
return (err);
}
@@ -633,8 +725,8 @@
struct mmc_command cmd;
struct mmc_data data;
- memset(&cmd, 0, sizeof(struct mmc_command));
- memset(&data, 0, sizeof(struct mmc_data));
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&data, 0, sizeof(data));
memset(res, 0, 64);
cmd.opcode = SD_SWITCH_FUNC;
@@ -649,12 +741,13 @@
data.len = 64;
data.flags = MMC_DATA_READ;
- err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
+ err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES);
return (err);
}
static int
-mmc_set_card_bus_width(struct mmc_softc *sc, uint16_t rca, int width)
+mmc_set_card_bus_width(struct mmc_softc *sc, struct mmc_ivars *ivar,
+ enum mmc_bus_timing timing)
{
struct mmc_command cmd;
int err;
@@ -661,17 +754,18 @@
uint8_t value;
if (mmcbr_get_mode(sc->dev) == mode_sd) {
- memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&cmd, 0, sizeof(cmd));
cmd.opcode = ACMD_SET_CLR_CARD_DETECT;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
cmd.arg = SD_CLR_CARD_DETECT;
- err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES);
+ err = mmc_wait_for_app_cmd(sc->dev, sc->dev, ivar->rca, &cmd,
+ CMD_RETRIES);
if (err != 0)
return (err);
- memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&cmd, 0, sizeof(cmd));
cmd.opcode = ACMD_SET_BUS_WIDTH;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
- switch (width) {
+ switch (ivar->bus_width) {
case bus_width_1:
cmd.arg = SD_BUS_WIDTH_1;
break;
@@ -681,79 +775,245 @@
default:
return (MMC_ERR_INVALID);
}
- err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES);
+ err = mmc_wait_for_app_cmd(sc->dev, sc->dev, ivar->rca, &cmd,
+ CMD_RETRIES);
} else {
- switch (width) {
+ switch (ivar->bus_width) {
case bus_width_1:
+ if (timing == bus_timing_mmc_hs400 ||
+ timing == bus_timing_mmc_hs400es)
+ return (MMC_ERR_INVALID);
value = EXT_CSD_BUS_WIDTH_1;
break;
case bus_width_4:
- value = EXT_CSD_BUS_WIDTH_4;
+ switch (timing) {
+ case bus_timing_mmc_ddr52:
+ value = EXT_CSD_BUS_WIDTH_4_DDR;
+ break;
+ case bus_timing_mmc_hs400:
+ case bus_timing_mmc_hs400es:
+ return (MMC_ERR_INVALID);
+ default:
+ value = EXT_CSD_BUS_WIDTH_4;
+ break;
+ }
break;
case bus_width_8:
- value = EXT_CSD_BUS_WIDTH_8;
+ value = 0;
+ switch (timing) {
+ case bus_timing_mmc_hs400es:
+ value = EXT_CSD_BUS_WIDTH_ES;
+ /* FALLTHROUGH */
+ case bus_timing_mmc_ddr52:
+ case bus_timing_mmc_hs400:
+ value |= EXT_CSD_BUS_WIDTH_8_DDR;
+ break;
+ default:
+ value = EXT_CSD_BUS_WIDTH_8;
+ break;
+ }
break;
default:
return (MMC_ERR_INVALID);
}
- err = mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
- value);
+ err = mmc_switch(sc->dev, sc->dev, ivar->rca,
+ EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, value,
+ ivar->cmd6_time, true);
}
return (err);
}
static int
-mmc_set_timing(struct mmc_softc *sc, int timing)
+mmc_set_power_class(struct mmc_softc *sc, struct mmc_ivars *ivar)
{
- int err;
- uint8_t value;
- u_char switch_res[64];
+ device_t dev;
+ const uint8_t *ext_csd;
+ uint32_t clock;
+ uint8_t value;
- switch (timing) {
- case bus_timing_normal:
- value = 0;
+ dev = sc->dev;
+ if (mmcbr_get_mode(dev) != mode_mmc || ivar->csd.spec_vers < 4)
+ return (MMC_ERR_NONE);
+
+ value = 0;
+ ext_csd = ivar->raw_ext_csd;
+ clock = mmcbr_get_clock(dev);
+ switch (1 << mmcbr_get_vdd(dev)) {
+ case MMC_OCR_LOW_VOLTAGE:
+ if (clock <= MMC_TYPE_HS_26_MAX)
+ value = ext_csd[EXT_CSD_PWR_CL_26_195];
+ else if (clock <= MMC_TYPE_HS_52_MAX) {
+ if (mmcbr_get_timing(dev) >= bus_timing_mmc_ddr52 &&
+ ivar->bus_width >= bus_width_4)
+ value = ext_csd[EXT_CSD_PWR_CL_52_195_DDR];
+ else
+ value = ext_csd[EXT_CSD_PWR_CL_52_195];
+ } else if (clock <= MMC_TYPE_HS200_HS400ES_MAX)
+ value = ext_csd[EXT_CSD_PWR_CL_200_195];
break;
- case bus_timing_hs:
- value = 1;
+ case MMC_OCR_270_280:
+ case MMC_OCR_280_290:
+ case MMC_OCR_290_300:
+ case MMC_OCR_300_310:
+ case MMC_OCR_310_320:
+ case MMC_OCR_320_330:
+ case MMC_OCR_330_340:
+ case MMC_OCR_340_350:
+ case MMC_OCR_350_360:
+ if (clock <= MMC_TYPE_HS_26_MAX)
+ value = ext_csd[EXT_CSD_PWR_CL_26_360];
+ else if (clock <= MMC_TYPE_HS_52_MAX) {
+ if (mmcbr_get_timing(dev) == bus_timing_mmc_ddr52 &&
+ ivar->bus_width >= bus_width_4)
+ value = ext_csd[EXT_CSD_PWR_CL_52_360_DDR];
+ else
+ value = ext_csd[EXT_CSD_PWR_CL_52_360];
+ } else if (clock <= MMC_TYPE_HS200_HS400ES_MAX) {
+ if (ivar->bus_width == bus_width_8)
+ value = ext_csd[EXT_CSD_PWR_CL_200_360_DDR];
+ else
+ value = ext_csd[EXT_CSD_PWR_CL_200_360];
+ }
break;
default:
+ device_printf(dev, "No power class support for VDD 0x%x\n",
+ 1 << mmcbr_get_vdd(dev));
return (MMC_ERR_INVALID);
}
- if (mmcbr_get_mode(sc->dev) == mode_sd)
+
+ if (ivar->bus_width == bus_width_8)
+ value = (value & EXT_CSD_POWER_CLASS_8BIT_MASK) >>
+ EXT_CSD_POWER_CLASS_8BIT_SHIFT;
+ else
+ value = (value & EXT_CSD_POWER_CLASS_4BIT_MASK) >>
+ EXT_CSD_POWER_CLASS_4BIT_SHIFT;
+
+ if (value == 0)
+ return (MMC_ERR_NONE);
+
+ return (mmc_switch(dev, dev, ivar->rca, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_POWER_CLASS, value, ivar->cmd6_time, true));
+}
+
+static int
+mmc_set_timing(struct mmc_softc *sc, struct mmc_ivars *ivar,
+ enum mmc_bus_timing timing)
+{
+ u_char switch_res[64];
+ uint8_t value;
+ int err;
+
+ if (mmcbr_get_mode(sc->dev) == mode_sd) {
+ switch (timing) {
+ case bus_timing_normal:
+ value = SD_SWITCH_NORMAL_MODE;
+ break;
+ case bus_timing_hs:
+ value = SD_SWITCH_HS_MODE;
+ break;
+ default:
+ return (MMC_ERR_INVALID);
+ }
err = mmc_sd_switch(sc, SD_SWITCH_MODE_SET, SD_SWITCH_GROUP1,
value, switch_res);
- else
- err = mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, value);
+ if (err != MMC_ERR_NONE)
+ return (err);
+ if ((switch_res[16] & 0xf) != value)
+ return (MMC_ERR_FAILED);
+ mmcbr_set_timing(sc->dev, timing);
+ mmcbr_update_ios(sc->dev);
+ } else {
+ switch (timing) {
+ case bus_timing_normal:
+ value = EXT_CSD_HS_TIMING_BC;
+ break;
+ case bus_timing_hs:
+ case bus_timing_mmc_ddr52:
+ value = EXT_CSD_HS_TIMING_HS;
+ break;
+ case bus_timing_mmc_hs200:
+ value = EXT_CSD_HS_TIMING_HS200;
+ break;
+ case bus_timing_mmc_hs400:
+ case bus_timing_mmc_hs400es:
+ value = EXT_CSD_HS_TIMING_HS400;
+ break;
+ default:
+ return (MMC_ERR_INVALID);
+ }
+ err = mmc_switch(sc->dev, sc->dev, ivar->rca,
+ EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, value,
+ ivar->cmd6_time, false);
+ if (err != MMC_ERR_NONE)
+ return (err);
+ mmcbr_set_timing(sc->dev, timing);
+ mmcbr_update_ios(sc->dev);
+ err = mmc_switch_status(sc->dev, sc->dev, ivar->rca,
+ ivar->cmd6_time);
+ }
return (err);
}
static int
+mmc_set_vccq(struct mmc_softc *sc, struct mmc_ivars *ivar,
+ enum mmc_bus_timing timing)
+{
+
+ if (isset(&ivar->vccq_120, timing))
+ mmcbr_set_vccq(sc->dev, vccq_120);
+ else if (isset(&ivar->vccq_180, timing))
+ mmcbr_set_vccq(sc->dev, vccq_180);
+ else
+ mmcbr_set_vccq(sc->dev, vccq_330);
+ if (mmcbr_switch_vccq(sc->dev) != 0)
+ return (MMC_ERR_INVALID);
+ else
+ return (MMC_ERR_NONE);
+}
+
+static const uint8_t p8[8] = {
+ 0x55, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t p8ok[8] = {
+ 0xAA, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t p4[4] = {
+ 0x5A, 0x00, 0x00, 0x00
+};
+
+static const uint8_t p4ok[4] = {
+ 0xA5, 0x00, 0x00, 0x00
+};
+
+static int
mmc_test_bus_width(struct mmc_softc *sc)
{
struct mmc_command cmd;
struct mmc_data data;
+ uint8_t buf[8];
int err;
- uint8_t buf[8];
- uint8_t p8[8] = { 0x55, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- uint8_t p8ok[8] = { 0xAA, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- uint8_t p4[4] = { 0x5A, 0x00, 0x00, 0x00, };
- uint8_t p4ok[4] = { 0xA5, 0x00, 0x00, 0x00, };
if (mmcbr_get_caps(sc->dev) & MMC_CAP_8_BIT_DATA) {
mmcbr_set_bus_width(sc->dev, bus_width_8);
mmcbr_update_ios(sc->dev);
+ sc->squelched++; /* Errors are expected, squelch reporting. */
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&data, 0, sizeof(data));
cmd.opcode = MMC_BUSTEST_W;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
cmd.data = &data;
- data.data = p8;
+ data.data = __DECONST(void *, p8);
data.len = 8;
data.flags = MMC_DATA_WRITE;
- mmc_wait_for_cmd(sc, &cmd, 0);
-
+ mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, 0);
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&data, 0, sizeof(data));
cmd.opcode = MMC_BUSTEST_R;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -762,8 +1022,9 @@
data.data = buf;
data.len = 8;
data.flags = MMC_DATA_READ;
- err = mmc_wait_for_cmd(sc, &cmd, 0);
-
+ err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, 0);
+ sc->squelched--;
+
mmcbr_set_bus_width(sc->dev, bus_width_1);
mmcbr_update_ios(sc->dev);
@@ -775,16 +1036,21 @@
mmcbr_set_bus_width(sc->dev, bus_width_4);
mmcbr_update_ios(sc->dev);
+ sc->squelched++; /* Errors are expected, squelch reporting. */
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&data, 0, sizeof(data));
cmd.opcode = MMC_BUSTEST_W;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
cmd.data = &data;
- data.data = p4;
+ data.data = __DECONST(void *, p4);
data.len = 4;
data.flags = MMC_DATA_WRITE;
- mmc_wait_for_cmd(sc, &cmd, 0);
-
+ mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, 0);
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&data, 0, sizeof(data));
cmd.opcode = MMC_BUSTEST_R;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
@@ -793,7 +1059,8 @@
data.data = buf;
data.len = 4;
data.flags = MMC_DATA_READ;
- err = mmc_wait_for_cmd(sc, &cmd, 0);
+ err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, 0);
+ sc->squelched--;
mmcbr_set_bus_width(sc->dev, bus_width_1);
mmcbr_update_ios(sc->dev);
@@ -810,6 +1077,7 @@
const int i = (bit_len / 32) - (start / 32) - 1;
const int shift = start & 31;
uint32_t retval = bits[i] >> shift;
+
if (size + shift > 32)
retval |= bits[i - 1] << (32 - shift);
return (retval & ((1llu << size) - 1));
@@ -834,7 +1102,7 @@
}
static void
-mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid)
+mmc_decode_cid_mmc(uint32_t *raw_cid, struct mmc_cid *cid, bool is_4_41p)
{
int i;
@@ -848,7 +1116,11 @@
cid->prv = mmc_get_bits(raw_cid, 128, 48, 8);
cid->psn = mmc_get_bits(raw_cid, 128, 16, 32);
cid->mdt_month = mmc_get_bits(raw_cid, 128, 12, 4);
- cid->mdt_year = mmc_get_bits(raw_cid, 128, 8, 4) + 1997;
+ cid->mdt_year = mmc_get_bits(raw_cid, 128, 8, 4);
+ if (is_4_41p)
+ cid->mdt_year += 2013;
+ else
+ cid->mdt_year += 1997;
}
static void
@@ -861,9 +1133,12 @@
/*
* Format a card ID string for use by the mmcsd driver, it's what
* appears between the <> in the following:
- * mmcsd0: 968MB <SD SD01G 8.0 SN 2686905 Mfg 08/2008 by 3 TN> at mmc0
+ * mmcsd0: 968MB <SD SD01G 8.0 SN 2686905 MFG 08/2008 by 3 TN> at mmc0
* 22.5MHz/4bit/128-block
*
+ * Also format just the card serial number, which the mmcsd driver will
+ * use as the disk->d_ident string.
+ *
* The card_id_string in mmc_ivars is currently allocated as 64 bytes,
* and our max formatted length is currently 55 bytes if every field
* contains the largest value.
@@ -877,8 +1152,10 @@
snprintf(oidstr, sizeof(oidstr), "%c%c", c1, c2);
else
snprintf(oidstr, sizeof(oidstr), "0x%04x", ivar->cid.oid);
+ snprintf(ivar->card_sn_string, sizeof(ivar->card_sn_string),
+ "%08X", ivar->cid.psn);
snprintf(ivar->card_id_string, sizeof(ivar->card_id_string),
- "%s%s %s %d.%d SN %u MFG %02d/%04d by %d %s",
+ "%s%s %s %d.%d SN %08X MFG %02d/%04d by %d %s",
ivar->mode == mode_sd ? "SD" : "MMC", ivar->high_cap ? "HC" : "",
ivar->cid.pnm, ivar->cid.prv >> 4, ivar->cid.prv & 0x0f,
ivar->cid.psn, ivar->cid.mdt_month, ivar->cid.mdt_year,
@@ -901,7 +1178,7 @@
1000, 5000, 10000, 25000, 35000, 45000, 800000, 200000
};
-static void
+static int
mmc_decode_csd_sd(uint32_t *raw_csd, struct mmc_csd *csd)
{
int v;
@@ -924,10 +1201,14 @@
csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1);
csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1);
csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1);
- csd->vdd_r_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 59, 3)];
- csd->vdd_r_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 56, 3)];
- csd->vdd_w_curr_min = cur_min[mmc_get_bits(raw_csd, 128, 53, 3)];
- csd->vdd_w_curr_max = cur_max[mmc_get_bits(raw_csd, 128, 50, 3)];
+ csd->vdd_r_curr_min =
+ cur_min[mmc_get_bits(raw_csd, 128, 59, 3)];
+ csd->vdd_r_curr_max =
+ cur_max[mmc_get_bits(raw_csd, 128, 56, 3)];
+ csd->vdd_w_curr_min =
+ cur_min[mmc_get_bits(raw_csd, 128, 53, 3)];
+ csd->vdd_w_curr_max =
+ cur_max[mmc_get_bits(raw_csd, 128, 50, 3)];
m = mmc_get_bits(raw_csd, 128, 62, 12);
e = mmc_get_bits(raw_csd, 128, 47, 3);
csd->capacity = ((1 + m) << (e + 2)) * csd->read_bl_len;
@@ -938,6 +1219,7 @@
csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3);
csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4);
csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1);
+ return (MMC_ERR_NONE);
} else if (v == 1) {
m = mmc_get_bits(raw_csd, 128, 115, 4);
e = mmc_get_bits(raw_csd, 128, 112, 3);
@@ -952,8 +1234,8 @@
csd->write_blk_misalign = mmc_get_bits(raw_csd, 128, 78, 1);
csd->read_blk_misalign = mmc_get_bits(raw_csd, 128, 77, 1);
csd->dsr_imp = mmc_get_bits(raw_csd, 128, 76, 1);
- csd->capacity = ((uint64_t)mmc_get_bits(raw_csd, 128, 48, 22) + 1) *
- 512 * 1024;
+ csd->capacity = ((uint64_t)mmc_get_bits(raw_csd, 128, 48, 22) +
+ 1) * 512 * 1024;
csd->erase_blk_en = mmc_get_bits(raw_csd, 128, 46, 1);
csd->erase_sector = mmc_get_bits(raw_csd, 128, 39, 7) + 1;
csd->wp_grp_size = mmc_get_bits(raw_csd, 128, 32, 7);
@@ -961,8 +1243,9 @@
csd->r2w_factor = 1 << mmc_get_bits(raw_csd, 128, 26, 3);
csd->write_bl_len = 1 << mmc_get_bits(raw_csd, 128, 22, 4);
csd->write_bl_partial = mmc_get_bits(raw_csd, 128, 21, 1);
- } else
- panic("unknown SD CSD version");
+ return (MMC_ERR_NONE);
+ }
+ return (MMC_ERR_INVALID);
}
static void
@@ -1046,11 +1329,12 @@
struct mmc_command cmd;
int err;
+ memset(&cmd, 0, sizeof(cmd));
cmd.opcode = MMC_ALL_SEND_CID;
cmd.arg = 0;
cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
cmd.data = NULL;
- err = mmc_wait_for_cmd(sc, &cmd, 0);
+ err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES);
memcpy(rawcid, cmd.resp, 4 * sizeof(uint32_t));
return (err);
}
@@ -1061,11 +1345,12 @@
struct mmc_command cmd;
int err;
+ memset(&cmd, 0, sizeof(cmd));
cmd.opcode = MMC_SEND_CSD;
cmd.arg = rca << 16;
cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
cmd.data = NULL;
- err = mmc_wait_for_cmd(sc, &cmd, 0);
+ err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES);
memcpy(rawcsd, cmd.resp, 4 * sizeof(uint32_t));
return (err);
}
@@ -1077,8 +1362,8 @@
struct mmc_command cmd;
struct mmc_data data;
- memset(&cmd, 0, sizeof(struct mmc_command));
- memset(&data, 0, sizeof(struct mmc_data));
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&data, 0, sizeof(data));
memset(rawscr, 0, 8);
cmd.opcode = ACMD_SEND_SCR;
@@ -1090,7 +1375,7 @@
data.len = 8;
data.flags = MMC_DATA_READ;
- err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES);
+ err = mmc_wait_for_app_cmd(sc->dev, sc->dev, rca, &cmd, CMD_RETRIES);
rawscr[0] = be32toh(rawscr[0]);
rawscr[1] = be32toh(rawscr[1]);
return (err);
@@ -1097,38 +1382,14 @@
}
static int
-mmc_send_ext_csd(struct mmc_softc *sc, uint8_t *rawextcsd)
+mmc_app_sd_status(struct mmc_softc *sc, uint16_t rca, uint32_t *rawsdstatus)
{
- int err;
struct mmc_command cmd;
struct mmc_data data;
-
- memset(&cmd, 0, sizeof(struct mmc_command));
- memset(&data, 0, sizeof(struct mmc_data));
-
- memset(rawextcsd, 0, 512);
- cmd.opcode = MMC_SEND_EXT_CSD;
- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
- cmd.arg = 0;
- cmd.data = &data;
-
- data.data = rawextcsd;
- data.len = 512;
- data.flags = MMC_DATA_READ;
-
- err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
- return (err);
-}
-
-static int
-mmc_app_sd_status(struct mmc_softc *sc, uint16_t rca, uint32_t *rawsdstatus)
-{
int err, i;
- struct mmc_command cmd;
- struct mmc_data data;
- memset(&cmd, 0, sizeof(struct mmc_command));
- memset(&data, 0, sizeof(struct mmc_data));
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&data, 0, sizeof(data));
memset(rawsdstatus, 0, 64);
cmd.opcode = ACMD_SD_STATUS;
@@ -1140,7 +1401,7 @@
data.len = 64;
data.flags = MMC_DATA_READ;
- err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES);
+ err = mmc_wait_for_app_cmd(sc->dev, sc->dev, rca, &cmd, CMD_RETRIES);
for (i = 0; i < 16; i++)
rawsdstatus[i] = be32toh(rawsdstatus[i]);
return (err);
@@ -1152,11 +1413,12 @@
struct mmc_command cmd;
int err;
+ memset(&cmd, 0, sizeof(cmd));
cmd.opcode = MMC_SET_RELATIVE_ADDR;
cmd.arg = resp << 16;
cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
cmd.data = NULL;
- err = mmc_wait_for_cmd(sc, &cmd, 0);
+ err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES);
return (err);
}
@@ -1166,56 +1428,155 @@
struct mmc_command cmd;
int err;
+ memset(&cmd, 0, sizeof(cmd));
cmd.opcode = SD_SEND_RELATIVE_ADDR;
cmd.arg = 0;
cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
cmd.data = NULL;
- err = mmc_wait_for_cmd(sc, &cmd, 0);
+ err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES);
*resp = cmd.resp[0];
return (err);
}
static int
-mmc_send_status(struct mmc_softc *sc, uint16_t rca, uint32_t *status)
+mmc_set_blocklen(struct mmc_softc *sc, uint32_t len)
{
struct mmc_command cmd;
int err;
- cmd.opcode = MMC_SEND_STATUS;
- cmd.arg = rca << 16;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = MMC_SET_BLOCKLEN;
+ cmd.arg = len;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
cmd.data = NULL;
- err = mmc_wait_for_cmd(sc, &cmd, 0);
- *status = cmd.resp[0];
+ err = mmc_wait_for_cmd(sc->dev, sc->dev, &cmd, CMD_RETRIES);
return (err);
}
-static int
-mmc_set_blocklen(struct mmc_softc *sc, uint32_t len)
+static uint32_t
+mmc_timing_to_dtr(struct mmc_ivars *ivar, enum mmc_bus_timing timing)
{
- struct mmc_command cmd;
- int err;
- cmd.opcode = MMC_SET_BLOCKLEN;
- cmd.arg = len;
- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
- cmd.data = NULL;
- err = mmc_wait_for_cmd(sc, &cmd, 0);
- return (err);
+ switch (timing) {
+ case bus_timing_normal:
+ return (ivar->tran_speed);
+ case bus_timing_hs:
+ return (ivar->hs_tran_speed);
+ case bus_timing_uhs_sdr12:
+ return (SD_SDR12_MAX);
+ case bus_timing_uhs_sdr25:
+ return (SD_SDR25_MAX);
+ case bus_timing_uhs_ddr50:
+ return (SD_DDR50_MAX);
+ case bus_timing_uhs_sdr50:
+ return (SD_SDR50_MAX);
+ case bus_timing_uhs_sdr104:
+ return (SD_SDR104_MAX);
+ case bus_timing_mmc_ddr52:
+ return (MMC_TYPE_DDR52_MAX);
+ case bus_timing_mmc_hs200:
+ case bus_timing_mmc_hs400:
+ case bus_timing_mmc_hs400es:
+ return (MMC_TYPE_HS200_HS400ES_MAX);
+ }
+ return (0);
}
+static const char *
+mmc_timing_to_string(enum mmc_bus_timing timing)
+{
+
+ switch (timing) {
+ case bus_timing_normal:
+ return ("normal speed");
+ case bus_timing_hs:
+ return ("high speed");
+ case bus_timing_uhs_sdr12:
+ case bus_timing_uhs_sdr25:
+ case bus_timing_uhs_sdr50:
+ case bus_timing_uhs_sdr104:
+ return ("single data rate");
+ case bus_timing_uhs_ddr50:
+ case bus_timing_mmc_ddr52:
+ return ("dual data rate");
+ case bus_timing_mmc_hs200:
+ return ("HS200");
+ case bus_timing_mmc_hs400:
+ return ("HS400");
+ case bus_timing_mmc_hs400es:
+ return ("HS400 with enhanced strobe");
+ }
+ return ("");
+}
+
+static bool
+mmc_host_timing(device_t dev, enum mmc_bus_timing timing)
+{
+ int host_caps;
+
+ host_caps = mmcbr_get_caps(dev);
+
+#define HOST_TIMING_CAP(host_caps, cap) ({ \
+ bool retval; \
+ if (((host_caps) & (cap)) == (cap)) \
+ retval = true; \
+ else \
+ retval = false; \
+ retval; \
+})
+
+ switch (timing) {
+ case bus_timing_normal:
+ return (true);
+ case bus_timing_hs:
+ return (HOST_TIMING_CAP(host_caps, MMC_CAP_HSPEED));
+ case bus_timing_uhs_sdr12:
+ return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR12));
+ case bus_timing_uhs_sdr25:
+ return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR25));
+ case bus_timing_uhs_ddr50:
+ return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_DDR50));
+ case bus_timing_uhs_sdr50:
+ return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR50));
+ case bus_timing_uhs_sdr104:
+ return (HOST_TIMING_CAP(host_caps, MMC_CAP_UHS_SDR104));
+ case bus_timing_mmc_ddr52:
+ return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_DDR52));
+ case bus_timing_mmc_hs200:
+ return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS200));
+ case bus_timing_mmc_hs400:
+ return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400));
+ case bus_timing_mmc_hs400es:
+ return (HOST_TIMING_CAP(host_caps, MMC_CAP_MMC_HS400 |
+ MMC_CAP_MMC_ENH_STROBE));
+ }
+
+#undef HOST_TIMING_CAP
+
+ return (false);
+}
+
static void
mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard)
{
- device_printf(dev, "Card at relative address %d%s:\n",
+ enum mmc_bus_timing max_timing, timing;
+
+ device_printf(dev, "Card at relative address 0x%04x%s:\n",
ivar->rca, newcard ? " added" : "");
device_printf(dev, " card: %s\n", ivar->card_id_string);
- device_printf(dev, " bus: %ubit, %uMHz%s\n",
+ max_timing = bus_timing_normal;
+ for (timing = bus_timing_max; timing > bus_timing_normal; timing--) {
+ if (isset(&ivar->timings, timing)) {
+ max_timing = timing;
+ break;
+ }
+ }
+ device_printf(dev, " quirks: %b\n", ivar->quirks, MMC_QUIRKS_FMT);
+ device_printf(dev, " bus: %ubit, %uMHz (%s timing)\n",
(ivar->bus_width == bus_width_1 ? 1 :
(ivar->bus_width == bus_width_4 ? 4 : 8)),
- (ivar->timing == bus_timing_hs ?
- ivar->hs_tran_speed : ivar->tran_speed) / 1000000,
- ivar->timing == bus_timing_hs ? ", high speed timing" : "");
+ mmc_timing_to_dtr(ivar, timing) / 1000000,
+ mmc_timing_to_string(timing));
device_printf(dev, " memory: %u blocks, erase sector %u blocks%s\n",
ivar->sec_count, ivar->erase_sector,
ivar->read_only ? ", read-only" : "");
@@ -1224,18 +1585,23 @@
static void
mmc_discover_cards(struct mmc_softc *sc)
{
+ u_char switch_res[64];
+ uint32_t raw_cid[4];
struct mmc_ivars *ivar = NULL;
- device_t *devlist;
- int err, i, devcount, newcard;
- uint32_t raw_cid[4], resp, sec_count, status;
+ const struct mmc_quirk *quirk;
device_t child;
+ int err, host_caps, i, newcard;
+ uint32_t resp, sec_count, status;
uint16_t rca = 2;
- u_char switch_res[64];
+ host_caps = mmcbr_get_caps(sc->dev);
if (bootverbose || mmc_debug)
device_printf(sc->dev, "Probing cards\n");
while (1) {
+ child = NULL;
+ sc->squelched++; /* Errors are expected, squelch reporting. */
err = mmc_all_send_cid(sc, raw_cid);
+ sc->squelched--;
if (err == MMC_ERR_TIMEOUT)
break;
if (err != MMC_ERR_NONE) {
@@ -1243,18 +1609,17 @@
break;
}
newcard = 1;
- if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
- return;
- for (i = 0; i < devcount; i++) {
- ivar = device_get_ivars(devlist[i]);
- if (memcmp(ivar->raw_cid, raw_cid, sizeof(raw_cid)) == 0) {
+ for (i = 0; i < sc->child_count; i++) {
+ ivar = device_get_ivars(sc->child_list[i]);
+ if (memcmp(ivar->raw_cid, raw_cid, sizeof(raw_cid)) ==
+ 0) {
newcard = 0;
break;
}
}
- free(devlist, M_TEMP);
if (bootverbose || mmc_debug) {
- device_printf(sc->dev, "%sard detected (CID %08x%08x%08x%08x)\n",
+ device_printf(sc->dev,
+ "%sard detected (CID %08x%08x%08x%08x)\n",
newcard ? "New c" : "C",
raw_cid[0], raw_cid[1], raw_cid[2], raw_cid[3]);
}
@@ -1261,21 +1626,29 @@
if (newcard) {
ivar = malloc(sizeof(struct mmc_ivars), M_DEVBUF,
M_WAITOK | M_ZERO);
- if (!ivar)
- return;
memcpy(ivar->raw_cid, raw_cid, sizeof(raw_cid));
}
if (mmcbr_get_ro(sc->dev))
ivar->read_only = 1;
ivar->bus_width = bus_width_1;
- ivar->timing = bus_timing_normal;
+ setbit(&ivar->timings, bus_timing_normal);
ivar->mode = mmcbr_get_mode(sc->dev);
if (ivar->mode == mode_sd) {
mmc_decode_cid_sd(ivar->raw_cid, &ivar->cid);
- mmc_send_relative_addr(sc, &resp);
+ err = mmc_send_relative_addr(sc, &resp);
+ if (err != MMC_ERR_NONE) {
+ device_printf(sc->dev,
+ "Error getting RCA %d\n", err);
+ goto free_ivar;
+ }
ivar->rca = resp >> 16;
/* Get card CSD. */
- mmc_send_csd(sc, ivar->rca, ivar->raw_csd);
+ err = mmc_send_csd(sc, ivar->rca, ivar->raw_csd);
+ if (err != MMC_ERR_NONE) {
+ device_printf(sc->dev,
+ "Error getting CSD %d\n", err);
+ goto free_ivar;
+ }
if (bootverbose || mmc_debug)
device_printf(sc->dev,
"%sard detected (CSD %08x%08x%08x%08x)\n",
@@ -1282,42 +1655,74 @@
newcard ? "New c" : "C", ivar->raw_csd[0],
ivar->raw_csd[1], ivar->raw_csd[2],
ivar->raw_csd[3]);
- mmc_decode_csd_sd(ivar->raw_csd, &ivar->csd);
+ err = mmc_decode_csd_sd(ivar->raw_csd, &ivar->csd);
+ if (err != MMC_ERR_NONE) {
+ device_printf(sc->dev, "Error decoding CSD\n");
+ goto free_ivar;
+ }
ivar->sec_count = ivar->csd.capacity / MMC_SECTOR_SIZE;
if (ivar->csd.csd_structure > 0)
ivar->high_cap = 1;
ivar->tran_speed = ivar->csd.tran_speed;
- ivar->erase_sector = ivar->csd.erase_sector *
+ ivar->erase_sector = ivar->csd.erase_sector *
ivar->csd.write_bl_len / MMC_SECTOR_SIZE;
-
- err = mmc_send_status(sc, ivar->rca, &status);
+
+ err = mmc_send_status(sc->dev, sc->dev, ivar->rca,
+ &status);
if (err != MMC_ERR_NONE) {
device_printf(sc->dev,
"Error reading card status %d\n", err);
- break;
+ goto free_ivar;
}
if ((status & R1_CARD_IS_LOCKED) != 0) {
device_printf(sc->dev,
- "Card is password protected, skipping.\n");
- break;
+ "Card is password protected, skipping\n");
+ goto free_ivar;
}
- /* Get card SCR. Card must be selected to fetch it. */
- mmc_select_card(sc, ivar->rca);
- mmc_app_send_scr(sc, ivar->rca, ivar->raw_scr);
+ /* Get card SCR. Card must be selected to fetch it. */
+ err = mmc_select_card(sc, ivar->rca);
+ if (err != MMC_ERR_NONE) {
+ device_printf(sc->dev,
+ "Error selecting card %d\n", err);
+ goto free_ivar;
+ }
+ err = mmc_app_send_scr(sc, ivar->rca, ivar->raw_scr);
+ if (err != MMC_ERR_NONE) {
+ device_printf(sc->dev,
+ "Error reading SCR %d\n", err);
+ goto free_ivar;
+ }
mmc_app_decode_scr(ivar->raw_scr, &ivar->scr);
/* Get card switch capabilities (command class 10). */
if ((ivar->scr.sda_vsn >= 1) &&
- (ivar->csd.ccc & (1<<10))) {
- mmc_sd_switch(sc, SD_SWITCH_MODE_CHECK,
+ (ivar->csd.ccc & (1 << 10))) {
+ err = mmc_sd_switch(sc, SD_SWITCH_MODE_CHECK,
SD_SWITCH_GROUP1, SD_SWITCH_NOCHANGE,
switch_res);
- if (switch_res[13] & 2) {
- ivar->timing = bus_timing_hs;
- ivar->hs_tran_speed = SD_MAX_HS;
+ if (err == MMC_ERR_NONE &&
+ switch_res[13] & (1 << SD_SWITCH_HS_MODE)) {
+ setbit(&ivar->timings, bus_timing_hs);
+ ivar->hs_tran_speed = SD_HS_MAX;
}
}
- mmc_app_sd_status(sc, ivar->rca, ivar->raw_sd_status);
+
+ /*
+ * We deselect then reselect the card here. Some cards
+ * become unselected and timeout with the above two
+ * commands, although the state tables / diagrams in the
+ * standard suggest they go back to the transfer state.
+ * Other cards don't become deselected, and if we
+ * attempt to blindly re-select them, we get timeout
+ * errors from some controllers. So we deselect then
+ * reselect to handle all situations. The only thing we
+ * use from the sd_status is the erase sector size, but
+ * it is still nice to get that right.
+ */
+ (void)mmc_select_card(sc, 0);
+ (void)mmc_select_card(sc, ivar->rca);
+ (void)mmc_app_sd_status(sc, ivar->rca,
+ ivar->raw_sd_status);
mmc_app_decode_sd_status(ivar->raw_sd_status,
&ivar->sd_status);
if (ivar->sd_status.au_size != 0) {
@@ -1324,41 +1729,25 @@
ivar->erase_sector =
16 << ivar->sd_status.au_size;
}
- mmc_select_card(sc, 0);
- /* Find max supported bus width. */
- if ((mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA) &&
+ /* Find maximum supported bus width. */
+ if ((host_caps & MMC_CAP_4_BIT_DATA) &&
(ivar->scr.bus_widths & SD_SCR_BUS_WIDTH_4))
ivar->bus_width = bus_width_4;
- /*
- * Some cards that report maximum I/O block sizes
- * greater than 512 require the block length to be
- * set to 512, even though that is supposed to be
- * the default. Example:
- *
- * Transcend 2GB SDSC card, CID:
- * mid=0x1b oid=0x534d pnm="00000" prv=1.0 mdt=00.2000
- */
- if (ivar->csd.read_bl_len != MMC_SECTOR_SIZE ||
- ivar->csd.write_bl_len != MMC_SECTOR_SIZE)
- mmc_set_blocklen(sc, MMC_SECTOR_SIZE);
-
- mmc_format_card_id_string(ivar);
-
- if (bootverbose || mmc_debug)
- mmc_log_card(sc->dev, ivar, newcard);
- if (newcard) {
- /* Add device. */
- child = device_add_child(sc->dev, NULL, -1);
- device_set_ivars(child, ivar);
- }
- return;
+ goto child_common;
}
- mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid);
ivar->rca = rca++;
- mmc_set_relative_addr(sc, ivar->rca);
+ err = mmc_set_relative_addr(sc, ivar->rca);
+ if (err != MMC_ERR_NONE) {
+ device_printf(sc->dev, "Error setting RCA %d\n", err);
+ goto free_ivar;
+ }
/* Get card CSD. */
- mmc_send_csd(sc, ivar->rca, ivar->raw_csd);
+ err = mmc_send_csd(sc, ivar->rca, ivar->raw_csd);
+ if (err != MMC_ERR_NONE) {
+ device_printf(sc->dev, "Error getting CSD %d\n", err);
+ goto free_ivar;
+ }
if (bootverbose || mmc_debug)
device_printf(sc->dev,
"%sard detected (CSD %08x%08x%08x%08x)\n",
@@ -1369,26 +1758,37 @@
mmc_decode_csd_mmc(ivar->raw_csd, &ivar->csd);
ivar->sec_count = ivar->csd.capacity / MMC_SECTOR_SIZE;
ivar->tran_speed = ivar->csd.tran_speed;
- ivar->erase_sector = ivar->csd.erase_sector *
+ ivar->erase_sector = ivar->csd.erase_sector *
ivar->csd.write_bl_len / MMC_SECTOR_SIZE;
- err = mmc_send_status(sc, ivar->rca, &status);
+ err = mmc_send_status(sc->dev, sc->dev, ivar->rca, &status);
if (err != MMC_ERR_NONE) {
device_printf(sc->dev,
"Error reading card status %d\n", err);
- break;
+ goto free_ivar;
}
if ((status & R1_CARD_IS_LOCKED) != 0) {
device_printf(sc->dev,
- "Card is password protected, skipping.\n");
- break;
+ "Card is password protected, skipping\n");
+ goto free_ivar;
}
- /* Only MMC >= 4.x cards support EXT_CSD. */
+ err = mmc_select_card(sc, ivar->rca);
+ if (err != MMC_ERR_NONE) {
+ device_printf(sc->dev, "Error selecting card %d\n",
+ err);
+ goto free_ivar;
+ }
+
+ /* Only MMC >= 4.x devices support EXT_CSD. */
if (ivar->csd.spec_vers >= 4) {
- /* Card must be selected to fetch EXT_CSD. */
- mmc_select_card(sc, ivar->rca);
- mmc_send_ext_csd(sc, ivar->raw_ext_csd);
+ err = mmc_send_ext_csd(sc->dev, sc->dev,
+ ivar->raw_ext_csd);
+ if (err != MMC_ERR_NONE) {
+ device_printf(sc->dev,
+ "Error reading EXT_CSD %d\n", err);
+ goto free_ivar;
+ }
/* Handle extended capacity from EXT_CSD */
sec_count = ivar->raw_ext_csd[EXT_CSD_SEC_CNT] +
(ivar->raw_ext_csd[EXT_CSD_SEC_CNT + 1] << 8) +
@@ -1398,31 +1798,116 @@
ivar->sec_count = sec_count;
ivar->high_cap = 1;
}
- /* Get card speed in high speed mode. */
- ivar->timing = bus_timing_hs;
- if (ivar->raw_ext_csd[EXT_CSD_CARD_TYPE]
- & EXT_CSD_CARD_TYPE_52)
- ivar->hs_tran_speed = MMC_TYPE_52_MAX_HS;
- else if (ivar->raw_ext_csd[EXT_CSD_CARD_TYPE]
- & EXT_CSD_CARD_TYPE_26)
- ivar->hs_tran_speed = MMC_TYPE_26_MAX_HS;
- else
- ivar->hs_tran_speed = ivar->tran_speed;
- /* Find max supported bus width. */
+ /* Find maximum supported bus width. */
ivar->bus_width = mmc_test_bus_width(sc);
- mmc_select_card(sc, 0);
+ /* Get device speeds beyond normal mode. */
+ if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] &
+ EXT_CSD_CARD_TYPE_HS_52) != 0) {
+ setbit(&ivar->timings, bus_timing_hs);
+ ivar->hs_tran_speed = MMC_TYPE_HS_52_MAX;
+ } else if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] &
+ EXT_CSD_CARD_TYPE_HS_26) != 0) {
+ setbit(&ivar->timings, bus_timing_hs);
+ ivar->hs_tran_speed = MMC_TYPE_HS_26_MAX;
+ }
+ if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] &
+ EXT_CSD_CARD_TYPE_DDR_52_1_2V) != 0 &&
+ (host_caps & MMC_CAP_SIGNALING_120) != 0) {
+ setbit(&ivar->timings, bus_timing_mmc_ddr52);
+ setbit(&ivar->vccq_120, bus_timing_mmc_ddr52);
+ }
+ if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] &
+ EXT_CSD_CARD_TYPE_DDR_52_1_8V) != 0 &&
+ (host_caps & MMC_CAP_SIGNALING_180) != 0) {
+ setbit(&ivar->timings, bus_timing_mmc_ddr52);
+ setbit(&ivar->vccq_180, bus_timing_mmc_ddr52);
+ }
+ if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] &
+ EXT_CSD_CARD_TYPE_HS200_1_2V) != 0 &&
+ (host_caps & MMC_CAP_SIGNALING_120) != 0) {
+ setbit(&ivar->timings, bus_timing_mmc_hs200);
+ setbit(&ivar->vccq_120, bus_timing_mmc_hs200);
+ }
+ if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] &
+ EXT_CSD_CARD_TYPE_HS200_1_8V) != 0 &&
+ (host_caps & MMC_CAP_SIGNALING_180) != 0) {
+ setbit(&ivar->timings, bus_timing_mmc_hs200);
+ setbit(&ivar->vccq_180, bus_timing_mmc_hs200);
+ }
+ if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] &
+ EXT_CSD_CARD_TYPE_HS400_1_2V) != 0 &&
+ (host_caps & MMC_CAP_SIGNALING_120) != 0 &&
+ ivar->bus_width == bus_width_8) {
+ setbit(&ivar->timings, bus_timing_mmc_hs400);
+ setbit(&ivar->vccq_120, bus_timing_mmc_hs400);
+ }
+ if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] &
+ EXT_CSD_CARD_TYPE_HS400_1_8V) != 0 &&
+ (host_caps & MMC_CAP_SIGNALING_180) != 0 &&
+ ivar->bus_width == bus_width_8) {
+ setbit(&ivar->timings, bus_timing_mmc_hs400);
+ setbit(&ivar->vccq_180, bus_timing_mmc_hs400);
+ }
+ if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] &
+ EXT_CSD_CARD_TYPE_HS400_1_2V) != 0 &&
+ (ivar->raw_ext_csd[EXT_CSD_STROBE_SUPPORT] &
+ EXT_CSD_STROBE_SUPPORT_EN) != 0 &&
+ (host_caps & MMC_CAP_SIGNALING_120) != 0 &&
+ ivar->bus_width == bus_width_8) {
+ setbit(&ivar->timings, bus_timing_mmc_hs400es);
+ setbit(&ivar->vccq_120, bus_timing_mmc_hs400es);
+ }
+ if ((ivar->raw_ext_csd[EXT_CSD_CARD_TYPE] &
+ EXT_CSD_CARD_TYPE_HS400_1_8V) != 0 &&
+ (ivar->raw_ext_csd[EXT_CSD_STROBE_SUPPORT] &
+ EXT_CSD_STROBE_SUPPORT_EN) != 0 &&
+ (host_caps & MMC_CAP_SIGNALING_180) != 0 &&
+ ivar->bus_width == bus_width_8) {
+ setbit(&ivar->timings, bus_timing_mmc_hs400es);
+ setbit(&ivar->vccq_180, bus_timing_mmc_hs400es);
+ }
+ /*
+ * Determine generic switch timeout (provided in
+ * units of 10 ms), defaulting to 500 ms.
+ */
+ ivar->cmd6_time = 500 * 1000;
+ if (ivar->raw_ext_csd[EXT_CSD_REV] >= 6)
+ ivar->cmd6_time = 10 *
+ ivar->raw_ext_csd[EXT_CSD_GEN_CMD6_TIME];
/* Handle HC erase sector size. */
if (ivar->raw_ext_csd[EXT_CSD_ERASE_GRP_SIZE] != 0) {
ivar->erase_sector = 1024 *
ivar->raw_ext_csd[EXT_CSD_ERASE_GRP_SIZE];
- mmc_switch(sc, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_ERASE_GRP_DEF, 1);
+ err = mmc_switch(sc->dev, sc->dev, ivar->rca,
+ EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_ERASE_GRP_DEF,
+ EXT_CSD_ERASE_GRP_DEF_EN,
+ ivar->cmd6_time, true);
+ if (err != MMC_ERR_NONE) {
+ device_printf(sc->dev,
+ "Error setting erase group %d\n",
+ err);
+ goto free_ivar;
+ }
}
- } else {
- ivar->bus_width = bus_width_1;
- ivar->timing = bus_timing_normal;
}
+ mmc_decode_cid_mmc(ivar->raw_cid, &ivar->cid,
+ ivar->raw_ext_csd[EXT_CSD_REV] >= 5);
+
+child_common:
+ for (quirk = &mmc_quirks[0]; quirk->mid != 0x0; quirk++) {
+ if ((quirk->mid == MMC_QUIRK_MID_ANY ||
+ quirk->mid == ivar->cid.mid) &&
+ (quirk->oid == MMC_QUIRK_OID_ANY ||
+ quirk->oid == ivar->cid.oid) &&
+ strncmp(quirk->pnm, ivar->cid.pnm,
+ sizeof(ivar->cid.pnm)) == 0) {
+ ivar->quirks = quirk->quirks;
+ break;
+ }
+ }
+
/*
* Some cards that report maximum I/O block sizes greater
* than 512 require the block length to be set to 512, even
@@ -1442,53 +1927,111 @@
if (newcard) {
/* Add device. */
child = device_add_child(sc->dev, NULL, -1);
- device_set_ivars(child, ivar);
+ if (child != NULL) {
+ device_set_ivars(child, ivar);
+ sc->child_list = realloc(sc->child_list,
+ sizeof(device_t) * sc->child_count + 1,
+ M_DEVBUF, M_WAITOK);
+ sc->child_list[sc->child_count++] = child;
+ } else
+ device_printf(sc->dev, "Error adding child\n");
}
+
+free_ivar:
+ if (newcard && child == NULL)
+ free(ivar, M_DEVBUF);
+ (void)mmc_select_card(sc, 0);
+ /*
+ * Not returning here when one MMC device could no be added
+ * potentially would mean looping forever when that device
+ * is broken (in which case it also may impact the remainder
+ * of the bus anyway, though).
+ */
+ if ((newcard && child == NULL) ||
+ mmcbr_get_mode(sc->dev) == mode_sd)
+ return;
}
}
static void
+mmc_update_child_list(struct mmc_softc *sc)
+{
+ device_t child;
+ int i, j;
+
+ if (sc->child_count == 0) {
+ free(sc->child_list, M_DEVBUF);
+ return;
+ }
+ for (i = j = 0; i < sc->child_count; i++) {
+ for (;;) {
+ child = sc->child_list[j++];
+ if (child != NULL)
+ break;
+ }
+ if (i != j)
+ sc->child_list[i] = child;
+ }
+ sc->child_list = realloc(sc->child_list, sizeof(device_t) *
+ sc->child_count, M_DEVBUF, M_WAITOK);
+}
+
+static void
mmc_rescan_cards(struct mmc_softc *sc)
{
- struct mmc_ivars *ivar = NULL;
- device_t *devlist;
- int err, i, devcount;
+ struct mmc_ivars *ivar;
+ int err, i, j;
- if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
- return;
- for (i = 0; i < devcount; i++) {
- ivar = device_get_ivars(devlist[i]);
- if (mmc_select_card(sc, ivar->rca)) {
+ for (i = j = 0; i < sc->child_count; i++) {
+ ivar = device_get_ivars(sc->child_list[i]);
+ if (mmc_select_card(sc, ivar->rca) != MMC_ERR_NONE) {
if (bootverbose || mmc_debug)
- device_printf(sc->dev, "Card at relative address %d lost.\n",
+ device_printf(sc->dev,
+ "Card at relative address %d lost\n",
ivar->rca);
- device_delete_child(sc->dev, devlist[i]);
+ err = device_delete_child(sc->dev, sc->child_list[i]);
+ if (err != 0) {
+ j++;
+ continue;
+ }
free(ivar, M_DEVBUF);
- }
+ } else
+ j++;
}
- free(devlist, M_TEMP);
- mmc_select_card(sc, 0);
+ if (sc->child_count == j)
+ goto out;
+ sc->child_count = j;
+ mmc_update_child_list(sc);
+out:
+ (void)mmc_select_card(sc, 0);
}
static int
-mmc_delete_cards(struct mmc_softc *sc)
+mmc_delete_cards(struct mmc_softc *sc, bool final)
{
struct mmc_ivars *ivar;
- device_t *devlist;
- int err, i, devcount;
+ int err, i, j;
- if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
- return (err);
- for (i = 0; i < devcount; i++) {
- ivar = device_get_ivars(devlist[i]);
+ err = 0;
+ for (i = j = 0; i < sc->child_count; i++) {
+ ivar = device_get_ivars(sc->child_list[i]);
if (bootverbose || mmc_debug)
- device_printf(sc->dev, "Card at relative address %d deleted.\n",
+ device_printf(sc->dev,
+ "Card at relative address %d deleted\n",
ivar->rca);
- device_delete_child(sc->dev, devlist[i]);
+ err = device_delete_child(sc->dev, sc->child_list[i]);
+ if (err != 0) {
+ j++;
+ if (final == false)
+ continue;
+ else
+ break;
+ }
free(ivar, M_DEVBUF);
}
- free(devlist, M_TEMP);
- return (0);
+ sc->child_count = j;
+ mmc_update_child_list(sc);
+ return (err);
}
static void
@@ -1503,6 +2046,7 @@
/*
* First, try SD modes
*/
+ sc->squelched++; /* Errors are expected, squelch reporting. */
mmcbr_set_mode(dev, mode_sd);
mmc_power_up(sc);
mmcbr_set_bus_mode(dev, pushpull);
@@ -1511,7 +2055,8 @@
mmc_idle_cards(sc);
err = mmc_send_if_cond(sc, 1);
if ((bootverbose || mmc_debug) && err == 0)
- device_printf(sc->dev, "SD 2.0 interface conditions: OK\n");
+ device_printf(sc->dev,
+ "SD 2.0 interface conditions: OK\n");
if (mmc_send_app_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) {
if (bootverbose || mmc_debug)
device_printf(sc->dev, "SD probe: failed\n");
@@ -1521,13 +2066,16 @@
mmcbr_set_mode(dev, mode_mmc);
if (mmc_send_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) {
if (bootverbose || mmc_debug)
- device_printf(sc->dev, "MMC probe: failed\n");
+ device_printf(sc->dev,
+ "MMC probe: failed\n");
ocr = 0; /* Failed both, powerdown. */
} else if (bootverbose || mmc_debug)
device_printf(sc->dev,
"MMC probe: OK (OCR: 0x%08x)\n", ocr);
} else if (bootverbose || mmc_debug)
- device_printf(sc->dev, "SD probe: OK (OCR: 0x%08x)\n", ocr);
+ device_printf(sc->dev, "SD probe: OK (OCR: 0x%08x)\n",
+ ocr);
+ sc->squelched--;
mmcbr_set_ocr(dev, mmc_select_vdd(sc, ocr));
if (mmcbr_get_ocr(dev) != 0)
@@ -1534,7 +2082,7 @@
mmc_idle_cards(sc);
} else {
mmcbr_set_bus_mode(dev, opendrain);
- mmcbr_set_clock(dev, mmcbr_get_f_min(dev));
+ mmcbr_set_clock(dev, SD_MMC_CARD_ID_FREQUENCY);
mmcbr_update_ios(dev);
/* XXX recompute vdd based on new cards? */
}
@@ -1543,9 +2091,11 @@
* one card on the bus.
*/
if (bootverbose || mmc_debug)
- device_printf(sc->dev, "Current OCR: 0x%08x\n", mmcbr_get_ocr(dev));
+ device_printf(sc->dev, "Current OCR: 0x%08x\n",
+ mmcbr_get_ocr(dev));
if (mmcbr_get_ocr(dev) == 0) {
- mmc_delete_cards(sc);
+ device_printf(sc->dev, "No compatible cards found on bus\n");
+ (void)mmc_delete_cards(sc, false);
mmc_power_down(sc);
return;
}
@@ -1557,7 +2107,7 @@
mmc_send_app_op_cond(sc,
(err ? 0 : MMC_OCR_CCS) | mmcbr_get_ocr(dev), NULL);
} else
- mmc_send_op_cond(sc, mmcbr_get_ocr(dev), NULL);
+ mmc_send_op_cond(sc, MMC_OCR_CCS | mmcbr_get_ocr(dev), NULL);
mmc_discover_cards(sc);
mmc_rescan_cards(sc);
@@ -1564,67 +2114,355 @@
mmcbr_set_bus_mode(dev, pushpull);
mmcbr_update_ios(dev);
mmc_calculate_clock(sc);
- bus_generic_attach(dev);
-/* mmc_update_children_sysctl(dev);*/
}
static int
mmc_calculate_clock(struct mmc_softc *sc)
{
- int max_dtr, max_hs_dtr, max_timing;
- int nkid, i, f_min, f_max;
- device_t *kids;
+ device_t dev;
struct mmc_ivars *ivar;
-
- f_min = mmcbr_get_f_min(sc->dev);
- f_max = mmcbr_get_f_max(sc->dev);
- max_dtr = max_hs_dtr = f_max;
- if ((mmcbr_get_caps(sc->dev) & MMC_CAP_HSPEED))
- max_timing = bus_timing_hs;
- else
- max_timing = bus_timing_normal;
- if (device_get_children(sc->dev, &kids, &nkid) != 0)
- panic("can't get children");
- for (i = 0; i < nkid; i++) {
- ivar = device_get_ivars(kids[i]);
- if (ivar->timing < max_timing)
- max_timing = ivar->timing;
- if (ivar->tran_speed < max_dtr)
- max_dtr = ivar->tran_speed;
- if (ivar->hs_tran_speed < max_hs_dtr)
- max_hs_dtr = ivar->hs_tran_speed;
+ int i;
+ uint32_t dtr, max_dtr;
+ uint16_t rca;
+ enum mmc_bus_timing max_timing, timing;
+ bool changed, hs400;
+
+ dev = sc->dev;
+ max_dtr = mmcbr_get_f_max(dev);
+ max_timing = bus_timing_max;
+ do {
+ changed = false;
+ for (i = 0; i < sc->child_count; i++) {
+ ivar = device_get_ivars(sc->child_list[i]);
+ if (isclr(&ivar->timings, max_timing) ||
+ !mmc_host_timing(dev, max_timing)) {
+ for (timing = max_timing - 1; timing >=
+ bus_timing_normal; timing--) {
+ if (isset(&ivar->timings, timing) &&
+ mmc_host_timing(dev, timing)) {
+ max_timing = timing;
+ break;
+ }
+ }
+ changed = true;
+ }
+ dtr = mmc_timing_to_dtr(ivar, max_timing);
+ if (dtr < max_dtr) {
+ max_dtr = dtr;
+ changed = true;
+ }
+ }
+ } while (changed == true);
+
+ if (bootverbose || mmc_debug) {
+ device_printf(dev,
+ "setting transfer rate to %d.%03dMHz (%s timing)\n",
+ max_dtr / 1000000, (max_dtr / 1000) % 1000,
+ mmc_timing_to_string(max_timing));
}
- for (i = 0; i < nkid; i++) {
- ivar = device_get_ivars(kids[i]);
- if (ivar->timing == bus_timing_normal)
+
+ /*
+ * HS400 must be tuned in HS200 mode, so in case of HS400 we begin
+ * with HS200 following the sequence as described in "6.6.2.2 HS200
+ * timing mode selection" of the eMMC specification v5.1, too, and
+ * switch to max_timing later. HS400ES requires no tuning and, thus,
+ * can be switch to directly, but requires the same detour via high
+ * speed mode as does HS400 (see mmc_switch_to_hs400()).
+ */
+ hs400 = max_timing == bus_timing_mmc_hs400;
+ timing = hs400 == true ? bus_timing_mmc_hs200 : max_timing;
+ for (i = 0; i < sc->child_count; i++) {
+ ivar = device_get_ivars(sc->child_list[i]);
+ if ((ivar->timings & ~(1 << bus_timing_normal)) == 0)
continue;
- mmc_select_card(sc, ivar->rca);
- mmc_set_timing(sc, max_timing);
+
+ rca = ivar->rca;
+ if (mmc_select_card(sc, rca) != MMC_ERR_NONE) {
+ device_printf(dev, "Card at relative address %d "
+ "failed to select\n", rca);
+ continue;
+ }
+
+ if (timing == bus_timing_mmc_hs200 || /* includes HS400 */
+ timing == bus_timing_mmc_hs400es) {
+ if (mmc_set_vccq(sc, ivar, timing) != MMC_ERR_NONE) {
+ device_printf(dev, "Failed to set VCCQ for "
+ "card at relative address %d\n", rca);
+ continue;
+ }
+ }
+
+ if (timing == bus_timing_mmc_hs200) { /* includes HS400 */
+ /* Set bus width (required for initial tuning). */
+ if (mmc_set_card_bus_width(sc, ivar, timing) !=
+ MMC_ERR_NONE) {
+ device_printf(dev, "Card at relative address "
+ "%d failed to set bus width\n", rca);
+ continue;
+ }
+ mmcbr_set_bus_width(dev, ivar->bus_width);
+ mmcbr_update_ios(dev);
+ } else if (timing == bus_timing_mmc_hs400es) {
+ if (mmc_switch_to_hs400(sc, ivar, max_dtr, timing) !=
+ MMC_ERR_NONE) {
+ device_printf(dev, "Card at relative address "
+ "%d failed to set %s timing\n", rca,
+ mmc_timing_to_string(timing));
+ continue;
+ }
+ goto power_class;
+ }
+
+ if (mmc_set_timing(sc, ivar, timing) != MMC_ERR_NONE) {
+ device_printf(dev, "Card at relative address %d "
+ "failed to set %s timing\n", rca,
+ mmc_timing_to_string(timing));
+ continue;
+ }
+
+ if (timing == bus_timing_mmc_ddr52) {
+ /*
+ * Set EXT_CSD_BUS_WIDTH_n_DDR in EXT_CSD_BUS_WIDTH
+ * (must be done after switching to EXT_CSD_HS_TIMING).
+ */
+ if (mmc_set_card_bus_width(sc, ivar, timing) !=
+ MMC_ERR_NONE) {
+ device_printf(dev, "Card at relative address "
+ "%d failed to set bus width\n", rca);
+ continue;
+ }
+ mmcbr_set_bus_width(dev, ivar->bus_width);
+ mmcbr_update_ios(dev);
+ if (mmc_set_vccq(sc, ivar, timing) != MMC_ERR_NONE) {
+ device_printf(dev, "Failed to set VCCQ for "
+ "card at relative address %d\n", rca);
+ continue;
+ }
+ }
+
+ /* Set clock (must be done before initial tuning). */
+ mmcbr_set_clock(dev, max_dtr);
+ mmcbr_update_ios(dev);
+
+ if (mmcbr_tune(dev, hs400) != 0) {
+ device_printf(dev, "Card at relative address %d "
+ "failed to execute initial tuning\n", rca);
+ continue;
+ }
+
+ if (hs400 == true && mmc_switch_to_hs400(sc, ivar, max_dtr,
+ max_timing) != MMC_ERR_NONE) {
+ device_printf(dev, "Card at relative address %d "
+ "failed to set %s timing\n", rca,
+ mmc_timing_to_string(max_timing));
+ continue;
+ }
+
+power_class:
+ if (mmc_set_power_class(sc, ivar) != MMC_ERR_NONE) {
+ device_printf(dev, "Card at relative address %d "
+ "failed to set power class\n", rca);
+ }
}
- mmc_select_card(sc, 0);
- free(kids, M_TEMP);
- if (max_timing == bus_timing_hs)
- max_dtr = max_hs_dtr;
- if (bootverbose || mmc_debug) {
- device_printf(sc->dev,
- "setting transfer rate to %d.%03dMHz%s\n",
- max_dtr / 1000000, (max_dtr / 1000) % 1000,
- max_timing == bus_timing_hs ? " (high speed timing)" : "");
+ (void)mmc_select_card(sc, 0);
+ return (max_dtr);
+}
+
+/*
+ * Switch from HS200 to HS400 (either initially or for re-tuning) or directly
+ * to HS400ES. This follows the sequences described in "6.6.2.3 HS400 timing
+ * mode selection" of the eMMC specification v5.1.
+ */
+static int
+mmc_switch_to_hs400(struct mmc_softc *sc, struct mmc_ivars *ivar,
+ uint32_t clock, enum mmc_bus_timing max_timing)
+{
+ device_t dev;
+ int err;
+ uint16_t rca;
+
+ dev = sc->dev;
+ rca = ivar->rca;
+
+ /*
+ * Both clock and timing must be set as appropriate for high speed
+ * before eventually switching to HS400/HS400ES; mmc_set_timing()
+ * will issue mmcbr_update_ios().
+ */
+ mmcbr_set_clock(dev, ivar->hs_tran_speed);
+ err = mmc_set_timing(sc, ivar, bus_timing_hs);
+ if (err != MMC_ERR_NONE)
+ return (err);
+
+ /*
+ * Set EXT_CSD_BUS_WIDTH_8_DDR in EXT_CSD_BUS_WIDTH (and additionally
+ * EXT_CSD_BUS_WIDTH_ES for HS400ES).
+ */
+ err = mmc_set_card_bus_width(sc, ivar, max_timing);
+ if (err != MMC_ERR_NONE)
+ return (err);
+ mmcbr_set_bus_width(dev, ivar->bus_width);
+ mmcbr_update_ios(dev);
+
+ /* Finally, switch to HS400/HS400ES mode. */
+ err = mmc_set_timing(sc, ivar, max_timing);
+ if (err != MMC_ERR_NONE)
+ return (err);
+ mmcbr_set_clock(dev, clock);
+ mmcbr_update_ios(dev);
+ return (MMC_ERR_NONE);
+}
+
+/*
+ * Switch from HS400 to HS200 (for re-tuning).
+ */
+static int
+mmc_switch_to_hs200(struct mmc_softc *sc, struct mmc_ivars *ivar,
+ uint32_t clock)
+{
+ device_t dev;
+ int err;
+ uint16_t rca;
+
+ dev = sc->dev;
+ rca = ivar->rca;
+
+ /*
+ * Both clock and timing must initially be set as appropriate for
+ * DDR52 before eventually switching to HS200; mmc_set_timing()
+ * will issue mmcbr_update_ios().
+ */
+ mmcbr_set_clock(dev, ivar->hs_tran_speed);
+ err = mmc_set_timing(sc, ivar, bus_timing_mmc_ddr52);
+ if (err != MMC_ERR_NONE)
+ return (err);
+
+ /*
+ * Next, switch to high speed. Thus, clear EXT_CSD_BUS_WIDTH_n_DDR
+ * in EXT_CSD_BUS_WIDTH and update bus width and timing in ios.
+ */
+ err = mmc_set_card_bus_width(sc, ivar, bus_timing_hs);
+ if (err != MMC_ERR_NONE)
+ return (err);
+ mmcbr_set_bus_width(dev, ivar->bus_width);
+ mmcbr_set_timing(sc->dev, bus_timing_hs);
+ mmcbr_update_ios(dev);
+
+ /* Finally, switch to HS200 mode. */
+ err = mmc_set_timing(sc, ivar, bus_timing_mmc_hs200);
+ if (err != MMC_ERR_NONE)
+ return (err);
+ mmcbr_set_clock(dev, clock);
+ mmcbr_update_ios(dev);
+ return (MMC_ERR_NONE);
+}
+
+static int
+mmc_retune(device_t busdev, device_t dev, bool reset)
+{
+ struct mmc_softc *sc;
+ struct mmc_ivars *ivar;
+ int err;
+ uint32_t clock;
+ enum mmc_bus_timing timing;
+
+ if (device_get_parent(dev) != busdev)
+ return (MMC_ERR_INVALID);
+
+ sc = device_get_softc(busdev);
+ if (sc->retune_needed != 1 && sc->retune_paused != 0)
+ return (MMC_ERR_INVALID);
+
+ timing = mmcbr_get_timing(busdev);
+ if (timing == bus_timing_mmc_hs400) {
+ /*
+ * Controllers use the data strobe line to latch data from
+ * the devices in HS400 mode so periodic re-tuning isn't
+ * expected to be required, i. e. only if a CRC or tuning
+ * error is signaled to the bridge. In these latter cases
+ * we are asked to reset the tuning circuit and need to do
+ * the switch timing dance.
+ */
+ if (reset == false)
+ return (0);
+ ivar = device_get_ivars(dev);
+ clock = mmcbr_get_clock(busdev);
+ if (mmc_switch_to_hs200(sc, ivar, clock) != MMC_ERR_NONE)
+ return (MMC_ERR_BADCRC);
}
- mmcbr_set_timing(sc->dev, max_timing);
- mmcbr_set_clock(sc->dev, max_dtr);
- mmcbr_update_ios(sc->dev);
- return max_dtr;
+ err = mmcbr_retune(busdev, reset);
+ if (err != 0 && timing == bus_timing_mmc_hs400)
+ return (MMC_ERR_BADCRC);
+ switch (err) {
+ case 0:
+ break;
+ case EIO:
+ return (MMC_ERR_FAILED);
+ default:
+ return (MMC_ERR_INVALID);
+ }
+ if (timing == bus_timing_mmc_hs400) {
+ if (mmc_switch_to_hs400(sc, ivar, clock, timing) !=
+ MMC_ERR_NONE)
+ return (MMC_ERR_BADCRC);
+ }
+ return (MMC_ERR_NONE);
}
static void
+mmc_retune_pause(device_t busdev, device_t dev, bool retune)
+{
+ struct mmc_softc *sc;
+
+ sc = device_get_softc(busdev);
+ KASSERT(device_get_parent(dev) == busdev,
+ ("%s: %s is not a child of %s", __func__, device_get_nameunit(dev),
+ device_get_nameunit(busdev)));
+ KASSERT(sc->owner != NULL,
+ ("%s: Request from %s without bus being acquired.", __func__,
+ device_get_nameunit(dev)));
+
+ if (retune == true && sc->retune_paused == 0)
+ sc->retune_needed = 1;
+ sc->retune_paused++;
+}
+
+static void
+mmc_retune_unpause(device_t busdev, device_t dev)
+{
+ struct mmc_softc *sc;
+
+ sc = device_get_softc(busdev);
+ KASSERT(device_get_parent(dev) == busdev,
+ ("%s: %s is not a child of %s", __func__, device_get_nameunit(dev),
+ device_get_nameunit(busdev)));
+ KASSERT(sc->owner != NULL,
+ ("%s: Request from %s without bus being acquired.", __func__,
+ device_get_nameunit(dev)));
+ KASSERT(sc->retune_paused != 0,
+ ("%s: Re-tune pause count already at 0", __func__));
+
+ sc->retune_paused--;
+}
+
+static void
mmc_scan(struct mmc_softc *sc)
{
device_t dev = sc->dev;
+ int err;
- mmc_acquire_bus(dev, dev);
+ err = mmc_acquire_bus(dev, dev);
+ if (err != 0) {
+ device_printf(dev, "Failed to acquire bus for scanning\n");
+ return;
+ }
mmc_go_discovery(sc);
- mmc_release_bus(dev, dev);
+ err = mmc_release_bus(dev, dev);
+ if (err != 0) {
+ device_printf(dev, "Failed to release bus after scanning\n");
+ return;
+ }
+ (void)bus_generic_attach(dev);
}
static int
@@ -1635,6 +2473,9 @@
switch (which) {
default:
return (EINVAL);
+ case MMC_IVAR_SPEC_VERS:
+ *result = ivar->csd.spec_vers;
+ break;
case MMC_IVAR_DSR_IMP:
*result = ivar->csd.dsr_imp;
break;
@@ -1668,9 +2509,18 @@
case MMC_IVAR_MAX_DATA:
*result = mmcbr_get_max_data(bus);
break;
+ case MMC_IVAR_CMD6_TIMEOUT:
+ *result = ivar->cmd6_time;
+ break;
+ case MMC_IVAR_QUIRKS:
+ *result = ivar->quirks;
+ break;
case MMC_IVAR_CARD_ID_STRING:
*(char **)result = ivar->card_id_string;
break;
+ case MMC_IVAR_CARD_SN_STRING:
+ *(char **)result = ivar->card_sn_string;
+ break;
}
return (0);
}
@@ -1678,6 +2528,7 @@
static int
mmc_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
{
+
/*
* None are writable ATM
*/
@@ -1688,7 +2539,7 @@
mmc_delayed_attach(void *xsc)
{
struct mmc_softc *sc = xsc;
-
+
mmc_scan(sc);
config_intrhook_disestablish(&sc->config_intrhook);
}
@@ -1716,6 +2567,8 @@
DEVMETHOD(bus_child_location_str, mmc_child_location_str),
/* MMC Bus interface */
+ DEVMETHOD(mmcbus_retune_pause, mmc_retune_pause),
+ DEVMETHOD(mmcbus_retune_unpause, mmc_retune_unpause),
DEVMETHOD(mmcbus_wait_for_request, mmc_wait_for_request),
DEVMETHOD(mmcbus_acquire_bus, mmc_acquire_bus),
DEVMETHOD(mmcbus_release_bus, mmc_release_bus),
@@ -1723,12 +2576,11 @@
DEVMETHOD_END
};
-static driver_t mmc_driver = {
+driver_t mmc_driver = {
"mmc",
mmc_methods,
sizeof(struct mmc_softc),
};
-static devclass_t mmc_devclass;
+devclass_t mmc_devclass;
-DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, NULL, NULL);
-DRIVER_MODULE(mmc, sdhci, mmc_driver, mmc_devclass, NULL, NULL);
+MODULE_VERSION(mmc, MMC_VERSION);
Added: trunk/sys/dev/mmc/mmc_ioctl.h
===================================================================
--- trunk/sys/dev/mmc/mmc_ioctl.h (rev 0)
+++ trunk/sys/dev/mmc/mmc_ioctl.h 2018-05-27 23:36:35 UTC (rev 10096)
@@ -0,0 +1,65 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2017 Marius Strobl <marius at FreeBSD.org>
+ * 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: stable/10/sys/dev/mmc/mmc_ioctl.h 322120 2017-08-06 16:07:34Z marius $
+ */
+
+#ifndef _DEV_MMC_MMC_IOCTL_H_
+#define _DEV_MMC_MMC_IOCTL_H_
+
+struct mmc_ioc_cmd {
+ int write_flag; /* 0: RD, 1: WR, (1 << 31): reliable WR */
+ int is_acmd; /* 0: normal, 1: use CMD55 */
+ uint32_t opcode;
+ uint32_t arg;
+ uint32_t response[4];
+ u_int flags;
+ u_int blksz;
+ u_int blocks;
+ u_int __spare[4];
+ uint32_t __pad;
+ uint64_t data_ptr;
+};
+
+#define mmc_ioc_cmd_set_data(mic, ptr) \
+ (mic).data_ptr = (uint64_t)(uintptr_t)(ptr)
+
+struct mmc_ioc_multi_cmd {
+ uint64_t num_of_cmds;
+ struct mmc_ioc_cmd cmds[0];
+};
+
+#define MMC_IOC_BASE 'M'
+
+#define MMC_IOC_CMD _IOWR(MMC_IOC_BASE, 0, struct mmc_ioc_cmd)
+#define MMC_IOC_MULTI_CMD _IOWR(MMC_IOC_BASE, 1, struct mmc_ioc_multi_cmd)
+
+/* Maximum accepted data transfer size */
+#define MMC_IOC_MAX_BYTES (512 * 256)
+/* Maximum accepted number of commands */
+#define MMC_IOC_MAX_CMDS 255
+
+#endif /* _DEV_MMC_MMC_IOCTL_H_ */
Property changes on: trunk/sys/dev/mmc/mmc_ioctl.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/mmc/mmc_private.h
===================================================================
--- trunk/sys/dev/mmc/mmc_private.h (rev 0)
+++ trunk/sys/dev/mmc/mmc_private.h 2018-05-27 23:36:35 UTC (rev 10096)
@@ -0,0 +1,75 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2006 Bernd Walter. All rights reserved.
+ * Copyright (c) 2006 M. Warner Losh. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this software may have been developed with reference to
+ * the SD Simplified Specification. The following disclaimer may apply:
+ *
+ * The following conditions apply to the release of the simplified
+ * specification ("Simplified Specification") by the SD Card Association and
+ * the SD Group. The Simplified Specification is a subset of the complete SD
+ * Specification which is owned by the SD Card Association and the SD
+ * Group. This Simplified Specification is provided on a non-confidential
+ * basis subject to the disclaimers below. Any implementation of the
+ * Simplified Specification may require a license from the SD Card
+ * Association, SD Group, SD-3C LLC or other third parties.
+ *
+ * Disclaimers:
+ *
+ * The information contained in the Simplified Specification is presented only
+ * as a standard specification for SD Cards and SD Host/Ancillary products and
+ * is provided "AS-IS" without any representations or warranties of any
+ * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+ * Card Association for any damages, any infringements of patents or other
+ * right of the SD Group, SD-3C LLC, the SD Card Association or any third
+ * parties, which may result from its use. No license is granted by
+ * implication, estoppel or otherwise under any patent or other rights of the
+ * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+ * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+ * or the SD Card Association to disclose or distribute any technical
+ * information, know-how or other confidential information to any third party.
+ *
+ * $FreeBSD: stable/10/sys/dev/mmc/mmc_private.h 322120 2017-08-06 16:07:34Z marius $
+ */
+
+#ifndef DEV_MMC_PRIVATE_H
+#define DEV_MMC_PRIVATE_H
+
+struct mmc_softc {
+ device_t dev;
+ struct mtx sc_mtx;
+ struct intr_config_hook config_intrhook;
+ device_t owner;
+ device_t *child_list;
+ int child_count;
+ uint16_t last_rca;
+ uint16_t retune_paused;
+ uint8_t retune_needed;
+ uint8_t retune_ongoing;
+ uint16_t squelched; /* suppress reporting of (expected) errors */
+ int log_count;
+ struct timeval log_time;
+};
+
+#endif /* DEV_MMC_PRIVATE_H */
Property changes on: trunk/sys/dev/mmc/mmc_private.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/mmc/mmc_subr.c
===================================================================
--- trunk/sys/dev/mmc/mmc_subr.c (rev 0)
+++ trunk/sys/dev/mmc/mmc_subr.c 2018-05-27 23:36:35 UTC (rev 10096)
@@ -0,0 +1,265 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2006 Bernd Walter. All rights reserved.
+ * Copyright (c) 2006 M. Warner Losh. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this software may have been developed with reference to
+ * the SD Simplified Specification. The following disclaimer may apply:
+ *
+ * The following conditions apply to the release of the simplified
+ * specification ("Simplified Specification") by the SD Card Association and
+ * the SD Group. The Simplified Specification is a subset of the complete SD
+ * Specification which is owned by the SD Card Association and the SD
+ * Group. This Simplified Specification is provided on a non-confidential
+ * basis subject to the disclaimers below. Any implementation of the
+ * Simplified Specification may require a license from the SD Card
+ * Association, SD Group, SD-3C LLC or other third parties.
+ *
+ * Disclaimers:
+ *
+ * The information contained in the Simplified Specification is presented only
+ * as a standard specification for SD Cards and SD Host/Ancillary products and
+ * is provided "AS-IS" without any representations or warranties of any
+ * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+ * Card Association for any damages, any infringements of patents or other
+ * right of the SD Group, SD-3C LLC, the SD Card Association or any third
+ * parties, which may result from its use. No license is granted by
+ * implication, estoppel or otherwise under any patent or other rights of the
+ * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+ * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+ * or the SD Card Association to disclose or distribute any technical
+ * information, know-how or other confidential information to any third party.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: stable/10/sys/dev/mmc/mmc_subr.c 322120 2017-08-06 16:07:34Z marius $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/time.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmc_private.h>
+#include <dev/mmc/mmc_subr.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcbrvar.h>
+
+#include "mmcbus_if.h"
+
+#define CMD_RETRIES 3
+#define LOG_PPS 5 /* Log no more than 5 errors per second. */
+
+int
+mmc_wait_for_cmd(device_t busdev, device_t dev, struct mmc_command *cmd,
+ int retries)
+{
+ struct mmc_request mreq;
+ struct mmc_softc *sc;
+ int err;
+
+ do {
+ memset(&mreq, 0, sizeof(mreq));
+ memset(cmd->resp, 0, sizeof(cmd->resp));
+ cmd->retries = 0; /* Retries done here, not in hardware. */
+ cmd->mrq = &mreq;
+ if (cmd->data != NULL)
+ cmd->data->mrq = &mreq;
+ mreq.cmd = cmd;
+ if (MMCBUS_WAIT_FOR_REQUEST(busdev, dev, &mreq) != 0)
+ err = MMC_ERR_FAILED;
+ else
+ err = cmd->error;
+ } while (err != MMC_ERR_NONE && retries-- > 0);
+
+ if (err != MMC_ERR_NONE && busdev == dev) {
+ sc = device_get_softc(busdev);
+ if (sc->squelched == 0 && ppsratecheck(&sc->log_time,
+ &sc->log_count, LOG_PPS)) {
+ device_printf(sc->dev, "CMD%d failed, RESULT: %d\n",
+ cmd->opcode, err);
+ }
+ }
+
+ return (err);
+}
+
+int
+mmc_wait_for_app_cmd(device_t busdev, device_t dev, uint16_t rca,
+ struct mmc_command *cmd, int retries)
+{
+ struct mmc_command appcmd;
+ struct mmc_softc *sc;
+ int err;
+
+ sc = device_get_softc(busdev);
+
+ /* Squelch error reporting at lower levels, we report below. */
+ sc->squelched++;
+ do {
+ memset(&appcmd, 0, sizeof(appcmd));
+ appcmd.opcode = MMC_APP_CMD;
+ appcmd.arg = (uint32_t)rca << 16;
+ appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ if (mmc_wait_for_cmd(busdev, dev, &appcmd, 0) != 0)
+ err = MMC_ERR_FAILED;
+ else
+ err = appcmd.error;
+ if (err == MMC_ERR_NONE) {
+ if (!(appcmd.resp[0] & R1_APP_CMD))
+ err = MMC_ERR_FAILED;
+ else if (mmc_wait_for_cmd(busdev, dev, cmd, 0) != 0)
+ err = MMC_ERR_FAILED;
+ else
+ err = cmd->error;
+ }
+ } while (err != MMC_ERR_NONE && retries-- > 0);
+ sc->squelched--;
+
+ if (err != MMC_ERR_NONE && busdev == dev) {
+ if (sc->squelched == 0 && ppsratecheck(&sc->log_time,
+ &sc->log_count, LOG_PPS)) {
+ device_printf(sc->dev, "ACMD%d failed, RESULT: %d\n",
+ cmd->opcode, err);
+ }
+ }
+
+ return (err);
+}
+
+int
+mmc_switch(device_t busdev, device_t dev, uint16_t rca, uint8_t set,
+ uint8_t index, uint8_t value, u_int timeout, bool status)
+{
+ struct mmc_command cmd;
+ struct mmc_softc *sc;
+ int err;
+
+ KASSERT(timeout != 0, ("%s: no timeout", __func__));
+
+ sc = device_get_softc(busdev);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = MMC_SWITCH_FUNC;
+ cmd.arg = (MMC_SWITCH_FUNC_WR << 24) | (index << 16) | (value << 8) |
+ set;
+ /*
+ * If the hardware supports busy detection but the switch timeout
+ * exceeds the maximum host timeout, use a R1 instead of a R1B
+ * response in order to keep the hardware from timing out.
+ */
+ if (mmcbr_get_caps(busdev) & MMC_CAP_WAIT_WHILE_BUSY &&
+ timeout > mmcbr_get_max_busy_timeout(busdev))
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ else
+ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ /*
+ * Pause re-tuning so it won't interfere with the busy state and also
+ * so that the result of CMD13 will always refer to switching rather
+ * than to a tuning command that may have snuck in between.
+ */
+ sc->retune_paused++;
+ err = mmc_wait_for_cmd(busdev, dev, &cmd, CMD_RETRIES);
+ if (err != MMC_ERR_NONE || status == false)
+ goto out;
+ err = mmc_switch_status(busdev, dev, rca, timeout);
+out:
+ sc->retune_paused--;
+ return (err);
+}
+
+int
+mmc_switch_status(device_t busdev, device_t dev, uint16_t rca, u_int timeout)
+{
+ struct timeval cur, end;
+ int err;
+ uint32_t status;
+
+ KASSERT(timeout != 0, ("%s: no timeout", __func__));
+
+ /*
+ * Note that when using a R1B response in mmc_switch(), bridges of
+ * type MMC_CAP_WAIT_WHILE_BUSY will issue mmc_send_status() only
+ * once and then exit the loop.
+ */
+ end.tv_sec = end.tv_usec = 0;
+ for (;;) {
+ err = mmc_send_status(busdev, dev, rca, &status);
+ if (err != MMC_ERR_NONE)
+ break;
+ if (R1_CURRENT_STATE(status) == R1_STATE_TRAN)
+ break;
+ getmicrouptime(&cur);
+ if (end.tv_sec == 0 && end.tv_usec == 0) {
+ end.tv_usec = timeout;
+ timevaladd(&end, &cur);
+ }
+ if (timevalcmp(&cur, &end, >)) {
+ err = MMC_ERR_TIMEOUT;
+ break;
+ }
+ }
+ if (err == MMC_ERR_NONE && (status & R1_SWITCH_ERROR) != 0)
+ return (MMC_ERR_FAILED);
+ return (err);
+}
+
+int
+mmc_send_ext_csd(device_t busdev, device_t dev, uint8_t *rawextcsd)
+{
+ struct mmc_command cmd;
+ struct mmc_data data;
+ int err;
+
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&data, 0, sizeof(data));
+
+ memset(rawextcsd, 0, MMC_EXTCSD_SIZE);
+ cmd.opcode = MMC_SEND_EXT_CSD;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd.data = &data;
+
+ data.data = rawextcsd;
+ data.len = MMC_EXTCSD_SIZE;
+ data.flags = MMC_DATA_READ;
+
+ err = mmc_wait_for_cmd(busdev, dev, &cmd, CMD_RETRIES);
+ return (err);
+}
+
+int
+mmc_send_status(device_t busdev, device_t dev, uint16_t rca, uint32_t *status)
+{
+ struct mmc_command cmd;
+ int err;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = (uint32_t)rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(busdev, dev, &cmd, CMD_RETRIES);
+ *status = cmd.resp[0];
+ return (err);
+}
Property changes on: trunk/sys/dev/mmc/mmc_subr.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/dev/mmc/mmc_subr.h
===================================================================
--- trunk/sys/dev/mmc/mmc_subr.h (rev 0)
+++ trunk/sys/dev/mmc/mmc_subr.h 2018-05-27 23:36:35 UTC (rev 10096)
@@ -0,0 +1,73 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2006 Bernd Walter. All rights reserved.
+ * Copyright (c) 2006 M. Warner Losh. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Portions of this software may have been developed with reference to
+ * the SD Simplified Specification. The following disclaimer may apply:
+ *
+ * The following conditions apply to the release of the simplified
+ * specification ("Simplified Specification") by the SD Card Association and
+ * the SD Group. The Simplified Specification is a subset of the complete SD
+ * Specification which is owned by the SD Card Association and the SD
+ * Group. This Simplified Specification is provided on a non-confidential
+ * basis subject to the disclaimers below. Any implementation of the
+ * Simplified Specification may require a license from the SD Card
+ * Association, SD Group, SD-3C LLC or other third parties.
+ *
+ * Disclaimers:
+ *
+ * The information contained in the Simplified Specification is presented only
+ * as a standard specification for SD Cards and SD Host/Ancillary products and
+ * is provided "AS-IS" without any representations or warranties of any
+ * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+ * Card Association for any damages, any infringements of patents or other
+ * right of the SD Group, SD-3C LLC, the SD Card Association or any third
+ * parties, which may result from its use. No license is granted by
+ * implication, estoppel or otherwise under any patent or other rights of the
+ * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+ * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+ * or the SD Card Association to disclose or distribute any technical
+ * information, know-how or other confidential information to any third party.
+ *
+ * $FreeBSD: stable/10/sys/dev/mmc/mmc_subr.h 322120 2017-08-06 16:07:34Z marius $
+ */
+
+#ifndef DEV_MMC_SUBR_H
+#define DEV_MMC_SUBR_H
+
+struct mmc_command;
+
+int mmc_send_ext_csd(device_t busdev, device_t dev, uint8_t *rawextcsd);
+int mmc_send_status(device_t busdev, device_t dev, uint16_t rca,
+ uint32_t *status);
+int mmc_switch(device_t busdev, device_t dev, uint16_t rca, uint8_t set,
+ uint8_t index, uint8_t value, u_int timeout, bool send_status);
+int mmc_switch_status(device_t busdev, device_t dev, uint16_t rca,
+ u_int timeout);
+int mmc_wait_for_app_cmd(device_t busdev, device_t dev, uint16_t rca,
+ struct mmc_command *cmd, int retries);
+int mmc_wait_for_cmd(device_t busdev, device_t dev, struct mmc_command *cmd,
+ int retries);
+
+#endif /* DEV_MMC_SUBR_H */
Property changes on: trunk/sys/dev/mmc/mmc_subr.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/dev/mmc/mmcbr_if.m
===================================================================
--- trunk/sys/dev/mmc/mmcbr_if.m 2018-05-27 23:36:22 UTC (rev 10095)
+++ trunk/sys/dev/mmc/mmcbr_if.m 2018-05-27 23:36:35 UTC (rev 10096)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
#-
# Copyright (c) 2006 M. Warner Losh
# All rights reserved.
@@ -50,11 +51,10 @@
# or the SD Card Association to disclose or distribute any technical
# information, know-how or other confidential information to any third party.
#
-# $MidnightBSD$
+# $FreeBSD: stable/10/sys/dev/mmc/mmcbr_if.m 322120 2017-08-06 16:07:34Z marius $
#
#include <sys/types.h>
-#include <dev/mmc/bridge.h>
#include <dev/mmc/mmcreg.h>
#
@@ -65,9 +65,38 @@
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.
+# Default implementations of some methods.
#
+CODE {
+ static int
+ null_switch_vccq(device_t brdev __unused, device_t reqdev __unused)
+ {
+
+ return (0);
+ }
+
+ static int
+ null_retune(device_t brdev __unused, device_t reqdev __unused,
+ bool reset __unused)
+ {
+
+ return (0);
+ }
+
+ static int
+ null_tune(device_t brdev __unused, device_t reqdev __unused,
+ bool hs400 __unused)
+ {
+
+ return (0);
+ }
+};
+
+#
+# Called by the mmcbus to set up the IO pins correctly, the common/core
+# supply voltage (VDD/VCC) to use for the device, the clock frequency, the
+# type of SPI chip select, power mode and bus width.
+#
METHOD int update_ios {
device_t brdev;
device_t reqdev;
@@ -74,10 +103,37 @@
};
#
+# Called by the mmcbus to switch the signaling voltage (VCCQ).
+#
+METHOD int switch_vccq {
+ device_t brdev;
+ device_t reqdev;
+} DEFAULT null_switch_vccq;
+
+#
+# Called by the mmcbus with the bridge claimed to execute initial tuning.
+#
+METHOD int tune {
+ device_t brdev;
+ device_t reqdev;
+ bool hs400;
+} DEFAULT null_tune;
+
+#
+# Called by the mmcbus with the bridge claimed to execute re-tuning.
+#
+METHOD int retune {
+ device_t brdev;
+ device_t reqdev;
+ bool reset;
+} DEFAULT null_retune;
+
+#
# 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.
+# of the status of the request, who then notifies the requesting device
+# by calling the completion function supplied as part of the request.
+# Requires the bridge to be claimed.
#
METHOD int request {
device_t brdev;
@@ -100,7 +156,7 @@
METHOD int acquire_host {
device_t brdev;
device_t reqdev;
-}
+};
#
# Release the current bridge.
@@ -108,4 +164,4 @@
METHOD int release_host {
device_t brdev;
device_t reqdev;
-}
+};
Property changes on: trunk/sys/dev/mmc/mmcbr_if.m
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/mmc/mmcbrvar.h
===================================================================
--- trunk/sys/dev/mmc/mmcbrvar.h 2018-05-27 23:36:22 UTC (rev 10095)
+++ trunk/sys/dev/mmc/mmcbrvar.h 2018-05-27 23:36:35 UTC (rev 10096)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 2006 Bernd Walter. All rights reserved.
* Copyright (c) 2006 M. Warner Losh. All rights reserved.
@@ -49,14 +50,14 @@
* or the SD Card Association to disclose or distribute any technical
* information, know-how or other confidential information to any third party.
*
- * "$MidnightBSD$"
+ * $FreeBSD: stable/10/sys/dev/mmc/mmcbrvar.h 322120 2017-08-06 16:07:34Z marius $
*/
#ifndef DEV_MMC_MMCBRVAR_H
-#define DEV_MMC_MMCBRVAR_H
+#define DEV_MMC_MMCBRVAR_H
-#include <dev/mmc/bridge.h>
#include <dev/mmc/mmcreg.h>
+
#include "mmcbr_if.h"
enum mmcbr_device_ivars {
@@ -70,16 +71,19 @@
MMCBR_IVAR_MODE,
MMCBR_IVAR_OCR,
MMCBR_IVAR_POWER_MODE,
+ MMCBR_IVAR_RETUNE_REQ,
MMCBR_IVAR_VDD,
+ MMCBR_IVAR_VCCQ,
MMCBR_IVAR_CAPS,
MMCBR_IVAR_TIMING,
- MMCBR_IVAR_MAX_DATA
+ MMCBR_IVAR_MAX_DATA,
+ MMCBR_IVAR_MAX_BUSY_TIMEOUT
};
/*
- * Simplified accessors for pci devices
+ * Simplified accessors for bridge devices
*/
-#define MMCBR_ACCESSOR(var, ivar, type) \
+#define MMCBR_ACCESSOR(var, ivar, type) \
__BUS_ACCESSOR(mmcbr, var, MMCBR, ivar, type)
MMCBR_ACCESSOR(bus_mode, BUS_MODE, int)
@@ -92,20 +96,46 @@
MMCBR_ACCESSOR(mode, MODE, int)
MMCBR_ACCESSOR(ocr, OCR, int)
MMCBR_ACCESSOR(power_mode, POWER_MODE, int)
+MMCBR_ACCESSOR(retune_req, RETUNE_REQ, int)
MMCBR_ACCESSOR(vdd, VDD, int)
+MMCBR_ACCESSOR(vccq, VCCQ, int)
MMCBR_ACCESSOR(caps, CAPS, int)
MMCBR_ACCESSOR(timing, TIMING, int)
MMCBR_ACCESSOR(max_data, MAX_DATA, int)
+MMCBR_ACCESSOR(max_busy_timeout, MAX_BUSY_TIMEOUT, u_int)
static int __inline
mmcbr_update_ios(device_t dev)
{
+
return (MMCBR_UPDATE_IOS(device_get_parent(dev), dev));
}
static int __inline
+mmcbr_tune(device_t dev, bool hs400)
+{
+
+ return (MMCBR_TUNE(device_get_parent(dev), dev, hs400));
+}
+
+static int __inline
+mmcbr_retune(device_t dev, bool reset)
+{
+
+ return (MMCBR_RETUNE(device_get_parent(dev), dev, reset));
+}
+
+static int __inline
+mmcbr_switch_vccq(device_t dev)
+{
+
+ return (MMCBR_SWITCH_VCCQ(device_get_parent(dev), dev));
+}
+
+static int __inline
mmcbr_get_ro(device_t dev)
{
+
return (MMCBR_GET_RO(device_get_parent(dev), dev));
}
Modified: trunk/sys/dev/mmc/mmcbus_if.m
===================================================================
--- trunk/sys/dev/mmc/mmcbus_if.m 2018-05-27 23:36:22 UTC (rev 10095)
+++ trunk/sys/dev/mmc/mmcbus_if.m 2018-05-27 23:36:35 UTC (rev 10096)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
#-
# Copyright (c) 2006 M. Warner Losh
# All rights reserved.
@@ -50,41 +51,59 @@
# or the SD Card Association to disclose or distribute any technical
# information, know-how or other confidential information to any third party.
#
-# $MidnightBSD$
+# $FreeBSD: stable/10/sys/dev/mmc/mmcbus_if.m 322120 2017-08-06 16:07:34Z marius $
#
+#include <sys/types.h>
#include <dev/mmc/mmcreg.h>
-#include <dev/mmc/bridge.h>
#
-# This is the set of callbacks that mmc bridges call into the bus, or
-# that mmc/sd card drivers call to make requests.
+# This is the set of callbacks that the MMC subroutines and the mmc/sd card
+# driver call into the bus to make requests.
#
INTERFACE mmcbus;
#
-# Queue and wait for a request.
+# Pause re-tuning, optionally with triggering re-tuning up-front. Requires
+# the bus to be claimed.
#
+METHOD void retune_pause {
+ device_t busdev;
+ device_t reqdev;
+ bool retune;
+};
+
+#
+# Unpause re-tuning. Requires the bus to be claimed.
+#
+METHOD void retune_unpause {
+ device_t busdev;
+ device_t reqdev;
+};
+
+#
+# Queue and wait for a request. Requires the bus to be claimed.
+#
METHOD int wait_for_request {
- device_t brdev;
+ device_t busdev;
device_t reqdev;
struct mmc_request *req;
};
#
-# Claim the current bridge, blocking the current thread until the host
-# is no longer busy.
+# Claim the current bus, blocking the current thread until the host is no
+# longer busy.
#
METHOD int acquire_bus {
- device_t brdev;
+ device_t busdev;
device_t reqdev;
-}
+};
#
-# Release the current bridge.
+# Release the current bus.
#
METHOD int release_bus {
- device_t brdev;
+ device_t busdev;
device_t reqdev;
-}
+};
Property changes on: trunk/sys/dev/mmc/mmcbus_if.m
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sys/dev/mmc/mmcreg.h
===================================================================
--- trunk/sys/dev/mmc/mmcreg.h 2018-05-27 23:36:22 UTC (rev 10095)
+++ trunk/sys/dev/mmc/mmcreg.h 2018-05-27 23:36:35 UTC (rev 10096)
@@ -1,5 +1,7 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 2006 M. Warner Losh. All rights reserved.
+ * Copyright (c) 2017 Marius Strobl <marius at FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -48,7 +50,7 @@
* or the SD Card Association to disclose or distribute any technical
* information, know-how or other confidential information to any third party.
*
- * $MidnightBSD$
+ * $FreeBSD: stable/10/sys/dev/mmc/mmcreg.h 322389 2017-08-11 00:41:43Z marius $
*/
#ifndef DEV_MMC_MMCREG_H
@@ -85,8 +87,11 @@
#define MMC_RSP_R1B (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY)
#define MMC_RSP_R2 (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC)
#define MMC_RSP_R3 (MMC_RSP_PRESENT)
-#define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC)
-#define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC)
+#define MMC_RSP_R4 (MMC_RSP_PRESENT)
+#define MMC_RSP_R5 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_R5B (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY)
+#define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
+#define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
#define MMC_RSP(x) ((x) & MMC_RSP_MASK)
uint32_t retries;
uint32_t error;
@@ -97,7 +102,7 @@
#define MMC_ERR_FAILED 4
#define MMC_ERR_INVALID 5
#define MMC_ERR_NO_MEMORY 6
-#define MMC_ERR_MAX 6
+#define MMC_ERR_MAX 6
struct mmc_data *data; /* Data segment with cmd */
struct mmc_request *mrq; /* backpointer to request */
};
@@ -137,6 +142,7 @@
#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_SWITCH_ERROR (1u << 7) /* sx, c */
#define R1_APP_CMD (1u << 5) /* sr, c */
#define R1_AKE_SEQ_ERROR (1u << 3) /* er, c */
#define R1_STATUS(x) ((x) & 0xFFFFE000)
@@ -170,6 +176,7 @@
void *done_data; /* requestor set data */
uint32_t flags;
#define MMC_REQ_DONE 1
+#define MMC_TUNE_DONE 2
};
/* Command definitions */
@@ -181,7 +188,7 @@
#define MMC_SET_RELATIVE_ADDR 3
#define SD_SEND_RELATIVE_ADDR 3
#define MMC_SET_DSR 4
- /* reserved: 5 */
+#define MMC_SLEEP_AWAKE 5
#define MMC_SWITCH_FUNC 6
#define MMC_SWITCH_FUNC_CMDS 0
#define MMC_SWITCH_FUNC_SET 1
@@ -204,11 +211,11 @@
#define MMC_SET_BLOCKLEN 16
#define MMC_READ_SINGLE_BLOCK 17
#define MMC_READ_MULTIPLE_BLOCK 18
- /* reserved: 19 */
+#define MMC_SEND_TUNING_BLOCK 19
+#define MMC_SEND_TUNING_BLOCK_HS200 21
/* Class 3: Stream write commands */
#define MMC_WRITE_DAT_UNTIL_STOP 20
- /* reserved: 21 */
/* reserved: 22 */
/* Class 4: Block oriented write commands */
@@ -232,6 +239,13 @@
#define MMC_ERASE_GROUP_END 36
/* 37 -- reserved old command */
#define MMC_ERASE 38
+#define MMC_ERASE_ERASE 0x00000000
+#define MMC_ERASE_TRIM 0x00000001
+#define MMC_ERASE_FULE 0x00000002
+#define MMC_ERASE_DISCARD 0x00000003
+#define MMC_ERASE_SECURE_ERASE 0x80000000
+#define MMC_ERASE_SECURE_TRIM1 0x80000001
+#define MMC_ERASE_SECURE_TRIM2 0x80008000
/* Class 9: I/O mode commands */
#define MMC_FAST_IO 39
@@ -275,7 +289,6 @@
/* reserved: 50 */
/* reserved: 57 */
-
/* Application specific commands for SD */
#define ACMD_SET_BUS_WIDTH 6
#define ACMD_SD_STATUS 13
@@ -288,53 +301,172 @@
/*
* EXT_CSD fields
*/
-#define EXT_CSD_ERASE_GRP_DEF 175 /* R/W */
-#define EXT_CSD_BUS_WIDTH 183 /* R/W */
-#define EXT_CSD_HS_TIMING 185 /* R/W */
-#define EXT_CSD_CARD_TYPE 196 /* RO */
-#define EXT_CSD_REV 192 /* RO */
-#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
-#define EXT_CSD_ERASE_TO_MULT 223 /* RO */
-#define EXT_CSD_ERASE_GRP_SIZE 224 /* RO */
+#define EXT_CSD_EXT_PART_ATTR 52 /* R/W, 2 bytes */
+#define EXT_CSD_ENH_START_ADDR 136 /* R/W, 4 bytes */
+#define EXT_CSD_ENH_SIZE_MULT 140 /* R/W, 3 bytes */
+#define EXT_CSD_GP_SIZE_MULT 143 /* R/W, 12 bytes */
+#define EXT_CSD_PART_SET 155 /* R/W */
+#define EXT_CSD_PART_ATTR 156 /* R/W */
+#define EXT_CSD_PART_SUPPORT 160 /* RO */
+#define EXT_CSD_RPMB_MULT 168 /* RO */
+#define EXT_CSD_BOOT_WP_STATUS 174 /* RO */
+#define EXT_CSD_ERASE_GRP_DEF 175 /* R/W */
+#define EXT_CSD_PART_CONFIG 179 /* R/W */
+#define EXT_CSD_BUS_WIDTH 183 /* R/W */
+#define EXT_CSD_STROBE_SUPPORT 184 /* RO */
+#define EXT_CSD_HS_TIMING 185 /* R/W */
+#define EXT_CSD_POWER_CLASS 187 /* R/W */
+#define EXT_CSD_CARD_TYPE 196 /* RO */
+#define EXT_CSD_DRIVER_STRENGTH 197 /* RO */
+#define EXT_CSD_REV 192 /* RO */
+#define EXT_CSD_PART_SWITCH_TO 199 /* RO */
+#define EXT_CSD_PWR_CL_52_195 200 /* RO */
+#define EXT_CSD_PWR_CL_26_195 201 /* RO */
+#define EXT_CSD_PWR_CL_52_360 202 /* RO */
+#define EXT_CSD_PWR_CL_26_360 203 /* RO */
+#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
+#define EXT_CSD_HC_WP_GRP_SIZE 221 /* RO */
+#define EXT_CSD_ERASE_TO_MULT 223 /* RO */
+#define EXT_CSD_ERASE_GRP_SIZE 224 /* RO */
+#define EXT_CSD_BOOT_SIZE_MULT 226 /* RO */
+#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */
+#define EXT_CSD_PWR_CL_200_195 236 /* RO */
+#define EXT_CSD_PWR_CL_200_360 237 /* RO */
+#define EXT_CSD_PWR_CL_52_195_DDR 238 /* RO */
+#define EXT_CSD_PWR_CL_52_360_DDR 239 /* RO */
+#define EXT_CSD_GEN_CMD6_TIME 248 /* RO */
+#define EXT_CSD_PWR_CL_200_360_DDR 253 /* RO */
/*
* EXT_CSD field definitions
*/
-#define EXT_CSD_CMD_SET_NORMAL 1
-#define EXT_CSD_CMD_SET_SECURE 2
-#define EXT_CSD_CMD_SET_CPSECURE 4
+#define EXT_CSD_EXT_PART_ATTR_DEFAULT 0x0
+#define EXT_CSD_EXT_PART_ATTR_SYSTEMCODE 0x1
+#define EXT_CSD_EXT_PART_ATTR_NPERSISTENT 0x2
-#define EXT_CSD_CARD_TYPE_26 1
-#define EXT_CSD_CARD_TYPE_52 2
+#define EXT_CSD_PART_SET_COMPLETED 0x01
-#define EXT_CSD_BUS_WIDTH_1 0
-#define EXT_CSD_BUS_WIDTH_4 1
-#define EXT_CSD_BUS_WIDTH_8 2
+#define EXT_CSD_PART_ATTR_ENH_USR 0x01
+#define EXT_CSD_PART_ATTR_ENH_GP0 0x02
+#define EXT_CSD_PART_ATTR_ENH_GP1 0x04
+#define EXT_CSD_PART_ATTR_ENH_GP2 0x08
+#define EXT_CSD_PART_ATTR_ENH_GP3 0x10
+#define EXT_CSD_PART_ATTR_ENH_MASK 0x1f
-#define MMC_TYPE_26_MAX_HS 26000000
-#define MMC_TYPE_52_MAX_HS 52000000
+#define EXT_CSD_PART_SUPPORT_EN 0x01
+#define EXT_CSD_PART_SUPPORT_ENH_ATTR_EN 0x02
+#define EXT_CSD_PART_SUPPORT_EXT_ATTR_EN 0x04
+#define EXT_CSD_BOOT_WP_STATUS_BOOT0_PWR 0x01
+#define EXT_CSD_BOOT_WP_STATUS_BOOT0_PERM 0x02
+#define EXT_CSD_BOOT_WP_STATUS_BOOT0_MASK 0x03
+#define EXT_CSD_BOOT_WP_STATUS_BOOT1_PWR 0x04
+#define EXT_CSD_BOOT_WP_STATUS_BOOT1_PERM 0x08
+#define EXT_CSD_BOOT_WP_STATUS_BOOT1_MASK 0x0c
+
+#define EXT_CSD_ERASE_GRP_DEF_EN 0x01
+
+#define EXT_CSD_PART_CONFIG_ACC_DEFAULT 0x00
+#define EXT_CSD_PART_CONFIG_ACC_BOOT0 0x01
+#define EXT_CSD_PART_CONFIG_ACC_BOOT1 0x02
+#define EXT_CSD_PART_CONFIG_ACC_RPMB 0x03
+#define EXT_CSD_PART_CONFIG_ACC_GP0 0x04
+#define EXT_CSD_PART_CONFIG_ACC_GP1 0x05
+#define EXT_CSD_PART_CONFIG_ACC_GP2 0x06
+#define EXT_CSD_PART_CONFIG_ACC_GP3 0x07
+#define EXT_CSD_PART_CONFIG_ACC_MASK 0x07
+#define EXT_CSD_PART_CONFIG_BOOT0 0x08
+#define EXT_CSD_PART_CONFIG_BOOT1 0x10
+#define EXT_CSD_PART_CONFIG_BOOT_USR 0x38
+#define EXT_CSD_PART_CONFIG_BOOT_MASK 0x38
+#define EXT_CSD_PART_CONFIG_BOOT_ACK 0x40
+
+#define EXT_CSD_CMD_SET_NORMAL 1
+#define EXT_CSD_CMD_SET_SECURE 2
+#define EXT_CSD_CMD_SET_CPSECURE 4
+
+#define EXT_CSD_HS_TIMING_BC 0
+#define EXT_CSD_HS_TIMING_HS 1
+#define EXT_CSD_HS_TIMING_HS200 2
+#define EXT_CSD_HS_TIMING_HS400 3
+#define EXT_CSD_HS_TIMING_DRV_STR_SHIFT 4
+
+#define EXT_CSD_POWER_CLASS_8BIT_MASK 0xf0
+#define EXT_CSD_POWER_CLASS_8BIT_SHIFT 4
+#define EXT_CSD_POWER_CLASS_4BIT_MASK 0x0f
+#define EXT_CSD_POWER_CLASS_4BIT_SHIFT 0
+
+#define EXT_CSD_CARD_TYPE_HS_26 0x0001
+#define EXT_CSD_CARD_TYPE_HS_52 0x0002
+#define EXT_CSD_CARD_TYPE_DDR_52_1_8V 0x0004
+#define EXT_CSD_CARD_TYPE_DDR_52_1_2V 0x0008
+#define EXT_CSD_CARD_TYPE_HS200_1_8V 0x0010
+#define EXT_CSD_CARD_TYPE_HS200_1_2V 0x0020
+#define EXT_CSD_CARD_TYPE_HS400_1_8V 0x0040
+#define EXT_CSD_CARD_TYPE_HS400_1_2V 0x0080
+
+#define EXT_CSD_BUS_WIDTH_1 0
+#define EXT_CSD_BUS_WIDTH_4 1
+#define EXT_CSD_BUS_WIDTH_8 2
+#define EXT_CSD_BUS_WIDTH_4_DDR 5
+#define EXT_CSD_BUS_WIDTH_8_DDR 6
+#define EXT_CSD_BUS_WIDTH_ES 0x80
+
+#define EXT_CSD_STROBE_SUPPORT_EN 0x01
+
+#define EXT_CSD_SEC_FEATURE_SUPPORT_ER_EN 0x01
+#define EXT_CSD_SEC_FEATURE_SUPPORT_BD_BLK_EN 0x04
+#define EXT_CSD_SEC_FEATURE_SUPPORT_GB_CL_EN 0x10
+#define EXT_CSD_SEC_FEATURE_SUPPORT_SANITIZE 0x40
+
/*
+ * Vendor specific EXT_CSD fields
+ */
+/* SanDisk iNAND */
+#define EXT_CSD_INAND_CMD38 113
+#define EXT_CSD_INAND_CMD38_ERASE 0x00
+#define EXT_CSD_INAND_CMD38_TRIM 0x01
+#define EXT_CSD_INAND_CMD38_SECURE_ERASE 0x80
+#define EXT_CSD_INAND_CMD38_SECURE_TRIM1 0x81
+#define EXT_CSD_INAND_CMD38_SECURE_TRIM2 0x82
+
+#define MMC_TYPE_HS_26_MAX 26000000
+#define MMC_TYPE_HS_52_MAX 52000000
+#define MMC_TYPE_DDR52_MAX 52000000
+#define MMC_TYPE_HS200_HS400ES_MAX 200000000
+
+/*
* SD bus widths
*/
-#define SD_BUS_WIDTH_1 0
-#define SD_BUS_WIDTH_4 2
+#define SD_BUS_WIDTH_1 0
+#define SD_BUS_WIDTH_4 2
/*
* SD Switch
*/
-#define SD_SWITCH_MODE_CHECK 0
-#define SD_SWITCH_MODE_SET 1
-#define SD_SWITCH_GROUP1 0
-#define SD_SWITCH_NORMAL_MODE 0
-#define SD_SWITCH_HS_MODE 1
-#define SD_SWITCH_NOCHANGE 0xF
+#define SD_SWITCH_MODE_CHECK 0
+#define SD_SWITCH_MODE_SET 1
+#define SD_SWITCH_GROUP1 0
+#define SD_SWITCH_NORMAL_MODE 0
+#define SD_SWITCH_HS_MODE 1
+#define SD_SWITCH_SDR50_MODE 2
+#define SD_SWITCH_SDR104_MODE 3
+#define SD_SWITCH_DDR50 4
+#define SD_SWITCH_NOCHANGE 0xF
#define SD_CLR_CARD_DETECT 0
#define SD_SET_CARD_DETECT 1
-#define SD_MAX_HS 50000000
+#define SD_HS_MAX 50000000
+#define SD_DDR50_MAX 50000000
+#define SD_SDR12_MAX 25000000
+#define SD_SDR25_MAX 50000000
+#define SD_SDR50_MAX 100000000
+#define SD_SDR104_MAX 208000000
+/* Specifications require 400 kHz max. during ID phase. */
+#define SD_MMC_CARD_ID_FREQUENCY 400000
+
/* OCR bits */
/*
@@ -352,6 +484,7 @@
*/
#define MMC_OCR_VOLTAGE 0x3fffffffU /* Vdd Voltage mask */
#define MMC_OCR_LOW_VOLTAGE (1u << 7) /* Low Voltage Range -- tbd */
+#define MMC_OCR_MIN_VOLTAGE_SHIFT 7
#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 */
@@ -368,6 +501,13 @@
#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_MAX_VOLTAGE_SHIFT 23
+#define MMC_OCR_S18R (1U << 24) /* Switching to 1.8 V requested (SD) */
+#define MMC_OCR_S18A MMC_OCR_S18R /* Switching to 1.8 V accepted (SD) */
+#define MMC_OCR_XPC (1U << 28) /* SDXC Power Control */
+#define MMC_OCR_ACCESS_MODE_BYTE (0U << 29) /* Access Mode Byte (MMC) */
+#define MMC_OCR_ACCESS_MODE_SECT (1U << 29) /* Access Mode Sector (MMC) */
+#define MMC_OCR_ACCESS_MODE_MASK (3U << 29)
#define MMC_OCR_CCS (1u << 30) /* Card Capacity status (SD vs SDHC) */
#define MMC_OCR_CARD_BUSY (1U << 31) /* Card Power up status */
@@ -383,8 +523,7 @@
uint8_t fwrev;
};
-struct mmc_csd
-{
+struct mmc_csd {
uint8_t csd_structure;
uint8_t spec_vers;
uint16_t ccc;
@@ -410,16 +549,14 @@
wp_grp_enable:1;
};
-struct mmc_scr
-{
+struct mmc_scr {
unsigned char sda_vsn;
unsigned char bus_widths;
-#define SD_SCR_BUS_WIDTH_1 (1<<0)
-#define SD_SCR_BUS_WIDTH_4 (1<<2)
+#define SD_SCR_BUS_WIDTH_1 (1 << 0)
+#define SD_SCR_BUS_WIDTH_4 (1 << 2)
};
-struct mmc_sd_status
-{
+struct mmc_sd_status {
uint8_t bus_width;
uint8_t secured_mode;
uint16_t card_type;
@@ -432,12 +569,39 @@
uint8_t erase_offset;
};
+struct mmc_quirk {
+ uint32_t mid;
+#define MMC_QUIRK_MID_ANY ((uint32_t)-1)
+ uint16_t oid;
+#define MMC_QUIRK_OID_ANY ((uint16_t)-1)
+ const char *pnm;
+ uint32_t quirks;
+#define MMC_QUIRK_INAND_CMD38 0x0001
+#define MMC_QUIRK_BROKEN_TRIM 0x0002
+};
+
+#define MMC_QUIRKS_FMT "\020" "\001INAND_CMD38" "\002BROKEN_TRIM"
+
/*
+ * Various MMC/SD constants
+ */
+#define MMC_BOOT_RPMB_BLOCK_SIZE (128 * 1024)
+
+#define MMC_EXTCSD_SIZE 512
+
+#define MMC_PART_GP_MAX 4
+#define MMC_PART_MAX 8
+
+#define MMC_TUNING_MAX 64 /* Maximum tuning iterations */
+#define MMC_TUNING_LEN 64 /* Size of tuning data */
+#define MMC_TUNING_LEN_HS200 128 /* Size of tuning data in HS200 mode */
+
+/*
* Older versions of the MMC standard had a variable sector size. However,
* I've been able to find no old MMC or SD cards that have a non 512
* byte sector size anywhere, so we assume that such cards are very rare
* and only note their existance in passing here...
*/
-#define MMC_SECTOR_SIZE 512
+#define MMC_SECTOR_SIZE 512
#endif /* DEV_MMCREG_H */
Modified: trunk/sys/dev/mmc/mmcsd.c
===================================================================
--- trunk/sys/dev/mmc/mmcsd.c 2018-05-27 23:36:22 UTC (rev 10095)
+++ trunk/sys/dev/mmc/mmcsd.c 2018-05-27 23:36:35 UTC (rev 10096)
@@ -1,6 +1,8 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 2006 Bernd Walter. All rights reserved.
* Copyright (c) 2006 M. Warner Losh. All rights reserved.
+ * Copyright (c) 2017 Marius Strobl <marius at FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -51,7 +53,7 @@
*/
#include <sys/cdefs.h>
-__MBSDID("$MidnightBSD$");
+__FBSDID("$FreeBSD: stable/10/sys/dev/mmc/mmcsd.c 331037 2018-03-15 23:01:04Z marius $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -58,6 +60,8 @@
#include <sys/bio.h>
#include <sys/bus.h>
#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/ioccom.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/lock.h>
@@ -64,8 +68,15 @@
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
+#include <sys/slicer.h>
+#include <sys/time.h>
+
+#include <geom/geom.h>
#include <geom/geom_disk.h>
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmc_ioctl.h>
+#include <dev/mmc/mmc_subr.h>
#include <dev/mmc/mmcbrvar.h>
#include <dev/mmc/mmcreg.h>
#include <dev/mmc/mmcvar.h>
@@ -77,17 +88,70 @@
#define kproc_exit kthread_exit
#endif
-struct mmcsd_softc {
- device_t dev;
- struct mtx sc_mtx;
+#define MMCSD_CMD_RETRIES 5
+
+#define MMCSD_FMT_BOOT "mmcsd%dboot"
+#define MMCSD_FMT_GP "mmcsd%dgp"
+#define MMCSD_FMT_RPMB "mmcsd%drpmb"
+#define MMCSD_LABEL_ENH "enh"
+
+#define MMCSD_PART_NAMELEN (16 + 1)
+
+struct mmcsd_softc;
+
+struct mmcsd_part {
+ struct mtx disk_mtx;
+ struct mtx ioctl_mtx;
+ struct mmcsd_softc *sc;
struct disk *disk;
struct proc *p;
struct bio_queue_head bio_queue;
daddr_t eblock, eend; /* Range remaining after the last erase. */
+ u_int cnt;
+ u_int type;
int running;
int suspend;
+ int ioctl;
+ bool ro;
+ char name[MMCSD_PART_NAMELEN];
};
+struct mmcsd_softc {
+ device_t dev;
+ device_t mmcbus;
+ struct mmcsd_part *part[MMC_PART_MAX];
+ enum mmc_card_mode mode;
+ u_int max_data; /* Maximum data size [blocks] */
+ u_int erase_sector; /* Device native erase sector size [blocks] */
+ uint8_t high_cap; /* High Capacity device (block addressed) */
+ uint8_t part_curr; /* Partition currently switched to */
+ uint8_t ext_csd[MMC_EXTCSD_SIZE];
+ uint16_t rca;
+ uint32_t flags;
+#define MMCSD_INAND_CMD38 0x0001
+#define MMCSD_USE_TRIM 0x0002
+ uint32_t cmd6_time; /* Generic switch timeout [us] */
+ uint32_t part_time; /* Partition switch timeout [us] */
+ off_t enh_base; /* Enhanced user data area slice base ... */
+ off_t enh_size; /* ... and size [bytes] */
+ int log_count;
+ struct timeval log_time;
+ struct cdev *rpmb_dev;
+};
+
+static const char *errmsg[] =
+{
+ "None",
+ "Timeout",
+ "Bad CRC",
+ "Fifo",
+ "Failed",
+ "Invalid",
+ "NO MEMORY"
+};
+
+#define LOG_PPS 5 /* Log no more than 5 errors per second. */
+
/* bus entry points */
static int mmcsd_attach(device_t dev);
static int mmcsd_detach(device_t dev);
@@ -96,24 +160,55 @@
/* disk routines */
static int mmcsd_close(struct disk *dp);
static int mmcsd_dump(void *arg, void *virtual, vm_offset_t physical,
- off_t offset, size_t length);
+ off_t offset, size_t length);
+static int mmcsd_getattr(struct bio *);
+static int mmcsd_ioctl_disk(struct disk *disk, u_long cmd, void *data,
+ int fflag, struct thread *td);
static int mmcsd_open(struct disk *dp);
static void mmcsd_strategy(struct bio *bp);
static void mmcsd_task(void *arg);
+/* RMPB cdev interface */
+static int mmcsd_ioctl_rpmb(struct cdev *dev, u_long cmd, caddr_t data,
+ int fflag, struct thread *td);
+
+static void mmcsd_add_part(struct mmcsd_softc *sc, u_int type,
+ const char *name, u_int cnt, off_t media_size, bool ro);
static int mmcsd_bus_bit_width(device_t dev);
-static daddr_t mmcsd_delete(struct mmcsd_softc *sc, struct bio *bp);
-static daddr_t mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp);
+static daddr_t mmcsd_delete(struct mmcsd_part *part, struct bio *bp);
+static const char *mmcsd_errmsg(int e);
+static int mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data,
+ int fflag);
+static int mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic,
+ int fflag);
+static uintmax_t mmcsd_pretty_size(off_t size, char *unit);
+static daddr_t mmcsd_rw(struct mmcsd_part *part, struct bio *bp);
+static int mmcsd_set_blockcount(struct mmcsd_softc *sc, u_int count, bool rel);
+static int mmcsd_slicer(device_t dev, const char *provider,
+ struct flash_slice *slices, int *nslices);
+static int mmcsd_switch_part(device_t bus, device_t dev, uint16_t rca,
+ u_int part);
-#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);
+#define MMCSD_DISK_LOCK(_part) mtx_lock(&(_part)->disk_mtx)
+#define MMCSD_DISK_UNLOCK(_part) mtx_unlock(&(_part)->disk_mtx)
+#define MMCSD_DISK_LOCK_INIT(_part) \
+ mtx_init(&(_part)->disk_mtx, (_part)->name, "mmcsd disk", MTX_DEF)
+#define MMCSD_DISK_LOCK_DESTROY(_part) mtx_destroy(&(_part)->disk_mtx);
+#define MMCSD_DISK_ASSERT_LOCKED(_part) \
+ mtx_assert(&(_part)->disk_mtx, MA_OWNED);
+#define MMCSD_DISK_ASSERT_UNLOCKED(_part) \
+ mtx_assert(&(_part)->disk_mtx, MA_NOTOWNED);
+#define MMCSD_IOCTL_LOCK(_part) mtx_lock(&(_part)->ioctl_mtx)
+#define MMCSD_IOCTL_UNLOCK(_part) mtx_unlock(&(_part)->ioctl_mtx)
+#define MMCSD_IOCTL_LOCK_INIT(_part) \
+ mtx_init(&(_part)->ioctl_mtx, (_part)->name, "mmcsd IOCTL", MTX_DEF)
+#define MMCSD_IOCTL_LOCK_DESTROY(_part) mtx_destroy(&(_part)->ioctl_mtx);
+#define MMCSD_IOCTL_ASSERT_LOCKED(_part) \
+ mtx_assert(&(_part)->ioctl_mtx, MA_OWNED);
+#define MMCSD_IOCLT_ASSERT_UNLOCKED(_part) \
+ mtx_assert(&(_part)->ioctl_mtx, MA_NOTOWNED);
+
static int
mmcsd_probe(device_t dev)
{
@@ -126,97 +221,454 @@
static int
mmcsd_attach(device_t dev)
{
+ device_t mmcbus;
struct mmcsd_softc *sc;
- struct disk *d;
- intmax_t mb;
- uint32_t speed;
- uint32_t maxblocks;
- char unit;
+ const uint8_t *ext_csd;
+ off_t erase_size, sector_size, size, wp_size;
+ uintmax_t bytes;
+ int err, i;
+ uint32_t quirks;
+ uint8_t rev;
+ bool comp, ro;
+ char unit[2];
sc = device_get_softc(dev);
sc->dev = dev;
- MMCSD_LOCK_INIT(sc);
+ sc->mmcbus = mmcbus = device_get_parent(dev);
+ sc->mode = mmcbr_get_mode(mmcbus);
+ /*
+ * Note that in principle with an SDHCI-like re-tuning implementation,
+ * the maximum data size can change at runtime due to a device removal/
+ * insertion that results in switches to/from a transfer mode involving
+ * re-tuning, iff there are multiple devices on a given bus. Until now
+ * mmc(4) lacks support for rescanning already attached buses, however,
+ * and sdhci(4) to date has no support for shared buses in the first
+ * place either.
+ */
+ sc->max_data = mmc_get_max_data(dev);
+ sc->high_cap = mmc_get_high_cap(dev);
+ sc->rca = mmc_get_rca(dev);
+ sc->cmd6_time = mmc_get_cmd6_timeout(dev);
+ quirks = mmc_get_quirks(dev);
- d = sc->disk = disk_alloc();
- d->d_open = mmcsd_open;
- d->d_close = mmcsd_close;
- d->d_strategy = mmcsd_strategy;
- d->d_dump = mmcsd_dump;
- d->d_name = "mmcsd";
- d->d_drv1 = sc;
- d->d_maxsize = 4*1024*1024; /* Maximum defined SD card AU size. */
- d->d_sectorsize = mmc_get_sector_size(dev);
- d->d_mediasize = (off_t)mmc_get_media_size(dev) * d->d_sectorsize;
- d->d_stripeoffset = 0;
- d->d_stripesize = mmc_get_erase_sector(dev) * d->d_sectorsize;
- d->d_unit = device_get_unit(dev);
- d->d_flags = DISKFLAG_CANDELETE;
+ /* Only MMC >= 4.x devices support EXT_CSD. */
+ if (mmc_get_spec_vers(dev) >= 4) {
+ MMCBUS_ACQUIRE_BUS(mmcbus, dev);
+ err = mmc_send_ext_csd(mmcbus, dev, sc->ext_csd);
+ MMCBUS_RELEASE_BUS(mmcbus, dev);
+ if (err != MMC_ERR_NONE) {
+ device_printf(dev, "Error reading EXT_CSD %s\n",
+ mmcsd_errmsg(err));
+ return (ENXIO);
+ }
+ }
+ ext_csd = sc->ext_csd;
+
+ if ((quirks & MMC_QUIRK_INAND_CMD38) != 0) {
+ if (mmc_get_spec_vers(dev) < 4) {
+ device_printf(dev,
+ "MMC_QUIRK_INAND_CMD38 set but no EXT_CSD\n");
+ return (EINVAL);
+ }
+ sc->flags |= MMCSD_INAND_CMD38;
+ }
+
/*
- * Display in most natural units. There's no cards < 1MB.
- * The SD standard goes to 2GiB, but the data format supports
- * up to 4GiB and some card makers push it up to this limit.
- * The SDHC standard only goes to 32GiB (the data format in
- * SDHC is good to 2TiB however, which isn't too ugly at
- * 2048GiBm, so we note it in passing here and don't add the
- * code to print TiB).
+ * EXT_CSD_SEC_FEATURE_SUPPORT_GB_CL_EN denotes support for both
+ * insecure and secure TRIM.
*/
- mb = d->d_mediasize >> 20; /* 1MiB == 1 << 20 */
- unit = 'M';
- if (mb >= 10240) { /* 1GiB = 1024 MiB */
- unit = 'G';
- mb /= 1024;
+ if ((ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] &
+ EXT_CSD_SEC_FEATURE_SUPPORT_GB_CL_EN) != 0 &&
+ (quirks & MMC_QUIRK_BROKEN_TRIM) == 0) {
+ if (bootverbose)
+ device_printf(dev, "taking advantage of TRIM\n");
+ sc->flags |= MMCSD_USE_TRIM;
+ sc->erase_sector = 1;
+ } else
+ sc->erase_sector = mmc_get_erase_sector(dev);
+
+ /*
+ * Enhanced user data area and general purpose partitions are only
+ * supported in revision 1.4 (EXT_CSD_REV == 4) and later, the RPMB
+ * partition in revision 1.5 (MMC v4.41, EXT_CSD_REV == 5) and later.
+ */
+ rev = ext_csd[EXT_CSD_REV];
+
+ /*
+ * Ignore user-creatable enhanced user data area and general purpose
+ * partitions partitions as long as partitioning hasn't been finished.
+ */
+ comp = (ext_csd[EXT_CSD_PART_SET] & EXT_CSD_PART_SET_COMPLETED) != 0;
+
+ /*
+ * Add enhanced user data area slice, unless it spans the entirety of
+ * the user data area. The enhanced area is of a multiple of high
+ * capacity write protect groups ((ERASE_GRP_SIZE + HC_WP_GRP_SIZE) *
+ * 512 KB) and its offset given in either sectors or bytes, depending
+ * on whether it's a high capacity device or not.
+ * NB: The slicer and its slices need to be registered before adding
+ * the disk for the corresponding user data area as re-tasting is
+ * racy.
+ */
+ sector_size = mmc_get_sector_size(dev);
+ size = ext_csd[EXT_CSD_ENH_SIZE_MULT] +
+ (ext_csd[EXT_CSD_ENH_SIZE_MULT + 1] << 8) +
+ (ext_csd[EXT_CSD_ENH_SIZE_MULT + 2] << 16);
+ if (rev >= 4 && comp == TRUE && size > 0 &&
+ (ext_csd[EXT_CSD_PART_SUPPORT] &
+ EXT_CSD_PART_SUPPORT_ENH_ATTR_EN) != 0 &&
+ (ext_csd[EXT_CSD_PART_ATTR] & (EXT_CSD_PART_ATTR_ENH_USR)) != 0) {
+ erase_size = ext_csd[EXT_CSD_ERASE_GRP_SIZE] * 1024 *
+ MMC_SECTOR_SIZE;
+ wp_size = ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+ size *= erase_size * wp_size;
+ if (size != mmc_get_media_size(dev) * sector_size) {
+ sc->enh_size = size;
+ sc->enh_base = (ext_csd[EXT_CSD_ENH_START_ADDR] +
+ (ext_csd[EXT_CSD_ENH_START_ADDR + 1] << 8) +
+ (ext_csd[EXT_CSD_ENH_START_ADDR + 2] << 16) +
+ (ext_csd[EXT_CSD_ENH_START_ADDR + 3] << 24)) *
+ (sc->high_cap != 0 ? MMC_SECTOR_SIZE : 1);
+ } else if (bootverbose)
+ device_printf(dev,
+ "enhanced user data area spans entire device\n");
}
+
/*
- * Report the clock speed of the underlying hardware, which might be
- * different than what the card reports due to hardware limitations.
- * Report how many blocks the hardware transfers at once, but clip the
- * number to MAXPHYS since the system won't initiate larger transfers.
+ * Add default partition. This may be the only one or the user
+ * data area in case partitions are supported.
*/
- speed = mmcbr_get_clock(device_get_parent(dev));
- maxblocks = mmc_get_max_data(dev);
- if (maxblocks > MAXPHYS)
- maxblocks = MAXPHYS;
- device_printf(dev, "%ju%cB <%s>%s at %s %d.%01dMHz/%dbit/%d-block\n",
- mb, unit, mmc_get_card_id_string(dev),
- mmc_get_read_only(dev) ? " (read-only)" : "",
- device_get_nameunit(device_get_parent(dev)),
- speed / 1000000, (speed / 100000) % 10,
- mmcsd_bus_bit_width(dev), maxblocks);
- disk_create(d, DISK_VERSION);
- bioq_init(&sc->bio_queue);
+ ro = mmc_get_read_only(dev);
+ mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_DEFAULT, "mmcsd",
+ device_get_unit(dev), mmc_get_media_size(dev) * sector_size, ro);
- sc->running = 1;
- sc->suspend = 0;
- sc->eblock = sc->eend = 0;
- kproc_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card");
+ if (mmc_get_spec_vers(dev) < 3)
+ return (0);
+ /* Belatedly announce enhanced user data slice. */
+ if (sc->enh_size != 0) {
+ bytes = mmcsd_pretty_size(size, unit);
+ printf(FLASH_SLICES_FMT ": %ju%sB enhanced user data area "
+ "slice offset 0x%jx at %s\n", device_get_nameunit(dev),
+ MMCSD_LABEL_ENH, bytes, unit, (uintmax_t)sc->enh_base,
+ device_get_nameunit(dev));
+ }
+
+ /*
+ * Determine partition switch timeout (provided in units of 10 ms)
+ * and ensure it's at least 300 ms as some eMMC chips lie.
+ */
+ sc->part_time = max(ext_csd[EXT_CSD_PART_SWITCH_TO] * 10 * 1000,
+ 300 * 1000);
+
+ /* Add boot partitions, which are of a fixed multiple of 128 KB. */
+ size = ext_csd[EXT_CSD_BOOT_SIZE_MULT] * MMC_BOOT_RPMB_BLOCK_SIZE;
+ if (size > 0 && (mmcbr_get_caps(mmcbus) & MMC_CAP_BOOT_NOACC) == 0) {
+ mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_BOOT0,
+ MMCSD_FMT_BOOT, 0, size,
+ ro | ((ext_csd[EXT_CSD_BOOT_WP_STATUS] &
+ EXT_CSD_BOOT_WP_STATUS_BOOT0_MASK) != 0));
+ mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_BOOT1,
+ MMCSD_FMT_BOOT, 1, size,
+ ro | ((ext_csd[EXT_CSD_BOOT_WP_STATUS] &
+ EXT_CSD_BOOT_WP_STATUS_BOOT1_MASK) != 0));
+ }
+
+ /* Add RPMB partition, which also is of a fixed multiple of 128 KB. */
+ size = ext_csd[EXT_CSD_RPMB_MULT] * MMC_BOOT_RPMB_BLOCK_SIZE;
+ if (rev >= 5 && size > 0)
+ mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_RPMB,
+ MMCSD_FMT_RPMB, 0, size, ro);
+
+ if (rev <= 3 || comp == FALSE)
+ return (0);
+
+ /*
+ * Add general purpose partitions, which are of a multiple of high
+ * capacity write protect groups, too.
+ */
+ if ((ext_csd[EXT_CSD_PART_SUPPORT] & EXT_CSD_PART_SUPPORT_EN) != 0) {
+ erase_size = ext_csd[EXT_CSD_ERASE_GRP_SIZE] * 1024 *
+ MMC_SECTOR_SIZE;
+ wp_size = ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+ for (i = 0; i < MMC_PART_GP_MAX; i++) {
+ size = ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3] +
+ (ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3 + 1] << 8) +
+ (ext_csd[EXT_CSD_GP_SIZE_MULT + i * 3 + 2] << 16);
+ if (size == 0)
+ continue;
+ mmcsd_add_part(sc, EXT_CSD_PART_CONFIG_ACC_GP0 + i,
+ MMCSD_FMT_GP, i, size * erase_size * wp_size, ro);
+ }
+ }
return (0);
}
+static uintmax_t
+mmcsd_pretty_size(off_t size, char *unit)
+{
+ uintmax_t bytes;
+ int i;
+
+ /*
+ * Display in most natural units. There's no card < 1MB. However,
+ * RPMB partitions occasionally are smaller than that, though. The
+ * SD standard goes to 2 GiB due to its reliance on FAT, but the data
+ * format supports up to 4 GiB and some card makers push it up to this
+ * limit. The SDHC standard only goes to 32 GiB due to FAT32, but the
+ * data format supports up to 2 TiB however. 2048 GB isn't too ugly,
+ * so we note it in passing here and don't add the code to print TB).
+ * Since these cards are sold in terms of MB and GB not MiB and GiB,
+ * report them like that. We also round to the nearest unit, since
+ * many cards are a few percent short, even of the power of 10 size.
+ */
+ bytes = size;
+ unit[0] = unit[1] = '\0';
+ for (i = 0; i <= 2 && bytes >= 1000; i++) {
+ bytes = (bytes + 1000 / 2 - 1) / 1000;
+ switch (i) {
+ case 0:
+ unit[0] = 'k';
+ break;
+ case 1:
+ unit[0] = 'M';
+ break;
+ case 2:
+ unit[0] = 'G';
+ break;
+ default:
+ break;
+ }
+ }
+ return (bytes);
+}
+
+static struct cdevsw mmcsd_rpmb_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name = "mmcsdrpmb",
+ .d_ioctl = mmcsd_ioctl_rpmb
+};
+
+static void
+mmcsd_add_part(struct mmcsd_softc *sc, u_int type, const char *name, u_int cnt,
+ off_t media_size, bool ro)
+{
+ struct make_dev_args args;
+ device_t dev, mmcbus;
+ const char *ext;
+ const uint8_t *ext_csd;
+ struct mmcsd_part *part;
+ struct disk *d;
+ uintmax_t bytes;
+ u_int gp;
+ uint32_t speed;
+ uint8_t extattr;
+ bool enh;
+ char unit[2];
+
+ dev = sc->dev;
+ mmcbus = sc->mmcbus;
+ part = sc->part[type] = malloc(sizeof(*part), M_DEVBUF,
+ M_WAITOK | M_ZERO);
+ part->sc = sc;
+ part->cnt = cnt;
+ part->type = type;
+ part->ro = ro;
+ snprintf(part->name, sizeof(part->name), name, device_get_unit(dev));
+
+ MMCSD_IOCTL_LOCK_INIT(part);
+
+ /*
+ * For the RPMB partition, allow IOCTL access only.
+ * NB: If ever attaching RPMB partitions to disk(9), the re-tuning
+ * implementation and especially its pausing need to be revisited,
+ * because then re-tuning requests may be issued by the IOCTL half
+ * of this driver while re-tuning is already paused by the disk(9)
+ * one and vice versa.
+ */
+ if (type == EXT_CSD_PART_CONFIG_ACC_RPMB) {
+ make_dev_args_init(&args);
+ args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK;
+ args.mda_devsw = &mmcsd_rpmb_cdevsw;
+ args.mda_uid = UID_ROOT;
+ args.mda_gid = GID_OPERATOR;
+ args.mda_mode = 0640;
+ args.mda_si_drv1 = part;
+ if (make_dev_s(&args, &sc->rpmb_dev, "%s", part->name) != 0) {
+ device_printf(dev, "Failed to make RPMB device\n");
+ free(part, M_DEVBUF);
+ return;
+ }
+ } else {
+ MMCSD_DISK_LOCK_INIT(part);
+
+ d = part->disk = disk_alloc();
+ d->d_open = mmcsd_open;
+ d->d_close = mmcsd_close;
+ d->d_strategy = mmcsd_strategy;
+ d->d_ioctl = mmcsd_ioctl_disk;
+ d->d_dump = mmcsd_dump;
+ d->d_getattr = mmcsd_getattr;
+ d->d_name = part->name;
+ d->d_drv1 = part;
+ d->d_sectorsize = mmc_get_sector_size(dev);
+ d->d_maxsize = sc->max_data * d->d_sectorsize;
+ d->d_mediasize = media_size;
+ d->d_stripesize = sc->erase_sector * d->d_sectorsize;
+ d->d_unit = cnt;
+ d->d_flags = DISKFLAG_CANDELETE;
+ d->d_delmaxsize = mmc_get_erase_sector(dev) * d->d_sectorsize;
+ strlcpy(d->d_ident, mmc_get_card_sn_string(dev),
+ sizeof(d->d_ident));
+ strlcpy(d->d_descr, mmc_get_card_id_string(dev),
+ sizeof(d->d_descr));
+ d->d_rotation_rate = DISK_RR_NON_ROTATING;
+
+ disk_create(d, DISK_VERSION);
+ bioq_init(&part->bio_queue);
+
+ part->running = 1;
+ kproc_create(&mmcsd_task, part, &part->p, 0, 0,
+ "%s%d: mmc/sd card", part->name, cnt);
+ }
+
+ bytes = mmcsd_pretty_size(media_size, unit);
+ if (type == EXT_CSD_PART_CONFIG_ACC_DEFAULT) {
+ speed = mmcbr_get_clock(mmcbus);
+ printf("%s%d: %ju%sB <%s>%s at %s %d.%01dMHz/%dbit/%d-block\n",
+ part->name, cnt, bytes, unit, mmc_get_card_id_string(dev),
+ ro ? " (read-only)" : "", device_get_nameunit(mmcbus),
+ speed / 1000000, (speed / 100000) % 10,
+ mmcsd_bus_bit_width(dev), sc->max_data);
+ } else if (type == EXT_CSD_PART_CONFIG_ACC_RPMB) {
+ printf("%s: %ju%sB partion %d%s at %s\n", part->name, bytes,
+ unit, type, ro ? " (read-only)" : "",
+ device_get_nameunit(dev));
+ } else {
+ enh = false;
+ ext = NULL;
+ extattr = 0;
+ if (type >= EXT_CSD_PART_CONFIG_ACC_GP0 &&
+ type <= EXT_CSD_PART_CONFIG_ACC_GP3) {
+ ext_csd = sc->ext_csd;
+ gp = type - EXT_CSD_PART_CONFIG_ACC_GP0;
+ if ((ext_csd[EXT_CSD_PART_SUPPORT] &
+ EXT_CSD_PART_SUPPORT_ENH_ATTR_EN) != 0 &&
+ (ext_csd[EXT_CSD_PART_ATTR] &
+ (EXT_CSD_PART_ATTR_ENH_GP0 << gp)) != 0)
+ enh = true;
+ else if ((ext_csd[EXT_CSD_PART_SUPPORT] &
+ EXT_CSD_PART_SUPPORT_EXT_ATTR_EN) != 0) {
+ extattr = (ext_csd[EXT_CSD_EXT_PART_ATTR +
+ (gp / 2)] >> (4 * (gp % 2))) & 0xF;
+ switch (extattr) {
+ case EXT_CSD_EXT_PART_ATTR_DEFAULT:
+ break;
+ case EXT_CSD_EXT_PART_ATTR_SYSTEMCODE:
+ ext = "system code";
+ break;
+ case EXT_CSD_EXT_PART_ATTR_NPERSISTENT:
+ ext = "non-persistent";
+ break;
+ default:
+ ext = "reserved";
+ break;
+ }
+ }
+ }
+ if (ext == NULL)
+ printf("%s%d: %ju%sB partion %d%s%s at %s\n",
+ part->name, cnt, bytes, unit, type, enh ?
+ " enhanced" : "", ro ? " (read-only)" : "",
+ device_get_nameunit(dev));
+ else
+ printf("%s%d: %ju%sB partion %d extended 0x%x "
+ "(%s)%s at %s\n", part->name, cnt, bytes, unit,
+ type, extattr, ext, ro ? " (read-only)" : "",
+ device_get_nameunit(dev));
+ }
+}
+
static int
+mmcsd_slicer(device_t dev, const char *provider,
+ struct flash_slice *slices, int *nslices)
+{
+ char name[MMCSD_PART_NAMELEN];
+ struct mmcsd_softc *sc;
+ struct mmcsd_part *part;
+
+ *nslices = 0;
+ if (slices == NULL)
+ return (ENOMEM);
+
+ sc = device_get_softc(dev);
+ if (sc->enh_size == 0)
+ return (ENXIO);
+
+ part = sc->part[EXT_CSD_PART_CONFIG_ACC_DEFAULT];
+ snprintf(name, sizeof(name), "%s%d", part->disk->d_name,
+ part->disk->d_unit);
+ if (strcmp(name, provider) != 0)
+ return (ENXIO);
+
+ *nslices = 1;
+ slices[0].base = sc->enh_base;
+ slices[0].size = sc->enh_size;
+ slices[0].label = MMCSD_LABEL_ENH;
+ return (0);
+}
+
+static int
mmcsd_detach(device_t dev)
{
struct mmcsd_softc *sc = device_get_softc(dev);
+ struct mmcsd_part *part;
+ int i;
- MMCSD_LOCK(sc);
- sc->suspend = 0;
- if (sc->running > 0) {
- /* kill thread */
- sc->running = 0;
- wakeup(sc);
- /* wait for thread to finish. */
- while (sc->running != -1)
- msleep(sc, &sc->sc_mtx, 0, "detach", 0);
+ for (i = 0; i < MMC_PART_MAX; i++) {
+ part = sc->part[i];
+ if (part != NULL) {
+ if (part->disk != NULL) {
+ MMCSD_DISK_LOCK(part);
+ part->suspend = 0;
+ if (part->running > 0) {
+ /* kill thread */
+ part->running = 0;
+ wakeup(part);
+ /* wait for thread to finish. */
+ while (part->running != -1)
+ msleep(part, &part->disk_mtx, 0,
+ "mmcsd disk detach", 0);
+ }
+ MMCSD_DISK_UNLOCK(part);
+ }
+ MMCSD_IOCTL_LOCK(part);
+ while (part->ioctl > 0)
+ msleep(part, &part->ioctl_mtx, 0,
+ "mmcsd IOCTL detach", 0);
+ part->ioctl = -1;
+ MMCSD_IOCTL_UNLOCK(part);
+ }
}
- MMCSD_UNLOCK(sc);
- /* Flush the request queue. */
- bioq_flush(&sc->bio_queue, NULL, ENXIO);
- /* kill disk */
- disk_destroy(sc->disk);
+ if (sc->rpmb_dev != NULL)
+ destroy_dev(sc->rpmb_dev);
- MMCSD_LOCK_DESTROY(sc);
+ for (i = 0; i < MMC_PART_MAX; i++) {
+ part = sc->part[i];
+ if (part != NULL) {
+ if (part->disk != NULL) {
+ /* Flush the request queue. */
+ bioq_flush(&part->bio_queue, NULL, ENXIO);
+ /* kill disk */
+ disk_destroy(part->disk);
+ MMCSD_DISK_LOCK_DESTROY(part);
+ }
+ MMCSD_IOCTL_LOCK_DESTROY(part);
+ free(part, M_DEVBUF);
+ }
+ }
return (0);
}
@@ -224,18 +676,34 @@
mmcsd_suspend(device_t dev)
{
struct mmcsd_softc *sc = device_get_softc(dev);
+ struct mmcsd_part *part;
+ int i;
- MMCSD_LOCK(sc);
- sc->suspend = 1;
- if (sc->running > 0) {
- /* kill thread */
- sc->running = 0;
- wakeup(sc);
- /* wait for thread to finish. */
- while (sc->running != -1)
- msleep(sc, &sc->sc_mtx, 0, "detach", 0);
+ for (i = 0; i < MMC_PART_MAX; i++) {
+ part = sc->part[i];
+ if (part != NULL) {
+ if (part->disk != NULL) {
+ MMCSD_DISK_LOCK(part);
+ part->suspend = 1;
+ if (part->running > 0) {
+ /* kill thread */
+ part->running = 0;
+ wakeup(part);
+ /* wait for thread to finish. */
+ while (part->running != -1)
+ msleep(part, &part->disk_mtx, 0,
+ "mmcsd disk suspension", 0);
+ }
+ MMCSD_DISK_UNLOCK(part);
+ }
+ MMCSD_IOCTL_LOCK(part);
+ while (part->ioctl > 0)
+ msleep(part, &part->ioctl_mtx, 0,
+ "mmcsd IOCTL suspension", 0);
+ part->ioctl = -1;
+ MMCSD_IOCTL_UNLOCK(part);
+ }
}
- MMCSD_UNLOCK(sc);
return (0);
}
@@ -243,20 +711,34 @@
mmcsd_resume(device_t dev)
{
struct mmcsd_softc *sc = device_get_softc(dev);
+ struct mmcsd_part *part;
+ int i;
- MMCSD_LOCK(sc);
- sc->suspend = 0;
- if (sc->running <= 0) {
- sc->running = 1;
- MMCSD_UNLOCK(sc);
- kproc_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card");
- } else
- MMCSD_UNLOCK(sc);
+ for (i = 0; i < MMC_PART_MAX; i++) {
+ part = sc->part[i];
+ if (part != NULL) {
+ if (part->disk != NULL) {
+ MMCSD_DISK_LOCK(part);
+ part->suspend = 0;
+ if (part->running <= 0) {
+ part->running = 1;
+ MMCSD_DISK_UNLOCK(part);
+ kproc_create(&mmcsd_task, part,
+ &part->p, 0, 0, "%s%d: mmc/sd card",
+ part->name, part->cnt);
+ } else
+ MMCSD_DISK_UNLOCK(part);
+ }
+ MMCSD_IOCTL_LOCK(part);
+ part->ioctl = 0;
+ MMCSD_IOCTL_UNLOCK(part);
+ }
+ }
return (0);
}
static int
-mmcsd_open(struct disk *dp)
+mmcsd_open(struct disk *dp __unused)
{
return (0);
@@ -263,7 +745,7 @@
}
static int
-mmcsd_close(struct disk *dp)
+mmcsd_close(struct disk *dp __unused)
{
return (0);
@@ -273,21 +755,367 @@
mmcsd_strategy(struct bio *bp)
{
struct mmcsd_softc *sc;
+ struct mmcsd_part *part;
- sc = (struct mmcsd_softc *)bp->bio_disk->d_drv1;
- MMCSD_LOCK(sc);
- if (sc->running > 0 || sc->suspend > 0) {
- bioq_disksort(&sc->bio_queue, bp);
- MMCSD_UNLOCK(sc);
- wakeup(sc);
+ part = bp->bio_disk->d_drv1;
+ sc = part->sc;
+ MMCSD_DISK_LOCK(part);
+ if (part->running > 0 || part->suspend > 0) {
+ bioq_disksort(&part->bio_queue, bp);
+ MMCSD_DISK_UNLOCK(part);
+ wakeup(part);
} else {
- MMCSD_UNLOCK(sc);
+ MMCSD_DISK_UNLOCK(part);
biofinish(bp, NULL, ENXIO);
}
}
+static int
+mmcsd_ioctl_rpmb(struct cdev *dev, u_long cmd, caddr_t data,
+ int fflag, struct thread *td __unused)
+{
+
+ return (mmcsd_ioctl(dev->si_drv1, cmd, data, fflag));
+}
+
+static int
+mmcsd_ioctl_disk(struct disk *disk, u_long cmd, void *data, int fflag,
+ struct thread *td __unused)
+{
+
+ return (mmcsd_ioctl(disk->d_drv1, cmd, data, fflag));
+}
+
+static int
+mmcsd_ioctl(struct mmcsd_part *part, u_long cmd, void *data, int fflag)
+{
+ struct mmc_ioc_cmd *mic;
+ struct mmc_ioc_multi_cmd *mimc;
+ int i, err;
+ u_long cnt, size;
+
+ if ((fflag & FREAD) == 0)
+ return (EBADF);
+
+ err = 0;
+ switch (cmd) {
+ case MMC_IOC_CMD:
+ mic = data;
+ err = mmcsd_ioctl_cmd(part, mic, fflag);
+ break;
+ case MMC_IOC_MULTI_CMD:
+ mimc = data;
+ if (mimc->num_of_cmds == 0)
+ break;
+ if (mimc->num_of_cmds > MMC_IOC_MAX_CMDS)
+ return (EINVAL);
+ cnt = mimc->num_of_cmds;
+ size = sizeof(*mic) * cnt;
+ mic = malloc(size, M_TEMP, M_WAITOK);
+ err = copyin((const void *)mimc->cmds, mic, size);
+ if (err == 0) {
+ for (i = 0; i < cnt; i++) {
+ err = mmcsd_ioctl_cmd(part, &mic[i], fflag);
+ if (err != 0)
+ break;
+ }
+ }
+ free(mic, M_TEMP);
+ break;
+ default:
+ return (ENOIOCTL);
+ }
+ return (err);
+}
+
+static int
+mmcsd_ioctl_cmd(struct mmcsd_part *part, struct mmc_ioc_cmd *mic, int fflag)
+{
+ struct mmc_command cmd;
+ struct mmc_data data;
+ struct mmcsd_softc *sc;
+ device_t dev, mmcbus;
+ void *dp;
+ u_long len;
+ int err, retries;
+ uint32_t status;
+ uint16_t rca;
+
+ if ((fflag & FWRITE) == 0 && mic->write_flag != 0)
+ return (EBADF);
+
+ if (part->ro == TRUE && mic->write_flag != 0)
+ return (EROFS);
+
+ /*
+ * We don't need to explicitly lock against the disk(9) half of this
+ * driver as MMCBUS_ACQUIRE_BUS() will serialize us. However, it's
+ * necessary to protect against races with detachment and suspension,
+ * especially since it's required to switch away from RPMB partitions
+ * again after an access (see mmcsd_switch_part()).
+ */
+ MMCSD_IOCTL_LOCK(part);
+ while (part->ioctl != 0) {
+ if (part->ioctl < 0) {
+ MMCSD_IOCTL_UNLOCK(part);
+ return (ENXIO);
+ }
+ msleep(part, &part->ioctl_mtx, 0, "mmcsd IOCTL", 0);
+ }
+ part->ioctl = 1;
+ MMCSD_IOCTL_UNLOCK(part);
+
+ err = 0;
+ dp = NULL;
+ len = mic->blksz * mic->blocks;
+ if (len > MMC_IOC_MAX_BYTES) {
+ err = EOVERFLOW;
+ goto out;
+ }
+ if (len != 0) {
+ dp = malloc(len, M_TEMP, M_WAITOK);
+ err = copyin((void *)(uintptr_t)mic->data_ptr, dp, len);
+ if (err != 0)
+ goto out;
+ }
+ memset(&cmd, 0, sizeof(cmd));
+ memset(&data, 0, sizeof(data));
+ cmd.opcode = mic->opcode;
+ cmd.arg = mic->arg;
+ cmd.flags = mic->flags;
+ if (len != 0) {
+ data.len = len;
+ data.data = dp;
+ data.flags = mic->write_flag != 0 ? MMC_DATA_WRITE :
+ MMC_DATA_READ;
+ cmd.data = &data;
+ }
+ sc = part->sc;
+ rca = sc->rca;
+ if (mic->is_acmd == 0) {
+ /* Enforce/patch/restrict RCA-based commands */
+ switch (cmd.opcode) {
+ case MMC_SET_RELATIVE_ADDR:
+ case MMC_SELECT_CARD:
+ err = EPERM;
+ goto out;
+ case MMC_STOP_TRANSMISSION:
+ if ((cmd.arg & 0x1) == 0)
+ break;
+ /* FALLTHROUGH */
+ case MMC_SLEEP_AWAKE:
+ case MMC_SEND_CSD:
+ case MMC_SEND_CID:
+ case MMC_SEND_STATUS:
+ case MMC_GO_INACTIVE_STATE:
+ case MMC_FAST_IO:
+ case MMC_APP_CMD:
+ cmd.arg = (cmd.arg & 0x0000FFFF) | (rca << 16);
+ break;
+ default:
+ break;
+ }
+ /*
+ * No partition switching in userland; it's almost impossible
+ * to recover from that, especially if things go wrong.
+ */
+ if (cmd.opcode == MMC_SWITCH_FUNC && dp != NULL &&
+ (((uint8_t *)dp)[EXT_CSD_PART_CONFIG] &
+ EXT_CSD_PART_CONFIG_ACC_MASK) != part->type) {
+ err = EINVAL;
+ goto out;
+ }
+ }
+ dev = sc->dev;
+ mmcbus = sc->mmcbus;
+ MMCBUS_ACQUIRE_BUS(mmcbus, dev);
+ err = mmcsd_switch_part(mmcbus, dev, rca, part->type);
+ if (err != MMC_ERR_NONE)
+ goto release;
+ if (part->type == EXT_CSD_PART_CONFIG_ACC_RPMB) {
+ err = mmcsd_set_blockcount(sc, mic->blocks,
+ mic->write_flag & (1 << 31));
+ if (err != MMC_ERR_NONE)
+ goto switch_back;
+ }
+ if (mic->is_acmd != 0)
+ (void)mmc_wait_for_app_cmd(mmcbus, dev, rca, &cmd, 0);
+ else
+ (void)mmc_wait_for_cmd(mmcbus, dev, &cmd, 0);
+ if (part->type == EXT_CSD_PART_CONFIG_ACC_RPMB) {
+ /*
+ * If the request went to the RPMB partition, try to ensure
+ * that the command actually has completed.
+ */
+ retries = MMCSD_CMD_RETRIES;
+ do {
+ err = mmc_send_status(mmcbus, dev, rca, &status);
+ if (err != MMC_ERR_NONE)
+ break;
+ if (R1_STATUS(status) == 0 &&
+ R1_CURRENT_STATE(status) != R1_STATE_PRG)
+ break;
+ DELAY(1000);
+ } while (retries-- > 0);
+ }
+ /*
+ * If EXT_CSD was changed, our copy is outdated now. Specifically,
+ * the upper bits of EXT_CSD_PART_CONFIG used in mmcsd_switch_part(),
+ * so retrieve EXT_CSD again.
+ */
+ if (cmd.opcode == MMC_SWITCH_FUNC) {
+ err = mmc_send_ext_csd(mmcbus, dev, sc->ext_csd);
+ if (err != MMC_ERR_NONE)
+ goto release;
+ }
+switch_back:
+ if (part->type == EXT_CSD_PART_CONFIG_ACC_RPMB) {
+ /*
+ * If the request went to the RPMB partition, always switch
+ * back to the default partition (see mmcsd_switch_part()).
+ */
+ err = mmcsd_switch_part(mmcbus, dev, rca,
+ EXT_CSD_PART_CONFIG_ACC_DEFAULT);
+ if (err != MMC_ERR_NONE)
+ goto release;
+ }
+ MMCBUS_RELEASE_BUS(mmcbus, dev);
+ if (cmd.error != MMC_ERR_NONE) {
+ switch (cmd.error) {
+ case MMC_ERR_TIMEOUT:
+ err = ETIMEDOUT;
+ break;
+ case MMC_ERR_BADCRC:
+ err = EILSEQ;
+ break;
+ case MMC_ERR_INVALID:
+ err = EINVAL;
+ break;
+ case MMC_ERR_NO_MEMORY:
+ err = ENOMEM;
+ break;
+ default:
+ err = EIO;
+ break;
+ }
+ goto out;
+ }
+ memcpy(mic->response, cmd.resp, 4 * sizeof(uint32_t));
+ if (mic->write_flag == 0 && len != 0) {
+ err = copyout(dp, (void *)(uintptr_t)mic->data_ptr, len);
+ if (err != 0)
+ goto out;
+ }
+ goto out;
+
+release:
+ MMCBUS_RELEASE_BUS(mmcbus, dev);
+ err = EIO;
+
+out:
+ MMCSD_IOCTL_LOCK(part);
+ part->ioctl = 0;
+ MMCSD_IOCTL_UNLOCK(part);
+ wakeup(part);
+ if (dp != NULL)
+ free(dp, M_TEMP);
+ return (err);
+}
+
+static int
+mmcsd_getattr(struct bio *bp)
+{
+ struct mmcsd_part *part;
+ device_t dev;
+
+ if (strcmp(bp->bio_attribute, "MMC::device") == 0) {
+ if (bp->bio_length != sizeof(dev))
+ return (EFAULT);
+ part = bp->bio_disk->d_drv1;
+ dev = part->sc->dev;
+ bcopy(&dev, bp->bio_data, sizeof(dev));
+ bp->bio_completed = bp->bio_length;
+ return (0);
+ }
+ return (-1);
+}
+
+static int
+mmcsd_set_blockcount(struct mmcsd_softc *sc, u_int count, bool reliable)
+{
+ struct mmc_command cmd;
+ struct mmc_request req;
+
+ memset(&req, 0, sizeof(req));
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.mrq = &req;
+ req.cmd = &cmd;
+ cmd.opcode = MMC_SET_BLOCK_COUNT;
+ cmd.arg = count & 0x0000FFFF;
+ if (reliable)
+ cmd.arg |= 1 << 31;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ MMCBUS_WAIT_FOR_REQUEST(sc->mmcbus, sc->dev, &req);
+ return (cmd.error);
+}
+
+static int
+mmcsd_switch_part(device_t bus, device_t dev, uint16_t rca, u_int part)
+{
+ struct mmcsd_softc *sc;
+ int err;
+ uint8_t value;
+
+ sc = device_get_softc(dev);
+
+ if (sc->mode == mode_sd)
+ return (MMC_ERR_NONE);
+
+ /*
+ * According to section "6.2.2 Command restrictions" of the eMMC
+ * specification v5.1, CMD19/CMD21 aren't allowed to be used with
+ * RPMB partitions. So we pause re-tuning along with triggering
+ * it up-front to decrease the likelihood of re-tuning becoming
+ * necessary while accessing an RPMB partition. Consequently, an
+ * RPMB partition should immediately be switched away from again
+ * after an access in order to allow for re-tuning to take place
+ * anew.
+ */
+ if (part == EXT_CSD_PART_CONFIG_ACC_RPMB)
+ MMCBUS_RETUNE_PAUSE(sc->mmcbus, sc->dev, true);
+
+ if (sc->part_curr == part)
+ return (MMC_ERR_NONE);
+
+ value = (sc->ext_csd[EXT_CSD_PART_CONFIG] &
+ ~EXT_CSD_PART_CONFIG_ACC_MASK) | part;
+ /* Jump! */
+ err = mmc_switch(bus, dev, rca, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_PART_CONFIG, value, sc->part_time, true);
+ if (err != MMC_ERR_NONE) {
+ if (part == EXT_CSD_PART_CONFIG_ACC_RPMB)
+ MMCBUS_RETUNE_UNPAUSE(sc->mmcbus, sc->dev);
+ return (err);
+ }
+
+ sc->ext_csd[EXT_CSD_PART_CONFIG] = value;
+ if (sc->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB)
+ MMCBUS_RETUNE_UNPAUSE(sc->mmcbus, sc->dev);
+ sc->part_curr = part;
+ return (MMC_ERR_NONE);
+}
+
+static const char *
+mmcsd_errmsg(int e)
+{
+
+ if (e < 0 || e > MMC_ERR_MAX)
+ return "Bad error code";
+ return (errmsg[e]);
+}
+
static daddr_t
-mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp)
+mmcsd_rw(struct mmcsd_part *part, struct bio *bp)
{
daddr_t block, end;
struct mmc_command cmd;
@@ -294,18 +1122,26 @@
struct mmc_command stop;
struct mmc_request req;
struct mmc_data data;
- device_t dev = sc->dev;
- int sz = sc->disk->d_sectorsize;
+ struct mmcsd_softc *sc;
+ device_t dev, mmcbus;
+ u_int numblocks, sz;
+ char *vaddr;
+ sc = part->sc;
+ dev = sc->dev;
+ mmcbus = sc->mmcbus;
+
block = bp->bio_pblkno;
+ sz = part->disk->d_sectorsize;
end = bp->bio_pblkno + (bp->bio_bcount / sz);
while (block < end) {
- char *vaddr = bp->bio_data +
- (block - bp->bio_pblkno) * sz;
- int numblocks = min(end - block, mmc_get_max_data(dev));
+ vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz;
+ numblocks = min(end - block, sc->max_data);
memset(&req, 0, sizeof(req));
- memset(&cmd, 0, sizeof(cmd));
+ memset(&cmd, 0, sizeof(cmd));
memset(&stop, 0, sizeof(stop));
+ memset(&data, 0, sizeof(data));
+ cmd.mrq = &req;
req.cmd = &cmd;
cmd.data = &data;
if (bp->bio_cmd == BIO_READ) {
@@ -320,7 +1156,7 @@
cmd.opcode = MMC_WRITE_BLOCK;
}
cmd.arg = block;
- if (!mmc_get_high_cap(dev))
+ if (sc->high_cap == 0)
cmd.arg <<= 9;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
data.data = vaddr;
@@ -335,14 +1171,18 @@
stop.opcode = MMC_STOP_TRANSMISSION;
stop.arg = 0;
stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ stop.mrq = &req;
req.stop = &stop;
}
-// printf("Len %d %lld-%lld flags %#x sz %d\n",
-// (int)data.len, (long long)block, (long long)end, data.flags, sz);
- MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
- &req);
- if (req.cmd->error != MMC_ERR_NONE)
+ MMCBUS_WAIT_FOR_REQUEST(mmcbus, dev, &req);
+ if (req.cmd->error != MMC_ERR_NONE) {
+ if (ppsratecheck(&sc->log_time, &sc->log_count,
+ LOG_PPS))
+ device_printf(dev, "Error indicated: %d %s\n",
+ req.cmd->error,
+ mmcsd_errmsg(req.cmd->error));
break;
+ }
block += numblocks;
}
return (block);
@@ -349,38 +1189,74 @@
}
static daddr_t
-mmcsd_delete(struct mmcsd_softc *sc, struct bio *bp)
+mmcsd_delete(struct mmcsd_part *part, struct bio *bp)
{
daddr_t block, end, start, stop;
struct mmc_command cmd;
struct mmc_request req;
- device_t dev = sc->dev;
- int sz = sc->disk->d_sectorsize;
- int erase_sector;
+ struct mmcsd_softc *sc;
+ device_t dev, mmcbus;
+ u_int erase_sector, sz;
+ int err;
+ bool use_trim;
+ sc = part->sc;
+ dev = sc->dev;
+ mmcbus = sc->mmcbus;
+
block = bp->bio_pblkno;
+ sz = part->disk->d_sectorsize;
end = bp->bio_pblkno + (bp->bio_bcount / sz);
- /* Coalesce with part remaining from previous request. */
- if (block > sc->eblock && block <= sc->eend)
- block = sc->eblock;
- if (end >= sc->eblock && end < sc->eend)
- end = sc->eend;
- /* Safe round to the erase sector boundaries. */
- erase_sector = mmc_get_erase_sector(dev);
- start = block + erase_sector - 1; /* Round up. */
- start -= start % erase_sector;
- stop = end; /* Round down. */
- stop -= end % erase_sector;
- /* We can't erase area smaller then sector, store it for later. */
- if (start >= stop) {
- sc->eblock = block;
- sc->eend = end;
- return (end);
+ use_trim = sc->flags & MMCSD_USE_TRIM;
+ if (use_trim == true) {
+ start = block;
+ stop = end;
+ } else {
+ /* Coalesce with the remainder of the previous request. */
+ if (block > part->eblock && block <= part->eend)
+ block = part->eblock;
+ if (end >= part->eblock && end < part->eend)
+ end = part->eend;
+ /* Safely round to the erase sector boundaries. */
+ erase_sector = sc->erase_sector;
+ start = block + erase_sector - 1; /* Round up. */
+ start -= start % erase_sector;
+ stop = end; /* Round down. */
+ stop -= end % erase_sector;
+ /*
+ * We can't erase an area smaller than an erase sector, so
+ * store it for later.
+ */
+ if (start >= stop) {
+ part->eblock = block;
+ part->eend = end;
+ return (end);
+ }
}
+ if ((sc->flags & MMCSD_INAND_CMD38) != 0) {
+ err = mmc_switch(mmcbus, dev, sc->rca, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_INAND_CMD38, use_trim == true ?
+ EXT_CSD_INAND_CMD38_TRIM : EXT_CSD_INAND_CMD38_ERASE,
+ sc->cmd6_time, true);
+ if (err != MMC_ERR_NONE) {
+ device_printf(dev,
+ "Setting iNAND erase command failed %s\n",
+ mmcsd_errmsg(err));
+ return (block);
+ }
+ }
+
+ /*
+ * Pause re-tuning so it won't interfere with the order of erase
+ * commands. Note that these latter don't use the data lines, so
+ * re-tuning shouldn't actually become necessary during erase.
+ */
+ MMCBUS_RETUNE_PAUSE(mmcbus, dev, false);
/* Set erase start position. */
memset(&req, 0, sizeof(req));
memset(&cmd, 0, sizeof(cmd));
+ cmd.mrq = &req;
req.cmd = &cmd;
if (mmc_get_card_type(dev) == mode_sd)
cmd.opcode = SD_ERASE_WR_BLK_START;
@@ -387,14 +1263,15 @@
else
cmd.opcode = MMC_ERASE_GROUP_START;
cmd.arg = start;
- if (!mmc_get_high_cap(dev))
+ if (sc->high_cap == 0)
cmd.arg <<= 9;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
- MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
- &req);
+ MMCBUS_WAIT_FOR_REQUEST(mmcbus, dev, &req);
if (req.cmd->error != MMC_ERR_NONE) {
- printf("erase err1: %d\n", req.cmd->error);
- return (block);
+ device_printf(dev, "Setting erase start position failed %s\n",
+ mmcsd_errmsg(req.cmd->error));
+ block = bp->bio_pblkno;
+ goto unpause;
}
/* Set erase stop position. */
memset(&req, 0, sizeof(req));
@@ -405,15 +1282,16 @@
else
cmd.opcode = MMC_ERASE_GROUP_END;
cmd.arg = stop;
- if (!mmc_get_high_cap(dev))
+ if (sc->high_cap == 0)
cmd.arg <<= 9;
cmd.arg--;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
- MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
- &req);
+ MMCBUS_WAIT_FOR_REQUEST(mmcbus, dev, &req);
if (req.cmd->error != MMC_ERR_NONE) {
- printf("erase err2: %d\n", req.cmd->error);
- return (block);
+ device_printf(dev, "Setting erase stop position failed %s\n",
+ mmcsd_errmsg(req.cmd->error));
+ block = bp->bio_pblkno;
+ goto unpause;
}
/* Erase range. */
memset(&req, 0, sizeof(req));
@@ -420,39 +1298,53 @@
memset(&cmd, 0, sizeof(cmd));
req.cmd = &cmd;
cmd.opcode = MMC_ERASE;
- cmd.arg = 0;
+ cmd.arg = use_trim == true ? MMC_ERASE_TRIM : MMC_ERASE_ERASE;
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
- MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
- &req);
+ MMCBUS_WAIT_FOR_REQUEST(mmcbus, dev, &req);
if (req.cmd->error != MMC_ERR_NONE) {
- printf("erase err3 %d\n", req.cmd->error);
- return (block);
+ device_printf(dev, "Issuing erase command failed %s\n",
+ mmcsd_errmsg(req.cmd->error));
+ block = bp->bio_pblkno;
+ goto unpause;
}
- /* Store one of remaining parts for the next call. */
- if (bp->bio_pblkno >= sc->eblock || block == start) {
- sc->eblock = stop; /* Predict next forward. */
- sc->eend = end;
- } else {
- sc->eblock = block; /* Predict next backward. */
- sc->eend = start;
+ if (use_trim == false) {
+ /* Store one of the remaining parts for the next call. */
+ if (bp->bio_pblkno >= part->eblock || block == start) {
+ part->eblock = stop; /* Predict next forward. */
+ part->eend = end;
+ } else {
+ part->eblock = block; /* Predict next backward. */
+ part->eend = start;
+ }
}
- return (end);
+ block = end;
+unpause:
+ MMCBUS_RETUNE_UNPAUSE(mmcbus, dev);
+ return (block);
}
static int
-mmcsd_dump(void *arg, void *virtual, vm_offset_t physical,
- off_t offset, size_t length)
+mmcsd_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset,
+ size_t length)
{
- struct disk *disk = arg;
- struct mmcsd_softc *sc = (struct mmcsd_softc *)disk->d_drv1;
- device_t dev = sc->dev;
struct bio bp;
daddr_t block, end;
+ struct disk *disk;
+ struct mmcsd_softc *sc;
+ struct mmcsd_part *part;
+ device_t dev, mmcbus;
+ int err;
/* length zero is special and really means flush buffers to media */
if (!length)
return (0);
+ disk = arg;
+ part = disk->d_drv1;
+ sc = part->sc;
+ dev = sc->dev;
+ mmcbus = sc->mmcbus;
+
bzero(&bp, sizeof(struct bio));
bp.bio_disk = disk;
bp.bio_pblkno = offset / disk->d_sectorsize;
@@ -459,10 +1351,17 @@
bp.bio_bcount = length;
bp.bio_data = virtual;
bp.bio_cmd = BIO_WRITE;
- end = bp.bio_pblkno + bp.bio_bcount / sc->disk->d_sectorsize;
- MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
- block = mmcsd_rw(sc, &bp);
- MMCBUS_RELEASE_BUS(device_get_parent(dev), dev);
+ end = bp.bio_pblkno + bp.bio_bcount / disk->d_sectorsize;
+ MMCBUS_ACQUIRE_BUS(mmcbus, dev);
+ err = mmcsd_switch_part(mmcbus, dev, sc->rca, part->type);
+ if (err != MMC_ERR_NONE) {
+ if (ppsratecheck(&sc->log_time, &sc->log_count, LOG_PPS))
+ device_printf(dev, "Partition switch error\n");
+ MMCBUS_RELEASE_BUS(mmcbus, dev);
+ return (EIO);
+ }
+ block = mmcsd_rw(part, &bp);
+ MMCBUS_RELEASE_BUS(mmcbus, dev);
return ((end < block) ? EIO : 0);
}
@@ -469,24 +1368,30 @@
static void
mmcsd_task(void *arg)
{
- struct mmcsd_softc *sc = (struct mmcsd_softc*)arg;
+ daddr_t block, end;
+ struct mmcsd_part *part;
+ struct mmcsd_softc *sc;
struct bio *bp;
- int sz;
- daddr_t block, end;
- device_t dev;
+ device_t dev, mmcbus;
+ int err, sz;
+ part = arg;
+ sc = part->sc;
dev = sc->dev;
+ mmcbus = sc->mmcbus;
+
while (1) {
- MMCSD_LOCK(sc);
+ MMCSD_DISK_LOCK(part);
do {
- if (sc->running == 0)
+ if (part->running == 0)
goto out;
- bp = bioq_takefirst(&sc->bio_queue);
+ bp = bioq_takefirst(&part->bio_queue);
if (bp == NULL)
- msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0);
+ msleep(part, &part->disk_mtx, PRIBIO,
+ "mmcsd disk jobqueue", 0);
} while (bp == NULL);
- MMCSD_UNLOCK(sc);
- if (bp->bio_cmd != BIO_READ && mmc_get_read_only(dev)) {
+ MMCSD_DISK_UNLOCK(part);
+ if (bp->bio_cmd != BIO_READ && part->ro) {
bp->bio_error = EROFS;
bp->bio_resid = bp->bio_bcount;
bp->bio_flags |= BIO_ERROR;
@@ -493,31 +1398,41 @@
biodone(bp);
continue;
}
- MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
- sz = sc->disk->d_sectorsize;
+ MMCBUS_ACQUIRE_BUS(mmcbus, dev);
+ sz = part->disk->d_sectorsize;
block = bp->bio_pblkno;
end = bp->bio_pblkno + (bp->bio_bcount / sz);
+ err = mmcsd_switch_part(mmcbus, dev, sc->rca, part->type);
+ if (err != MMC_ERR_NONE) {
+ if (ppsratecheck(&sc->log_time, &sc->log_count,
+ LOG_PPS))
+ device_printf(dev, "Partition switch error\n");
+ goto release;
+ }
if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
/* Access to the remaining erase block obsoletes it. */
- if (block < sc->eend && end > sc->eblock)
- sc->eblock = sc->eend = 0;
- block = mmcsd_rw(sc, bp);
+ if (block < part->eend && end > part->eblock)
+ part->eblock = part->eend = 0;
+ block = mmcsd_rw(part, bp);
} else if (bp->bio_cmd == BIO_DELETE) {
- block = mmcsd_delete(sc, bp);
+ block = mmcsd_delete(part, bp);
}
- MMCBUS_RELEASE_BUS(device_get_parent(dev), dev);
+release:
+ MMCBUS_RELEASE_BUS(mmcbus, dev);
if (block < end) {
bp->bio_error = EIO;
bp->bio_resid = (end - block) * sz;
bp->bio_flags |= BIO_ERROR;
+ } else {
+ bp->bio_resid = 0;
}
biodone(bp);
}
out:
/* tell parent we're done */
- sc->running = -1;
- MMCSD_UNLOCK(sc);
- wakeup(sc);
+ part->running = -1;
+ MMCSD_DISK_UNLOCK(part);
+ wakeup(part);
kproc_exit(0);
}
@@ -549,4 +1464,22 @@
};
static devclass_t mmcsd_devclass;
-DRIVER_MODULE(mmcsd, mmc, mmcsd_driver, mmcsd_devclass, NULL, NULL);
+static int
+mmcsd_handler(module_t mod __unused, int what, void *arg __unused)
+{
+
+ switch (what) {
+ case MOD_LOAD:
+ flash_register_slicer(mmcsd_slicer, FLASH_SLICES_TYPE_MMC,
+ TRUE);
+ return (0);
+ case MOD_UNLOAD:
+ flash_register_slicer(NULL, FLASH_SLICES_TYPE_MMC, TRUE);
+ return (0);
+ }
+ return (0);
+}
+
+DRIVER_MODULE(mmcsd, mmc, mmcsd_driver, mmcsd_devclass, mmcsd_handler, NULL);
+MODULE_DEPEND(mmcsd, g_flashmap, 0, 0, 0);
+MMC_DEPEND(mmcsd);
Modified: trunk/sys/dev/mmc/mmcvar.h
===================================================================
--- trunk/sys/dev/mmc/mmcvar.h 2018-05-27 23:36:22 UTC (rev 10095)
+++ trunk/sys/dev/mmc/mmcvar.h 2018-05-27 23:36:35 UTC (rev 10096)
@@ -1,3 +1,4 @@
+/* $MidnightBSD$ */
/*-
* Copyright (c) 2006 Bernd Walter. All rights reserved.
* Copyright (c) 2006 M. Warner Losh. All rights reserved.
@@ -49,15 +50,14 @@
* or the SD Card Association to disclose or distribute any technical
* information, know-how or other confidential information to any third party.
*
- * "$MidnightBSD$"
+ * $FreeBSD: stable/10/sys/dev/mmc/mmcvar.h 322389 2017-08-11 00:41:43Z marius $
*/
#ifndef DEV_MMC_MMCVAR_H
#define DEV_MMC_MMCVAR_H
-#include <dev/mmc/bridge.h>
-
enum mmc_device_ivars {
+ MMC_IVAR_SPEC_VERS,
MMC_IVAR_DSR_IMP,
MMC_IVAR_MEDIA_SIZE,
MMC_IVAR_RCA,
@@ -69,15 +69,19 @@
MMC_IVAR_BUS_WIDTH,
MMC_IVAR_ERASE_SECTOR,
MMC_IVAR_MAX_DATA,
- MMC_IVAR_CARD_ID_STRING
+ MMC_IVAR_CMD6_TIMEOUT,
+ MMC_IVAR_QUIRKS,
+ MMC_IVAR_CARD_ID_STRING,
+ MMC_IVAR_CARD_SN_STRING,
};
/*
- * Simplified accessors for pci devices
+ * Simplified accessors for mmc devices
*/
#define MMC_ACCESSOR(var, ivar, type) \
__BUS_ACCESSOR(mmc, var, MMC, ivar, type)
+MMC_ACCESSOR(spec_vers, SPEC_VERS, uint8_t)
MMC_ACCESSOR(dsr_imp, DSR_IMP, int)
MMC_ACCESSOR(media_size, MEDIA_SIZE, long)
MMC_ACCESSOR(rca, RCA, int)
@@ -89,6 +93,9 @@
MMC_ACCESSOR(bus_width, BUS_WIDTH, int)
MMC_ACCESSOR(erase_sector, ERASE_SECTOR, int)
MMC_ACCESSOR(max_data, MAX_DATA, int)
+MMC_ACCESSOR(cmd6_timeout, CMD6_TIMEOUT, u_int)
+MMC_ACCESSOR(quirks, QUIRKS, u_int)
MMC_ACCESSOR(card_id_string, CARD_ID_STRING, const char *)
+MMC_ACCESSOR(card_sn_string, CARD_SN_STRING, const char *)
#endif /* DEV_MMC_MMCVAR_H */
More information about the Midnightbsd-cvs
mailing list