From patchwork Tue Jan 16 13:46:36 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Graf X-Patchwork-Id: 124691 Delivered-To: patch@linaro.org Received: by 10.46.64.148 with SMTP id r20csp1019616lje; Tue, 16 Jan 2018 05:48:28 -0800 (PST) X-Google-Smtp-Source: ACJfBos2ZZFAGNCM0k9sBEa09FQgC148zY4X9UNDjW9aO9W3A1hti/kAGkLbK0mApU0zmzuIdrff X-Received: by 10.80.145.41 with SMTP id e38mr22713481eda.63.1516110507978; Tue, 16 Jan 2018 05:48:27 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516110507; cv=none; d=google.com; s=arc-20160816; b=KEZLjgv88wSTbtqJoiYDddUGPgBWnb1CYldOBqh3/EqW0zszEvfRFnNeXYtXjRdVa9 oiRi8n+fTaZhsLJivXm7l0tx+p6iMJglDnkhpb1JEMyOvsUkJitu9VZwO4NfUfve+VcW sCKj0yqdon5LAzAkpHNQ7sBg89J7bbE/bqr1VFFDO9QHN1BAkCfEljsM5iL5r77O1znc O43roES8TDKB/esHj9I1WrobMZ+92lrQakZOC5SFNTS7pN4wfwgOtHYgMZoTqLjzktPc ZFSPtFzWNK7hcKroNrECWAOpWHIess3idl+TFVlC6X553FyscuE0AyyuTG0hPFtt1l6G cWkQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:arc-authentication-results; bh=uzbCpuW6Dx3uxnnH1j8RzuG+JM0Fe8fVoCzsYd/xT/w=; b=0KZRyfUCgMOoajNn4Zmt1GIGeF5oVIiP3ibLQ3ETcoXH1mhylQ4XpHDqNItT9Z+FJh xE11Y5rnQO40ewNvM0DfbbUwgq4s+SFmvSsvW2ujBfzZ304OZ7VA3MN4AFasl3xgyK4Z QML+K9WnDX1Vp/6nohsufrOaMQlfbR5vh4lJiAvyTS3KpmeEZUYNCvW2oA1q3KouECNv YAer9QWC2AsYjF+/wMtLRgKxBFzODD/bI6T7QpFPs9YW5vicsW1MUf9Qdl5xj2q7jcZr KKl7NxOoTzPwLpULUOdLo6vSzG4WEwOG2ZMlMgTqSH0OJ+TRmXznBhbdNG5HQnbbf30l b6DA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de Return-Path: Received: from lists.denx.de (dione.denx.de. [81.169.180.215]) by mx.google.com with ESMTP id 6si2045941edf.460.2018.01.16.05.48.27; Tue, 16 Jan 2018 05:48:27 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) client-ip=81.169.180.215; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de Received: by lists.denx.de (Postfix, from userid 105) id 8825AC21DEF; Tue, 16 Jan 2018 13:47:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 86953C21E0F; Tue, 16 Jan 2018 13:46:48 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 91015C21DB0; Tue, 16 Jan 2018 13:46:45 +0000 (UTC) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) by lists.denx.de (Postfix) with ESMTPS id 1BE88C21C2F for ; Tue, 16 Jan 2018 13:46:45 +0000 (UTC) X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 7E4CDAD05; Tue, 16 Jan 2018 13:46:44 +0000 (UTC) From: Alexander Graf To: u-boot@lists.denx.de Date: Tue, 16 Jan 2018 14:46:36 +0100 Message-Id: <20180116134638.3879-2-agraf@suse.de> X-Mailer: git-send-email 2.12.3 In-Reply-To: <20180116134638.3879-1-agraf@suse.de> References: <20180116134638.3879-1-agraf@suse.de> Subject: [U-Boot] [PATCH 1/3] bcm2835_gpio: Add support for pinmux X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" On the bcm2835 the GPIO IP block is responsible to control pin muxing of the configurable pins on the chip. This adds a simple helper function that allows a device driver to set pin muxing according to device tree configuration on those devices. Signed-off-by: Alexander Graf --- arch/arm/mach-bcm283x/include/mach/gpio.h | 1 + drivers/gpio/bcm2835_gpio.c | 40 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/arch/arm/mach-bcm283x/include/mach/gpio.h b/arch/arm/mach-bcm283x/include/mach/gpio.h index daaee52f81..751594d09f 100644 --- a/arch/arm/mach-bcm283x/include/mach/gpio.h +++ b/arch/arm/mach-bcm283x/include/mach/gpio.h @@ -62,5 +62,6 @@ struct bcm2835_gpio_platdata { }; int bcm2835_gpio_get_func_id(struct udevice *dev, unsigned gpio); +void bcm2835_gpio_set_pinmux(struct udevice *dev, int handle); #endif /* _BCM2835_GPIO_H_ */ diff --git a/drivers/gpio/bcm2835_gpio.c b/drivers/gpio/bcm2835_gpio.c index beaa21853a..209cbed9e6 100644 --- a/drivers/gpio/bcm2835_gpio.c +++ b/drivers/gpio/bcm2835_gpio.c @@ -83,6 +83,46 @@ int bcm2835_gpio_get_func_id(struct udevice *dev, unsigned gpio) return (val >> BCM2835_GPIO_FSEL_SHIFT(gpio) & BCM2835_GPIO_FSEL_MASK); } +static void bcm2835_gpio_set_func_id(struct udevice *dev, unsigned gpio, int func) +{ + struct bcm2835_gpios *gpios = dev_get_priv(dev); + u32 val; + + val = readl(&gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]); + val &= ~(BCM2835_GPIO_FSEL_MASK << BCM2835_GPIO_FSEL_SHIFT(gpio)); + val |= (func << BCM2835_GPIO_FSEL_SHIFT(gpio)); + writel(val, &gpios->reg->gpfsel[BCM2835_GPIO_FSEL_BANK(gpio)]); +} + +void bcm2835_gpio_set_pinmux(struct udevice *dev, int handle) +{ + int node = fdt_node_offset_by_phandle(gd->fdt_blob, handle); + u32 pins[16]; + int len; + u32 func; + int i; + + if (!node) + goto err; + + func = fdtdec_get_int(gd->fdt_blob, node, "brcm,function", -1); + if (func <= 0) + goto err; + + len = fdtdec_get_int_array_count(gd->fdt_blob, node, "brcm,pins", pins, + ARRAY_SIZE(pins)); + if (len <= 0) + goto err; + + for (i = 0; i < len; i++) + bcm2835_gpio_set_func_id(dev, pins[i], func); + + return; +err: + printf("Failed to pinmux phandle 0x%x\n", handle); + return; +} + static int bcm2835_gpio_get_function(struct udevice *dev, unsigned offset) { int funcid = bcm2835_gpio_get_func_id(dev, offset); From patchwork Tue Jan 16 13:46:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Graf X-Patchwork-Id: 124692 Delivered-To: patch@linaro.org Received: by 10.46.64.148 with SMTP id r20csp1019681lje; Tue, 16 Jan 2018 05:48:40 -0800 (PST) X-Google-Smtp-Source: ACJfBovpdku/rzwX06FC7FO5NSDyX4VEu9G4/AfTKlDktTwyOTO9/IMUESj0S9+eN4aJK2uw2nDp X-Received: by 10.80.153.194 with SMTP id n2mr35683440edb.303.1516110520475; Tue, 16 Jan 2018 05:48:40 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516110520; cv=none; d=google.com; s=arc-20160816; b=lGW2EXD1ootbN/yImuRfDAJrFYDwNaKiplNyYIQTo8JITHsD2S8yJxjo27686ccc1L r5dbUmcGan68wikOWIENa152HZtHDQS/QuMpkUYbPDXVoURECyz/Ey5/GvSh/sbEHENn U+oE6I/C7TiDwO5dxvyq1V/zezZ9Hf94m5QS3k4sPYeiHWcRc8gOWwY9ztv4h/Km3u5j Ij+eE4At8oNy1Xom7dHUh9FMm9mLnovgEpBAjwaefHwcOuFP3Bx8PlxfrgxIPuSfqpFl fWD8Bl1J+Yd/74IjDSaKxKPIDqIyN+I2OsPWpnDmOeOvGBnpRflAgyJljVlpragbTImw PchA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:arc-authentication-results; bh=yvYseF/XmsN7W7fTiGcVQ7bfBSeg3ThKDuM+Q/FzfoM=; b=rN1pRheqwZxuN3uSdJg7/p6a6kUxKG8oXtH1Z2h0nGXD0kpjbV0kLuz0YHcU8Oz2ft wOwa1EZZ2GW8VqLPx1ez8S+UGQn3AtdoTxr+c2YluXzgKy/2yQRJnNJKDg6ShcYMUIgS 2OpdlWfDGk6lBhujJAVzgtIvRpSboQeNkQM6iJ81bGsRvxEUdVbdDZQDSNLCNnlOlJDh 77ALPZEG4OEGira/zl4IwMR/WGbydwNlLc9kNjHkKqMU83X/2J0VVg/3pfQlcX7Zpaiq bhIcKG/ZQVxjF6+g9t0XFnwxN9jCtTFCiNcpqWFsUaLGoq+rekbmFZZWMLxpSvlSkNob eMfw== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de Return-Path: Received: from lists.denx.de (dione.denx.de. [81.169.180.215]) by mx.google.com with ESMTP id s10si2257955edc.387.2018.01.16.05.48.40; Tue, 16 Jan 2018 05:48:40 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) client-ip=81.169.180.215; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de Received: by lists.denx.de (Postfix, from userid 105) id DD87DC21E0A; Tue, 16 Jan 2018 13:47:47 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 4C9F6C21E1D; Tue, 16 Jan 2018 13:46:49 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 32DEBC21C2F; Tue, 16 Jan 2018 13:46:46 +0000 (UTC) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) by lists.denx.de (Postfix) with ESMTPS id 1C0FDC21DA5 for ; Tue, 16 Jan 2018 13:46:45 +0000 (UTC) X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 7E3FCAD02; Tue, 16 Jan 2018 13:46:44 +0000 (UTC) From: Alexander Graf To: u-boot@lists.denx.de Date: Tue, 16 Jan 2018 14:46:37 +0100 Message-Id: <20180116134638.3879-3-agraf@suse.de> X-Mailer: git-send-email 2.12.3 In-Reply-To: <20180116134638.3879-1-agraf@suse.de> References: <20180116134638.3879-1-agraf@suse.de> Subject: [U-Boot] [PATCH 2/3] mmc: Add bcm2835 sdhost controller X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" 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 --- drivers/mmc/Kconfig | 14 + drivers/mmc/Makefile | 1 + drivers/mmc/bcm2835_sdhost.c | 1000 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1015 insertions(+) create mode 100644 drivers/mmc/bcm2835_sdhost.c diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index ab0627a8af..9b90db908b 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -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 diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 64b6f21c61..42113e2603 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -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 diff --git a/drivers/mmc/bcm2835_sdhost.c b/drivers/mmc/bcm2835_sdhost.c new file mode 100644 index 0000000000..62fa68ae69 --- /dev/null +++ b/drivers/mmc/bcm2835_sdhost.c @@ -0,0 +1,1000 @@ +/* + * 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 + * and is based on drivers/mmc/host/bcm2835.c in Linux which is written by + * Phil Elwell + * 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. + * + * 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + +#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 */ +#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 to complete */ + + /* 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"); +} + +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 */ + writel(0, host->ioaddr + SDHCFG); + writel(0, host->ioaddr + SDHBCT); + writel(0, host->ioaddr + SDHBLC); + + /* 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); + 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) || + (fsm == SDEDM_FSM_DATAMODE)) + break; + if (fsm == alternate_idle) { + writel(edm | SDEDM_FORCE_DATA_MODE, + host->ioaddr + SDEDM); + break; + } + + timediff++; + if (timediff == 100000) { + 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; + + blksize = host->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 *)host->data->dest : (u32 *)host->data->src; + + if (is_read) + host->data->dest += len; + else + host->data->src += len; + + copy_words = len / 4; + + 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); + + 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) + *(buf++) = readl(host->ioaddr + SDDATA); + else + writel(*(buf++), host->ioaddr + SDDATA); + words--; + } + } + + if (hsts & SDHSTS_ERROR_MASK) + break; + } + + return 0; +} + +static int bcm2835_transfer_pio(struct bcm2835_host *host) +{ + u32 sdhsts; + bool is_read; + int r = 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; + + /* 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) +{ + 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); + 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) +{ + 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; + } + + 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; + } + + 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) { + return -ETIMEDOUT; + } else { + printf("unexpected command %d error\n", + host->cmd->cmdidx); + bcm2835_dumpregs(host); + return -EILSEQ; + } + } + } + + 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. */ + 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; + + if (!(intmask & SDHSTS_ERROR_MASK)) + return 0; + + if (!host->cmd) + return -EINVAL; + + 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; + } + bcm2835_dumpregs(host); + return r; +} + +static int bcm2835_check_data_error(struct bcm2835_host *host, u32 intmask) +{ + int r = 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); + return; + } + + if (WARN_ON(!host->use_busy)) { + bcm2835_dumpregs(host); + return; + } + host->use_busy = false; + + bcm2835_finish_command(host); +} + +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. + */ + 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; + 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); + + 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 (!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; + int pinctrl_handle; + + 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; + + host->ioaddr = devm_ioremap(dev, host->phys_addr, SZ_256); + if (!host->ioaddr) + return -ENOMEM; + + host->max_clk = bcm2835_get_mmc_clock(); + + pinctrl_handle = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "pinctrl-0", -1); + if (pinctrl_handle != -1) { + struct udevice *dev; + + /* Need to set up pinmuxing */ + if (!uclass_first_device(UCLASS_GPIO, &dev) && dev) + bcm2835_gpio_set_pinmux(dev, pinctrl_handle); + } + + ret = bcm2835_add_host(host); + if (ret) + goto err; + + 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, +}; From patchwork Tue Jan 16 13:46:38 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexander Graf X-Patchwork-Id: 124690 Delivered-To: patch@linaro.org Received: by 10.46.64.148 with SMTP id r20csp1019566lje; Tue, 16 Jan 2018 05:48:12 -0800 (PST) X-Google-Smtp-Source: ACJfBotLz2ZylNRxmWpr6BRrZskXqkt3MdTyfSyok4lhkUYSYNZA0oqWdtyu5IZIhljulwaZAdjP X-Received: by 10.80.190.137 with SMTP id b9mr53002568edk.246.1516110492794; Tue, 16 Jan 2018 05:48:12 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516110492; cv=none; d=google.com; s=arc-20160816; b=j6VugPVL7ngUuxkGIXS1ndbTNKIrfpBdSSnadOgzoaxXLiIAaA+NJsxAV5tj7vF6rD GuAkcq3Q0idW0Xg5RKT54FrUT1U0BEMeZcFA/GklK+7rQWcLENtrlQKPFp2kamJA9Gg5 kgZYgqF8WERlM+WsMHVWhWqUnJ3DVE5kR4CEBPfGxsCa2GF3USMxrRCE4nNL6be24kZx X7qTgI2WMPt9XksvtRodke+5nEA7FKN36kOwz83IM83ZWOillx7vwKOQ1TTvfv7IGRp1 su7azpssVdSdLNQTN4/1QgCNT1MmoHsW4VOXKfuYCM4yAUU35JL6ui+rUj35mdtGjE1j UsEA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:arc-authentication-results; bh=XG3Jc/gqRUoGmHMFj5B4t+SUDstG33qZGE5UoIZfdaY=; b=ZD3kUfM7RmLHz/+eTmC7s2pqQ/cyP6CaAZny6tJjimURRQebxJz3GttLdAamvTtS+6 wqYhbr9irirldDOQrOBydMho62/VO5xXOS5uIr7cLTyLtuGir87IpTS1Ghlz091ws0To w9qC/Ldq/XviOI5zAsR1jTG0eejbRmgThkpVGs5i7HJeO+S++QsDZfO5ghEJd4jQDhTz 9iKqZWAkOePK6w7xXyoqDSd/omKkEEqlmUCIPsRq1zpVJ/ZlQgwx5wK1Al69W6CQQvgk NWf4v/psTktQ7NITI55l6q/zHqcSig2ucqj7ZbsxuvvHevQS4OjRGDZYl+NSWqrHqSxU MbdQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de Return-Path: Received: from lists.denx.de (dione.denx.de. [81.169.180.215]) by mx.google.com with ESMTP id y91si2291692edy.242.2018.01.16.05.48.12; Tue, 16 Jan 2018 05:48:12 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) client-ip=81.169.180.215; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of u-boot-bounces@lists.denx.de designates 81.169.180.215 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de Received: by lists.denx.de (Postfix, from userid 105) id 6BE1FC21DC1; Tue, 16 Jan 2018 13:47:07 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=none autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id ABA22C21C2F; Tue, 16 Jan 2018 13:46:47 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 806DFC21DA6; Tue, 16 Jan 2018 13:46:45 +0000 (UTC) Received: from mx2.suse.de (mx2.suse.de [195.135.220.15]) by lists.denx.de (Postfix) with ESMTPS id 1E2BFC21DA6 for ; Tue, 16 Jan 2018 13:46:45 +0000 (UTC) X-Virus-Scanned: by amavisd-new at test-mx.suse.de Received: from relay2.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 7EC60AD0C; Tue, 16 Jan 2018 13:46:44 +0000 (UTC) From: Alexander Graf To: u-boot@lists.denx.de Date: Tue, 16 Jan 2018 14:46:38 +0100 Message-Id: <20180116134638.3879-4-agraf@suse.de> X-Mailer: git-send-email 2.12.3 In-Reply-To: <20180116134638.3879-1-agraf@suse.de> References: <20180116134638.3879-1-agraf@suse.de> Subject: [U-Boot] [PATCH 3/3] mmc: bcm2835_sdhci: Add pinmux support X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" The bcm2835 firmware provided device trees expect device tree users to support pin muxing for the SD devices to work properly. This patch adds pin muxing support to the sdhci based SD controller on said family of SoCs, so that its pins are getting configured correctly on boot. Signed-off-by: Alexander Graf --- drivers/mmc/bcm2835_sdhci.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c index 3157354d2a..62ad109361 100644 --- a/drivers/mmc/bcm2835_sdhci.c +++ b/drivers/mmc/bcm2835_sdhci.c @@ -45,6 +45,7 @@ #include #include #include +#include /* 400KHz is max freq for card ID etc. Use that as min */ #define MIN_FREQ 400000 @@ -178,6 +179,7 @@ static int bcm2835_sdhci_probe(struct udevice *dev) fdt_addr_t base; int emmc_freq; int ret; + int pinctrl_handle; base = devfdt_get_addr(dev); if (base == FDT_ADDR_T_NONE) @@ -190,6 +192,15 @@ static int bcm2835_sdhci_probe(struct udevice *dev) } emmc_freq = ret; + pinctrl_handle = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "pinctrl-0", -1); + if (pinctrl_handle != -1) { + struct udevice *dev; + + /* Need to set up pinmuxing */ + if (!uclass_first_device(UCLASS_GPIO, &dev) && dev) + bcm2835_gpio_set_pinmux(dev, pinctrl_handle); + } + /* * See the comments in bcm2835_sdhci_raw_writel(). *