All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 0/4] LoongArch: Introduce the Loongson-2K MMC host controller driver
@ 2025-04-10  8:40 Binbin Zhou
  2025-04-10  8:40 ` [PATCH v1 1/4] dt-bindings: mmc: Add Loongson-2K SD/SDIO/eMMC controller binding Binbin Zhou
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Binbin Zhou @ 2025-04-10  8:40 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Ulf Hansson
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-mmc,
	Binbin Zhou

Hi all:

This patchset introduce the MMC host controller on Loongson-2K series
CPUs.

They are similar, except for the interface characteristics and the use of
DMA engine, specifically, the Loongson-2K0500/Loongson-2K1000 use an
externally shared APBDMA engine, while the Loongson-2K2000 uses an
internally exclusive DMA.

Based on this, I'm splitting the driver into two patches.

List of the patchset:
Patch1: bindings for Loongson-2K0500/Loongson-2K1000;
Patch2: driver for MMC controller using externally shared APBDMA engine;
Patch3: bindings for Loongson-2K2000;
Patch4: driver for MMC controller using internally exclusive DMA.

Thanks.

Binbin Zhou (4):
  dt-bindings: mmc: Add Loongson-2K SD/SDIO/eMMC controller binding
  mmc: loongson2: Add Loongson-2K SD/SDIO controller driver
  dt-bindings: mmc: loongson,ls2k-mmc: Add compatible for
    Loongson-2K2000
  mmc: loongson2: Add Loongson-2K2000 SD/SDIO controller driver

 .../bindings/mmc/loongson,ls2k-mmc.yaml       | 110 +++
 MAINTAINERS                                   |   8 +
 drivers/mmc/host/Kconfig                      |  13 +
 drivers/mmc/host/Makefile                     |   1 +
 drivers/mmc/host/loongson2-mmc.c              | 781 ++++++++++++++++++
 drivers/mmc/host/loongson2-mmc.h              | 224 +++++
 6 files changed, 1137 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
 create mode 100644 drivers/mmc/host/loongson2-mmc.c
 create mode 100644 drivers/mmc/host/loongson2-mmc.h


base-commit: 6b8dba9a7fdba6d669e4119e390a071e44383934
-- 
2.47.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v1 1/4] dt-bindings: mmc: Add Loongson-2K SD/SDIO/eMMC controller binding
  2025-04-10  8:40 [PATCH v1 0/4] LoongArch: Introduce the Loongson-2K MMC host controller driver Binbin Zhou
@ 2025-04-10  8:40 ` Binbin Zhou
  2025-04-11 17:03   ` Rob Herring (Arm)
  2025-04-10  8:40 ` [PATCH v1 2/4] mmc: loongson2: Add Loongson-2K SD/SDIO controller driver Binbin Zhou
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 10+ messages in thread
From: Binbin Zhou @ 2025-04-10  8:40 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Ulf Hansson
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-mmc,
	Binbin Zhou

Add the Loongson-2K SoC's SD/SDIO/eMMC controller binding with DT schema
format using json-schema.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 .../bindings/mmc/loongson,ls2k-mmc.yaml       | 67 +++++++++++++++++++
 MAINTAINERS                                   |  6 ++
 2 files changed, 73 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml

diff --git a/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml b/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
new file mode 100644
index 000000000000..f3e94f5f3a35
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mmc/loongson,ls2k-mmc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: The SD/SDIO/eMMC host controller for Loongson-2K family SoCs
+
+description:
+  The MMC host controller on the Loongson-2K0500/2K1000 (using an externally
+  shared apbdma controller) provides the SD and SDIO device interfaces.
+
+maintainers:
+  - Binbin Zhou <zhoubinbin@loongson.cn>
+
+allOf:
+  - $ref: mmc-controller.yaml#
+
+properties:
+  compatible:
+    enum:
+      - loongson,ls2k0500-mmc
+      - loongson,ls2k1000-mmc
+
+  reg:
+    maxItems: 2
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  dmas:
+    maxItems: 1
+
+  dma-names:
+    const: rx-tx
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - dmas
+  - dma-names
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/clock/loongson,ls2k-clk.h>
+
+    mmc@1fe2c000 {
+        compatible = "loongson,ls2k1000-mmc";
+        reg = <0x1fe2c000 0x68>,
+              <0x1fe00438 0x8>;
+        interrupt-parent = <&liointc0>;
+        interrupts = <31 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&clk LOONGSON2_APB_CLK>;
+        dmas = <&apbdma1 0>;
+        dma-names = "rx-tx";
+        bus-width = <4>;
+        cd-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>;
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index 96b827049501..32c3733a764a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13935,6 +13935,12 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/hwinfo/loongson,ls2k-chipid.yaml
 F:	drivers/soc/loongson/loongson2_guts.c
 
+LOONGSON-2 SOC SERIES MMC/SD/SDIO CONTROLLER DRIVER
+M:	Binbin Zhou <zhoubinbin@loongson.cn>
+L:	linux-mmc@vger.kernel.org
+S:	Supported
+F:	Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
+
 LOONGSON-2 SOC SERIES PM DRIVER
 M:	Yinbo Zhu <zhuyinbo@loongson.cn>
 L:	linux-pm@vger.kernel.org
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v1 2/4] mmc: loongson2: Add Loongson-2K SD/SDIO controller driver
  2025-04-10  8:40 [PATCH v1 0/4] LoongArch: Introduce the Loongson-2K MMC host controller driver Binbin Zhou
  2025-04-10  8:40 ` [PATCH v1 1/4] dt-bindings: mmc: Add Loongson-2K SD/SDIO/eMMC controller binding Binbin Zhou
@ 2025-04-10  8:40 ` Binbin Zhou
  2025-04-28 15:09   ` Ulf Hansson
  2025-04-10  8:40 ` [PATCH v1 3/4] dt-bindings: mmc: loongson,ls2k-mmc: Add compatible for Loongson-2K2000 Binbin Zhou
  2025-04-10  8:41 ` [PATCH v1 4/4] mmc: loongson2: Add Loongson-2K2000 SD/SDIO controller driver Binbin Zhou
  3 siblings, 1 reply; 10+ messages in thread
From: Binbin Zhou @ 2025-04-10  8:40 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Ulf Hansson
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-mmc,
	Binbin Zhou

The MMC controllers on the Loongson-2K series CPUs are similar,
except for the interface characteristics and the use of DMA controllers.

This patch describes the MMC controllers on the Loongson-2K0500/2K1000,
with the distinguishing feature being the use of an externally shared
APBDMA engine.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 MAINTAINERS                      |   2 +
 drivers/mmc/host/Kconfig         |  13 +
 drivers/mmc/host/Makefile        |   1 +
 drivers/mmc/host/loongson2-mmc.c | 636 +++++++++++++++++++++++++++++++
 drivers/mmc/host/loongson2-mmc.h | 177 +++++++++
 5 files changed, 829 insertions(+)
 create mode 100644 drivers/mmc/host/loongson2-mmc.c
 create mode 100644 drivers/mmc/host/loongson2-mmc.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 32c3733a764a..e218a3e204ef 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13940,6 +13940,8 @@ M:	Binbin Zhou <zhoubinbin@loongson.cn>
 L:	linux-mmc@vger.kernel.org
 S:	Supported
 F:	Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
+F:	drivers/mmc/host/loongson2-mmc.c
+F:	drivers/mmc/host/loongson2-mmc.h
 
 LOONGSON-2 SOC SERIES PM DRIVER
 M:	Yinbo Zhu <zhuyinbo@loongson.cn>
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 264e11fa58ea..c8f46901c0e8 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -1097,6 +1097,19 @@ config MMC_OWL
 	  This selects support for the SD/MMC Host Controller on
 	  Actions Semi Owl SoCs.
 
+config MMC_LOONGSON2
+	tristate "Loongso-2K SD/SDIO/eMMC Host Interface support"
+	depends on LOONGARCH || COMPILE_TEST
+	depends on HAS_DMA
+	help
+	  This selects support for the SD/SDIO/eMMC Host Controller on
+	  Loongson-2K series CPUs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called mmc_loongson2.
+
+	  If unsure, say N.
+
 config MMC_SDHCI_EXTERNAL_DMA
 	bool
 
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 5147467ec825..ad09453d65da 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_MMC_USDHI6ROL0)	+= usdhi6rol0.o
 obj-$(CONFIG_MMC_TOSHIBA_PCI)	+= toshsd.o
 obj-$(CONFIG_MMC_BCM2835)	+= bcm2835.o
 obj-$(CONFIG_MMC_OWL)		+= owl-mmc.o
+obj-$(CONFIG_MMC_LOONGSON2)	+= loongson2-mmc.o
 
 obj-$(CONFIG_MMC_REALTEK_PCI)	+= rtsx_pci_sdmmc.o
 obj-$(CONFIG_MMC_REALTEK_USB)	+= rtsx_usb_sdmmc.o
diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c
new file mode 100644
index 000000000000..6348728694e7
--- /dev/null
+++ b/drivers/mmc/host/loongson2-mmc.c
@@ -0,0 +1,636 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Loongson-2K MMC/SDIO controller driver
+ *
+ * Copyright (C) 2018-2025 Loongson Technology Corporation Limited.
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitrev.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "loongson2-mmc.h"
+
+static void loongson2_mmc_send_command(struct loongson2_mmc_host *host,
+				       struct mmc_command *cmd)
+{
+	u32 cctrl;
+
+	if (cmd->data)
+		host->state = STATE_XFERFINISH_RSPFIN;
+	else if (cmd->flags & MMC_RSP_PRESENT)
+		host->state = STATE_RSPFIN;
+	else
+		host->state = STATE_CMDSENT;
+
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, cmd->arg);
+
+	cctrl = FIELD_PREP(LOONGSON2_MMC_CCTL_INDEX, cmd->opcode);
+	cctrl |= LOONGSON2_MMC_CCTL_HOST | LOONGSON2_MMC_CCTL_START;
+
+	if (cmd->opcode == SD_SWITCH && cmd->data)
+		cctrl |= LOONGSON2_MMC_CCTL_CMD6;
+
+	if (cmd->flags & MMC_RSP_PRESENT)
+		cctrl |= LOONGSON2_MMC_CCTL_WAIT_RSP;
+
+	if (cmd->flags & MMC_RSP_136)
+		cctrl |= LOONGSON2_MMC_CCTL_LONG_RSP;
+
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, cctrl);
+}
+
+static int loongson2_mmc_setup_data(struct loongson2_mmc_host *host,
+				    struct mmc_data *data)
+{
+	u32 dctrl;
+
+	if ((data->blksz & 3) != 0)
+		return -EINVAL;
+
+	dctrl = FIELD_PREP(LOONGSON2_MMC_DCTL_BNUM, data->blocks);
+	dctrl |= LOONGSON2_MMC_DCTL_START | LOONGSON2_MMC_DCTL_ENDMA;
+
+	if (host->bus_width == MMC_BUS_WIDTH_4)
+		dctrl |= LOONGSON2_MMC_DCTL_WIDE;
+	else if (host->bus_width == MMC_BUS_WIDTH_8)
+		dctrl |= LOONGSON2_MMC_DCTL_8BIT_BUS;
+
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_DCTL, dctrl);
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_BSIZE, data->blksz);
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_TIMER, U32_MAX);
+
+	return 0;
+}
+
+static int loongson2_mmc_prepare_dma(struct loongson2_mmc_host *host,
+				     struct mmc_data *data)
+{
+	int ret;
+
+	if (!data)
+		return 0;
+
+	ret = loongson2_mmc_setup_data(host, data);
+	if (ret)
+		return ret;
+
+	host->dma_complete = 0;
+
+	return host->pdata->prepare_dma(host, data);
+}
+
+static void loongson2_mmc_send_request(struct mmc_host *mmc)
+{
+	int ret;
+	struct loongson2_mmc_host *host = mmc_priv(mmc);
+	struct mmc_request *mrq = host->mrq;
+	struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
+
+	ret = loongson2_mmc_prepare_dma(host, cmd->data);
+	if (ret) {
+		dev_err(host->dev, "DMA data prepared failed with %d\n", ret);
+		cmd->error = ret;
+		cmd->data->error = ret;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	loongson2_mmc_send_command(host, cmd);
+
+	/* Fix deselect card no irq */
+	if (cmd->opcode == MMC_SELECT_CARD && cmd->arg == 0) {
+		cmd->error = 0;
+		mmc_request_done(mmc, mrq);
+	}
+}
+
+static irqreturn_t loongson2_mmc_irq_worker(int irq, void *devid)
+{
+	struct loongson2_mmc_host *host = (struct loongson2_mmc_host *)devid;
+	struct mmc_request *mrq = host->mrq;
+	struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
+
+	if (cmd->data)
+		dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg, cmd->data->sg_len,
+			     mmc_get_dma_dir(cmd->data));
+
+	if (cmd->data && !cmd->error &&
+	    !cmd->data->error && !host->dma_complete)
+		return IRQ_HANDLED;
+
+	/* Read response from controller. */
+	regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP0, &cmd->resp[0]);
+	regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP1, &cmd->resp[1]);
+	regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP2, &cmd->resp[2]);
+	regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP3, &cmd->resp[3]);
+
+	/* Cleanup controller */
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, 0);
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, 0);
+
+	if (cmd->data && cmd->error)
+		cmd->data->error = cmd->error;
+
+	if (cmd->data && cmd->data->stop && !host->cmd_is_stop) {
+		host->cmd_is_stop = 1;
+		loongson2_mmc_send_request(host->mmc);
+		return IRQ_HANDLED;
+	}
+
+	/* If we have no data transfer we are finished here */
+	if (!mrq->data)
+		goto request_done;
+
+	/* Calculate the amount of bytes transfer if there was no error */
+	if (mrq->data->error == 0) {
+		mrq->data->bytes_xfered =
+			(mrq->data->blocks * mrq->data->blksz);
+	} else {
+		mrq->data->bytes_xfered = 0;
+	}
+
+request_done:
+	host->state = STATE_NONE;
+	host->mrq = NULL;
+	mmc_request_done(host->mmc, mrq);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t loongson2_mmc_irq(int irq, void *dev_id)
+{
+	struct loongson2_mmc_host *host = dev_id;
+	struct mmc_command *cmd;
+	unsigned long iflags;
+	u32 dsts, imsk;
+
+	regmap_read(host->regmap, LOONGSON2_MMC_REG_INT, &imsk);
+	regmap_read(host->regmap, LOONGSON2_MMC_REG_DSTS, &dsts);
+
+	if ((dsts & LOONGSON2_MMC_DSTS_IRQ) &&
+	    (imsk & LOONGSON2_MMC_INT_SDIOIRQ)) {
+		regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_INT,
+				   LOONGSON2_MMC_INT_SDIOIRQ,
+				   LOONGSON2_MMC_INT_SDIOIRQ);
+
+		mmc_signal_sdio_irq(host->mmc);
+		return IRQ_HANDLED;
+	}
+
+	spin_lock_irqsave(&host->lock, iflags);
+
+	if (host->state == STATE_NONE || host->state == STATE_FINALIZE ||
+	    !host->mrq)
+		goto irq_out;
+
+	cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;
+	if (!cmd)
+		goto irq_out;
+
+	cmd->error = 0;
+
+	if (imsk & LOONGSON2_MMC_INT_CTIMEOUT) {
+		cmd->error = -ETIMEDOUT;
+		goto close_transfer;
+	}
+
+	if (imsk & LOONGSON2_MMC_INT_CSENT) {
+		if (host->state == STATE_RSPFIN || host->state == STATE_CMDSENT)
+			goto close_transfer;
+
+		if (host->state == STATE_XFERFINISH_RSPFIN)
+			host->state = STATE_XFERFINISH;
+	}
+
+	if (!cmd->data)
+		goto irq_out;
+
+	if (imsk & (LOONGSON2_MMC_INT_RXCRC | LOONGSON2_MMC_INT_TXCRC)) {
+		cmd->data->error = -EILSEQ;
+		goto close_transfer;
+	}
+
+	if (imsk & LOONGSON2_MMC_INT_DTIMEOUT) {
+		cmd->data->error = -ETIMEDOUT;
+		goto close_transfer;
+	}
+
+	if (imsk & LOONGSON2_MMC_INT_DFIN) {
+		if (host->state == STATE_XFERFINISH) {
+			host->dma_complete = 1;
+			goto close_transfer;
+		}
+
+		if (host->state == STATE_XFERFINISH_RSPFIN)
+			host->state = STATE_RSPFIN;
+	}
+
+irq_out:
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk);
+	spin_unlock_irqrestore(&host->lock, iflags);
+	return IRQ_HANDLED;
+
+close_transfer:
+	host->state = STATE_FINALIZE;
+	host->pdata->reorder_cmd_data(host, cmd);
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk);
+	spin_unlock_irqrestore(&host->lock, iflags);
+	return IRQ_WAKE_THREAD;
+}
+
+static void loongson2_mmc_set_clk(struct loongson2_mmc_host *host, struct mmc_ios *ios)
+{
+	u32 pre;
+
+	pre = DIV_ROUND_UP(host->rate, ios->clock);
+	if (pre > 255)
+		pre = 255;
+
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_PRE, pre | LOONGSON2_MMC_PRE_EN);
+}
+
+static void loongson2_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct loongson2_mmc_host *host = mmc_priv(mmc);
+
+	switch (ios->power_mode) {
+	case MMC_POWER_ON:
+	case MMC_POWER_UP:
+		regmap_write(host->regmap, LOONGSON2_MMC_REG_CTL, LOONGSON2_MMC_CTL_RESET);
+		mdelay(10);
+		regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, LOONGSON2_MMC_IEN_ALL);
+		regmap_write(host->regmap, LOONGSON2_MMC_REG_IEN, LOONGSON2_MMC_INT_CLEAR);
+		break;
+	case MMC_POWER_OFF:
+		regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL,
+				   LOONGSON2_MMC_CTL_RESET, LOONGSON2_MMC_CTL_RESET);
+		return;
+	default:
+		return;
+	}
+
+	loongson2_mmc_set_clk(host, ios);
+
+	/* Set CLOCK_ENABLE */
+	if (ios->clock)
+		regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL,
+				   LOONGSON2_MMC_CTL_ENCLK, LOONGSON2_MMC_CTL_ENCLK);
+
+	host->bus_width = ios->bus_width;
+}
+
+static void loongson2_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct loongson2_mmc_host *host = mmc_priv(mmc);
+
+	host->cmd_is_stop = 0;
+	host->mrq = mrq;
+	loongson2_mmc_send_request(mmc);
+}
+
+static void loongson2_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+	struct loongson2_mmc_host *host = mmc_priv(mmc);
+
+	regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_IEN,
+			   LOONGSON2_MMC_INT_SDIOIRQ, enable);
+}
+
+static struct mmc_host_ops loongson2_mmc_ops = {
+	.request	= loongson2_mmc_request,
+	.set_ios	= loongson2_mmc_set_ios,
+	.get_ro		= mmc_gpio_get_ro,
+	.get_cd		= mmc_gpio_get_cd,
+	.enable_sdio_irq = loongson2_mmc_enable_sdio_irq,
+};
+
+static const struct regmap_config ls2k1000_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = LOONGSON2_MMC_REG_IEN,
+};
+
+static int loongson2_reorder_cmd_list[] = { SD_APP_SEND_SCR, SD_APP_SEND_NUM_WR_BLKS,
+					    SD_APP_SD_STATUS, MMC_SEND_WRITE_PROT,
+					    SD_SWITCH };
+
+/*
+ * According to SD spec, ACMD13, ACMD22, ACMD51 and CMD30
+ * response datas has different byte order with usual data packets.
+ * However sdio controller will send these datas in usual data format,
+ * so we need to adjust these datas to a protocol consistent byte order.
+ */
+static void loongson2_mmc_reorder_cmd_data(struct loongson2_mmc_host *host,
+					   struct mmc_command *cmd)
+{
+	struct scatterlist *sg;
+	u32 *data;
+	int i, j;
+
+	if (mmc_cmd_type(cmd) != MMC_CMD_ADTC)
+		return;
+
+	for (i = 0; i < ARRAY_SIZE(loongson2_reorder_cmd_list); i++)
+		if (cmd->opcode == loongson2_reorder_cmd_list[i])
+			break;
+
+	if (i == ARRAY_SIZE(loongson2_reorder_cmd_list))
+		return;
+
+	for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) {
+		data = sg_virt(&sg[i]);
+		for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++)
+			if (cmd->opcode == SD_SWITCH)
+				data[j] = bitrev8x4(data[j]);
+			else
+				data[j] = cpu_to_be32(data[j]);
+	}
+}
+
+static int loongson2_mmc_prepare_external_dma(struct loongson2_mmc_host *host,
+					      struct mmc_data *data)
+{
+	struct dma_async_tx_descriptor *desc;
+	struct dma_slave_config conf = {
+		.src_addr = host->res->start + LOONGSON2_MMC_REG_DATA,
+		.dst_addr = host->res->start + LOONGSON2_MMC_REG_DATA,
+		.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+		.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+	};
+
+	conf.direction = !(data->flags & MMC_DATA_WRITE) ?
+			 DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
+
+	dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+		   mmc_get_dma_dir(data));
+
+	dmaengine_slave_config(host->chan, &conf);
+	desc = dmaengine_prep_slave_sg(host->chan, data->sg, data->sg_len,
+				       conf.direction,
+				       DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+	if (!desc)
+		goto unmap_exit;
+
+	dmaengine_submit(desc);
+	dma_async_issue_pending(host->chan);
+
+	return 0;
+
+unmap_exit:
+	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+		     mmc_get_dma_dir(data));
+	return -ENOMEM;
+}
+
+static void loongson2_mmc_release_external_dma(struct loongson2_mmc_host *host,
+					       struct device *dev)
+{
+	dma_release_channel(host->chan);
+}
+
+static int ls2k0500_mmc_set_external_dma(struct loongson2_mmc_host *host,
+					 struct platform_device *pdev)
+{
+	int ret, val;
+	void __iomem *regs;
+
+	regs = devm_platform_ioremap_resource(pdev, 1);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	val = readl(regs);
+	val |= FIELD_PREP(LS2K0500_SDIO_DMA_MASK, LS2K0500_DMA2_CONF);
+	writel(val, regs);
+
+	host->chan = dma_request_chan(&pdev->dev, "rx-tx");
+	ret = PTR_ERR_OR_ZERO(host->chan);
+	if (ret) {
+		dev_err(&pdev->dev, "cannot get DMA channel.\n");
+		return  ret;
+	}
+
+	return 0;
+}
+
+static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = {
+	.regmap_config		= ls2k1000_regmap_config,
+	.reorder_cmd_data	= loongson2_mmc_reorder_cmd_data,
+	.setting_dma		= ls2k0500_mmc_set_external_dma,
+	.prepare_dma		= loongson2_mmc_prepare_external_dma,
+	.release_dma		= loongson2_mmc_release_external_dma,
+};
+
+static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host,
+					 struct platform_device *pdev)
+{
+	int ret, val;
+	void __iomem *regs;
+
+	regs = devm_platform_ioremap_resource(pdev, 1);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	val = readl(regs);
+	val |= FIELD_PREP(LS2K1000_SDIO_DMA_MASK, LS2K1000_DMA1_CONF);
+	writel(val, regs);
+
+	host->chan = dma_request_chan(&pdev->dev, "rx-tx");
+	ret = PTR_ERR_OR_ZERO(host->chan);
+	if (ret) {
+		dev_err(&pdev->dev, "cannot get DMA channel.\n");
+		return  ret;
+	}
+
+	return 0;
+}
+
+static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = {
+	.regmap_config		= ls2k1000_regmap_config,
+	.reorder_cmd_data	= loongson2_mmc_reorder_cmd_data,
+	.setting_dma		= ls2k1000_mmc_set_external_dma,
+	.prepare_dma		= loongson2_mmc_prepare_external_dma,
+	.release_dma		= loongson2_mmc_release_external_dma,
+};
+
+static int loongson2_mmc_resource_request(struct platform_device *pdev,
+					  struct loongson2_mmc_host *host)
+{
+	struct device *dev = &pdev->dev;
+	void __iomem *base;
+	int ret, irq;
+
+	base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	host->regmap = devm_regmap_init_mmio(dev, base, &host->pdata->regmap_config);
+	if (IS_ERR(host->regmap))
+		return PTR_ERR(host->regmap);
+
+	host->clk = devm_clk_get_optional_enabled(dev, NULL);
+	if (IS_ERR(host->clk))
+		return PTR_ERR(host->clk);
+
+	if (host->clk) {
+		ret = devm_clk_rate_exclusive_get(dev, host->clk);
+		if (ret)
+			return PTR_ERR(host->clk);
+
+		host->rate = clk_get_rate(host->clk);
+	} else {
+		/* For ACPI, we get rate through clock-frequency attribute */
+		device_property_read_u64(dev, "clock-frequency", &host->rate);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	ret = devm_request_threaded_irq(dev, irq, loongson2_mmc_irq,
+					loongson2_mmc_irq_worker,
+					IRQF_ONESHOT, "loongson2-mmc", host);
+	if (ret)
+		return ret;
+
+	ret = host->pdata->setting_dma(host, pdev);
+	if (ret)
+		return ret;
+
+	return dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+}
+
+static int loongson2_mmc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct loongson2_mmc_host *host;
+	struct mmc_host	*mmc;
+	int ret;
+
+	mmc = mmc_alloc_host(sizeof(*host), dev);
+	if (!mmc) {
+		dev_err(dev, "Failed to alloc mmc host\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, mmc);
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->state = STATE_NONE;
+	spin_lock_init(&host->lock);
+
+	host->pdata = device_get_match_data(dev);
+	if (!host->pdata) {
+		dev_err(dev, "Failed to get match data\n");
+		ret = -EINVAL;
+		goto free_host;
+	}
+
+	ret = loongson2_mmc_resource_request(pdev, host);
+	if (ret) {
+		dev_err(dev, "Failed to request resource\n");
+		goto free_host;
+	}
+
+	mmc->ops = &loongson2_mmc_ops;
+	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->f_min = DIV_ROUND_UP(host->rate, 256);
+	mmc->f_max = host->rate;
+	mmc->max_blk_count = 4095;
+	mmc->max_blk_size = 4095;
+	mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size;
+	mmc->max_segs = 1;
+	mmc->max_seg_size = mmc->max_req_size;
+
+	ret = mmc_of_parse(mmc);
+	if (ret) {
+		dev_err(dev, "Failed to parse device node\n");
+		goto free_dma;
+	}
+
+	ret = mmc_add_host(mmc);
+	if (ret) {
+		dev_err(dev, "Failed to add mmc host.\n");
+		goto free_dma;
+	}
+
+	return 0;
+
+free_dma:
+	host->pdata->release_dma(host, dev);
+free_host:
+	mmc_free_host(mmc);
+	return ret;
+}
+
+static void loongson2_mmc_remove(struct platform_device *pdev)
+{
+	struct mmc_host *mmc  = platform_get_drvdata(pdev);
+	struct loongson2_mmc_host *host = mmc_priv(mmc);
+
+	mmc_remove_host(mmc);
+	host->pdata->release_dma(host, &pdev->dev);
+	mmc_free_host(mmc);
+}
+
+static const struct of_device_id loongson2_mmc_of_ids[] = {
+	{ .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata },
+	{ .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, loongson2_mmc_of_ids);
+
+static int loongson2_mmc_suspend(struct device *dev)
+{
+	struct mmc_host *mmc  = dev_get_drvdata(dev);
+	struct loongson2_mmc_host *host = mmc_priv(mmc);
+
+	clk_disable_unprepare(host->clk);
+
+	return 0;
+}
+
+static int loongson2_mmc_resume(struct device *dev)
+{
+	struct mmc_host *mmc  = dev_get_drvdata(dev);
+	struct loongson2_mmc_host *host = mmc_priv(mmc);
+
+	return clk_prepare_enable(host->clk);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(loongson2_mmc_pm_ops, loongson2_mmc_suspend,
+				loongson2_mmc_resume);
+
+static struct platform_driver loongson2_mmc_driver = {
+	.driver	= {
+		.name = "loongson2-mmc",
+		.of_match_table = loongson2_mmc_of_ids,
+		.pm = pm_ptr(&loongson2_mmc_pm_ops),
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
+	},
+	.probe = loongson2_mmc_probe,
+	.remove = loongson2_mmc_remove,
+};
+
+module_platform_driver(loongson2_mmc_driver);
+
+MODULE_DESCRIPTION("Loongson-2K SD/SDIO/eMMC Interface driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/loongson2-mmc.h b/drivers/mmc/host/loongson2-mmc.h
new file mode 100644
index 000000000000..4d8ada650350
--- /dev/null
+++ b/drivers/mmc/host/loongson2-mmc.h
@@ -0,0 +1,177 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef LOONGSON2_MMC_H
+#define LOONGSON2_MMC_H
+
+#define LOONGSON2_MMC_REG_CTL		0x00 /* Control Register */
+#define LOONGSON2_MMC_REG_PRE		0x04 /* Prescaler Register */
+#define LOONGSON2_MMC_REG_CARG		0x08 /* Command Register */
+#define LOONGSON2_MMC_REG_CCTL		0x0c /* Command Control Register */
+#define LOONGSON2_MMC_REG_CSTS		0x10 /* Command Status Register */
+#define LOONGSON2_MMC_REG_RSP0		0x14 /* Command Response Register 0 */
+#define LOONGSON2_MMC_REG_RSP1		0x18 /* Command Response Register 1 */
+#define LOONGSON2_MMC_REG_RSP2		0x1c /* Command Response Register 2 */
+#define LOONGSON2_MMC_REG_RSP3		0x20 /* Command Response Register 3 */
+#define LOONGSON2_MMC_REG_TIMER		0x24 /* Data Timeout Register */
+#define LOONGSON2_MMC_REG_BSIZE		0x28 /* Block Size Register */
+#define LOONGSON2_MMC_REG_DCTL		0x2c /* Data Control Register */
+#define LOONGSON2_MMC_REG_DCNT		0x30 /* Data Counter Register */
+#define LOONGSON2_MMC_REG_DSTS		0x34 /* Data Status Register */
+#define LOONGSON2_MMC_REG_FSTS		0x38 /* FIFO Status Register */
+#define LOONGSON2_MMC_REG_INT		0x3c /* Interrupt Register */
+#define LOONGSON2_MMC_REG_DATA		0x40 /* Data Register */
+#define LOONGSON2_MMC_REG_IEN		0x64 /* Interrupt Enable Register */
+
+/* Bitfields of control register */
+#define LOONGSON2_MMC_CTL_ENCLK		BIT(0)
+#define LOONGSON2_MMC_CTL_RESET		BIT(8)
+
+/* Bitfields of prescaler register */
+#define LOONGSON2_MMC_PRE		GENMASK(9, 0)
+#define LOONGSON2_MMC_PRE_EN		BIT(31)
+
+/* Bitfields of command control register */
+#define LOONGSON2_MMC_CCTL_INDEX		GENMASK(5, 0)
+#define LOONGSON2_MMC_CCTL_HOST		BIT(6)
+#define LOONGSON2_MMC_CCTL_START		BIT(8)
+#define LOONGSON2_MMC_CCTL_WAIT_RSP	BIT(9)
+#define LOONGSON2_MMC_CCTL_LONG_RSP	BIT(10)
+#define LOONGSON2_MMC_CCTL_ABORT		BIT(12)
+#define LOONGSON2_MMC_CCTL_CHECK		BIT(13)
+#define LOONGSON2_MMC_CCTL_SDIO		BIT(14)
+#define LOONGSON2_MMC_CCTL_CMD6		BIT(18)
+
+/* Bitfields of command status register */
+#define LOONGSON2_MMC_CSTS_INDEX		GENMASK(7, 0)
+#define LOONGSON2_MMC_CSTS_ON		BIT(8)
+#define LOONGSON2_MMC_CSTS_RSP		BIT(9)
+#define LOONGSON2_MMC_CSTS_TIMEOUT	BIT(10)
+#define LOONGSON2_MMC_CSTS_END		BIT(11)
+#define LOONGSON2_MMC_CSTS_CRC_ERR	BIT(12)
+#define LOONGSON2_MMC_CSTS_AUTO_STOP	BIT(13)
+#define LOONGSON2_MMC_CSTS_FIN		BIT(14)
+
+/* Bitfields of data timeout register */
+#define LOONGSON2_MMC_DTIMR		GENMASK(23, 0)
+
+/* Bitfields of block size register */
+#define LOONGSON2_MMC_BSIZE		GENMASK(11, 0)
+
+/* Bitfields of data control register */
+#define LOONGSON2_MMC_DCTL_BNUM		GENMASK(11, 0)
+#define LOONGSON2_MMC_DCTL_START		BIT(14)
+#define LOONGSON2_MMC_DCTL_ENDMA		BIT(15)
+#define LOONGSON2_MMC_DCTL_WIDE		BIT(16)
+#define LOONGSON2_MMC_DCTL_RWAIT		BIT(17)
+#define LOONGSON2_MMC_DCTL_IO_SUSPEND	BIT(18)
+#define LOONGSON2_MMC_DCTL_IO_RESUME	BIT(19)
+#define LOONGSON2_MMC_DCTL_RW_RESUME	BIT(20)
+#define LOONGSON2_MMC_DCTL_8BIT_BUS      BIT(26)
+
+/* Bitfields of sata counter register */
+#define LOONGSON2_MMC_DCNT_BNUM		GENMASK(11, 0)
+#define LOONGSON2_MMC_DCNT_BYTE		GENMASK(23, 12)
+
+/* Bitfields of command status register */
+#define LOONGSON2_MMC_DSTS_RXON		BIT(0)
+#define LOONGSON2_MMC_DSTS_TXON		BIT(1)
+#define LOONGSON2_MMC_DSTS_SBITERR	BIT(2)
+#define LOONGSON2_MMC_DSTS_BUSYFIN	BIT(3)
+#define LOONGSON2_MMC_DSTS_XFERFIN	BIT(4)
+#define LOONGSON2_MMC_DSTS_DTIMEOUT	BIT(5)
+#define LOONGSON2_MMC_DSTS_RXCRC		BIT(6)
+#define LOONGSON2_MMC_DSTS_TXCRC		BIT(7)
+#define LOONGSON2_MMC_DSTS_IRQ		BIT(8)
+#define LOONGSON2_MMC_DSTS_START		BIT(13)
+#define LOONGSON2_MMC_DSTS_RESUME	BIT(15)
+#define LOONGSON2_MMC_DSTS_SUSPEND	BIT(16)
+
+/* Bitfields of interrupt register */
+#define LOONGSON2_MMC_INT_DFIN		BIT(0)
+#define LOONGSON2_MMC_INT_DTIMEOUT	BIT(1)
+#define LOONGSON2_MMC_INT_RXCRC		BIT(2)
+#define LOONGSON2_MMC_INT_TXCRC		BIT(3)
+#define LOONGSON2_MMC_INT_PROGERR	BIT(4)
+#define LOONGSON2_MMC_INT_SDIOIRQ	BIT(5)
+#define LOONGSON2_MMC_INT_CSENT		BIT(6)
+#define LOONGSON2_MMC_INT_CTIMEOUT	BIT(7)
+#define LOONGSON2_MMC_INT_RESPCRC	BIT(8)
+#define LOONGSON2_MMC_INT_BUSYEND	BIT(9)
+
+/* Bitfields of interrupt enable register */
+#define LOONGSON2_MMC_IEN_DFIN		BIT(0)
+#define LOONGSON2_MMC_IEN_DTIMEOUT	BIT(1)
+#define LOONGSON2_MMC_IEN_RXCRC		BIT(2)
+#define LOONGSON2_MMC_IEN_TXCRC		BIT(3)
+#define LOONGSON2_MMC_IEN_PROGERR	BIT(4)
+#define LOONGSON2_MMC_IEN_SDIOIRQ	BIT(5)
+#define LOONGSON2_MMC_IEN_CSENT		BIT(6)
+#define LOONGSON2_MMC_IEN_CTIMEOUT	BIT(7)
+#define LOONGSON2_MMC_IEN_RESPCRC	BIT(8)
+#define LOONGSON2_MMC_IEN_BUSYEND	BIT(9)
+
+#define LOONGSON2_MMC_IEN_ALL		GENMASK(9, 0)
+#define LOONGSON2_MMC_INT_CLEAR		GENMASK(9, 0)
+
+/* Loongson-2K1000 SDIO2 DMA routing register */
+#define LS2K1000_SDIO_DMA_MASK		GENMASK(17, 15)
+#define LS2K1000_DMA0_CONF		0x0
+#define LS2K1000_DMA1_CONF		0x1
+#define LS2K1000_DMA2_CONF		0x2
+#define LS2K1000_DMA3_CONF		0x3
+#define LS2K1000_DMA4_CONF		0x4
+
+/* Loongson-2K0500 SDIO2 DMA routing register */
+#define LS2K0500_SDIO_DMA_MASK		GENMASK(15, 14)
+#define LS2K0500_DMA0_CONF		0x1
+#define LS2K0500_DMA1_CONF		0x2
+#define LS2K0500_DMA2_CONF		0x3
+
+enum loongson2_mmc_state {
+	STATE_NONE,
+	STATE_FINALIZE,
+	STATE_CMDSENT,
+	STATE_RSPFIN,
+	STATE_XFERFINISH,
+	STATE_XFERFINISH_RSPFIN,
+};
+
+struct loongson2_dma_desc {
+	u32 ndesc_addr;
+	u32 mem_addr;
+	u32 apb_addr;
+	u32 len;
+	u32 step_len;
+	u32 step_times;
+	u32 cmd;
+	u32 stats;
+	u32 high_ndesc_addr;
+	u32 high_mem_addr;
+	u32 reserved[2];
+} __packed;
+
+struct loongson2_mmc_host {
+	struct device *dev;
+	struct mmc_host *mmc;
+	struct mmc_request *mrq;
+	struct regmap *regmap;
+	struct resource *res;
+	struct clk *clk;
+	u64 rate;
+	int dma_complete;
+	struct dma_chan *chan;
+	int cmd_is_stop;
+	int bus_width;
+	spinlock_t lock;
+	enum loongson2_mmc_state state;
+	const struct loongson2_mmc_pdata *pdata;
+};
+
+struct loongson2_mmc_pdata {
+	const struct regmap_config regmap_config;
+	void (*reorder_cmd_data)(struct loongson2_mmc_host *host, struct mmc_command *cmd);
+	int (*setting_dma)(struct loongson2_mmc_host *host, struct platform_device *pdev);
+	int (*prepare_dma)(struct loongson2_mmc_host *host, struct mmc_data *data);
+	void (*release_dma)(struct loongson2_mmc_host *host, struct device *dev);
+};
+#endif
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v1 3/4] dt-bindings: mmc: loongson,ls2k-mmc: Add compatible for Loongson-2K2000
  2025-04-10  8:40 [PATCH v1 0/4] LoongArch: Introduce the Loongson-2K MMC host controller driver Binbin Zhou
  2025-04-10  8:40 ` [PATCH v1 1/4] dt-bindings: mmc: Add Loongson-2K SD/SDIO/eMMC controller binding Binbin Zhou
  2025-04-10  8:40 ` [PATCH v1 2/4] mmc: loongson2: Add Loongson-2K SD/SDIO controller driver Binbin Zhou
@ 2025-04-10  8:40 ` Binbin Zhou
  2025-04-11 17:05   ` Rob Herring
  2025-04-10  8:41 ` [PATCH v1 4/4] mmc: loongson2: Add Loongson-2K2000 SD/SDIO controller driver Binbin Zhou
  3 siblings, 1 reply; 10+ messages in thread
From: Binbin Zhou @ 2025-04-10  8:40 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Ulf Hansson
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-mmc,
	Binbin Zhou

Add the devicetree compatible for Loongson-2K2000 EMMC/SD/SDIO controller.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 .../bindings/mmc/loongson,ls2k-mmc.yaml       | 47 ++++++++++++++++++-
 1 file changed, 45 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml b/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
index f3e94f5f3a35..24d217a9bbe6 100644
--- a/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
+++ b/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
@@ -9,6 +9,9 @@ title: The SD/SDIO/eMMC host controller for Loongson-2K family SoCs
 description:
   The MMC host controller on the Loongson-2K0500/2K1000 (using an externally
   shared apbdma controller) provides the SD and SDIO device interfaces.
+  The two MMC host controllers on the Loongson-2K2000 are similar,
+  except that they use internal exclusive DMA. one controller provides
+  the eMMC interface and the other provides the SD/SDIO interface.
 
 maintainers:
   - Binbin Zhou <zhoubinbin@loongson.cn>
@@ -21,8 +24,10 @@ properties:
     enum:
       - loongson,ls2k0500-mmc
       - loongson,ls2k1000-mmc
+      - loongson,ls2k2000-mmc
 
   reg:
+    minItems: 1
     maxItems: 2
 
   interrupts:
@@ -42,11 +47,31 @@ required:
   - reg
   - interrupts
   - clocks
-  - dmas
-  - dma-names
 
 unevaluatedProperties: false
 
+if:
+  properties:
+    compatible:
+      contains:
+        enum:
+          - loongson,ls2k0500-mmc
+          - loongson,ls2k1000-mmc
+
+then:
+  properties:
+    reg:
+      minItems: 2
+
+  required:
+    - dmas
+    - dma-names
+
+else:
+  properties:
+    reg:
+      maxItems: 1
+
 examples:
   - |
     #include <dt-bindings/gpio/gpio.h>
@@ -65,3 +90,21 @@ examples:
         bus-width = <4>;
         cd-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>;
     };
+
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/clock/loongson,ls2k-clk.h>
+
+    mmc@79990000 {
+        compatible = "loongson,ls2k2000-mmc";
+        reg = <0x79990000 0x1000>;
+        interrupt-parent = <&pic>;
+        interrupts = <51 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&clk LOONGSON2_EMMC_CLK>;
+        bus-width = <8>;
+        non-removable;
+        cap-mmc-highspeed;
+        mmc-hs200-1_8v;
+        no-sd;
+        no-sdio;
+    };
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v1 4/4] mmc: loongson2: Add Loongson-2K2000 SD/SDIO controller driver
  2025-04-10  8:40 [PATCH v1 0/4] LoongArch: Introduce the Loongson-2K MMC host controller driver Binbin Zhou
                   ` (2 preceding siblings ...)
  2025-04-10  8:40 ` [PATCH v1 3/4] dt-bindings: mmc: loongson,ls2k-mmc: Add compatible for Loongson-2K2000 Binbin Zhou
@ 2025-04-10  8:41 ` Binbin Zhou
  3 siblings, 0 replies; 10+ messages in thread
From: Binbin Zhou @ 2025-04-10  8:41 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Ulf Hansson
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, linux-mmc,
	Binbin Zhou

This patch introduces the eMMC controller and SDIO controller built into
the Loongson-2K2000 SoC. They have similar register structures and both
support only exclusive DMA communication.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/mmc/host/loongson2-mmc.c | 149 ++++++++++++++++++++++++++++++-
 drivers/mmc/host/loongson2-mmc.h |  47 ++++++++++
 2 files changed, 194 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c
index 6348728694e7..7e1cca47b496 100644
--- a/drivers/mmc/host/loongson2-mmc.c
+++ b/drivers/mmc/host/loongson2-mmc.c
@@ -98,7 +98,7 @@ static int loongson2_mmc_prepare_dma(struct loongson2_mmc_host *host,
 
 static void loongson2_mmc_send_request(struct mmc_host *mmc)
 {
-	int ret;
+	int ret, val;
 	struct loongson2_mmc_host *host = mmc_priv(mmc);
 	struct mmc_request *mrq = host->mrq;
 	struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
@@ -112,6 +112,14 @@ static void loongson2_mmc_send_request(struct mmc_host *mmc)
 		return;
 	}
 
+	if (cmd->opcode == MMC_WRITE_BLOCK || cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) {
+		ret = regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_FSTS, val,
+					       (val & LOONGSON2_MMC_FSTS_TXFULL), 0, 500);
+		if (ret < 0)
+			return;
+	}
+
+	/* Send command */
 	loongson2_mmc_send_command(host, cmd);
 
 	/* Fix deselect card no irq */
@@ -254,6 +262,36 @@ static irqreturn_t loongson2_mmc_irq(int irq, void *dev_id)
 	return IRQ_WAKE_THREAD;
 }
 
+static void loongson2_mmc_ddr_mode_init(struct loongson2_mmc_host *host)
+{
+	u32 val, pad_delay, delay, ret;
+
+	regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_SEL,
+			   LOONGSON2_MMC_SEL_DATA, LOONGSON2_MMC_SEL_DATA);
+
+	val = FIELD_PREP(LOONGSON2_MMC_DLLCTL_TIME, 0xc8)
+	    | FIELD_PREP(LOONGSON2_MMC_DLLCTL_INCRE, 0x1)
+	    | FIELD_PREP(LOONGSON2_MMC_DLLCTL_START, 0x1)
+	    | FIELD_PREP(LOONGSON2_MMC_DLLCTL_CLK_MODE, 0x1)
+	    | FIELD_PREP(LOONGSON2_MMC_DLLCTL_START_BIT, 0x1)
+	    | FIELD_PREP(LOONGSON2_MMC_DLLCTL_TIME_BYPASS, 0xf);
+
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_DLLCTL, val);
+
+	ret = regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_DLLVAL, val,
+				       (val & LOONGSON2_MMC_DLLVAL_DONE), 0, 4000);
+	if (ret < 0)
+		return;
+
+	regmap_read(host->regmap, LOONGSON2_MMC_REG_DLLVAL, &val);
+	pad_delay = FIELD_GET(GENMASK(7, 1), val);
+
+	delay = FIELD_PREP(LOONGSON2_MMC_DELAY_PAD, pad_delay)
+	      | FIELD_PREP(LOONGSON2_MMC_DELAY_RD, pad_delay + 1);
+
+	regmap_write(host->regmap, LOONGSON2_MMC_REG_DELAY, delay);
+}
+
 static void loongson2_mmc_set_clk(struct loongson2_mmc_host *host, struct mmc_ios *ios)
 {
 	u32 pre;
@@ -263,6 +301,10 @@ static void loongson2_mmc_set_clk(struct loongson2_mmc_host *host, struct mmc_io
 		pre = 255;
 
 	regmap_write(host->regmap, LOONGSON2_MMC_REG_PRE, pre | LOONGSON2_MMC_PRE_EN);
+
+	/* EMMC DDR mode setting */
+	if (ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_MMC_DDR52)
+		loongson2_mmc_ddr_mode_init(host);
 }
 
 static void loongson2_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -469,6 +511,108 @@ static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = {
 	.release_dma		= loongson2_mmc_release_external_dma,
 };
 
+static const struct regmap_config ls2k2000_mmc_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = LOONGSON2_MMC_REG_RDMA_HI,
+};
+
+static int loongson2_mmc_set_internal_dma(struct loongson2_mmc_host *host,
+					  struct platform_device *pdev)
+{
+	host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
+					  &host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu)
+		return -ENOMEM;
+
+	memset(host->sg_cpu, 0, PAGE_SIZE);
+	return 0;
+}
+
+static void loongson2_mmc_release_internal_dma(struct loongson2_mmc_host *host,
+					       struct device *dev)
+{
+	dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+}
+
+static void ls2k2000_mmc_reorder_cmd_data(struct loongson2_mmc_host *host,
+					  struct mmc_command *cmd)
+{
+	struct scatterlist *sg;
+	u32 *data;
+	int i, j;
+
+	if (cmd->opcode != SD_SWITCH || mmc_cmd_type(cmd) != MMC_CMD_ADTC)
+		return;
+
+	for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) {
+		data = sg_virt(&sg[i]);
+		for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++)
+			data[j] = bitrev8x4(data[j]);
+	}
+}
+
+static int loongson2_mmc_prepare_internal_dma(struct loongson2_mmc_host *host,
+					      struct mmc_data *data)
+{
+	struct loongson2_dma_desc *pdes = (struct loongson2_dma_desc *)host->sg_cpu;
+	dma_addr_t next_desc = host->sg_dma;
+	struct scatterlist *sg;
+	int reg_lo, reg_hi;
+	u64 dma_order;
+	int i, ret;
+
+	ret = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+			 mmc_get_dma_dir(data));
+	if (!ret)
+		return -ENOMEM;
+
+	for_each_sg(data->sg, sg, data->sg_len, i) {
+		pdes[i].len = sg_dma_len(&sg[i]) / 4;
+		pdes[i].step_len = 0;
+		pdes[i].step_times = 1;
+		pdes[i].mem_addr = lower_32_bits(sg_dma_address(&sg[i]));
+		pdes[i].high_mem_addr = upper_32_bits(sg_dma_address(&sg[i]));
+		pdes[i].apb_addr = host->res->start + LOONGSON2_MMC_REG_DATA;
+		pdes[i].cmd = LOONGSON2_MMC_DMA_INT;
+
+		if (data->flags & MMC_DATA_READ) {
+			reg_lo = LOONGSON2_MMC_REG_RDMA_LO;
+			reg_hi = LOONGSON2_MMC_REG_RDMA_HI;
+		} else {
+			pdes[i].cmd |= LOONGSON2_MMC_DMA_DATA_DIR;
+			reg_lo = LOONGSON2_MMC_REG_WDMA_LO;
+			reg_hi = LOONGSON2_MMC_REG_WDMA_HI;
+		}
+
+		next_desc += sizeof(struct loongson2_dma_desc);
+		pdes[i].ndesc_addr = lower_32_bits(next_desc) |
+				     LOONGSON2_MMC_DMA_DESC_EN;
+		pdes[i].high_ndesc_addr = upper_32_bits(next_desc);
+	}
+
+	/* Setting the last descriptor enable bit */
+	pdes[i - 1].ndesc_addr &= ~LOONGSON2_MMC_DMA_DESC_EN;
+
+	dma_order = (host->sg_dma & ~LOONGSON2_MMC_DMA_CONFIG_MASK) |
+		    LOONGSON2_MMC_DMA_64BIT_EN |
+		    LOONGSON2_MMC_DMA_START;
+
+	regmap_write(host->regmap, reg_hi, upper_32_bits(dma_order));
+	regmap_write(host->regmap, reg_lo, lower_32_bits(dma_order));
+
+	return 0;
+}
+
+static struct loongson2_mmc_pdata ls2k2000_mmc_pdata = {
+	.regmap_config		= ls2k2000_mmc_regmap_config,
+	.reorder_cmd_data	= ls2k2000_mmc_reorder_cmd_data,
+	.setting_dma		= loongson2_mmc_set_internal_dma,
+	.prepare_dma		= loongson2_mmc_prepare_internal_dma,
+	.release_dma		= loongson2_mmc_release_internal_dma,
+};
+
 static int loongson2_mmc_resource_request(struct platform_device *pdev,
 					  struct loongson2_mmc_host *host)
 {
@@ -593,7 +737,8 @@ static void loongson2_mmc_remove(struct platform_device *pdev)
 static const struct of_device_id loongson2_mmc_of_ids[] = {
 	{ .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata },
 	{ .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata },
-	{ },
+	{ .compatible = "loongson,ls2k2000-mmc", .data = &ls2k2000_mmc_pdata },
+	{},
 };
 MODULE_DEVICE_TABLE(of, loongson2_mmc_of_ids);
 
diff --git a/drivers/mmc/host/loongson2-mmc.h b/drivers/mmc/host/loongson2-mmc.h
index 4d8ada650350..dfd523fea081 100644
--- a/drivers/mmc/host/loongson2-mmc.h
+++ b/drivers/mmc/host/loongson2-mmc.h
@@ -22,6 +22,18 @@
 #define LOONGSON2_MMC_REG_DATA		0x40 /* Data Register */
 #define LOONGSON2_MMC_REG_IEN		0x64 /* Interrupt Enable Register */
 
+/* EMMC DLL Mode Registers */
+#define LOONGSON2_MMC_REG_DLLVAL		0xf0
+#define LOONGSON2_MMC_REG_DLLCTL		0xf4
+#define LOONGSON2_MMC_REG_DELAY		0xf8
+#define LOONGSON2_MMC_REG_SEL		0xfc
+
+/* Exclusive DMA R/W Registers */
+#define LOONGSON2_MMC_REG_WDMA_LO	0x400
+#define LOONGSON2_MMC_REG_WDMA_HI	0x404
+#define LOONGSON2_MMC_REG_RDMA_LO	0x800
+#define LOONGSON2_MMC_REG_RDMA_HI	0x804
+
 /* Bitfields of control register */
 #define LOONGSON2_MMC_CTL_ENCLK		BIT(0)
 #define LOONGSON2_MMC_CTL_RESET		BIT(8)
@@ -86,6 +98,8 @@
 #define LOONGSON2_MMC_DSTS_RESUME	BIT(15)
 #define LOONGSON2_MMC_DSTS_SUSPEND	BIT(16)
 
+#define LOONGSON2_MMC_FSTS_TXFULL	BIT(11)
+
 /* Bitfields of interrupt register */
 #define LOONGSON2_MMC_INT_DFIN		BIT(0)
 #define LOONGSON2_MMC_INT_DTIMEOUT	BIT(1)
@@ -113,6 +127,37 @@
 #define LOONGSON2_MMC_IEN_ALL		GENMASK(9, 0)
 #define LOONGSON2_MMC_INT_CLEAR		GENMASK(9, 0)
 
+#define LOONGSON2_MMC_DLLVAL_DONE	BIT(8)
+
+#define LOONGSON2_MMC_DLLCTL_TIME	GENMASK(7, 0)
+#define LOONGSON2_MMC_DLLCTL_INCRE	GENMASK(15, 8)
+#define LOONGSON2_MMC_DLLCTL_START	GENMASK(23, 16)
+#define LOONGSON2_MMC_DLLCTL_CLK_MODE	BIT(24)
+#define LOONGSON2_MMC_DLLCTL_START_BIT	BIT(25)
+#define LOONGSON2_MMC_DLLCTL_TIME_BYPASS	GENMASK(29, 26)
+
+#define LOONGSON2_MMC_DELAY_PAD		GENMASK(7, 0)
+#define LOONGSON2_MMC_DELAY_RD		GENMASK(15, 8)
+
+#define LOONGSON2_MMC_SEL_DATA		BIT(0)	/* 0: SDR, 1: DDR */
+#define LOONGSON2_MMC_SEL_BUS		BIT(0)	/* 0: EMMC, 1: SDIO */
+
+/* Bitfields in Global Configuration Register */
+#define LOONGSON2_MMC_DMA_64BIT_EN	BIT(0) /* 1: 64 bit support */
+#define LOONGSON2_MMC_DMA_UNCOHERENT_EN	BIT(1) /* 0: cache, 1: uncache */
+#define LOONGSON2_MMC_DMA_ASK_VALID	BIT(2)
+#define LOONGSON2_MMC_DMA_START		BIT(3) /* DMA start operation */
+#define LOONGSON2_MMC_DMA_STOP		BIT(4) /* DMA stop operation */
+#define LOONGSON2_MMC_DMA_CONFIG_MASK	GENMASK_ULL(4, 0) /* DMA controller config bits mask */
+
+/* Bitfields in ndesc_addr field of HW descriptor */
+#define LOONGSON2_MMC_DMA_DESC_EN	BIT(0) /*1: The next descriptor is valid */
+#define LOONGSON2_MMC_DMA_DESC_ADDR_LOW	GENMASK(31, 1)
+
+/* Bitfields in cmd field of HW descriptor */
+#define LOONGSON2_MMC_DMA_INT		BIT(1) /* Enable DMA interrupts */
+#define LOONGSON2_MMC_DMA_DATA_DIR	BIT(12) /* 1: write to device, 0: read from device */
+
 /* Loongson-2K1000 SDIO2 DMA routing register */
 #define LS2K1000_SDIO_DMA_MASK		GENMASK(17, 15)
 #define LS2K1000_DMA0_CONF		0x0
@@ -158,6 +203,8 @@ struct loongson2_mmc_host {
 	struct resource *res;
 	struct clk *clk;
 	u64 rate;
+	void *sg_cpu;
+	dma_addr_t sg_dma;
 	int dma_complete;
 	struct dma_chan *chan;
 	int cmd_is_stop;
-- 
2.47.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 1/4] dt-bindings: mmc: Add Loongson-2K SD/SDIO/eMMC controller binding
  2025-04-10  8:40 ` [PATCH v1 1/4] dt-bindings: mmc: Add Loongson-2K SD/SDIO/eMMC controller binding Binbin Zhou
@ 2025-04-11 17:03   ` Rob Herring (Arm)
  0 siblings, 0 replies; 10+ messages in thread
From: Rob Herring (Arm) @ 2025-04-11 17:03 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Rob Herring, linux-mmc, Krzysztof Kozlowski, loongarch,
	Xuerui Wang, Huacai Chen, Conor Dooley, Huacai Chen, Binbin Zhou,
	Ulf Hansson, devicetree


On Thu, 10 Apr 2025 16:40:35 +0800, Binbin Zhou wrote:
> Add the Loongson-2K SoC's SD/SDIO/eMMC controller binding with DT schema
> format using json-schema.
> 
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---
>  .../bindings/mmc/loongson,ls2k-mmc.yaml       | 67 +++++++++++++++++++
>  MAINTAINERS                                   |  6 ++
>  2 files changed, 73 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
> 

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 3/4] dt-bindings: mmc: loongson,ls2k-mmc: Add compatible for Loongson-2K2000
  2025-04-10  8:40 ` [PATCH v1 3/4] dt-bindings: mmc: loongson,ls2k-mmc: Add compatible for Loongson-2K2000 Binbin Zhou
@ 2025-04-11 17:05   ` Rob Herring
  2025-04-14  1:31     ` Binbin Zhou
  0 siblings, 1 reply; 10+ messages in thread
From: Rob Herring @ 2025-04-11 17:05 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Binbin Zhou, Huacai Chen, Krzysztof Kozlowski, Conor Dooley,
	Ulf Hansson, Huacai Chen, Xuerui Wang, loongarch, devicetree,
	linux-mmc

On Thu, Apr 10, 2025 at 04:40:37PM +0800, Binbin Zhou wrote:
> Add the devicetree compatible for Loongson-2K2000 EMMC/SD/SDIO controller.
> 
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---
>  .../bindings/mmc/loongson,ls2k-mmc.yaml       | 47 ++++++++++++++++++-
>  1 file changed, 45 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml b/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
> index f3e94f5f3a35..24d217a9bbe6 100644
> --- a/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
> +++ b/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
> @@ -9,6 +9,9 @@ title: The SD/SDIO/eMMC host controller for Loongson-2K family SoCs
>  description:
>    The MMC host controller on the Loongson-2K0500/2K1000 (using an externally
>    shared apbdma controller) provides the SD and SDIO device interfaces.
> +  The two MMC host controllers on the Loongson-2K2000 are similar,
> +  except that they use internal exclusive DMA. one controller provides
> +  the eMMC interface and the other provides the SD/SDIO interface.
>  
>  maintainers:
>    - Binbin Zhou <zhoubinbin@loongson.cn>
> @@ -21,8 +24,10 @@ properties:
>      enum:
>        - loongson,ls2k0500-mmc
>        - loongson,ls2k1000-mmc
> +      - loongson,ls2k2000-mmc
>  
>    reg:
> +    minItems: 1
>      maxItems: 2

Missed this in the first patch, but you need to define what each entry 
is.

>  
>    interrupts:
> @@ -42,11 +47,31 @@ required:
>    - reg
>    - interrupts
>    - clocks
> -  - dmas
> -  - dma-names
>  
>  unevaluatedProperties: false
>  
> +if:
> +  properties:
> +    compatible:
> +      contains:
> +        enum:
> +          - loongson,ls2k0500-mmc
> +          - loongson,ls2k1000-mmc
> +
> +then:
> +  properties:
> +    reg:
> +      minItems: 2
> +
> +  required:
> +    - dmas
> +    - dma-names
> +
> +else:
> +  properties:
> +    reg:
> +      maxItems: 1
> +
>  examples:
>    - |
>      #include <dt-bindings/gpio/gpio.h>
> @@ -65,3 +90,21 @@ examples:
>          bus-width = <4>;
>          cd-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>;
>      };
> +
> +  - |
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +    #include <dt-bindings/clock/loongson,ls2k-clk.h>
> +
> +    mmc@79990000 {
> +        compatible = "loongson,ls2k2000-mmc";
> +        reg = <0x79990000 0x1000>;
> +        interrupt-parent = <&pic>;
> +        interrupts = <51 IRQ_TYPE_LEVEL_HIGH>;
> +        clocks = <&clk LOONGSON2_EMMC_CLK>;
> +        bus-width = <8>;
> +        non-removable;
> +        cap-mmc-highspeed;
> +        mmc-hs200-1_8v;
> +        no-sd;
> +        no-sdio;
> +    };
> -- 
> 2.47.1
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 3/4] dt-bindings: mmc: loongson,ls2k-mmc: Add compatible for Loongson-2K2000
  2025-04-11 17:05   ` Rob Herring
@ 2025-04-14  1:31     ` Binbin Zhou
  0 siblings, 0 replies; 10+ messages in thread
From: Binbin Zhou @ 2025-04-14  1:31 UTC (permalink / raw)
  To: Rob Herring
  Cc: Binbin Zhou, Huacai Chen, Krzysztof Kozlowski, Conor Dooley,
	Ulf Hansson, Huacai Chen, Xuerui Wang, loongarch, devicetree,
	linux-mmc

Hi Rob:

Thanks for your reply.


On Sat, Apr 12, 2025 at 1:05 AM Rob Herring <robh@kernel.org> wrote:
>
> On Thu, Apr 10, 2025 at 04:40:37PM +0800, Binbin Zhou wrote:
> > Add the devicetree compatible for Loongson-2K2000 EMMC/SD/SDIO controller.
> >
> > Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> > ---
> >  .../bindings/mmc/loongson,ls2k-mmc.yaml       | 47 ++++++++++++++++++-
> >  1 file changed, 45 insertions(+), 2 deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml b/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
> > index f3e94f5f3a35..24d217a9bbe6 100644
> > --- a/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
> > +++ b/Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
> > @@ -9,6 +9,9 @@ title: The SD/SDIO/eMMC host controller for Loongson-2K family SoCs
> >  description:
> >    The MMC host controller on the Loongson-2K0500/2K1000 (using an externally
> >    shared apbdma controller) provides the SD and SDIO device interfaces.
> > +  The two MMC host controllers on the Loongson-2K2000 are similar,
> > +  except that they use internal exclusive DMA. one controller provides
> > +  the eMMC interface and the other provides the SD/SDIO interface.
> >
> >  maintainers:
> >    - Binbin Zhou <zhoubinbin@loongson.cn>
> > @@ -21,8 +24,10 @@ properties:
> >      enum:
> >        - loongson,ls2k0500-mmc
> >        - loongson,ls2k1000-mmc
> > +      - loongson,ls2k2000-mmc
> >
> >    reg:
> > +    minItems: 1
> >      maxItems: 2
>
> Missed this in the first patch, but you need to define what each entry
> is.
Are you thinking that “minItems” should be added in patch-1?
I'm not sure if I'm right, but for patch-1 (Loongson-2K1000), his reg
count must be 2, so it doesn't need the “ minItems: 1” limitation,
whereas Loongson-2K2000 needs this limitation, and so it was put in
this patch.

Of course, for the reg description, it is true that I missed it, and I
will add it in patch-1.

>
> >
> >    interrupts:
> > @@ -42,11 +47,31 @@ required:
> >    - reg
> >    - interrupts
> >    - clocks
> > -  - dmas
> > -  - dma-names
> >
> >  unevaluatedProperties: false
> >
> > +if:
> > +  properties:
> > +    compatible:
> > +      contains:
> > +        enum:
> > +          - loongson,ls2k0500-mmc
> > +          - loongson,ls2k1000-mmc
> > +
> > +then:
> > +  properties:
> > +    reg:
> > +      minItems: 2
> > +
> > +  required:
> > +    - dmas
> > +    - dma-names
> > +
> > +else:
> > +  properties:
> > +    reg:
> > +      maxItems: 1
> > +
> >  examples:
> >    - |
> >      #include <dt-bindings/gpio/gpio.h>
> > @@ -65,3 +90,21 @@ examples:
> >          bus-width = <4>;
> >          cd-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>;
> >      };
> > +
> > +  - |
> > +    #include <dt-bindings/interrupt-controller/irq.h>
> > +    #include <dt-bindings/clock/loongson,ls2k-clk.h>
> > +
> > +    mmc@79990000 {
> > +        compatible = "loongson,ls2k2000-mmc";
> > +        reg = <0x79990000 0x1000>;
> > +        interrupt-parent = <&pic>;
> > +        interrupts = <51 IRQ_TYPE_LEVEL_HIGH>;
> > +        clocks = <&clk LOONGSON2_EMMC_CLK>;
> > +        bus-width = <8>;
> > +        non-removable;
> > +        cap-mmc-highspeed;
> > +        mmc-hs200-1_8v;
> > +        no-sd;
> > +        no-sdio;
> > +    };
> > --
> > 2.47.1
> >

--
Thanks.
Binbin

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 2/4] mmc: loongson2: Add Loongson-2K SD/SDIO controller driver
  2025-04-10  8:40 ` [PATCH v1 2/4] mmc: loongson2: Add Loongson-2K SD/SDIO controller driver Binbin Zhou
@ 2025-04-28 15:09   ` Ulf Hansson
  2025-05-06  2:50     ` Binbin Zhou
  0 siblings, 1 reply; 10+ messages in thread
From: Ulf Hansson @ 2025-04-28 15:09 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Huacai Chen, Xuerui Wang, loongarch, devicetree,
	linux-mmc

On Thu, 10 Apr 2025 at 10:41, Binbin Zhou <zhoubinbin@loongson.cn> wrote:
>
> The MMC controllers on the Loongson-2K series CPUs are similar,
> except for the interface characteristics and the use of DMA controllers.
>
> This patch describes the MMC controllers on the Loongson-2K0500/2K1000,
> with the distinguishing feature being the use of an externally shared
> APBDMA engine.
>
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>

Overall this looks good to me. A few things though, please have a look below.

> ---
>  MAINTAINERS                      |   2 +
>  drivers/mmc/host/Kconfig         |  13 +
>  drivers/mmc/host/Makefile        |   1 +
>  drivers/mmc/host/loongson2-mmc.c | 636 +++++++++++++++++++++++++++++++
>  drivers/mmc/host/loongson2-mmc.h | 177 +++++++++
>  5 files changed, 829 insertions(+)
>  create mode 100644 drivers/mmc/host/loongson2-mmc.c
>  create mode 100644 drivers/mmc/host/loongson2-mmc.h

I think we can put all code in the c-file, no need for a separate header file.

>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 32c3733a764a..e218a3e204ef 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -13940,6 +13940,8 @@ M:      Binbin Zhou <zhoubinbin@loongson.cn>
>  L:     linux-mmc@vger.kernel.org
>  S:     Supported
>  F:     Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
> +F:     drivers/mmc/host/loongson2-mmc.c
> +F:     drivers/mmc/host/loongson2-mmc.h
>

[...]

> +static irqreturn_t loongson2_mmc_irq(int irq, void *dev_id)
> +{
> +       struct loongson2_mmc_host *host = dev_id;
> +       struct mmc_command *cmd;
> +       unsigned long iflags;
> +       u32 dsts, imsk;
> +
> +       regmap_read(host->regmap, LOONGSON2_MMC_REG_INT, &imsk);
> +       regmap_read(host->regmap, LOONGSON2_MMC_REG_DSTS, &dsts);
> +
> +       if ((dsts & LOONGSON2_MMC_DSTS_IRQ) &&
> +           (imsk & LOONGSON2_MMC_INT_SDIOIRQ)) {
> +               regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_INT,
> +                                  LOONGSON2_MMC_INT_SDIOIRQ,
> +                                  LOONGSON2_MMC_INT_SDIOIRQ);
> +
> +               mmc_signal_sdio_irq(host->mmc);

mmc_signal_sdio_irq() is the legacy interface for managing SDIO irqs.

Please convert to use sdio_signal_irq() instead, along with the
->ack_sdio_irq() callback.

> +               return IRQ_HANDLED;
> +       }
> +
> +       spin_lock_irqsave(&host->lock, iflags);
> +
> +       if (host->state == STATE_NONE || host->state == STATE_FINALIZE ||
> +           !host->mrq)
> +               goto irq_out;
> +
> +       cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;
> +       if (!cmd)
> +               goto irq_out;
> +
> +       cmd->error = 0;
> +
> +       if (imsk & LOONGSON2_MMC_INT_CTIMEOUT) {
> +               cmd->error = -ETIMEDOUT;
> +               goto close_transfer;
> +       }
> +
> +       if (imsk & LOONGSON2_MMC_INT_CSENT) {
> +               if (host->state == STATE_RSPFIN || host->state == STATE_CMDSENT)
> +                       goto close_transfer;
> +
> +               if (host->state == STATE_XFERFINISH_RSPFIN)
> +                       host->state = STATE_XFERFINISH;
> +       }
> +
> +       if (!cmd->data)
> +               goto irq_out;
> +
> +       if (imsk & (LOONGSON2_MMC_INT_RXCRC | LOONGSON2_MMC_INT_TXCRC)) {
> +               cmd->data->error = -EILSEQ;
> +               goto close_transfer;
> +       }
> +
> +       if (imsk & LOONGSON2_MMC_INT_DTIMEOUT) {
> +               cmd->data->error = -ETIMEDOUT;
> +               goto close_transfer;
> +       }
> +
> +       if (imsk & LOONGSON2_MMC_INT_DFIN) {
> +               if (host->state == STATE_XFERFINISH) {
> +                       host->dma_complete = 1;
> +                       goto close_transfer;
> +               }
> +
> +               if (host->state == STATE_XFERFINISH_RSPFIN)
> +                       host->state = STATE_RSPFIN;
> +       }
> +
> +irq_out:
> +       regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk);
> +       spin_unlock_irqrestore(&host->lock, iflags);
> +       return IRQ_HANDLED;
> +
> +close_transfer:
> +       host->state = STATE_FINALIZE;
> +       host->pdata->reorder_cmd_data(host, cmd);
> +       regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk);
> +       spin_unlock_irqrestore(&host->lock, iflags);
> +       return IRQ_WAKE_THREAD;
> +}

[...]

> +
> +static int loongson2_mmc_resource_request(struct platform_device *pdev,
> +                                         struct loongson2_mmc_host *host)
> +{
> +       struct device *dev = &pdev->dev;
> +       void __iomem *base;
> +       int ret, irq;
> +
> +       base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->res);
> +       if (IS_ERR(base))
> +               return PTR_ERR(base);
> +
> +       host->regmap = devm_regmap_init_mmio(dev, base, &host->pdata->regmap_config);
> +       if (IS_ERR(host->regmap))
> +               return PTR_ERR(host->regmap);
> +
> +       host->clk = devm_clk_get_optional_enabled(dev, NULL);
> +       if (IS_ERR(host->clk))
> +               return PTR_ERR(host->clk);
> +
> +       if (host->clk) {
> +               ret = devm_clk_rate_exclusive_get(dev, host->clk);
> +               if (ret)
> +                       return PTR_ERR(host->clk);
> +
> +               host->rate = clk_get_rate(host->clk);
> +       } else {
> +               /* For ACPI, we get rate through clock-frequency attribute */
> +               device_property_read_u64(dev, "clock-frequency", &host->rate);

Is ACPI supported too?

> +       }
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0)
> +               return irq;
> +
> +       ret = devm_request_threaded_irq(dev, irq, loongson2_mmc_irq,
> +                                       loongson2_mmc_irq_worker,
> +                                       IRQF_ONESHOT, "loongson2-mmc", host);
> +       if (ret)
> +               return ret;
> +
> +       ret = host->pdata->setting_dma(host, pdev);
> +       if (ret)
> +               return ret;
> +
> +       return dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> +}
> +
> +static int loongson2_mmc_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct loongson2_mmc_host *host;
> +       struct mmc_host *mmc;
> +       int ret;
> +
> +       mmc = mmc_alloc_host(sizeof(*host), dev);

We have devm_mmc_alloc_host() too, perhaps worth to use it to simplify
the code a bit.

> +       if (!mmc) {
> +               dev_err(dev, "Failed to alloc mmc host\n");
> +               return -ENOMEM;
> +       }
> +
> +       platform_set_drvdata(pdev, mmc);
> +
> +       host = mmc_priv(mmc);
> +       host->mmc = mmc;
> +       host->state = STATE_NONE;
> +       spin_lock_init(&host->lock);
> +
> +       host->pdata = device_get_match_data(dev);
> +       if (!host->pdata) {
> +               dev_err(dev, "Failed to get match data\n");
> +               ret = -EINVAL;
> +               goto free_host;
> +       }
> +
> +       ret = loongson2_mmc_resource_request(pdev, host);
> +       if (ret) {
> +               dev_err(dev, "Failed to request resource\n");
> +               goto free_host;
> +       }
> +
> +       mmc->ops = &loongson2_mmc_ops;
> +       mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;

How is power to the card controlled for these platforms?

The preferred way is to use the regulator API to manage this - and the
mmc core also provides a couple of helper functions for this, like
mmc_regulator_get_supply(), for example.

> +       mmc->f_min = DIV_ROUND_UP(host->rate, 256);
> +       mmc->f_max = host->rate;
> +       mmc->max_blk_count = 4095;
> +       mmc->max_blk_size = 4095;
> +       mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size;
> +       mmc->max_segs = 1;
> +       mmc->max_seg_size = mmc->max_req_size;
> +
> +       ret = mmc_of_parse(mmc);
> +       if (ret) {
> +               dev_err(dev, "Failed to parse device node\n");
> +               goto free_dma;
> +       }
> +
> +       ret = mmc_add_host(mmc);
> +       if (ret) {
> +               dev_err(dev, "Failed to add mmc host.\n");
> +               goto free_dma;
> +       }
> +
> +       return 0;
> +
> +free_dma:
> +       host->pdata->release_dma(host, dev);
> +free_host:
> +       mmc_free_host(mmc);
> +       return ret;
> +}
> +

[...]

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v1 2/4] mmc: loongson2: Add Loongson-2K SD/SDIO controller driver
  2025-04-28 15:09   ` Ulf Hansson
@ 2025-05-06  2:50     ` Binbin Zhou
  0 siblings, 0 replies; 10+ messages in thread
From: Binbin Zhou @ 2025-05-06  2:50 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Huacai Chen, Xuerui Wang, loongarch, devicetree,
	linux-mmc

Hi Ulf:

Thanks for your review and sorry for the late reply.

On Mon, Apr 28, 2025 at 11:10 PM Ulf Hansson <ulf.hansson@linaro.org> wrote:
>
> On Thu, 10 Apr 2025 at 10:41, Binbin Zhou <zhoubinbin@loongson.cn> wrote:
> >
> > The MMC controllers on the Loongson-2K series CPUs are similar,
> > except for the interface characteristics and the use of DMA controllers.
> >
> > This patch describes the MMC controllers on the Loongson-2K0500/2K1000,
> > with the distinguishing feature being the use of an externally shared
> > APBDMA engine.
> >
> > Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
>
> Overall this looks good to me. A few things though, please have a look below.
>
> > ---
> >  MAINTAINERS                      |   2 +
> >  drivers/mmc/host/Kconfig         |  13 +
> >  drivers/mmc/host/Makefile        |   1 +
> >  drivers/mmc/host/loongson2-mmc.c | 636 +++++++++++++++++++++++++++++++
> >  drivers/mmc/host/loongson2-mmc.h | 177 +++++++++
> >  5 files changed, 829 insertions(+)
> >  create mode 100644 drivers/mmc/host/loongson2-mmc.c
> >  create mode 100644 drivers/mmc/host/loongson2-mmc.h
>
> I think we can put all code in the c-file, no need for a separate header file.

OK, I will do it.
>
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 32c3733a764a..e218a3e204ef 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -13940,6 +13940,8 @@ M:      Binbin Zhou <zhoubinbin@loongson.cn>
> >  L:     linux-mmc@vger.kernel.org
> >  S:     Supported
> >  F:     Documentation/devicetree/bindings/mmc/loongson,ls2k-mmc.yaml
> > +F:     drivers/mmc/host/loongson2-mmc.c
> > +F:     drivers/mmc/host/loongson2-mmc.h
> >
>
> [...]
>
> > +static irqreturn_t loongson2_mmc_irq(int irq, void *dev_id)
> > +{
> > +       struct loongson2_mmc_host *host = dev_id;
> > +       struct mmc_command *cmd;
> > +       unsigned long iflags;
> > +       u32 dsts, imsk;
> > +
> > +       regmap_read(host->regmap, LOONGSON2_MMC_REG_INT, &imsk);
> > +       regmap_read(host->regmap, LOONGSON2_MMC_REG_DSTS, &dsts);
> > +
> > +       if ((dsts & LOONGSON2_MMC_DSTS_IRQ) &&
> > +           (imsk & LOONGSON2_MMC_INT_SDIOIRQ)) {
> > +               regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_INT,
> > +                                  LOONGSON2_MMC_INT_SDIOIRQ,
> > +                                  LOONGSON2_MMC_INT_SDIOIRQ);
> > +
> > +               mmc_signal_sdio_irq(host->mmc);
>
> mmc_signal_sdio_irq() is the legacy interface for managing SDIO irqs.
>
> Please convert to use sdio_signal_irq() instead, along with the
> ->ack_sdio_irq() callback.

OK..
>
> > +               return IRQ_HANDLED;
> > +       }
> > +
> > +       spin_lock_irqsave(&host->lock, iflags);
> > +
> > +       if (host->state == STATE_NONE || host->state == STATE_FINALIZE ||
> > +           !host->mrq)
> > +               goto irq_out;
> > +
> > +       cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;
> > +       if (!cmd)
> > +               goto irq_out;
> > +
> > +       cmd->error = 0;
> > +
> > +       if (imsk & LOONGSON2_MMC_INT_CTIMEOUT) {
> > +               cmd->error = -ETIMEDOUT;
> > +               goto close_transfer;
> > +       }
> > +
> > +       if (imsk & LOONGSON2_MMC_INT_CSENT) {
> > +               if (host->state == STATE_RSPFIN || host->state == STATE_CMDSENT)
> > +                       goto close_transfer;
> > +
> > +               if (host->state == STATE_XFERFINISH_RSPFIN)
> > +                       host->state = STATE_XFERFINISH;
> > +       }
> > +
> > +       if (!cmd->data)
> > +               goto irq_out;
> > +
> > +       if (imsk & (LOONGSON2_MMC_INT_RXCRC | LOONGSON2_MMC_INT_TXCRC)) {
> > +               cmd->data->error = -EILSEQ;
> > +               goto close_transfer;
> > +       }
> > +
> > +       if (imsk & LOONGSON2_MMC_INT_DTIMEOUT) {
> > +               cmd->data->error = -ETIMEDOUT;
> > +               goto close_transfer;
> > +       }
> > +
> > +       if (imsk & LOONGSON2_MMC_INT_DFIN) {
> > +               if (host->state == STATE_XFERFINISH) {
> > +                       host->dma_complete = 1;
> > +                       goto close_transfer;
> > +               }
> > +
> > +               if (host->state == STATE_XFERFINISH_RSPFIN)
> > +                       host->state = STATE_RSPFIN;
> > +       }
> > +
> > +irq_out:
> > +       regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk);
> > +       spin_unlock_irqrestore(&host->lock, iflags);
> > +       return IRQ_HANDLED;
> > +
> > +close_transfer:
> > +       host->state = STATE_FINALIZE;
> > +       host->pdata->reorder_cmd_data(host, cmd);
> > +       regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk);
> > +       spin_unlock_irqrestore(&host->lock, iflags);
> > +       return IRQ_WAKE_THREAD;
> > +}
>
> [...]
>
> > +
> > +static int loongson2_mmc_resource_request(struct platform_device *pdev,
> > +                                         struct loongson2_mmc_host *host)
> > +{
> > +       struct device *dev = &pdev->dev;
> > +       void __iomem *base;
> > +       int ret, irq;
> > +
> > +       base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->res);
> > +       if (IS_ERR(base))
> > +               return PTR_ERR(base);
> > +
> > +       host->regmap = devm_regmap_init_mmio(dev, base, &host->pdata->regmap_config);
> > +       if (IS_ERR(host->regmap))
> > +               return PTR_ERR(host->regmap);
> > +
> > +       host->clk = devm_clk_get_optional_enabled(dev, NULL);
> > +       if (IS_ERR(host->clk))
> > +               return PTR_ERR(host->clk);
> > +
> > +       if (host->clk) {
> > +               ret = devm_clk_rate_exclusive_get(dev, host->clk);
> > +               if (ret)
> > +                       return PTR_ERR(host->clk);
> > +
> > +               host->rate = clk_get_rate(host->clk);
> > +       } else {
> > +               /* For ACPI, we get rate through clock-frequency attribute */
> > +               device_property_read_u64(dev, "clock-frequency", &host->rate);
>
> Is ACPI supported too?
Yes, like on the Loongson-2K2000 platform, we support ACPI and FDT.
For ACPI, we also match by compatible strings, so the interface is the
same as for FDT, except that the in-node attribute descriptions are
not exactly the same, e.g. clock-frequency.

>
> > +       }
> > +
> > +       irq = platform_get_irq(pdev, 0);
> > +       if (irq < 0)
> > +               return irq;
> > +
> > +       ret = devm_request_threaded_irq(dev, irq, loongson2_mmc_irq,
> > +                                       loongson2_mmc_irq_worker,
> > +                                       IRQF_ONESHOT, "loongson2-mmc", host);
> > +       if (ret)
> > +               return ret;
> > +
> > +       ret = host->pdata->setting_dma(host, pdev);
> > +       if (ret)
> > +               return ret;
> > +
> > +       return dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
> > +}
> > +
> > +static int loongson2_mmc_probe(struct platform_device *pdev)
> > +{
> > +       struct device *dev = &pdev->dev;
> > +       struct loongson2_mmc_host *host;
> > +       struct mmc_host *mmc;
> > +       int ret;
> > +
> > +       mmc = mmc_alloc_host(sizeof(*host), dev);
>
> We have devm_mmc_alloc_host() too, perhaps worth to use it to simplify
> the code a bit.

This is good news and I will do it.
>
> > +       if (!mmc) {
> > +               dev_err(dev, "Failed to alloc mmc host\n");
> > +               return -ENOMEM;
> > +       }
> > +
> > +       platform_set_drvdata(pdev, mmc);
> > +
> > +       host = mmc_priv(mmc);
> > +       host->mmc = mmc;
> > +       host->state = STATE_NONE;
> > +       spin_lock_init(&host->lock);
> > +
> > +       host->pdata = device_get_match_data(dev);
> > +       if (!host->pdata) {
> > +               dev_err(dev, "Failed to get match data\n");
> > +               ret = -EINVAL;
> > +               goto free_host;
> > +       }
> > +
> > +       ret = loongson2_mmc_resource_request(pdev, host);
> > +       if (ret) {
> > +               dev_err(dev, "Failed to request resource\n");
> > +               goto free_host;
> > +       }
> > +
> > +       mmc->ops = &loongson2_mmc_ops;
> > +       mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
>
> How is power to the card controlled for these platforms?
>
> The preferred way is to use the regulator API to manage this - and the
> mmc core also provides a couple of helper functions for this, like
> mmc_regulator_get_supply(), for example.

Emm, I will try to use this API.
>
> > +       mmc->f_min = DIV_ROUND_UP(host->rate, 256);
> > +       mmc->f_max = host->rate;
> > +       mmc->max_blk_count = 4095;
> > +       mmc->max_blk_size = 4095;
> > +       mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size;
> > +       mmc->max_segs = 1;
> > +       mmc->max_seg_size = mmc->max_req_size;
> > +
> > +       ret = mmc_of_parse(mmc);
> > +       if (ret) {
> > +               dev_err(dev, "Failed to parse device node\n");
> > +               goto free_dma;
> > +       }
> > +
> > +       ret = mmc_add_host(mmc);
> > +       if (ret) {
> > +               dev_err(dev, "Failed to add mmc host.\n");
> > +               goto free_dma;
> > +       }
> > +
> > +       return 0;
> > +
> > +free_dma:
> > +       host->pdata->release_dma(host, dev);
> > +free_host:
> > +       mmc_free_host(mmc);
> > +       return ret;
> > +}
> > +
>
> [...]
>
> Kind regards
> Uffe

-- 
Thanks.
Binbin

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-05-06  2:50 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-10  8:40 [PATCH v1 0/4] LoongArch: Introduce the Loongson-2K MMC host controller driver Binbin Zhou
2025-04-10  8:40 ` [PATCH v1 1/4] dt-bindings: mmc: Add Loongson-2K SD/SDIO/eMMC controller binding Binbin Zhou
2025-04-11 17:03   ` Rob Herring (Arm)
2025-04-10  8:40 ` [PATCH v1 2/4] mmc: loongson2: Add Loongson-2K SD/SDIO controller driver Binbin Zhou
2025-04-28 15:09   ` Ulf Hansson
2025-05-06  2:50     ` Binbin Zhou
2025-04-10  8:40 ` [PATCH v1 3/4] dt-bindings: mmc: loongson,ls2k-mmc: Add compatible for Loongson-2K2000 Binbin Zhou
2025-04-11 17:05   ` Rob Herring
2025-04-14  1:31     ` Binbin Zhou
2025-04-10  8:41 ` [PATCH v1 4/4] mmc: loongson2: Add Loongson-2K2000 SD/SDIO controller driver Binbin Zhou

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.