Rpi: Add support for second sd host controller
Subsystems (7)
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
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
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.
Signed-off-by Alexander Graf <[email protected]>--- 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@@ -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
PATCH v2 2/2 mmc: Add bcm2835 sdhost controller
In reply to Alexander GrafThe 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
@@ -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
@@ -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
@@ -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
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.
Signed-off-by Alexander Graf <[email protected]>---
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.cMAINTAINERS +1
-0@@ -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.hdrivers/mmc/Kconfig +14
-0@@ -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 SANDBOXdrivers/mmc/Makefile +1
-0@@ -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.odrivers/mmc/bcm2835_sdhost.c new file +31
-0@@ -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, +};
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.
Signed-off-by Alexander Graf <[email protected]>---
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.cMAINTAINERS +1
-0@@ -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.hdrivers/mmc/Kconfig +14
-0@@ -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 SANDBOXdrivers/mmc/Makefile +1
-0@@ -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.odrivers/mmc/bcm2835_sdhost.c new file +34
-0@@ -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