* [PATCH 1/2] mmc: cavium: Fix voltage reg. switching for card slots
2021-11-17 22:27 [PATCH 0/2] mmc: cavium: Fix MMC supply switching for cards Wojciech Bartczak
@ 2021-11-17 22:28 ` Wojciech Bartczak
2021-11-19 3:50 ` kernel test robot
2021-11-17 22:28 ` [PATCH 2/2] dt-bindings: mmc: Add vmmc/vqmmc for Cavium driver Wojciech Bartczak
1 sibling, 1 reply; 5+ messages in thread
From: Wojciech Bartczak @ 2021-11-17 22:28 UTC (permalink / raw)
To: linux-mmc
Cc: ulf.hansson, beanhuo, tanxiaofei, robh+dt, devicetree,
linux-kernel, wbartczak
Following commit enables usage of vqmmc and vmmc regulators
for devices with multiple voltages.
The MMC block is capable to control up to three devices at the same
time. However, only vmmc regulator has been used in the code.
The change is necessary to support SD and MMC devices
in single system. SD cards uses usually 3.3V regulators,
while faster MMC devices uses lower volatges (1.8V or 1.2V).
The switch operation logic remains unchanged. The sequence
has been rewritten to separate logic, register accesses and
voltage reguators manipulation.
Signed-off-by: Wojciech Bartczak <wbartczak@marvell.com>
---
drivers/mmc/host/cavium.c | 168 ++++++++++++++++++++++++++++++++++----
drivers/mmc/host/cavium.h | 8 +-
2 files changed, 157 insertions(+), 19 deletions(-)
diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c
index 95a41983c6c0..35d0ea3e3dd4 100644
--- a/drivers/mmc/host/cavium.c
+++ b/drivers/mmc/host/cavium.c
@@ -193,36 +193,166 @@ static int get_bus_id(u64 reg)
return FIELD_GET(GENMASK_ULL(61, 60), reg);
}
-/*
- * We never set the switch_exe bit since that would interfere
- * with the commands send by the MMC core.
+/**
+ * pre_switch - Switch voltage regulators
+ * @prev: Current slot, can be NULL
+ * @next: Next slot
+ *
+ * Switch between regulators used by slots. This call has to be done
+ * after call to pre_switch().
*/
-static void do_switch(struct cvm_mmc_host *host, u64 emm_switch)
+static void switch_vqmmc(struct cvm_mmc_slot *prev, struct cvm_mmc_slot *next)
{
- int retries = 100;
- u64 rsp_sts;
- int bus_id;
+ bool same_vqmmc = false;
+ struct regulator *next_vqmmc;
+ struct regulator *prev_vqmmc;
+
+ next_vqmmc = next->mmc->supply.vqmmc;
+ if (prev) {
+ prev_vqmmc = prev->mmc->supply.vqmmc;
+ /* Prev slot and next slot share vqmmc? */
+ same_vqmmc = prev_vqmmc == next_vqmmc;
+ /* Disable old regulator, if not the same */
+ if (!same_vqmmc && !IS_ERR_OR_NULL(prev_vqmmc))
+ regulator_disable(prev_vqmmc);
+ }
- /*
- * Modes setting only taken from slot 0. Work around that hardware
- * issue by first switching to slot 0.
- */
- bus_id = get_bus_id(emm_switch);
- clear_bus_id(&emm_switch);
- writeq(emm_switch, host->base + MIO_EMM_SWITCH(host));
+ /* Enable regulator for next slot */
+ if (!same_vqmmc && !IS_ERR_OR_NULL(next_vqmmc)) {
+ int ret;
- set_bus_id(&emm_switch, bus_id);
- writeq(emm_switch, host->base + MIO_EMM_SWITCH(host));
+ ret = regulator_enable(next_vqmmc);
+ if (ret)
+ dev_err(mmc_classdev(next->mmc),
+ "Can't enable vqmmc, error (%d)!\n", ret);
+ }
+}
+
+/**
+ * pre_switch - Start switch operation between slots
+ * @prev: Current slot, can be NULL
+ * @next: Next slot
+ *
+ * MMC block for octeontx2 shares data lines among the three card slots.
+ * To avoid transient states on DAT[0..7] and CLK lines set
+ * CMDn to tri-state when VQMMC is switched.
+ */
+static void pre_switch(struct cvm_mmc_slot *prev, struct cvm_mmc_slot *next)
+{
+ struct cvm_mmc_host *host; /* Common block for all slots */
+
+ host = next->host;
+ if (host->use_vqmmc)
+ /* Disable clock and data lines on all slots */
+ writeq(1ull << 3, host->base + MIO_EMM_CFG(host));
+
+ if (prev) {
+ /* Store current slot settings */
+ prev->cached_switch = readq(host->base + MIO_EMM_SWITCH(host));
+ prev->cached_rca = readq(host->base + MIO_EMM_RCA(host));
+ }
+}
+
+/**
+ * post_switch - Finish switch operation
+ * @prev: Current slot
+ * @next: Next slot
+ *
+ * Function finishes switch operation by enabling selcted slot
+ * and recovering its settigs.
+ */
+static void post_switch(struct cvm_mmc_slot *prev, struct cvm_mmc_slot *next)
+{
+ struct cvm_mmc_host *host; /* Common block for all slots */
+
+ host = next->host;
+ if (host->use_vqmmc)
+ /* Enable clock and data lines for current slot */
+ writeq(1ull << next->bus_id, host->base + MIO_EMM_CFG(host));
+
+ /* Read back values set during switch */
+ next->cached_switch = readq(host->base + MIO_EMM_SWITCH(host));
+ /* Set RCA value */
+ writeq(next->cached_rca, host->base + MIO_EMM_RCA(host));
+}
- /* wait for the switch to finish */
+/**
+ * mode_switch - Performs low level switch operation
+ * @host: Common hardware block for all slots
+ * @emm_switch: register value to use for the operation
+ *
+ * Function finishes switch operation by enabling selcted slot
+ * and recovering its settigs.
+ */
+static inline void mode_switch(struct cvm_mmc_host *host, u64 emm_switch)
+{
+ unsigned int retries = MODE_SWITCH_RETRIES;
+ u64 rsp_sts;
+
+ writeq(emm_switch, host->base + MIO_EMM_SWITCH(host));
+ /* Wait for switch completion */
do {
rsp_sts = readq(host->base + MIO_EMM_RSP_STS(host));
if (!(rsp_sts & MIO_EMM_RSP_STS_SWITCH_VAL))
break;
udelay(10);
} while (--retries);
+}
+
+/**
+ * do_switch - Performs switch operation
+ * @host: Common hardware block for all slots
+ * @emm_switch: register value to use for the operation
+ *
+ * Function performs switch operation between slost on hardware level.
+ * It is also called when the same slot changes mode, class or bus width.
+ * Switch requires sometimes to change current slot, store some of the slot
+ * information. Additionally, the volatge regulator for a card has to be
+ * switched too. Function uses use_vqmmc quirk for hardware.
+ * The quirk is related to errata that requires to go over slot 0
+ * for all switch operations, otherwise no parameters are accepted.
+ */
+static void do_switch(struct cvm_mmc_host *host, u64 emm_switch)
+{
+ struct cvm_mmc_slot *next, *prev;
+ int bus_id;
+ bool slot_changed;
+
+ /* Initially previous slot is not set */
+ prev = NULL;
+ /* Read back bus id and get slot. Check does bus_id != last_slot */
+ bus_id = FIELD_GET(MIO_EMM_SWITCH_BUS_ID, emm_switch);
+ next = host->slot[bus_id];
+ slot_changed = host->last_slot != bus_id;
+ if (host->last_slot != -1)
+ prev = host->slot[host->last_slot];
+
+ /* Prepare to slot switch, switch voltage regulators */
+ if (slot_changed) {
+ pre_switch(prev, next);
+ switch_vqmmc(prev, next);
+ }
+ /*
+ * Modes setting only taken from slot 0. Work around that hardware
+ * issue by first switching to slot 0.
+ */
+ if (bus_id) {
+ u64 emm_switch0;
+ /* Clear bus_id and switch through bus_id 0 */
+ emm_switch0 = emm_switch & (~MIO_EMM_SWITCH_BUS_ID);
+ mode_switch(host, emm_switch0);
+ }
+
+ /* Switch with target bus_id */
+ mode_switch(host, emm_switch);
check_switch_errors(host);
+
+ if (slot_changed)
+ post_switch(prev, next);
+
+ /* Set current slot id */
+ host->last_slot = bus_id;
}
static bool switch_val_changed(struct cvm_mmc_slot *slot, u64 new_val)
@@ -972,7 +1102,7 @@ static int cvm_mmc_of_parse(struct device *dev, struct cvm_mmc_slot *slot)
* Legacy Octeon firmware has no regulator entry, fall-back to
* a hard-coded voltage to get a sane OCR.
*/
- if (IS_ERR(mmc->supply.vmmc))
+ if (IS_ERR_OR_NULL(mmc->supply.vmmc))
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
/* Common MMC bindings */
@@ -1058,6 +1188,8 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host)
host->acquire_bus(host);
host->slot[id] = slot;
+ host->use_vqmmc = host->use_vqmmc ||
+ !IS_ERR_OR_NULL(slot->mmc->supply.vqmmc);
cvm_mmc_switch_to(slot);
cvm_mmc_init_lowlevel(slot);
host->release_bus(host);
diff --git a/drivers/mmc/host/cavium.h b/drivers/mmc/host/cavium.h
index f3eea5eaa678..a581e0462c7e 100644
--- a/drivers/mmc/host/cavium.h
+++ b/drivers/mmc/host/cavium.h
@@ -56,6 +56,7 @@ struct cvm_mmc_host {
struct device *dev;
void __iomem *base;
void __iomem *dma_base;
+
int reg_off;
int reg_off_dma;
u64 emm_cfg;
@@ -70,6 +71,7 @@ struct cvm_mmc_host {
bool use_sg;
bool has_ciu3;
+ bool use_vqmmc; /* Force disable slot over switch */
bool big_dma_addr;
bool need_irq_handler_lock;
spinlock_t irq_handler_lock;
@@ -206,10 +208,14 @@ struct cvm_mmc_cr_mods {
#define MIO_EMM_SWITCH_CLK_HI GENMASK_ULL(31, 16)
#define MIO_EMM_SWITCH_CLK_LO GENMASK_ULL(15, 0)
+/* Additional defines */
+#define MODE_SWITCH_RETRIES 100
+
/* Protoypes */
irqreturn_t cvm_mmc_interrupt(int irq, void *dev_id);
int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host);
int cvm_mmc_of_slot_remove(struct cvm_mmc_slot *slot);
extern const char *cvm_mmc_irq_names[];
-#endif
+#endif /* _CAVIUM_MMC_H_ */
+
--
2.17.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 2/2] dt-bindings: mmc: Add vmmc/vqmmc for Cavium driver
2021-11-17 22:27 [PATCH 0/2] mmc: cavium: Fix MMC supply switching for cards Wojciech Bartczak
2021-11-17 22:28 ` [PATCH 1/2] mmc: cavium: Fix voltage reg. switching for card slots Wojciech Bartczak
@ 2021-11-17 22:28 ` Wojciech Bartczak
2021-11-23 11:51 ` Ulf Hansson
1 sibling, 1 reply; 5+ messages in thread
From: Wojciech Bartczak @ 2021-11-17 22:28 UTC (permalink / raw)
To: linux-mmc
Cc: ulf.hansson, beanhuo, tanxiaofei, robh+dt, devicetree,
linux-kernel, wbartczak
Octeon/OcteonTX MMC supports up to 3 card slots.
Each slot can support SD or MMC cards with various speed.
However, any level-shifting to accommodate different signal voltages
for the cards is done by external hardware, under control of an optional
vqmmc regulator object, typically controlled by gpio.
The details of device-tree control of MMC signals via GPIO at reset
is available in this file.
If any mmc-slots have a vqmmc-supply property,
take it as a warning that we must switch carefully between
slots (unless they have the same vqmmc object), tri-stating
MMC signals to avoid any transient states as level-shifters
are enabled/disabled, by zeroing MIO_EMM_CFG[bus_id].
There's no need to list vqmmc property if all the mmc-slots
on a board run at same signal voltage, and have same width.
In this case the old behavior, enabling all probed slots in
MIO_EMM_CFG, allows faster slot-switching.
Signed-off-by: Wojciech Bartczak <wbartczak@marvell.com>
---
.../devicetree/bindings/mmc/cavium-mmc.txt | 47 ++++++++++++++++++-
1 file changed, 45 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/mmc/cavium-mmc.txt b/Documentation/devicetree/bindings/mmc/cavium-mmc.txt
index 1433e6201dff..d0b750e23332 100644
--- a/Documentation/devicetree/bindings/mmc/cavium-mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/cavium-mmc.txt
@@ -28,6 +28,46 @@ Deprecated properties:
- power-gpios : use vmmc-supply instead
- cavium,octeon-6130-mmc-slot : use mmc-slot instead
+GPIO control via vmmc-supply & vqmmc-supply:
+ Two types of regulator object can be specified as mmc properties,
+ typically regulator-fixed controlled by GPIO pins.
+
+ Octeon/OcteonTX chips commonly use GPIO8 as an MMC-reset pin.
+ In systems which may boot from MMC, it starts as input, and is gently
+ pulled up/down by board logic to indicate the active sense of the
+ signal. Chip reset then drives the signal in the opposite direction
+ to effect a reset of target devices.
+ Device tree should model this with a vmmc-supply regulator, gated by
+ GPIO8, so GPIO8 is driven in the non-reset direction when MMC devices
+ are probed, and held there until rmmod/shutdown/suspend.
+ This allows a warm reboot to reset the MMC devices.
+
+ Octeon/OcteonTX MMC supports up to 3 mmc slots, but any
+ level-shifting to accommodate different signal voltages is
+ done by external hardware, under control of an optional
+ vqmmc regulator object, typically controlled by GPIO.
+
+ If any mmc-slots have a vqmmc-supply property, it is taken as a warning
+ that we must switch carefully between slots (unless they have the same
+ vqmmc object), tri-stating MMC signals to avoid any transient states
+ as level-shifters are enabled/disabled.
+
+ Even when so-called bi-directional level shifters are used,
+ this technique should be employed when using different bus-widths
+ on different slots, disabling level shifters to avoid presenting
+ non-uniform impedance across DATA0-7 & CMD when non-selected
+ 4-wide slots are left enabled, while accessing 8-wide targets.
+
+ Note that it's not possible to specify multiple regulators
+ controlled by same GPIO pin, but with different active state.
+ If one GPIO line is require to switch voltage/routing between
+ different mmc-slots, specify a vqmmc-supply on one slot, but
+ not the other. The regulator_disable call on leaving that slot
+ will implicitly switch the state to support the unmarked slot.
+
+ There's no need to list vqmmc-supply if all the mmc-slots on
+ a board run at same voltage, and have same width.
+
Examples:
mmc_1_4: mmc@1,4 {
compatible = "cavium,thunder-8390-mmc";
@@ -40,7 +80,8 @@ Examples:
compatible = "mmc-slot";
reg = <0>;
vmmc-supply = <&mmc_supply_3v3>;
- max-frequency = <42000000>;
+ vqmmc-supply = <&vqmmc_3v3>;
+ max-frequency = <52000000>;
bus-width = <4>;
cap-sd-highspeed;
};
@@ -49,9 +90,11 @@ Examples:
compatible = "mmc-slot";
reg = <1>;
vmmc-supply = <&mmc_supply_3v3>;
- max-frequency = <42000000>;
+ vqmmc-supply = <&vqmmc_1v8>;
+ max-frequency = <100000000>;
bus-width = <8>;
cap-mmc-highspeed;
non-removable;
};
};
+
--
2.17.1
^ permalink raw reply related [flat|nested] 5+ messages in thread