Skip to contentSkip to conversation

Rpi: Add support for second sd host controller

PATCH Superseded Alexander Graf 3 participants 6 messages Jan 17, 2018 – Jan 23, 2018

Subsystems (7)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover Rpi: Add support for second sd host controller

The Raspberry Pi (bcm283x SoC) family contains 2 IP blocks to drive SD
peripherals: A pretty standard SDHCI one called iProc and a home grown one called "sdhost".

When driving U-Boot by the same device tree that we use for Linux, we end up in situations where only the "sdhost" device is available for use. In those situations, current U-Boot can simply not drive the SD card, because the device tree disables the SDHCI device.

This patch set also adds a full pinctrl driver for pinmuxing the SD devices properly to their respective pins. This is necessary because the RPi firmware doesn't mux the SD pins according to the device tree but instead expects the payload (usually Linux, U-Boot for us) to do that.

With this patch set, I can successfully use U-Boot on a RPi3 Compute Module with CONFIG_OF_BOARD=y and a device tree generated by the RPi firmware (plus a few overlays in config.txt for vc4, serial, etc).

v1 -> v2:

- Introduce real pinctrl driver
- Checkpatch fixes
- Remove hand written pinctrl support

Alexander Graf (2):
bcm283x: Add pinctrl driver
mmc: Add bcm2835 sdhost controller

 MAINTAINERS                                |   2 +
 arch/arm/mach-bcm283x/include/mach/gpio.h  |   2 -
 board/raspberrypi/rpi/rpi.c                |   5 +-
 configs/rpi_0_w_defconfig                  |   4 +
 configs/rpi_2_defconfig                    |   4 +
 configs/rpi_3_32b_defconfig                |   4 +
 configs/rpi_3_defconfig                    |   4 +
 configs/rpi_defconfig                      |   4 +
 drivers/gpio/bcm2835_gpio.c                |  29 +-
 drivers/mmc/Kconfig                        |  14 +
 drivers/mmc/Makefile                       |   1 +
 drivers/mmc/bcm2835_sdhost.c               | 994 +++++++++++++++++++++++++++++
 drivers/pinctrl/Kconfig                    |   1 +
 drivers/pinctrl/Makefile                   |   1 +
 drivers/pinctrl/broadcom/Kconfig           |   7 +
 drivers/pinctrl/broadcom/Makefile          |   7 +
 drivers/pinctrl/broadcom/pinctrl-bcm283x.c | 150 +++++
 17 files changed, 1210 insertions(+), 23 deletions(-)
 create mode 100644 drivers/mmc/bcm2835_sdhost.c
 create mode 100644 drivers/pinctrl/broadcom/Kconfig
 create mode 100644 drivers/pinctrl/broadcom/Makefile
 create mode 100644 drivers/pinctrl/broadcom/pinctrl-bcm283x.c
Signature · 1 line
2.12.3
#1 lore raw

PATCH v2 1/2 bcm283x: Add pinctrl driver

The bcm283x family of SoCs have a GPIO controller that also acts as pinctrl controller.

This patch introduces a new pinctrl driver that can actually properly mux devices into their device tree defined pin states and is now the primary owner of the gpio device. The previous GPIO driver gets moved into a subdevice of the pinctrl driver, bound to the same OF node.

That way whenever a device asks for pinctrl support, it gets it automatically from the pinctrl driver and GPIO support is still available in the normal command line phase.

---
 MAINTAINERS                                |   1 +
 arch/arm/mach-bcm283x/include/mach/gpio.h  |   2 -
 board/raspberrypi/rpi/rpi.c                |   5 +-
 configs/rpi_0_w_defconfig                  |   4 +
 configs/rpi_2_defconfig                    |   4 +
 configs/rpi_3_32b_defconfig                |   4 +
 configs/rpi_3_defconfig                    |   4 +
 configs/rpi_defconfig                      |   4 +
 drivers/gpio/bcm2835_gpio.c                |  29 ++----
 drivers/pinctrl/Kconfig                    |   1 +
 drivers/pinctrl/Makefile                   |   1 +
 drivers/pinctrl/broadcom/Kconfig           |   7 ++
 drivers/pinctrl/broadcom/Makefile          |   7 ++
 drivers/pinctrl/broadcom/pinctrl-bcm283x.c | 150 +++++++++++++++++++++++++++++
 14 files changed, 200 insertions(+), 23 deletions(-)
 create mode 100644 drivers/pinctrl/broadcom/Kconfig
 create mode 100644 drivers/pinctrl/broadcom/Makefile
 create mode 100644 drivers/pinctrl/broadcom/pinctrl-bcm283x.c
MAINTAINERS +1 -0
index 754db5553d..1f2545191b 100644
@@ -100,6 +100,7 @@ F:	drivers/mmc/bcm2835_sdhci.c
 F:	drivers/serial/serial_bcm283x_mu.c
 F:	drivers/video/bcm2835.c
 F:	include/dm/platform_data/serial_bcm283x_mu.h
+F:	drivers/pinctrl/broadcom/
 
 ARM FREESCALE IMX
 M:	Stefano Babic <[email protected]>
arch/arm/mach-bcm283x/include/mach/gpio.h +0 -2
index daaee52f81..7b4ddc9246 100644
@@ -61,6 +61,4 @@ struct bcm2835_gpio_platdata {
 	unsigned long base;
 };
 
-int bcm2835_gpio_get_func_id(struct udevice *dev, unsigned gpio);
-
 #endif /* _BCM2835_GPIO_H_ */
board/raspberrypi/rpi/rpi.c +3 -2
index 3b7a54f519..c8924d4362 100644
@@ -24,6 +24,7 @@
 #include <asm/armv8/mmu.h>
 #endif
 #include <watchdog.h>
+#include <dm/pinctrl.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -430,10 +431,10 @@ static bool rpi_is_serial_active(void)
 	 * out whether it is available is to check if the RX pin is muxed.
 	 */
 
-	if (uclass_first_device(UCLASS_GPIO, &dev) || !dev)
+	if (uclass_first_device(UCLASS_PINCTRL, &dev) || !dev)
 		return true;
 
-	if (bcm2835_gpio_get_func_id(dev, serial_gpio) != BCM2835_GPIO_ALT5)
+	if (pinctrl_get_gpio_mux(dev, 0, serial_gpio) != BCM2835_GPIO_ALT5)
 		return false;
 
 	return true;
configs/rpi_0_w_defconfig +4 -0
index 9a6d24be22..e8df255679 100644
@@ -25,3 +25,7 @@ CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_PHYS_TO_BUS=y
 CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+# CONFIG_PINCTRL_GENERIC is not set
+CONFIG_PINCTRL_BCM283X=y
configs/rpi_2_defconfig +4 -0
index c45ffb65af..b30e6e144c 100644
@@ -32,3 +32,7 @@ CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_PHYS_TO_BUS=y
 CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+# CONFIG_PINCTRL_GENERIC is not set
+CONFIG_PINCTRL_BCM283X=y
configs/rpi_3_32b_defconfig +4 -0
index f7aed35797..bb40644064 100644
@@ -34,3 +34,7 @@ CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_PHYS_TO_BUS=y
 CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+# CONFIG_PINCTRL_GENERIC is not set
+CONFIG_PINCTRL_BCM283X=y
configs/rpi_3_defconfig +4 -0
index 9416e3b8fe..8306bc251d 100644
@@ -34,3 +34,7 @@ CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_PHYS_TO_BUS=y
 CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+# CONFIG_PINCTRL_GENERIC is not set
+CONFIG_PINCTRL_BCM283X=y
configs/rpi_defconfig +4 -0
index 3bfa745c2e..a7a079ddab 100644
@@ -32,3 +32,7 @@ CONFIG_SYS_WHITE_ON_BLACK=y
 CONFIG_CONSOLE_SCROLL_LINES=10
 CONFIG_PHYS_TO_BUS=y
 CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_FULL=y
+# CONFIG_PINCTRL_GENERIC is not set
+CONFIG_PINCTRL_BCM283X=y
drivers/gpio/bcm2835_gpio.c +10 -19
index beaa21853a..d68f8df32d 100644
@@ -7,6 +7,7 @@
 
 #include <common.h>
 #include <dm.h>
+#include <dm/pinctrl.h>
 #include <errno.h>
 #include <asm/gpio.h>
 #include <asm/io.h>
@@ -14,6 +15,7 @@
 
 struct bcm2835_gpios {
 	struct bcm2835_gpio_regs *reg;
+	struct udevice *pinctrl;
 };
 
 static int bcm2835_gpio_direction_input(struct udevice *dev, unsigned gpio)
@@ -29,7 +31,7 @@ static int bcm2835_gpio_direction_input(struct udevice *dev, unsigned gpio)
 	return 0;
 }
 
-static int bcm2835_gpio_direction_output(struct udevice *dev, unsigned gpio,
+static int bcm2835_gpio_direction_output(struct udevice *dev, unsigned int gpio,
 					 int value)
 {
 	struct bcm2835_gpios *gpios = dev_get_priv(dev);
@@ -73,19 +75,12 @@ static int bcm2835_gpio_set_value(struct udevice *dev, unsigned gpio,
 	return 0;
 }
 
-int bcm2835_gpio_get_func_id(struct udevice *dev, unsigned gpio)
-{
-	struct bcm2835_gpios *gpios = dev_get_priv(dev);
-	u32 val;
-
-	val = readl(&gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]);
-
-	return (val >> BCM2835_GPIO_FSEL_SHIFT(gpio) & BCM2835_GPIO_FSEL_MASK);
-}
-
 static int bcm2835_gpio_get_function(struct udevice *dev, unsigned offset)
 {
-	int funcid = bcm2835_gpio_get_func_id(dev, offset);
+	struct bcm2835_gpios *priv = dev_get_priv(dev);
+	int funcid;
+
+	funcid = pinctrl_get_gpio_mux(priv->pinctrl, 0, offset);
 
 	switch (funcid) {
 	case BCM2835_GPIO_OUTPUT:
@@ -97,7 +92,6 @@ static int bcm2835_gpio_get_function(struct udevice *dev, unsigned offset)
 	}
 }
 
-
 static const struct dm_gpio_ops gpio_bcm2835_ops = {
 	.direction_input	= bcm2835_gpio_direction_input,
 	.direction_output	= bcm2835_gpio_direction_output,
@@ -116,15 +110,13 @@ static int bcm2835_gpio_probe(struct udevice *dev)
 	uc_priv->gpio_count = BCM2835_GPIO_COUNT;
 	gpios->reg = (struct bcm2835_gpio_regs *)plat->base;
 
+	/* We know we're spawned by the pinctrl driver */
+	gpios->pinctrl = dev->parent;
+
 	return 0;
 }
 
 #if CONFIG_IS_ENABLED(OF_CONTROL)
-static const struct udevice_id bcm2835_gpio_id[] = {
-	{.compatible = "brcm,bcm2835-gpio"},
-	{}
-};
-
 static int bcm2835_gpio_ofdata_to_platdata(struct udevice *dev)
 {
 	struct bcm2835_gpio_platdata *plat = dev_get_platdata(dev);
@@ -142,7 +134,6 @@ static int bcm2835_gpio_ofdata_to_platdata(struct udevice *dev)
 U_BOOT_DRIVER(gpio_bcm2835) = {
 	.name	= "gpio_bcm2835",
 	.id	= UCLASS_GPIO,
-	.of_match = of_match_ptr(bcm2835_gpio_id),
 	.ofdata_to_platdata = of_match_ptr(bcm2835_gpio_ofdata_to_platdata),
 	.platdata_auto_alloc_size = sizeof(struct bcm2835_gpio_platdata),
 	.ops	= &gpio_bcm2835_ops,
drivers/pinctrl/Kconfig +1 -0
index 7e8e4b0b27..0a4dd3c0cf 100644
@@ -306,5 +306,6 @@ source "drivers/pinctrl/renesas/Kconfig"
 source "drivers/pinctrl/uniphier/Kconfig"
 source "drivers/pinctrl/exynos/Kconfig"
 source "drivers/pinctrl/mvebu/Kconfig"
+source "drivers/pinctrl/broadcom/Kconfig"
 
 endmenu
drivers/pinctrl/Makefile +1 -0
index 8c04028dfb..c7135d29f8 100644
@@ -22,3 +22,4 @@ obj-$(CONFIG_ARCH_MVEBU)	+= mvebu/
 obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
 obj-$(CONFIG_PINCTRL_STI)	+= pinctrl-sti.o
 obj-$(CONFIG_PINCTRL_STM32)	+= pinctrl_stm32.o
+obj-y				+= broadcom/
drivers/pinctrl/broadcom/Kconfig new file +7 -0
new file mode 100644
index 0000000000..4056782213
@@ -0,0 +1,7 @@
+config PINCTRL_BCM283X
+	depends on ARCH_BCM283X && PINCTRL_FULL && OF_CONTROL
+	default y
+	bool "Broadcom 283x family pin control driver"
+	help
+	   Support pin multiplexing and pin configuration control on
+	   Broadcom's 283x family of SoCs.
drivers/pinctrl/broadcom/Makefile new file +7 -0
new file mode 100644
index 0000000000..2a1e550f88
@@ -0,0 +1,7 @@
+#
+# Copyright (C) 2018 Alexander Graf <[email protected]>
+#
+# SPDX-License-Identifier:	GPL-2.0
+# https://spdx.org/licenses
+
+obj-$(CONFIG_PINCTRL_BCM283X) += pinctrl-bcm283x.o
drivers/pinctrl/broadcom/pinctrl-bcm283x.c new file +150 -0

150 lines not on this page · raw message

Signature · 1 line
2.12.3
#2 lore raw

PATCH v2 1/2 bcm283x: Add pinctrl driver

Hi Alex,

Quoted 24 lines · Alexander Graf

The bcm283x family of SoCs have a GPIO controller that also acts as pinctrl controller.

This patch introduces a new pinctrl driver that can actually properly mux devices into their device tree defined pin states and is now the primary owner of the gpio device. The previous GPIO driver gets moved into a subdevice of the pinctrl driver, bound to the same OF node.

That way whenever a device asks for pinctrl support, it gets it automatically from the pinctrl driver and GPIO support is still available in the normal command line phase.

---
 MAINTAINERS                                |   1 +
 arch/arm/mach-bcm283x/include/mach/gpio.h  |   2 -
 board/raspberrypi/rpi/rpi.c                |   5 +-
 configs/rpi_0_w_defconfig                  |   4 +
 configs/rpi_2_defconfig                    |   4 +
 configs/rpi_3_32b_defconfig                |   4 +
 configs/rpi_3_defconfig                    |   4 +
 configs/rpi_defconfig                      |   4 +
 drivers/gpio/bcm2835_gpio.c                |  29 ++----
 drivers/pinctrl/Kconfig                    |   1 +
Quoted 8 lines
 drivers/pinctrl/Makefile                   |   1 +
 drivers/pinctrl/broadcom/Kconfig           |   7 ++
 drivers/pinctrl/broadcom/Makefile          |   7 ++
 drivers/pinctrl/broadcom/pinctrl-bcm283x.c | 150 +++++++++++++++++++++++++++++
 14 files changed, 200 insertions(+), 23 deletions(-)
 create mode 100644 drivers/pinctrl/broadcom/Kconfig
 create mode 100644 drivers/pinctrl/broadcom/Makefile
 create mode 100644 drivers/pinctrl/broadcom/pinctrl-bcm283x.c

That was quick!

[...]

Quoted 80 lines
drivers/pinctrl/broadcom/pinctrl-bcm283x.c new file +74 -0
new file mode 100644
index 0000000000..62c35698f5
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2018 Alexander Graf <[email protected]>
+ *
+ * Based on drivers/pinctrl/mvebu/pinctrl-mvebu.c and
+ *          drivers/gpio/bcm2835_gpio.c
+ *
+ * This driver gets instantiated by the GPIO driver, because both devices
+ * share the same device node.
+ *
+ * SPDX-License-Identifier:    GPL-2.0
+ * https://spdx.org/licenses
+ */
+
+#include <common.h>
+#include <config.h>
+#include <fdtdec.h>
+#include <errno.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <dm/root.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct bcm283x_pinctrl_priv {
+       u32 *base_reg;
+};
+
+#define MAX_PINS_PER_BANK 16
+
+static void bcm2835_gpio_set_func_id(struct udevice *dev, unsigned int gpio,
+                                    int func)
+{
+       struct bcm283x_pinctrl_priv *priv = dev_get_priv(dev);
+       int reg_offset;
+       int field_offset;
+
+       reg_offset = BCM2835_GPIO_FSEL_BANK(gpio);
+       field_offset = BCM2835_GPIO_FSEL_SHIFT(gpio);
+
+       clrsetbits_le32(&priv->base_reg[reg_offset],
+                       BCM2835_GPIO_FSEL_MASK << field_offset,
+                       (func & BCM2835_GPIO_FSEL_MASK) << field_offset);
+}
+
+static int bcm2835_gpio_get_func_id(struct udevice *dev, unsigned int gpio)
+{
+       struct bcm283x_pinctrl_priv *priv = dev_get_priv(dev);
+       u32 val;
+
+       val = readl(&priv->base_reg[BCM2835_GPIO_FSEL_BANK(gpio)]);
+
+       return (val >> BCM2835_GPIO_FSEL_SHIFT(gpio) & BCM2835_GPIO_FSEL_MASK);
+}
+
+/*
+ * bcm283x_pinctrl_set_state: configure pin functions.
+ * @dev: the pinctrl device to be configured.
+ * @config: the state to be configured.
+ * @return: 0 in success
+ */
+int bcm283x_pinctrl_set_state(struct udevice *dev, struct udevice *config)
+{
+       const void *blob = gd->fdt_blob;
+       int node = dev_of_offset(config);
+       u32 pin_arr[MAX_PINS_PER_BANK];
+       u32 function;
+       int i, pin_count;
+
+       pin_count = fdtdec_get_int_array_count(blob, node,

drivers/pinctrl/broadcom/pinctrl-bcm283x.c line 84

Quoted 10 lines
drivers/pinctrl/broadcom/pinctrl-bcm283x.c new file +10 -0
+                                              "brcm,pins",
+                                              pin_arr,
+                                              ARRAY_SIZE(pin_arr));
+       if (pin_count <= 0) {
+               debug("Failed reading pins array for pinconfig %s (%d)\n",
+                     config->name, pin_count);
+               return -EINVAL;
+       }
+
+       function = fdtdec_get_int(blob, node, "brcm,function", -1);

Please use dev_read_...() interface.

Quoted 39 lines
drivers/pinctrl/broadcom/pinctrl-bcm283x.c new file +39 -0
+       if (function < 0) {
+               debug("Failed reading function for pinconfig %s (%d)\n",
+                     config->name, function);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < pin_count; i++)
+               bcm2835_gpio_set_func_id(dev, pin_arr[i], function);
+
+       return 0;
+}
+
+static int bcm283x_pinctrl_get_gpio_mux(struct udevice *dev, int banknum,
+                                       int index)
+{
+       if (banknum != 0)
+               return -EINVAL;
+
+       return bcm2835_gpio_get_func_id(dev, index);
+}
+
+static const struct udevice_id bcm2835_pinctrl_id[] = {
+       {.compatible = "brcm,bcm2835-gpio"},
+       {}
+};
+
+int bcm283x_pinctl_probe(struct udevice *dev)
+{
+       struct bcm283x_pinctrl_priv *priv;
+       int ret;
+       struct udevice *pdev;
+
+       priv = dev_get_priv(dev);
+       if (!priv) {
+               debug("%s: Failed to get private\n", __func__);
+               return -EINVAL;
+       }
+
+       priv->base_reg = devfdt_get_addr_ptr(dev);

drivers/pinctrl/broadcom/pinctrl-bcm283x.c line 133

Quoted 10 lines
drivers/pinctrl/broadcom/pinctrl-bcm283x.c new file +10 -0
+       if (priv->base_reg == (void *)FDT_ADDR_T_NONE) {
+               debug("%s: Failed to get base address\n", __func__);
+               return -EINVAL;
+       }
+
+       /* Create GPIO device as well */
+       ret = device_bind(dev, lists_driver_lookup_name("gpio_bcm2835"),
+                         "gpio_bcm2835", NULL, dev_of_offset(dev), &pdev);
+       if (ret)
+               printf("Failed to bind GPIO driver\n");

Can you add a comment as to why this is non-fatal?

Quoted 18 lines
drivers/pinctrl/broadcom/pinctrl-bcm283x.c new file +17 -1
+
+       return 0;
+}
+
+static struct pinctrl_ops bcm283x_pinctrl_ops = {
+       .set_state      = bcm283x_pinctrl_set_state,
+       .get_gpio_mux   = bcm283x_pinctrl_get_gpio_mux,
+};
+
+U_BOOT_DRIVER(pinctrl_bcm283x) = {
+       .name           = "bcm283x_pinctrl",
+       .id             = UCLASS_PINCTRL,
+       .of_match       = of_match_ptr(bcm2835_pinctrl_id),
+       .priv_auto_alloc_size = sizeof(struct bcm283x_pinctrl_priv),
+       .ops            = &bcm283x_pinctrl_ops,
+       .probe          = bcm283x_pinctl_probe
+};
--
Quoted 2 lines

2.12.3

Regards,
Simon

#3 lore raw

PATCH v2 2/2 mmc: Add bcm2835 sdhost controller

In reply to Alexander Graf

The BCM2835 family of SoCs has 2 different SD controllers: One based on the SDHCI spec and a custom, home-grown one.

This patch implements a driver for the latter based on the Linux driver. This is needed so that we can make use of device trees that assume driver presence of both SD controllers.

---

v1 -> v2:

- Remove hand written pinctrl support
- Checkpatch fixes

---
 MAINTAINERS                  |   1 +
 drivers/mmc/Kconfig          |  14 +
 drivers/mmc/Makefile         |   1 +
 drivers/mmc/bcm2835_sdhost.c | 994 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1010 insertions(+)
 create mode 100644 drivers/mmc/bcm2835_sdhost.c
MAINTAINERS +1 -0
index 1f2545191b..728d38aebf 100644
@@ -97,6 +97,7 @@ S:	Orphaned (Since 2017-07)
 F:	arch/arm/mach-bcm283x/
 F:	drivers/gpio/bcm2835_gpio.c
 F:	drivers/mmc/bcm2835_sdhci.c
+F:	drivers/mmc/bcm2835_sdhost.c
 F:	drivers/serial/serial_bcm283x_mu.c
 F:	drivers/video/bcm2835.c
 F:	include/dm/platform_data/serial_bcm283x_mu.h
drivers/mmc/Kconfig +14 -0
index ab0627a8af..9b90db908b 100644
@@ -256,6 +256,20 @@ config MMC_UNIPHIER
 	  This selects support for the Matsushita SD/MMC Host Controller on
 	  SocioNext UniPhier and Renesas RCar SoCs.
 
+config MMC_BCM2835
+	bool "BCM2835 family custom SD/MMC Host Controller support"
+	depends on ARCH_BCM283X
+	depends on BLK && DM_MMC
+	depends on OF_CONTROL
+	default y
+	help
+	  This selects support for the custom SD host controller in the BCM2835
+	  family of devices.
+
+	  If you have a BCM2835 platform with SD or MMC devices, say Y here.
+
+	  If unsure, say N.
+
 config MMC_SANDBOX
 	bool "Sandbox MMC support"
 	depends on SANDBOX
drivers/mmc/Makefile +1 -0
index 64b6f21c61..42113e2603 100644
@@ -64,3 +64,4 @@ obj-$(CONFIG_MMC_SDHCI_ZYNQ)		+= zynq_sdhci.o
 
 obj-$(CONFIG_MMC_SUNXI)			+= sunxi_mmc.o
 obj-$(CONFIG_MMC_UNIPHIER)		+= uniphier-sd.o
+obj-$(CONFIG_MMC_BCM2835)		+= bcm2835_sdhost.o
drivers/mmc/bcm2835_sdhost.c new file +994 -0

994 lines not on this page · raw message

Signature · 1 line
2.12.3
#4 lore raw

PATCH v2 2/2 mmc: Add bcm2835 sdhost controller

Quoted 106 lines · Alexander Graf

The BCM2835 family of SoCs has 2 different SD controllers: One based on the SDHCI spec and a custom, home-grown one.

This patch implements a driver for the latter based on the Linux driver. This is needed so that we can make use of device trees that assume driver presence of both SD controllers.

---

v1 -> v2:

- Remove hand written pinctrl support
- Checkpatch fixes

---
 MAINTAINERS                  |   1 +
 drivers/mmc/Kconfig          |  14 +
 drivers/mmc/Makefile         |   1 +
 drivers/mmc/bcm2835_sdhost.c | 994 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1010 insertions(+)
 create mode 100644 drivers/mmc/bcm2835_sdhost.c
MAINTAINERS +1 -0
index 1f2545191b..728d38aebf 100644
@@ -97,6 +97,7 @@ S:	Orphaned (Since 2017-07)
 F:	arch/arm/mach-bcm283x/
 F:	drivers/gpio/bcm2835_gpio.c
 F:	drivers/mmc/bcm2835_sdhci.c
+F:	drivers/mmc/bcm2835_sdhost.c
 F:	drivers/serial/serial_bcm283x_mu.c
 F:	drivers/video/bcm2835.c
 F:	include/dm/platform_data/serial_bcm283x_mu.h
drivers/mmc/Kconfig +14 -0
index ab0627a8af..9b90db908b 100644
@@ -256,6 +256,20 @@ config MMC_UNIPHIER
 	  This selects support for the Matsushita SD/MMC Host Controller on
 	  SocioNext UniPhier and Renesas RCar SoCs.
 
+config MMC_BCM2835
+	bool "BCM2835 family custom SD/MMC Host Controller support"
+	depends on ARCH_BCM283X
+	depends on BLK && DM_MMC
+	depends on OF_CONTROL
+	default y
+	help
+	  This selects support for the custom SD host controller in the BCM2835
+	  family of devices.
+
+	  If you have a BCM2835 platform with SD or MMC devices, say Y here.
+
+	  If unsure, say N.
+
 config MMC_SANDBOX
 	bool "Sandbox MMC support"
 	depends on SANDBOX
drivers/mmc/Makefile +1 -0
index 64b6f21c61..42113e2603 100644
@@ -64,3 +64,4 @@ obj-$(CONFIG_MMC_SDHCI_ZYNQ)		+= zynq_sdhci.o
 
 obj-$(CONFIG_MMC_SUNXI)			+= sunxi_mmc.o
 obj-$(CONFIG_MMC_UNIPHIER)		+= uniphier-sd.o
+obj-$(CONFIG_MMC_BCM2835)		+= bcm2835_sdhost.o
drivers/mmc/bcm2835_sdhost.c new file +31 -0
new file mode 100644
index 0000000000..ad9c7adb5a
@@ -0,0 +1,994 @@
+/*
+ * bcm2835 sdhost driver.
+ *
+ * The 2835 has two SD controllers: The Arasan sdhci controller
+ * (supported by the iproc driver) and a custom sdhost controller
+ * (supported by this driver).
+ *
+ * The sdhci controller supports both sdcard and sdio.  The sdhost
+ * controller supports the sdcard only, but has better performance.
+ * Also note that the rpi3 has sdio wifi, so driving the sdcard with
+ * the sdhost controller allows to use the sdhci controller for wifi
+ * support.
+ *
+ * The configuration is done by devicetree via pin muxing.  Both
+ * SD controller are available on the same pins (2 pin groups = pin 22
+ * to 27 + pin 48 to 53).  So it's possible to use both SD controllers
+ * at the same time with different pin groups.
+ *
+ * This code was ported to U-Boot by
+ *  Alexander Graf <[email protected]>
+ * and is based on drivers/mmc/host/bcm2835.c in Linux which is written by
+ *  Phil Elwell <[email protected]>
+ *  Copyright (C) 2015-2016 Raspberry Pi (Trading) Ltd.
+ * which is based on
+ *  mmc-bcm2835.c by Gellert Weisz
+ * which is, in turn, based on
+ *  sdhci-bcm2708.c by Broadcom
+ *  sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko
+ *  sdhci.c and sdhci-pci.c by Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify it

drivers/mmc/bcm2835_sdhost.c line 41

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.

Can be replaced to SPDX-License-Identifier?

Quoted 4 lines
drivers/mmc/bcm2835_sdhost.c new file +4 -0
+ */
+#include <common.h>
+#include <clk.h>
+#include <fdtdec.h>

drivers/mmc/bcm2835_sdhost.c line 55

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+#include <mmc.h>
+#include <dm.h>
+#include <linux/compat.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+#include <power/regulator.h>
+#include <asm/unaligned.h>
+#include <mach/gpio.h>
+#include <asm/arch/msg.h>

Ordering.

Quoted 32 lines
drivers/mmc/bcm2835_sdhost.c new file +32 -0
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define msleep(a) udelay(a * 1000)
+
+#define SDCMD  0x00 /* Command to SD card              - 16 R/W */
+#define SDARG  0x04 /* Argument to SD card             - 32 R/W */
+#define SDTOUT 0x08 /* Start value for timeout counter - 32 R/W */
+#define SDCDIV 0x0c /* Start value for clock divider   - 11 R/W */
+#define SDRSP0 0x10 /* SD card response (31:0)         - 32 R   */
+#define SDRSP1 0x14 /* SD card response (63:32)        - 32 R   */
+#define SDRSP2 0x18 /* SD card response (95:64)        - 32 R   */
+#define SDRSP3 0x1c /* SD card response (127:96)       - 32 R   */
+#define SDHSTS 0x20 /* SD host status                  - 11 R/W */
+#define SDVDD  0x30 /* SD card power control           -  1 R/W */
+#define SDEDM  0x34 /* Emergency Debug Mode            - 13 R/W */
+#define SDHCFG 0x38 /* Host configuration              -  2 R/W */
+#define SDHBCT 0x3c /* Host byte count (debug)         - 32 R/W */
+#define SDDATA 0x40 /* Data to/from SD card            - 32 R/W */
+#define SDHBLC 0x50 /* Host block count (SDIO/SDHC)    -  9 R/W */
+
+#define SDCMD_NEW_FLAG			0x8000
+#define SDCMD_FAIL_FLAG			0x4000
+#define SDCMD_BUSYWAIT			0x800
+#define SDCMD_NO_RESPONSE		0x400
+#define SDCMD_LONG_RESPONSE		0x200
+#define SDCMD_WRITE_CMD			0x80
+#define SDCMD_READ_CMD			0x40
+#define SDCMD_CMD_MASK			0x3f
+
+#define SDCDIV_MAX_CDIV			0x7ff
+

drivers/mmc/bcm2835_sdhost.c line 97

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+#define SDHSTS_BUSY_IRPT		0x400
+#define SDHSTS_BLOCK_IRPT		0x200
+#define SDHSTS_SDIO_IRPT		0x100
+#define SDHSTS_REW_TIME_OUT		0x80
+#define SDHSTS_CMD_TIME_OUT		0x40
+#define SDHSTS_CRC16_ERROR		0x20
+#define SDHSTS_CRC7_ERROR		0x10
+#define SDHSTS_FIFO_ERROR		0x08
+/* Reserved */
+/* Reserved */

Remove unnecessary comment. What is reserved?

Quoted 109 lines
drivers/mmc/bcm2835_sdhost.c new file +109 -0
+#define SDHSTS_DATA_FLAG		0x01
+
+#define SDHSTS_TRANSFER_ERROR_MASK	(SDHSTS_CRC7_ERROR | \
+					 SDHSTS_CRC16_ERROR | \
+					 SDHSTS_REW_TIME_OUT | \
+					 SDHSTS_FIFO_ERROR)
+
+#define SDHSTS_ERROR_MASK		(SDHSTS_CMD_TIME_OUT | \
+					 SDHSTS_TRANSFER_ERROR_MASK)
+
+#define SDHCFG_BUSY_IRPT_EN	BIT(10)
+#define SDHCFG_BLOCK_IRPT_EN	BIT(8)
+#define SDHCFG_SDIO_IRPT_EN	BIT(5)
+#define SDHCFG_DATA_IRPT_EN	BIT(4)
+#define SDHCFG_SLOW_CARD	BIT(3)
+#define SDHCFG_WIDE_EXT_BUS	BIT(2)
+#define SDHCFG_WIDE_INT_BUS	BIT(1)
+#define SDHCFG_REL_CMD_LINE	BIT(0)
+
+#define SDVDD_POWER_OFF		0
+#define SDVDD_POWER_ON		1
+
+#define SDEDM_FORCE_DATA_MODE	BIT(19)
+#define SDEDM_CLOCK_PULSE	BIT(20)
+#define SDEDM_BYPASS		BIT(21)
+
+#define SDEDM_WRITE_THRESHOLD_SHIFT	9
+#define SDEDM_READ_THRESHOLD_SHIFT	14
+#define SDEDM_THRESHOLD_MASK		0x1f
+
+#define SDEDM_FSM_MASK		0xf
+#define SDEDM_FSM_IDENTMODE	0x0
+#define SDEDM_FSM_DATAMODE	0x1
+#define SDEDM_FSM_READDATA	0x2
+#define SDEDM_FSM_WRITEDATA	0x3
+#define SDEDM_FSM_READWAIT	0x4
+#define SDEDM_FSM_READCRC	0x5
+#define SDEDM_FSM_WRITECRC	0x6
+#define SDEDM_FSM_WRITEWAIT1	0x7
+#define SDEDM_FSM_POWERDOWN	0x8
+#define SDEDM_FSM_POWERUP	0x9
+#define SDEDM_FSM_WRITESTART1	0xa
+#define SDEDM_FSM_WRITESTART2	0xb
+#define SDEDM_FSM_GENPULSES	0xc
+#define SDEDM_FSM_WRITEWAIT2	0xd
+#define SDEDM_FSM_STARTPOWDOWN	0xf
+
+#define SDDATA_FIFO_WORDS	16
+
+#define FIFO_READ_THRESHOLD	4
+#define FIFO_WRITE_THRESHOLD	4
+#define SDDATA_FIFO_PIO_BURST	8
+
+struct bcm2835_plat {
+	struct mmc_config cfg;
+	struct mmc mmc;
+};
+
+struct bcm2835_host {
+	void __iomem		*ioaddr;
+	u32			phys_addr;
+
+	int			clock;		/* Current clock speed */
+	unsigned int		max_clk;	/* Max possible freq */
+	unsigned int		blocks;		/* remaining PIO blocks */
+	int			irq;		/* Device IRQ */
+
+	u32			ns_per_fifo_word;
+
+	/* cached registers */
+	u32			hcfg;
+	u32			cdiv;
+
+	struct mmc_cmd	*cmd;		/* Current command */
+	struct mmc_data		*data;		/* Current data request */
+	bool			data_complete:1;/* Data finished before cmd */
+	bool			use_busy:1;	/* Wait for busy interrupt */
+	bool			wait_data_complete:1;	/* Wait for data */
+
+	/* for threaded irq handler */
+	bool			irq_block;
+	bool			irq_busy;
+	bool			irq_data;
+
+	struct udevice		*dev;
+	struct mmc		*mmc;
+	struct bcm2835_plat	*plat;
+};
+
+static void bcm2835_dumpregs(struct bcm2835_host *host)
+{
+	dev_dbg(dev, "=========== REGISTER DUMP ===========\n");
+	dev_dbg(dev, "SDCMD  0x%08x\n", readl(host->ioaddr + SDCMD));
+	dev_dbg(dev, "SDARG  0x%08x\n", readl(host->ioaddr + SDARG));
+	dev_dbg(dev, "SDTOUT 0x%08x\n", readl(host->ioaddr + SDTOUT));
+	dev_dbg(dev, "SDCDIV 0x%08x\n", readl(host->ioaddr + SDCDIV));
+	dev_dbg(dev, "SDRSP0 0x%08x\n", readl(host->ioaddr + SDRSP0));
+	dev_dbg(dev, "SDRSP1 0x%08x\n", readl(host->ioaddr + SDRSP1));
+	dev_dbg(dev, "SDRSP2 0x%08x\n", readl(host->ioaddr + SDRSP2));
+	dev_dbg(dev, "SDRSP3 0x%08x\n", readl(host->ioaddr + SDRSP3));
+	dev_dbg(dev, "SDHSTS 0x%08x\n", readl(host->ioaddr + SDHSTS));
+	dev_dbg(dev, "SDVDD  0x%08x\n", readl(host->ioaddr + SDVDD));
+	dev_dbg(dev, "SDEDM  0x%08x\n", readl(host->ioaddr + SDEDM));
+	dev_dbg(dev, "SDHCFG 0x%08x\n", readl(host->ioaddr + SDHCFG));
+	dev_dbg(dev, "SDHBCT 0x%08x\n", readl(host->ioaddr + SDHBCT));
+	dev_dbg(dev, "SDHBLC 0x%08x\n", readl(host->ioaddr + SDHBLC));
+	dev_dbg(dev, "===========================================\n");
+}
+

drivers/mmc/bcm2835_sdhost.c line 216

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+static void bcm2835_reset_internal(struct bcm2835_host *host)
+{
+	u32 temp;
+
+	writel(SDVDD_POWER_OFF, host->ioaddr + SDVDD);
+	writel(0, host->ioaddr + SDCMD);
+	writel(0, host->ioaddr + SDARG);
+	writel(0xf00000, host->ioaddr + SDTOUT);
+	writel(0, host->ioaddr + SDCDIV);
+	writel(0x7f8, host->ioaddr + SDHSTS); /* Write 1s to clear */

What is 0xf00000 and 0x7f8? it's magic number. Don't use the magic number.

Quoted 4 lines
drivers/mmc/bcm2835_sdhost.c new file +4 -0
+	writel(0, host->ioaddr + SDHCFG);
+	writel(0, host->ioaddr + SDHBCT);
+	writel(0, host->ioaddr + SDHBLC);
+

drivers/mmc/bcm2835_sdhost.c line 230

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+	/* Limit fifo usage due to silicon bug */
+	temp = readl(host->ioaddr + SDEDM);
+	temp &= ~((SDEDM_THRESHOLD_MASK << SDEDM_READ_THRESHOLD_SHIFT) |
+		  (SDEDM_THRESHOLD_MASK << SDEDM_WRITE_THRESHOLD_SHIFT));
+	temp |= (FIFO_READ_THRESHOLD << SDEDM_READ_THRESHOLD_SHIFT) |
+		(FIFO_WRITE_THRESHOLD << SDEDM_WRITE_THRESHOLD_SHIFT);
+	writel(temp, host->ioaddr + SDEDM);
+	msleep(20);
+	writel(SDVDD_POWER_ON, host->ioaddr + SDVDD);
+	msleep(20);

Why need to sleep at here? Could you add the some comments? like /* Wait for stable power. */ ?

Quoted 24 lines
drivers/mmc/bcm2835_sdhost.c new file +24 -0
+	host->clock = 0;
+	writel(host->hcfg, host->ioaddr + SDHCFG);
+	writel(host->cdiv, host->ioaddr + SDCDIV);
+}
+
+static int bcm2835_finish_command(struct bcm2835_host *host);
+
+static void bcm2835_wait_transfer_complete(struct bcm2835_host *host)
+{
+	int timediff;
+	u32 alternate_idle;
+
+	alternate_idle = (host->data->flags & MMC_DATA_READ) ?
+		SDEDM_FSM_READWAIT : SDEDM_FSM_WRITESTART1;
+
+	timediff = 0;
+
+	while (1) {
+		u32 edm, fsm;
+
+		edm = readl(host->ioaddr + SDEDM);
+		fsm = edm & SDEDM_FSM_MASK;
+
+		if ((fsm == SDEDM_FSM_IDENTMODE) ||

drivers/mmc/bcm2835_sdhost.c line 264

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+		    (fsm == SDEDM_FSM_DATAMODE))
+			break;
+		if (fsm == alternate_idle) {
+			writel(edm | SDEDM_FORCE_DATA_MODE,
+			       host->ioaddr + SDEDM);
+			break;
+		}
+
+		timediff++;
+		if (timediff == 100000) {

timediff is 100000?

Quoted 37 lines
drivers/mmc/bcm2835_sdhost.c new file +37 -0
+			dev_err(host->dev,
+				"wait_transfer_complete - still waiting after %d retries\n",
+				timediff);
+			bcm2835_dumpregs(host);
+			return;
+		}
+	}
+}
+
+static int bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read)
+{
+	size_t blksize;
+	struct mmc_data *data = host->data;
+
+	blksize = data->blocksize;
+
+	while (blksize) {
+		int copy_words;
+		u32 hsts = 0;
+		size_t len;
+		u32 *buf;
+
+		len = blksize;
+		if (len % 4)
+			return -EINVAL;
+
+		blksize -= len;
+
+		buf = is_read ? (u32 *)data->dest : (u32 *)data->src;
+
+		if (is_read)
+			data->dest += len;
+		else
+			data->src += len;
+
+		copy_words = len / 4;
+

drivers/mmc/bcm2835_sdhost.c line 311

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+		while (copy_words) {
+			int burst_words, words;
+			u32 edm;
+
+			burst_words = min(SDDATA_FIFO_PIO_BURST, copy_words);
+			edm = readl(host->ioaddr + SDEDM);
+			if (is_read)
+				words = ((edm >> 4) & 0x1f);
+			else
+				words = SDDATA_FIFO_WORDS - ((edm >> 4) & 0x1f);

0x1f also is magic number. align?

Quoted 28 lines
drivers/mmc/bcm2835_sdhost.c new file +28 -0
+
+			if (words < burst_words) {
+				int fsm_state = (edm & SDEDM_FSM_MASK);
+
+				if ((is_read &&
+				     (fsm_state != SDEDM_FSM_READDATA &&
+				      fsm_state != SDEDM_FSM_READWAIT &&
+				      fsm_state != SDEDM_FSM_READCRC)) ||
+				    (!is_read &&
+				     (fsm_state != SDEDM_FSM_WRITEDATA &&
+				      fsm_state != SDEDM_FSM_WRITESTART1 &&
+				      fsm_state != SDEDM_FSM_WRITESTART2))) {
+					hsts = readl(host->ioaddr + SDHSTS);
+					printf("fsm %x, hsts %08x\n",
+					       fsm_state, hsts);
+					if (hsts & SDHSTS_ERROR_MASK)
+						break;
+				}
+
+				continue;
+			} else if (words > copy_words) {
+				words = copy_words;
+			}
+
+			copy_words -= words;
+
+			while (words) {
+				if (is_read)

drivers/mmc/bcm2835_sdhost.c line 349

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+					*(buf++) = readl(host->ioaddr + SDDATA);
+				else
+					writel(*(buf++), host->ioaddr + SDDATA);
+				words--;
+			}
+		}
+
+		if (hsts & SDHSTS_ERROR_MASK)
+			break;
+	}

hmm..

while() {
	while() {

		while() { }
	}
}

drivers/mmc/bcm2835_sdhost.c line 358

Quoted 9 lines
drivers/mmc/bcm2835_sdhost.c new file +9 -0
+
+	return 0;
+}
+
+static int bcm2835_transfer_pio(struct bcm2835_host *host)
+{
+	u32 sdhsts;
+	bool is_read;
+	int r = 0;

r? Coud you use the meaningful variable naming?

Quoted 49 lines
drivers/mmc/bcm2835_sdhost.c new file +49 -0
+
+	is_read = (host->data->flags & MMC_DATA_READ) != 0;
+	r = bcm2835_transfer_block_pio(host, is_read);
+
+	if (host->wait_data_complete)
+		bcm2835_wait_transfer_complete(host);
+
+	sdhsts = readl(host->ioaddr + SDHSTS);
+	if (sdhsts & (SDHSTS_CRC16_ERROR |
+		      SDHSTS_CRC7_ERROR |
+		      SDHSTS_FIFO_ERROR)) {
+		printf("%s transfer error - HSTS %08x\n",
+		       is_read ? "read" : "write", sdhsts);
+		r =  -EILSEQ;
+	} else if ((sdhsts & (SDHSTS_CMD_TIME_OUT |
+			      SDHSTS_REW_TIME_OUT))) {
+		printf("%s timeout error - HSTS %08x\n",
+		       is_read ? "read" : "write", sdhsts);
+		r = -ETIMEDOUT;
+	}
+
+	return r;
+}
+
+static void bcm2835_set_transfer_irqs(struct bcm2835_host *host)
+{
+	u32 all_irqs = SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN |
+		SDHCFG_BUSY_IRPT_EN;
+
+	host->hcfg = (host->hcfg & ~all_irqs) |
+		SDHCFG_DATA_IRPT_EN |
+		SDHCFG_BUSY_IRPT_EN;
+
+	writel(host->hcfg, host->ioaddr + SDHCFG);
+}
+
+static
+void bcm2835_prepare_data(struct bcm2835_host *host, struct mmc_cmd *cmd,
+			  struct mmc_data *data)
+{
+	WARN_ON(host->data);
+
+	host->data = data;
+	if (!data)
+		return;
+
+	host->wait_data_complete = cmd->cmdidx != MMC_CMD_READ_MULTIPLE_BLOCK;
+	host->data_complete = false;
+

drivers/mmc/bcm2835_sdhost.c line 417

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+	/* Use PIO */
+	host->blocks = data->blocks;
+
+	bcm2835_set_transfer_irqs(host);
+
+	writel(data->blocksize, host->ioaddr + SDHBCT);
+	writel(data->blocks, host->ioaddr + SDHBLC);
+}
+
+static u32 bcm2835_read_wait_sdcmd(struct bcm2835_host *host, u32 max_ms)

Does it need to pass the max_ms? When i have checked this driver, max_ms is always 100. Then just can use the define value like below

#define SDHST_TIMEOUT_MAX_MSEC 100000 ?

drivers/mmc/bcm2835_sdhost.c line 429

Quoted 12 lines
drivers/mmc/bcm2835_sdhost.c new file +12 -0
+{
+	u32 value;
+	int ret;
+	int timeout_us = (max_ms * 1000);
+
+	ret = readl_poll_timeout(host->ioaddr + SDCMD, value,
+				 !(value & SDCMD_NEW_FLAG), 10);
+	if (ret == -ETIMEDOUT)
+		/* if it takes a while make poll interval bigger */
+		ret = readl_poll_timeout(host->ioaddr + SDCMD, value,
+					 !(value & SDCMD_NEW_FLAG),
+					 timeout_us);

Why don't use the timeout_us at first time?

Quoted 7 lines
drivers/mmc/bcm2835_sdhost.c new file +7 -0
+	if (ret == -ETIMEDOUT)
+		printf("%s: timeout (%d ms)\n", __func__, max_ms);> +
+	return value;
+}
+
+static int bcm2835_send_command(struct bcm2835_host *host, struct mmc_cmd *cmd,
+				struct mmc_data *data)

drivers/mmc/bcm2835_sdhost.c line 447

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+{
+	u32 sdcmd, sdhsts;
+
+	WARN_ON(host->cmd);
+
+	sdcmd = bcm2835_read_wait_sdcmd(host, 100);
+	if (sdcmd & SDCMD_NEW_FLAG) {
+		printf("previous command never completed.\n");
+		bcm2835_dumpregs(host);
+		return -EILSEQ;

"Previous command never completed" means "busy state"? It's not illegal sequence error..can be to -EBUSY?

drivers/mmc/bcm2835_sdhost.c line 460

Quoted 13 lines
drivers/mmc/bcm2835_sdhost.c new file +13 -0
+	}
+
+	host->cmd = cmd;
+
+	/* Clear any error flags */
+	sdhsts = readl(host->ioaddr + SDHSTS);
+	if (sdhsts & SDHSTS_ERROR_MASK)
+		writel(sdhsts, host->ioaddr + SDHSTS);
+
+	if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) {
+		printf("unsupported response type!\n");
+		return -EINVAL;
+	}

If didn't suport these resp_types, can be located to top place.

Quoted 109 lines
drivers/mmc/bcm2835_sdhost.c new file +109 -0
+
+	bcm2835_prepare_data(host, cmd, data);
+
+	writel(cmd->cmdarg, host->ioaddr + SDARG);
+
+	sdcmd = cmd->cmdidx & SDCMD_CMD_MASK;
+
+	host->use_busy = false;
+	if (!(cmd->resp_type & MMC_RSP_PRESENT)) {
+		sdcmd |= SDCMD_NO_RESPONSE;
+	} else {
+		if (cmd->resp_type & MMC_RSP_136)
+			sdcmd |= SDCMD_LONG_RESPONSE;
+		if (cmd->resp_type & MMC_RSP_BUSY) {
+			sdcmd |= SDCMD_BUSYWAIT;
+			host->use_busy = true;
+		}
+	}
+
+	if (data) {
+		if (data->flags & MMC_DATA_WRITE)
+			sdcmd |= SDCMD_WRITE_CMD;
+		if (data->flags & MMC_DATA_READ)
+			sdcmd |= SDCMD_READ_CMD;
+	}
+
+	writel(sdcmd | SDCMD_NEW_FLAG, host->ioaddr + SDCMD);
+
+	return 0;
+}
+
+static int bcm2835_transfer_complete(struct bcm2835_host *host)
+{
+	int r = 0;
+
+	WARN_ON(!host->data_complete);
+
+	host->data = NULL;
+
+	return r;
+}
+
+static void bcm2835_finish_data(struct bcm2835_host *host)
+{
+	host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN);
+	writel(host->hcfg, host->ioaddr + SDHCFG);
+
+	host->data_complete = true;
+
+	if (host->cmd) {
+		/* Data managed to finish before the
+		 * command completed. Make sure we do
+		 * things in the proper order.
+		 */
+		dev_dbg(dev, "Finished early - HSTS %08x\n",
+			readl(host->ioaddr + SDHSTS));
+	} else {
+		bcm2835_transfer_complete(host);
+	}
+}
+
+static int bcm2835_finish_command(struct bcm2835_host *host)
+{
+	struct mmc_cmd *cmd = host->cmd;
+	u32 sdcmd;
+	int r = 0;
+
+	sdcmd = bcm2835_read_wait_sdcmd(host, 100);
+
+	/* Check for errors */
+	if (sdcmd & SDCMD_NEW_FLAG) {
+		printf("command never completed.\n");
+		bcm2835_dumpregs(host);
+		return -EIO;
+	} else if (sdcmd & SDCMD_FAIL_FLAG) {
+		u32 sdhsts = readl(host->ioaddr + SDHSTS);
+
+		/* Clear the errors */
+		writel(SDHSTS_ERROR_MASK, host->ioaddr + SDHSTS);
+
+		if (!(sdhsts & SDHSTS_CRC7_ERROR) ||
+		    (host->cmd->cmdidx != MMC_CMD_SEND_OP_COND)) {
+			if (sdhsts & SDHSTS_CMD_TIME_OUT) {
+				r = -ETIMEDOUT;
+			} else {
+				printf("unexpected command %d error\n",
+				       host->cmd->cmdidx);
+				bcm2835_dumpregs(host);
+				r = -EILSEQ;
+			}
+
+			return r;
+		}
+	}
+
+	if (cmd->resp_type & MMC_RSP_PRESENT) {
+		if (cmd->resp_type & MMC_RSP_136) {
+			int i;
+
+			for (i = 0; i < 4; i++) {
+				cmd->response[3 - i] =
+					readl(host->ioaddr + SDRSP0 + i * 4);
+			}
+		} else {
+			cmd->response[0] = readl(host->ioaddr + SDRSP0);
+		}
+	}
+
+	/* Processed actual command. */

drivers/mmc/bcm2835_sdhost.c line 579

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+	host->cmd = NULL;
+	if (host->data && host->data_complete)
+		r = bcm2835_transfer_complete(host);
+
+	return r;
+}
+
+static int bcm2835_check_cmd_error(struct bcm2835_host *host, u32 intmask)
+{
+	int r = -EINVAL;

int ret.

Quoted 7 lines
drivers/mmc/bcm2835_sdhost.c new file +7 -0
+
+	if (!(intmask & SDHSTS_ERROR_MASK))
+		return 0;
+
+	if (!host->cmd)
+		return -EINVAL;
+

drivers/mmc/bcm2835_sdhost.c line 596

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+	printf("sdhost_busy_irq: intmask %08x\n", intmask);
+	if (intmask & SDHSTS_CRC7_ERROR) {
+		r = -EILSEQ;
+	} else if (intmask & (SDHSTS_CRC16_ERROR |
+			      SDHSTS_FIFO_ERROR)) {
+		r = -EILSEQ;
+	} else if (intmask & SDHSTS_REW_TIME_OUT) {
+		r = -ETIMEDOUT;
+	} else if (intmask & SDHSTS_CMD_TIME_OUT) {
+		r = -ETIMEDOUT;
else if (intmask & (SDHST_CMD_TIMEOUT | SDHST_REW_TIME_OUT) {
		ret = -ETIMEOUT;
}

drivers/mmc/bcm2835_sdhost.c line 604

Quoted 8 lines
drivers/mmc/bcm2835_sdhost.c new file +8 -0
+	}
+	bcm2835_dumpregs(host);
+	return r;
+}
+
+static int bcm2835_check_data_error(struct bcm2835_host *host, u32 intmask)
+{
+	int r = 0;

ditto

Quoted 15 lines
drivers/mmc/bcm2835_sdhost.c new file +15 -0
+
+	if (!host->data)
+		return 0;
+	if (intmask & (SDHSTS_CRC16_ERROR | SDHSTS_FIFO_ERROR))
+		r = -EILSEQ;
+	if (intmask & SDHSTS_REW_TIME_OUT)
+		r = -ETIMEDOUT;
+
+	if (r)
+		printf("%s:%d %d\n", __func__, __LINE__, r);
+
+	return r;
+}
+
+static void bcm2835_busy_irq(struct bcm2835_host *host)

drivers/mmc/bcm2835_sdhost.c line 629

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+{
+	if (WARN_ON(!host->cmd)) {
+		bcm2835_dumpregs(host);
+		return;
+	}
+
+	if (WARN_ON(!host->use_busy)) {
+		bcm2835_dumpregs(host);
+		return;
+	}

Really need to use WARN_ON? for what?

Quoted 5 lines
drivers/mmc/bcm2835_sdhost.c new file +5 -0
+	host->use_busy = false;
+
+	bcm2835_finish_command(host);
+}
+

drivers/mmc/bcm2835_sdhost.c line 644

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+static void bcm2835_data_irq(struct bcm2835_host *host, u32 intmask)
+{
+	int r;
+
+	/* There are no dedicated data/space available interrupt
+	 * status bits, so it is necessary to use the single shared
+	 * data/space available FIFO status bits. It is therefore not
+	 * an error to get here when there is no data transfer in
+	 * progress.
+	 */

/*
* There are...
*/

Quoted 99 lines
drivers/mmc/bcm2835_sdhost.c new file +99 -0
+	if (!host->data)
+		return;
+
+	r = bcm2835_check_data_error(host, intmask);
+	if (r)
+		goto finished;
+
+	if (host->data->flags & MMC_DATA_WRITE) {
+		/* Use the block interrupt for writes after the first block */
+		host->hcfg &= ~(SDHCFG_DATA_IRPT_EN);
+		host->hcfg |= SDHCFG_BLOCK_IRPT_EN;
+		writel(host->hcfg, host->ioaddr + SDHCFG);
+		bcm2835_transfer_pio(host);
+	} else {
+		bcm2835_transfer_pio(host);
+		host->blocks--;
+		if ((host->blocks == 0))
+			goto finished;
+	}
+	return;
+
+finished:
+	host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN);
+	writel(host->hcfg, host->ioaddr + SDHCFG);
+}
+
+static void bcm2835_data_threaded_irq(struct bcm2835_host *host)
+{
+	if (!host->data)
+		return;
+	if ((host->blocks == 0))
+		bcm2835_finish_data(host);
+}
+
+static void bcm2835_block_irq(struct bcm2835_host *host)
+{
+	if (WARN_ON(!host->data)) {
+		bcm2835_dumpregs(host);
+		return;
+	}
+
+	WARN_ON(!host->blocks);
+	if ((--host->blocks == 0))
+		bcm2835_finish_data(host);
+	else
+		bcm2835_transfer_pio(host);
+}
+
+static irqreturn_t bcm2835_irq(int irq, void *dev_id)
+{
+	irqreturn_t result = IRQ_NONE;
+	struct bcm2835_host *host = dev_id;
+	u32 intmask;
+
+	intmask = readl(host->ioaddr + SDHSTS);
+
+	writel(SDHSTS_BUSY_IRPT |
+	       SDHSTS_BLOCK_IRPT |
+	       SDHSTS_SDIO_IRPT |
+	       SDHSTS_DATA_FLAG,
+	       host->ioaddr + SDHSTS);
+
+	if (intmask & SDHSTS_BLOCK_IRPT) {
+		bcm2835_check_data_error(host, intmask);
+		host->irq_block = true;
+		result = IRQ_WAKE_THREAD;
+	}
+
+	if (intmask & SDHSTS_BUSY_IRPT) {
+		if (!bcm2835_check_cmd_error(host, intmask)) {
+			host->irq_busy = true;
+			result = IRQ_WAKE_THREAD;
+		} else {
+			result = IRQ_HANDLED;
+		}
+	}
+
+	/* There is no true data interrupt status bit, so it is
+	 * necessary to qualify the data flag with the interrupt
+	 * enable bit.
+	 */
+	if ((intmask & SDHSTS_DATA_FLAG) &&
+	    (host->hcfg & SDHCFG_DATA_IRPT_EN)) {
+		bcm2835_data_irq(host, intmask);
+		host->irq_data = true;
+		result = IRQ_WAKE_THREAD;
+	}
+
+	return result;
+}
+
+static irqreturn_t bcm2835_threaded_irq(int irq, void *dev_id)
+{
+	struct bcm2835_host *host = dev_id;
+	bool block, busy, data;
+
+	block = host->irq_block;
+	busy  = host->irq_busy;
+	data  = host->irq_data;

drivers/mmc/bcm2835_sdhost.c line 753

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+	host->irq_block = false;
+	host->irq_busy  = false;
+	host->irq_data  = false;
+
+	if (block)
+		bcm2835_block_irq(host);
+	if (busy)
+		bcm2835_busy_irq(host);
+	if (data)
+		bcm2835_data_threaded_irq(host);
if (host->irq_block) {
	host->irq_block = false;
	bcm2835_block_irq(); 
}

Then you can remove the "bool block, busy, data".

Quoted 193 lines
drivers/mmc/bcm2835_sdhost.c new file +193 -0
+
+	return IRQ_HANDLED;
+}
+
+static void bcm2835_irq_poll(struct bcm2835_host *host)
+{
+	u32 intmask;
+
+	while (1) {
+		intmask = readl(host->ioaddr + SDHSTS);
+		if (intmask & (SDHSTS_BUSY_IRPT | SDHSTS_BLOCK_IRPT |
+			       SDHSTS_SDIO_IRPT | SDHSTS_DATA_FLAG)) {
+			bcm2835_irq(0, host);
+			bcm2835_threaded_irq(0, host);
+			return;
+		}
+	}
+}
+
+static void bcm2835_set_clock(struct bcm2835_host *host, unsigned int clock)
+{
+	int div;
+
+	/* The SDCDIV register has 11 bits, and holds (div - 2).  But
+	 * in data mode the max is 50MHz wihout a minimum, and only
+	 * the bottom 3 bits are used. Since the switch over is
+	 * automatic (unless we have marked the card as slow...),
+	 * chosen values have to make sense in both modes.  Ident mode
+	 * must be 100-400KHz, so can range check the requested
+	 * clock. CMD15 must be used to return to data mode, so this
+	 * can be monitored.
+	 *
+	 * clock 250MHz -> 0->125MHz, 1->83.3MHz, 2->62.5MHz, 3->50.0MHz
+	 *                 4->41.7MHz, 5->35.7MHz, 6->31.3MHz, 7->27.8MHz
+	 *
+	 *		 623->400KHz/27.8MHz
+	 *		 reset value (507)->491159/50MHz
+	 *
+	 * BUT, the 3-bit clock divisor in data mode is too small if
+	 * the core clock is higher than 250MHz, so instead use the
+	 * SLOW_CARD configuration bit to force the use of the ident
+	 * clock divisor at all times.
+	 */
+
+	if (clock < 100000) {
+		/* Can't stop the clock, but make it as slow as possible
+		 * to show willing
+		 */
+		host->cdiv = SDCDIV_MAX_CDIV;
+		writel(host->cdiv, host->ioaddr + SDCDIV);
+		return;
+	}
+
+	div = host->max_clk / clock;
+	if (div < 2)
+		div = 2;
+	if ((host->max_clk / div) > clock)
+		div++;
+	div -= 2;
+
+	if (div > SDCDIV_MAX_CDIV)
+		div = SDCDIV_MAX_CDIV;
+
+	clock = host->max_clk / (div + 2);
+	host->mmc->clock = clock;
+
+	/* Calibrate some delays */
+
+	host->ns_per_fifo_word = (1000000000 / clock) *
+		((host->mmc->card_caps & MMC_MODE_4BIT) ? 8 : 32);
+
+	host->cdiv = div;
+	writel(host->cdiv, host->ioaddr + SDCDIV);
+
+	/* Set the timeout to 500ms */
+	writel(host->mmc->clock / 2, host->ioaddr + SDTOUT);
+}
+
+static inline int is_power_of_2(u64 x)
+{
+	return !(x & (x - 1));
+}
+
+static int bcm2835_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
+			    struct mmc_data *data)
+{
+	struct bcm2835_host *host = dev_get_priv(dev);
+	u32 edm, fsm;
+	int r = 0;
+
+	if (data && !is_power_of_2(data->blocksize)) {
+		printf("unsupported block size (%d bytes)\n", data->blocksize);
+
+		if (cmd)
+			return -EINVAL;
+	}
+
+	edm = readl(host->ioaddr + SDEDM);
+	fsm = edm & SDEDM_FSM_MASK;
+
+	if ((fsm != SDEDM_FSM_IDENTMODE) &&
+	    (fsm != SDEDM_FSM_DATAMODE) &&
+	    (cmd && cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)) {
+		printf("previous command (%d) not complete (EDM %08x)\n",
+		       readl(host->ioaddr + SDCMD) & SDCMD_CMD_MASK, edm);
+		bcm2835_dumpregs(host);
+
+		if (cmd)
+			return -EILSEQ;
+
+		return 0;
+	}
+
+	if (cmd) {
+		r = bcm2835_send_command(host, cmd, data);
+		if (!r && !host->use_busy)
+			r = bcm2835_finish_command(host);
+	}
+
+	/* Wait for completion of busy signal or data transfer */
+	while (host->use_busy || host->data)
+		bcm2835_irq_poll(host);
+
+	return r;
+}
+
+static int bcm2835_set_ios(struct udevice *dev)
+{
+	struct bcm2835_host *host = dev_get_priv(dev);
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+
+	if (!mmc->clock || mmc->clock != host->clock) {
+		bcm2835_set_clock(host, mmc->clock);
+		host->clock = mmc->clock;
+	}
+
+	/* set bus width */
+	host->hcfg &= ~SDHCFG_WIDE_EXT_BUS;
+	if (mmc->bus_width == 4)
+		host->hcfg |= SDHCFG_WIDE_EXT_BUS;
+
+	host->hcfg |= SDHCFG_WIDE_INT_BUS;
+
+	/* Disable clever clock switching, to cope with fast core clocks */
+	host->hcfg |= SDHCFG_SLOW_CARD;
+
+	writel(host->hcfg, host->ioaddr + SDHCFG);
+
+	return 0;
+}
+
+static int bcm2835_add_host(struct bcm2835_host *host)
+{
+	struct mmc_config *cfg = &host->plat->cfg;
+
+	cfg->f_max = host->max_clk;
+	cfg->f_min = host->max_clk / SDCDIV_MAX_CDIV;
+	cfg->b_max = 65535;
+
+	dev_dbg(dev, "f_max %d, f_min %d\n",
+		cfg->f_max, cfg->f_min);
+
+	/* host controller capabilities */
+	cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_HS | MMC_MODE_HS_52MHz;
+
+	/* report supported voltage ranges */
+	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	/* Set interrupt enables */
+	host->hcfg = SDHCFG_BUSY_IRPT_EN;
+
+	bcm2835_reset_internal(host);
+
+	return 0;
+}
+
+static int bcm2835_probe(struct udevice *dev)
+{
+	struct bcm2835_plat *plat = dev_get_platdata(dev);
+	struct bcm2835_host *host = dev_get_priv(dev);
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+	int ret;
+
+	host->dev = dev;
+	host->mmc = mmc;
+	host->plat = plat;
+	upriv->mmc = &plat->mmc;
+	plat->cfg.name = dev->name;
+
+	host->phys_addr = devfdt_get_addr(dev);
+	if (host->phys_addr == FDT_ADDR_T_NONE)
+		return -EINVAL;

drivers/mmc/bcm2835_sdhost.c line 956

Quoted 10 lines
drivers/mmc/bcm2835_sdhost.c new file +10 -0
+
+	host->ioaddr = devm_ioremap(dev, host->phys_addr, SZ_256);
+	if (!host->ioaddr)
+		return -ENOMEM;
+
+	host->max_clk = bcm2835_get_mmc_clock();
+
+	ret = bcm2835_add_host(host);
+	if (ret)
+		goto err;

bcm2835_add_host is always returned to 0.
Never go to err...

Quoted 39 lines
drivers/mmc/bcm2835_sdhost.c new file +38 -0
+
+	dev_dbg(dev, "%s -> OK\n", __func__);
+
+	return 0;
+
+err:
+	dev_dbg(dev, "%s -> err %d\n", __func__, ret);
+
+	return ret;
+}
+
+static const struct udevice_id bcm2835_match[] = {
+	{ .compatible = "brcm,bcm2835-sdhost" },
+	{ }
+};
+
+static const struct dm_mmc_ops bcm2835_ops = {
+	.send_cmd = bcm2835_send_cmd,
+	.set_ios = bcm2835_set_ios,
+};
+
+static int bcm2835_bind(struct udevice *dev)
+{
+	struct bcm2835_plat *plat = dev_get_platdata(dev);
+
+	return mmc_bind(dev, &plat->mmc, &plat->cfg);
+}
+
+U_BOOT_DRIVER(bcm2835_sdhost) = {
+	.name = "bcm2835-sdhost",
+	.id = UCLASS_MMC,
+	.of_match = bcm2835_match,
+	.bind = bcm2835_bind,
+	.probe = bcm2835_probe,
+	.priv_auto_alloc_size = sizeof(struct bcm2835_host),
+	.platdata_auto_alloc_size = sizeof(struct bcm2835_plat),
+	.ops = &bcm2835_ops,
+};
#5 lore raw

PATCH v2 2/2 mmc: Add bcm2835 sdhost controller

Quoted 110 lines · Jaehoon Chung
Quoted 109 lines · Alexander Graf

The BCM2835 family of SoCs has 2 different SD controllers: One based on the SDHCI spec and a custom, home-grown one.

This patch implements a driver for the latter based on the Linux driver. This is needed so that we can make use of device trees that assume driver presence of both SD controllers.

---

v1 -> v2:

- Remove hand written pinctrl support
- Checkpatch fixes

---
  MAINTAINERS                  |   1 +
  drivers/mmc/Kconfig          |  14 +
  drivers/mmc/Makefile         |   1 +
  drivers/mmc/bcm2835_sdhost.c | 994 +++++++++++++++++++++++++++++++++++++++++++
  4 files changed, 1010 insertions(+)

create mode 100644 drivers/mmc/bcm2835_sdhost.c

MAINTAINERS +1 -0
index 1f2545191b..728d38aebf 100644
@@ -97,6 +97,7 @@ S:	Orphaned (Since 2017-07)
  F:	arch/arm/mach-bcm283x/
  F:	drivers/gpio/bcm2835_gpio.c
  F:	drivers/mmc/bcm2835_sdhci.c
+F:	drivers/mmc/bcm2835_sdhost.c
  F:	drivers/serial/serial_bcm283x_mu.c
  F:	drivers/video/bcm2835.c
  F:	include/dm/platform_data/serial_bcm283x_mu.h
drivers/mmc/Kconfig +14 -0
index ab0627a8af..9b90db908b 100644
@@ -256,6 +256,20 @@ config MMC_UNIPHIER
  	  This selects support for the Matsushita SD/MMC Host Controller on
  	  SocioNext UniPhier and Renesas RCar SoCs.
 
+config MMC_BCM2835
+	bool "BCM2835 family custom SD/MMC Host Controller support"
+	depends on ARCH_BCM283X
+	depends on BLK && DM_MMC
+	depends on OF_CONTROL
+	default y
+	help
+	  This selects support for the custom SD host controller in the BCM2835
+	  family of devices.
+
+	  If you have a BCM2835 platform with SD or MMC devices, say Y here.
+
+	  If unsure, say N.
+
  config MMC_SANDBOX
  	bool "Sandbox MMC support"
  	depends on SANDBOX
drivers/mmc/Makefile +1 -0
index 64b6f21c61..42113e2603 100644
@@ -64,3 +64,4 @@ obj-$(CONFIG_MMC_SDHCI_ZYNQ)		+= zynq_sdhci.o
 
  obj-$(CONFIG_MMC_SUNXI)			+= sunxi_mmc.o
  obj-$(CONFIG_MMC_UNIPHIER)		+= uniphier-sd.o
+obj-$(CONFIG_MMC_BCM2835)		+= bcm2835_sdhost.o
drivers/mmc/bcm2835_sdhost.c new file +34 -0
new file mode 100644
index 0000000000..ad9c7adb5a
@@ -0,0 +1,994 @@
+/*
+ * bcm2835 sdhost driver.
+ *
+ * The 2835 has two SD controllers: The Arasan sdhci controller
+ * (supported by the iproc driver) and a custom sdhost controller
+ * (supported by this driver).
+ *
+ * The sdhci controller supports both sdcard and sdio.  The sdhost
+ * controller supports the sdcard only, but has better performance.
+ * Also note that the rpi3 has sdio wifi, so driving the sdcard with
+ * the sdhost controller allows to use the sdhci controller for wifi
+ * support.
+ *
+ * The configuration is done by devicetree via pin muxing.  Both
+ * SD controller are available on the same pins (2 pin groups = pin 22
+ * to 27 + pin 48 to 53).  So it's possible to use both SD controllers
+ * at the same time with different pin groups.
+ *
+ * This code was ported to U-Boot by
+ *  Alexander Graf <[email protected]>
+ * and is based on drivers/mmc/host/bcm2835.c in Linux which is written by
+ *  Phil Elwell <[email protected]>
+ *  Copyright (C) 2015-2016 Raspberry Pi (Trading) Ltd.
+ * which is based on
+ *  mmc-bcm2835.c by Gellert Weisz
+ * which is, in turn, based on
+ *  sdhci-bcm2708.c by Broadcom
+ *  sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko
+ *  sdhci.c and sdhci-pci.c by Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
Quoted 10 lines

drivers/mmc/bcm2835_sdhost.c line 41

Quoted 7 lines
drivers/mmc/bcm2835_sdhost.c new file +7 -0
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.

Can be replaced to SPDX-License-Identifier?

Thanks a lot for the very in-depth review. I think I've fixed most of your comments :).

[...]

Quoted 18 lines
Quoted 18 lines
drivers/mmc/bcm2835_sdhost.c new file +18 -0
+
+	if (!host->data)
+		return 0;
+	if (intmask & (SDHSTS_CRC16_ERROR | SDHSTS_FIFO_ERROR))
+		r = -EILSEQ;
+	if (intmask & SDHSTS_REW_TIME_OUT)
+		r = -ETIMEDOUT;
+
+	if (r)
+		printf("%s:%d %d\n", __func__, __LINE__, r);
+
+	return r;
+}
+
+static void bcm2835_busy_irq(struct bcm2835_host *host)
+{
+	if (WARN_ON(!host->cmd)) {
+		bcm2835_dumpregs(host);
Quoted 8 lines

drivers/mmc/bcm2835_sdhost.c line 629

Quoted 7 lines
drivers/mmc/bcm2835_sdhost.c new file +7 -0
+		return;
+	}
+
+	if (WARN_ON(!host->use_busy)) {
+		bcm2835_dumpregs(host);
+		return;
+	}

Really need to use WARN_ON? for what?

We shouldn't get here without use_busy set. If we did, something went terribly wrong. I guess the alternative is an assert(), but that only runs when debug is defined.

Alex