diff options
author | Tom Rini <trini@konsulko.com> | 2016-09-27 12:47:25 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2016-09-27 12:47:25 -0400 |
commit | 40e1236afeeacdadfa3865f70fc7e3b8016acbe2 (patch) | |
tree | a915b83d54beceb8a5c4fa424c0bcb6df56238d8 /drivers | |
parent | 6d5565608f385b89f528ecf5680410cdc6cf63e9 (diff) | |
parent | 8e5d804f890b32959cc9d9f9349ccd2ff4a744a0 (diff) | |
download | u-boot-imx-40e1236afeeacdadfa3865f70fc7e3b8016acbe2.zip u-boot-imx-40e1236afeeacdadfa3865f70fc7e3b8016acbe2.tar.gz u-boot-imx-40e1236afeeacdadfa3865f70fc7e3b8016acbe2.tar.bz2 |
Merge branch 'master' of git://git.denx.de/u-boot-tegra
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/tegra/Kconfig | 7 | ||||
-rw-r--r-- | drivers/clk/tegra/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/tegra/tegra-car-clk.c | 103 | ||||
-rw-r--r-- | drivers/i2c/tegra_i2c.c | 68 | ||||
-rw-r--r-- | drivers/misc/Kconfig | 7 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/tegra_car.c | 68 | ||||
-rw-r--r-- | drivers/mmc/tegra_mmc.c | 522 | ||||
-rw-r--r-- | drivers/reset/Kconfig | 7 | ||||
-rw-r--r-- | drivers/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/reset/tegra-car-reset.c | 72 | ||||
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 13 |
12 files changed, 488 insertions, 382 deletions
diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig index 659fe02..ce80b1f 100644 --- a/drivers/clk/tegra/Kconfig +++ b/drivers/clk/tegra/Kconfig @@ -1,3 +1,10 @@ +config TEGRA_CAR_CLOCK + bool "Enable Tegra CAR-based clock driver" + depends on TEGRA_CAR + help + Enable support for manipulating Tegra's on-SoC clocks via direct + register access to the Tegra CAR (Clock And Reset controller). + config TEGRA186_CLOCK bool "Enable Tegra186 BPMP-based clock driver" depends on TEGRA186_BPMP diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index f32998c..0fcc5205 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -2,4 +2,5 @@ # # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_TEGRA_CAR_CLOCK) += tegra-car-clk.o obj-$(CONFIG_TEGRA186_CLOCK) += tegra186-clk.o diff --git a/drivers/clk/tegra/tegra-car-clk.c b/drivers/clk/tegra/tegra-car-clk.c new file mode 100644 index 0000000..b8a2c82 --- /dev/null +++ b/drivers/clk/tegra/tegra-car-clk.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <clk-uclass.h> +#include <dm.h> +#include <asm/arch/clock.h> +#include <asm/arch-tegra/clk_rst.h> + +static int tegra_car_clk_request(struct clk *clk) +{ + debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev, + clk->id); + + /* + * Note that the first PERIPH_ID_COUNT clock IDs (where the value + * varies per SoC) are the peripheral clocks, which use a numbering + * scheme that matches HW registers 1:1. There are other clock IDs + * beyond this that are assigned arbitrarily by the Tegra CAR DT + * binding. Due to the implementation of this driver, it currently + * only supports the peripheral IDs. + */ + if (clk->id >= PERIPH_ID_COUNT) + return -EINVAL; + + return 0; +} + +static int tegra_car_clk_free(struct clk *clk) +{ + debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev, + clk->id); + + return 0; +} + +static ulong tegra_car_clk_get_rate(struct clk *clk) +{ + enum clock_id parent; + + debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev, + clk->id); + + parent = clock_get_periph_parent(clk->id); + return clock_get_periph_rate(clk->id, parent); +} + +static ulong tegra_car_clk_set_rate(struct clk *clk, ulong rate) +{ + enum clock_id parent; + + debug("%s(clk=%p, rate=%lu) (dev=%p, id=%lu)\n", __func__, clk, rate, + clk->dev, clk->id); + + parent = clock_get_periph_parent(clk->id); + return clock_adjust_periph_pll_div(clk->id, parent, rate, NULL); +} + +static int tegra_car_clk_enable(struct clk *clk) +{ + debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev, + clk->id); + + clock_enable(clk->id); + + return 0; +} + +static int tegra_car_clk_disable(struct clk *clk) +{ + debug("%s(clk=%p) (dev=%p, id=%lu)\n", __func__, clk, clk->dev, + clk->id); + + clock_disable(clk->id); + + return 0; +} + +static struct clk_ops tegra_car_clk_ops = { + .request = tegra_car_clk_request, + .free = tegra_car_clk_free, + .get_rate = tegra_car_clk_get_rate, + .set_rate = tegra_car_clk_set_rate, + .enable = tegra_car_clk_enable, + .disable = tegra_car_clk_disable, +}; + +static int tegra_car_clk_probe(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +U_BOOT_DRIVER(tegra_car_clk) = { + .name = "tegra_car_clk", + .id = UCLASS_CLK, + .probe = tegra_car_clk_probe, + .ops = &tegra_car_clk_ops, +}; diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index 0dbcc5a..898f12a 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -12,27 +12,15 @@ #include <fdtdec.h> #include <i2c.h> #include <asm/io.h> -#ifdef CONFIG_TEGRA186 #include <clk.h> #include <reset.h> -#else +#ifndef CONFIG_TEGRA186 #include <asm/arch/clock.h> #include <asm/arch/funcmux.h> -#include <asm/arch/pinmux.h> -#include <asm/arch-tegra/clk_rst.h> #endif #include <asm/arch/gpio.h> #include <asm/arch-tegra/tegra_i2c.h> -/* - * FIXME: TODO: This driver contains a number of ifdef CONFIG_TEGRA186 that - * should not be present. These are needed because newer Tegra SoCs support - * only the standard clock/reset APIs, whereas older Tegra SoCs support only - * a custom Tegra-specific API. ASAP the older Tegra SoCs' code should be - * fixed to implement the standard APIs, and all drivers converted to solely - * use the new standard APIs, with no ifdefs. - */ - DECLARE_GLOBAL_DATA_PTR; enum i2c_type { @@ -44,12 +32,8 @@ enum i2c_type { /* Information about i2c controller */ struct i2c_bus { int id; -#ifdef CONFIG_TEGRA186 struct reset_ctl reset_ctl; struct clk clk; -#else - enum periph_id periph_id; -#endif int speed; int pinmux_config; struct i2c_control *control; @@ -81,20 +65,15 @@ static void set_packet_mode(struct i2c_bus *i2c_bus) static void i2c_reset_controller(struct i2c_bus *i2c_bus) { /* Reset I2C controller. */ -#ifdef CONFIG_TEGRA186 reset_assert(&i2c_bus->reset_ctl); udelay(1); reset_deassert(&i2c_bus->reset_ctl); udelay(1); -#else - reset_periph(i2c_bus->periph_id, 1); -#endif /* re-program config register to packet mode */ set_packet_mode(i2c_bus); } -#ifdef CONFIG_TEGRA186 static int i2c_init_clock(struct i2c_bus *i2c_bus, unsigned rate) { int ret; @@ -114,7 +93,6 @@ static int i2c_init_clock(struct i2c_bus *i2c_bus, unsigned rate) return 0; } -#endif static void i2c_init_controller(struct i2c_bus *i2c_bus) { @@ -126,12 +104,7 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus) * here, in section 23.3.1, but in fact we seem to need a factor of * 16 to get the right frequency. */ -#ifdef CONFIG_TEGRA186 i2c_init_clock(i2c_bus, i2c_bus->speed * 2 * 8); -#else - clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, - i2c_bus->speed * 2 * 8); -#endif if (i2c_bus->type == TYPE_114) { /* @@ -151,12 +124,7 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus) debug("%s: CLK_DIV_STD_FAST_MODE setting = %d\n", __func__, clk_div_stdfst_mode); -#ifdef CONFIG_TEGRA186 i2c_init_clock(i2c_bus, rate); -#else - clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, - rate); -#endif } /* Reset I2C controller. */ @@ -170,7 +138,7 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus) } #ifndef CONFIG_TEGRA186 - funcmux_select(i2c_bus->periph_id, i2c_bus->pinmux_config); + funcmux_select(i2c_bus->clk.id, i2c_bus->pinmux_config); #endif } @@ -392,23 +360,13 @@ static int tegra_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) static int tegra_i2c_probe(struct udevice *dev) { struct i2c_bus *i2c_bus = dev_get_priv(dev); -#ifdef CONFIG_TEGRA186 int ret; -#else - const void *blob = gd->fdt_blob; - int node = dev->of_offset; -#endif bool is_dvc; i2c_bus->id = dev->seq; i2c_bus->type = dev_get_driver_data(dev); i2c_bus->regs = (struct i2c_ctlr *)dev_get_addr(dev); - /* - * We don't have a binding for pinmux yet. Leave it out for now. So - * far no one needs anything other than the default. - */ -#ifdef CONFIG_TEGRA186 ret = reset_get_by_name(dev, "i2c", &i2c_bus->reset_ctl); if (ret) { error("reset_get_by_name() failed: %d\n", ret); @@ -419,9 +377,13 @@ static int tegra_i2c_probe(struct udevice *dev) error("clk_get_by_name() failed: %d\n", ret); return ret; } -#else + +#ifndef CONFIG_TEGRA186 + /* + * We don't have a binding for pinmux yet. Leave it out for now. So + * far no one needs anything other than the default. + */ i2c_bus->pinmux_config = FUNCMUX_DEFAULT; - i2c_bus->periph_id = clock_decode_periph_id(blob, node); /* * We can't specify the pinmux config in the fdt, so I2C2 will not @@ -429,11 +391,9 @@ static int tegra_i2c_probe(struct udevice *dev) * You could add in this little hack if you need to use it. * The correct solution is a pinmux binding in the fdt. * - * if (i2c_bus->periph_id == PERIPH_ID_I2C2) + * if (i2c_bus->clk.id == PERIPH_ID_I2C2) * i2c_bus->pinmux_config = FUNCMUX_I2C2_PTA; */ - if (i2c_bus->periph_id == -1) - return -EINVAL; #endif is_dvc = dev_get_driver_data(dev) == TYPE_DVC; @@ -444,14 +404,8 @@ static int tegra_i2c_probe(struct udevice *dev) i2c_bus->control = &i2c_bus->regs->control; } i2c_init_controller(i2c_bus); - debug("%s: controller bus %d at %p, periph_id %d, speed %d: ", - is_dvc ? "dvc" : "i2c", dev->seq, i2c_bus->regs, -#ifndef CONFIG_TEGRA186 - i2c_bus->periph_id, -#else - -1, -#endif - i2c_bus->speed); + debug("%s: controller bus %d at %p, speed %d: ", + is_dvc ? "dvc" : "i2c", dev->seq, i2c_bus->regs, i2c_bus->speed); return 0; } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 8990489..1aae4bc 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -129,6 +129,13 @@ config PCA9551_I2C_ADDR help The I2C address of the PCA9551 LED controller. +config TEGRA_CAR + bool "Enable support for the Tegra CAR driver" + depends on TEGRA_NO_BPMP + help + The Tegra CAR (Clock and Reset Controller) is a HW module that + controls almost all clocks and resets in a Tegra SoC. + config TEGRA186_BPMP bool "Enable support for the Tegra186 BPMP driver" depends on TEGRA186 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index e4f2464..9fbb5a7 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_SANDBOX) += spltest_sandbox.o endif endif obj-$(CONFIG_SANDBOX) += syscon_sandbox.o +obj-$(CONFIG_TEGRA_CAR) += tegra_car.o obj-$(CONFIG_TEGRA186_BPMP) += tegra186_bpmp.o obj-$(CONFIG_TWL4030_LED) += twl4030_led.o obj-$(CONFIG_FSL_IFC) += fsl_ifc.o diff --git a/drivers/misc/tegra_car.c b/drivers/misc/tegra_car.c new file mode 100644 index 0000000..0eb0096 --- /dev/null +++ b/drivers/misc/tegra_car.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <dm/lists.h> +#include <dm/root.h> + +/** + * The CAR exposes multiple different services. We create a sub-device for + * each separate type of service, since each device must be of the appropriate + * UCLASS. + */ +static int tegra_car_bpmp_bind(struct udevice *dev) +{ + int ret; + struct udevice *child; + + debug("%s(dev=%p)\n", __func__, dev); + + ret = device_bind_driver_to_node(dev, "tegra_car_clk", "tegra_car_clk", + dev->of_offset, &child); + if (ret) + return ret; + + ret = device_bind_driver_to_node(dev, "tegra_car_reset", + "tegra_car_reset", dev->of_offset, + &child); + if (ret) + return ret; + + return 0; +} + +static int tegra_car_bpmp_probe(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +static int tegra_car_bpmp_remove(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +static const struct udevice_id tegra_car_bpmp_ids[] = { + { .compatible = "nvidia,tegra20-car" }, + { .compatible = "nvidia,tegra30-car" }, + { .compatible = "nvidia,tegra114-car" }, + { .compatible = "nvidia,tegra124-car" }, + { .compatible = "nvidia,tegra210-car" }, + { } +}; + +U_BOOT_DRIVER(tegra_car_bpmp) = { + .name = "tegra_car", + .id = UCLASS_MISC, + .of_match = tegra_car_bpmp_ids, + .bind = tegra_car_bpmp_bind, + .probe = tegra_car_bpmp_probe, + .remove = tegra_car_bpmp_remove, +}; diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index 3d0845e..97b1154 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -2,7 +2,7 @@ * (C) Copyright 2009 SAMSUNG Electronics * Minkyu Kang <mk7.kang@samsung.com> * Jaehoon Chung <jh80.chung@samsung.com> - * Portions Copyright 2011-2015 NVIDIA Corporation + * Portions Copyright 2011-2016 NVIDIA Corporation * * SPDX-License-Identifier: GPL-2.0+ */ @@ -13,32 +13,26 @@ #include <errno.h> #include <asm/gpio.h> #include <asm/io.h> -#ifndef CONFIG_TEGRA186 -#include <asm/arch/clock.h> -#include <asm/arch-tegra/clk_rst.h> -#endif -#include <asm/arch-tegra/mmc.h> #include <asm/arch-tegra/tegra_mmc.h> #include <mmc.h> -/* - * FIXME: TODO: This driver contains a number of ifdef CONFIG_TEGRA186 that - * should not be present. These are needed because newer Tegra SoCs support - * only the standard clock/reset APIs, whereas older Tegra SoCs support only - * a custom Tegra-specific API. ASAP the older Tegra SoCs' code should be - * fixed to implement the standard APIs, and all drivers converted to solely - * use the new standard APIs, with no ifdefs. - */ - DECLARE_GLOBAL_DATA_PTR; -struct mmc_host mmc_host[CONFIG_SYS_MMC_MAX_DEVICE]; - -#if !CONFIG_IS_ENABLED(OF_CONTROL) -#error "Please enable device tree support to use this driver" -#endif +struct tegra_mmc_priv { + struct tegra_mmc *reg; + struct reset_ctl reset_ctl; + struct clk clk; + struct gpio_desc cd_gpio; /* Change Detect GPIO */ + struct gpio_desc pwr_gpio; /* Power GPIO */ + struct gpio_desc wp_gpio; /* Write Protect GPIO */ + unsigned int version; /* SDHCI spec. version */ + unsigned int clock; /* Current clock (MHz) */ + struct mmc_config cfg; /* mmc configuration */ + struct mmc *mmc; +}; -static void mmc_set_power(struct mmc_host *host, unsigned short power) +static void tegra_mmc_set_power(struct tegra_mmc_priv *priv, + unsigned short power) { u8 pwr = 0; debug("%s: power = %x\n", __func__, power); @@ -61,17 +55,18 @@ static void mmc_set_power(struct mmc_host *host, unsigned short power) debug("%s: pwr = %X\n", __func__, pwr); /* Set the bus voltage first (if any) */ - writeb(pwr, &host->reg->pwrcon); + writeb(pwr, &priv->reg->pwrcon); if (pwr == 0) return; /* Now enable bus power */ pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; - writeb(pwr, &host->reg->pwrcon); + writeb(pwr, &priv->reg->pwrcon); } -static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, - struct bounce_buffer *bbstate) +static void tegra_mmc_prepare_data(struct tegra_mmc_priv *priv, + struct mmc_data *data, + struct bounce_buffer *bbstate) { unsigned char ctrl; @@ -80,7 +75,7 @@ static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, bbstate->bounce_buffer, bbstate->user_buffer, data->blocks, data->blocksize); - writel((u32)(unsigned long)bbstate->bounce_buffer, &host->reg->sysad); + writel((u32)(unsigned long)bbstate->bounce_buffer, &priv->reg->sysad); /* * DMASEL[4:3] * 00 = Selects SDMA @@ -88,17 +83,18 @@ static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, * 10 = Selects 32-bit Address ADMA2 * 11 = Selects 64-bit Address ADMA2 */ - ctrl = readb(&host->reg->hostctl); + ctrl = readb(&priv->reg->hostctl); ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; - writeb(ctrl, &host->reg->hostctl); + writeb(ctrl, &priv->reg->hostctl); /* We do not handle DMA boundaries, so set it to max (512 KiB) */ - writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); - writew(data->blocks, &host->reg->blkcnt); + writew((7 << 12) | (data->blocksize & 0xFFF), &priv->reg->blksize); + writew(data->blocks, &priv->reg->blkcnt); } -static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) +static void tegra_mmc_set_transfer_mode(struct tegra_mmc_priv *priv, + struct mmc_data *data) { unsigned short mode; debug(" mmc_set_transfer_mode called\n"); @@ -121,13 +117,13 @@ static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) if (data->flags & MMC_DATA_READ) mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; - writew(mode, &host->reg->trnmod); + writew(mode, &priv->reg->trnmod); } -static int mmc_wait_inhibit(struct mmc_host *host, - struct mmc_cmd *cmd, - struct mmc_data *data, - unsigned int timeout) +static int tegra_mmc_wait_inhibit(struct tegra_mmc_priv *priv, + struct mmc_cmd *cmd, + struct mmc_data *data, + unsigned int timeout) { /* * PRNSTS @@ -143,7 +139,7 @@ static int mmc_wait_inhibit(struct mmc_host *host, if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; - while (readl(&host->reg->prnsts) & mask) { + while (readl(&priv->reg->prnsts) & mask) { if (timeout == 0) { printf("%s: timeout error\n", __func__); return -1; @@ -155,29 +151,30 @@ static int mmc_wait_inhibit(struct mmc_host *host, return 0; } -static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data, struct bounce_buffer *bbstate) +static int tegra_mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data, + struct bounce_buffer *bbstate) { - struct mmc_host *host = mmc->priv; + struct tegra_mmc_priv *priv = mmc->priv; int flags, i; int result; unsigned int mask = 0; unsigned int retry = 0x100000; debug(" mmc_send_cmd called\n"); - result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); + result = tegra_mmc_wait_inhibit(priv, cmd, data, 10 /* ms */); if (result < 0) return result; if (data) - mmc_prepare_data(host, data, bbstate); + tegra_mmc_prepare_data(priv, data, bbstate); debug("cmd->arg: %08x\n", cmd->cmdarg); - writel(cmd->cmdarg, &host->reg->argument); + writel(cmd->cmdarg, &priv->reg->argument); if (data) - mmc_set_transfer_mode(host, data); + tegra_mmc_set_transfer_mode(priv, data); if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) return -1; @@ -212,33 +209,33 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, debug("cmd: %d\n", cmd->cmdidx); - writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); + writew((cmd->cmdidx << 8) | flags, &priv->reg->cmdreg); for (i = 0; i < retry; i++) { - mask = readl(&host->reg->norintsts); + mask = readl(&priv->reg->norintsts); /* Command Complete */ if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { if (!data) - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); break; } } if (i == retry) { printf("%s: waiting for status update\n", __func__); - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); return -ETIMEDOUT; } if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { /* Timeout Error */ debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); return -ETIMEDOUT; } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { /* Error Interrupt */ debug("error: %08x cmd %d\n", mask, cmd->cmdidx); - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); return -1; } @@ -246,8 +243,8 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, if (cmd->resp_type & MMC_RSP_136) { /* CRC is stripped so we need to do some shifting. */ for (i = 0; i < 4; i++) { - unsigned long offset = - (unsigned long)(&host->reg->rspreg3 - i); + unsigned long offset = (unsigned long) + (&priv->reg->rspreg3 - i); cmd->response[i] = readl(offset) << 8; if (i != 3) { @@ -260,21 +257,21 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, } else if (cmd->resp_type & MMC_RSP_BUSY) { for (i = 0; i < retry; i++) { /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ - if (readl(&host->reg->prnsts) + if (readl(&priv->reg->prnsts) & (1 << 20)) /* DAT[0] */ break; } if (i == retry) { printf("%s: card is still busy\n", __func__); - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); return -ETIMEDOUT; } - cmd->response[0] = readl(&host->reg->rspreg0); + cmd->response[0] = readl(&priv->reg->rspreg0); debug("cmd->resp[0]: %08x\n", cmd->response[0]); } else { - cmd->response[0] = readl(&host->reg->rspreg0); + cmd->response[0] = readl(&priv->reg->rspreg0); debug("cmd->resp[0]: %08x\n", cmd->response[0]); } } @@ -283,11 +280,11 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, unsigned long start = get_timer(0); while (1) { - mask = readl(&host->reg->norintsts); + mask = readl(&priv->reg->norintsts); if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { /* Error Interrupt */ - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); printf("%s: error during transfer: 0x%08x\n", __func__, mask); return -1; @@ -296,31 +293,31 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, * DMA Interrupt, restart the transfer where * it was interrupted. */ - unsigned int address = readl(&host->reg->sysad); + unsigned int address = readl(&priv->reg->sysad); debug("DMA end\n"); writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, - &host->reg->norintsts); - writel(address, &host->reg->sysad); + &priv->reg->norintsts); + writel(address, &priv->reg->sysad); } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { /* Transfer Complete */ debug("r/w is done\n"); break; } else if (get_timer(start) > 8000UL) { - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); printf("%s: MMC Timeout\n" " Interrupt status 0x%08x\n" " Interrupt status enable 0x%08x\n" " Interrupt signal enable 0x%08x\n" " Present status 0x%08x\n", __func__, mask, - readl(&host->reg->norintstsen), - readl(&host->reg->norintsigen), - readl(&host->reg->prnsts)); + readl(&priv->reg->norintstsen), + readl(&priv->reg->norintsigen), + readl(&priv->reg->prnsts)); return -1; } } - writel(mask, &host->reg->norintsts); + writel(mask, &priv->reg->norintsts); } udelay(1000); @@ -328,7 +325,7 @@ static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd, } static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) + struct mmc_data *data) { void *buf; unsigned int bbflags; @@ -349,7 +346,7 @@ static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, bounce_buffer_start(&bbstate, buf, len, bbflags); } - ret = mmc_send_cmd_bounced(mmc, cmd, data, &bbstate); + ret = tegra_mmc_send_cmd_bounced(mmc, cmd, data, &bbstate); if (data) bounce_buffer_stop(&bbstate); @@ -357,8 +354,9 @@ static int tegra_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return ret; } -static void mmc_change_clock(struct mmc_host *host, uint clock) +static void tegra_mmc_change_clock(struct tegra_mmc_priv *priv, uint clock) { + ulong rate; int div; unsigned short clk; unsigned long timeout; @@ -370,18 +368,12 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) */ if (clock == 0) goto out; -#ifdef CONFIG_TEGRA186 - { - ulong rate = clk_set_rate(&host->clk, clock); - div = (rate + clock - 1) / clock; - } -#else - clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock, - &div); -#endif + + rate = clk_set_rate(&priv->clk, clock); + div = (rate + clock - 1) / clock; debug("div = %d\n", div); - writew(0, &host->reg->clkcon); + writew(0, &priv->reg->clkcon); /* * CLKCON @@ -393,11 +385,11 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) div >>= 1; clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); - writew(clk, &host->reg->clkcon); + writew(clk, &priv->reg->clkcon); /* Wait max 10 ms */ timeout = 10; - while (!(readw(&host->reg->clkcon) & + while (!(readw(&priv->reg->clkcon) & TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { if (timeout == 0) { printf("%s: timeout error\n", __func__); @@ -408,26 +400,26 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) } clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; - writew(clk, &host->reg->clkcon); + writew(clk, &priv->reg->clkcon); debug("mmc_change_clock: clkcon = %08X\n", clk); out: - host->clock = clock; + priv->clock = clock; } static void tegra_mmc_set_ios(struct mmc *mmc) { - struct mmc_host *host = mmc->priv; + struct tegra_mmc_priv *priv = mmc->priv; unsigned char ctrl; debug(" mmc_set_ios called\n"); debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); /* Change clock first */ - mmc_change_clock(host, mmc->clock); + tegra_mmc_change_clock(priv, mmc->clock); - ctrl = readb(&host->reg->hostctl); + ctrl = readb(&priv->reg->hostctl); /* * WIDE8[5] @@ -444,11 +436,38 @@ static void tegra_mmc_set_ios(struct mmc *mmc) else ctrl &= ~(1 << 1); - writeb(ctrl, &host->reg->hostctl); + writeb(ctrl, &priv->reg->hostctl); debug("mmc_set_ios: hostctl = %08X\n", ctrl); } -static void mmc_reset(struct mmc_host *host, struct mmc *mmc) +static void tegra_mmc_pad_init(struct tegra_mmc_priv *priv) +{ +#if defined(CONFIG_TEGRA30) + u32 val; + + debug("%s: sdmmc address = %08x\n", __func__, (unsigned int)priv->reg); + + /* Set the pad drive strength for SDMMC1 or 3 only */ + if (priv->reg != (void *)0x78000000 && + priv->reg != (void *)0x78000400) { + debug("%s: settings are only valid for SDMMC1/SDMMC3!\n", + __func__); + return; + } + + val = readl(&priv->reg->sdmemcmppadctl); + val &= 0xFFFFFFF0; + val |= MEMCOMP_PADCTRL_VREF; + writel(val, &priv->reg->sdmemcmppadctl); + + val = readl(&priv->reg->autocalcfg); + val &= 0xFFFF0000; + val |= AUTO_CAL_PU_OFFSET | AUTO_CAL_PD_OFFSET | AUTO_CAL_ENABLED; + writel(val, &priv->reg->autocalcfg); +#endif +} + +static void tegra_mmc_reset(struct tegra_mmc_priv *priv, struct mmc *mmc) { unsigned int timeout; debug(" mmc_reset called\n"); @@ -458,15 +477,15 @@ static void mmc_reset(struct mmc_host *host, struct mmc *mmc) * 1 = reset * 0 = work */ - writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); + writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &priv->reg->swrst); - host->clock = 0; + priv->clock = 0; /* Wait max 100 ms */ timeout = 100; /* hw clears the bit when it's done */ - while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { + while (readb(&priv->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { if (timeout == 0) { printf("%s: timeout error\n", __func__); return; @@ -476,30 +495,30 @@ static void mmc_reset(struct mmc_host *host, struct mmc *mmc) } /* Set SD bus voltage & enable bus power */ - mmc_set_power(host, fls(mmc->cfg->voltages) - 1); + tegra_mmc_set_power(priv, fls(mmc->cfg->voltages) - 1); debug("%s: power control = %02X, host control = %02X\n", __func__, - readb(&host->reg->pwrcon), readb(&host->reg->hostctl)); + readb(&priv->reg->pwrcon), readb(&priv->reg->hostctl)); /* Make sure SDIO pads are set up */ - pad_init_mmc(host); + tegra_mmc_pad_init(priv); } -static int tegra_mmc_core_init(struct mmc *mmc) +static int tegra_mmc_init(struct mmc *mmc) { - struct mmc_host *host = mmc->priv; + struct tegra_mmc_priv *priv = mmc->priv; unsigned int mask; - debug(" mmc_core_init called\n"); + debug(" tegra_mmc_init called\n"); - mmc_reset(host, mmc); + tegra_mmc_reset(priv, mmc); - host->version = readw(&host->reg->hcver); - debug("host version = %x\n", host->version); + priv->version = readw(&priv->reg->hcver); + debug("host version = %x\n", priv->version); /* mask all */ - writel(0xffffffff, &host->reg->norintstsen); - writel(0xffffffff, &host->reg->norintsigen); + writel(0xffffffff, &priv->reg->norintstsen); + writel(0xffffffff, &priv->reg->norintsigen); - writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ + writeb(0xe, &priv->reg->timeoutcon); /* TMCLK * 2^27 */ /* * NORMAL Interrupt Status Enable Register init * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable @@ -508,35 +527,35 @@ static int tegra_mmc_core_init(struct mmc *mmc) * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable * [0] ENSTACMDCMPLT : Command Complete Status Enable */ - mask = readl(&host->reg->norintstsen); + mask = readl(&priv->reg->norintstsen); mask &= ~(0xffff); mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); - writel(mask, &host->reg->norintstsen); + writel(mask, &priv->reg->norintstsen); /* * NORMAL Interrupt Signal Enable Register init * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable */ - mask = readl(&host->reg->norintsigen); + mask = readl(&priv->reg->norintsigen); mask &= ~(0xffff); mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; - writel(mask, &host->reg->norintsigen); + writel(mask, &priv->reg->norintsigen); return 0; } static int tegra_mmc_getcd(struct mmc *mmc) { - struct mmc_host *host = mmc->priv; + struct tegra_mmc_priv *priv = mmc->priv; debug("tegra_mmc_getcd called\n"); - if (dm_gpio_is_valid(&host->cd_gpio)) - return dm_gpio_get_value(&host->cd_gpio); + if (dm_gpio_is_valid(&priv->cd_gpio)) + return dm_gpio_get_value(&priv->cd_gpio); return 1; } @@ -544,61 +563,29 @@ static int tegra_mmc_getcd(struct mmc *mmc) static const struct mmc_ops tegra_mmc_ops = { .send_cmd = tegra_mmc_send_cmd, .set_ios = tegra_mmc_set_ios, - .init = tegra_mmc_core_init, + .init = tegra_mmc_init, .getcd = tegra_mmc_getcd, }; -static int do_mmc_init(int dev_index, bool removable) +static int tegra_mmc_probe(struct udevice *dev) { - struct mmc_host *host; - struct mmc *mmc; -#ifdef CONFIG_TEGRA186 - int ret; -#endif + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct tegra_mmc_priv *priv = dev_get_priv(dev); + int bus_width, ret; - /* DT should have been read & host config filled in */ - host = &mmc_host[dev_index]; - if (!host->enabled) - return -1; - - debug(" do_mmc_init: index %d, bus width %d pwr_gpio %d cd_gpio %d\n", - dev_index, host->width, gpio_get_number(&host->pwr_gpio), - gpio_get_number(&host->cd_gpio)); - - host->clock = 0; - -#ifdef CONFIG_TEGRA186 - ret = reset_assert(&host->reset_ctl); - if (ret) - return ret; - ret = clk_enable(&host->clk); - if (ret) - return ret; - ret = clk_set_rate(&host->clk, 20000000); - if (IS_ERR_VALUE(ret)) - return ret; - ret = reset_deassert(&host->reset_ctl); - if (ret) - return ret; -#else - clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); -#endif - - if (dm_gpio_is_valid(&host->pwr_gpio)) - dm_gpio_set_value(&host->pwr_gpio, 1); - - memset(&host->cfg, 0, sizeof(host->cfg)); + priv->cfg.name = "Tegra SD/MMC"; + priv->cfg.ops = &tegra_mmc_ops; - host->cfg.name = "Tegra SD/MMC"; - host->cfg.ops = &tegra_mmc_ops; + bus_width = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "bus-width", + 1); - host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; - host->cfg.host_caps = 0; - if (host->width == 8) - host->cfg.host_caps |= MMC_MODE_8BIT; - if (host->width >= 4) - host->cfg.host_caps |= MMC_MODE_4BIT; - host->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + priv->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + priv->cfg.host_caps = 0; + if (bus_width == 8) + priv->cfg.host_caps |= MMC_MODE_8BIT; + if (bus_width >= 4) + priv->cfg.host_caps |= MMC_MODE_4BIT; + priv->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; /* * min freq is for card identification, and is the highest @@ -606,182 +593,71 @@ static int do_mmc_init(int dev_index, bool removable) * max freq is highest HS eMMC clock as per the SD/MMC spec * (actually 52MHz) */ - host->cfg.f_min = 375000; - host->cfg.f_max = 48000000; - - host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; - - mmc = mmc_create(&host->cfg, host); - mmc->block_dev.removable = removable; - if (mmc == NULL) - return -1; - - return 0; -} - -/** - * Get the host address and peripheral ID for a node. - * - * @param blob fdt blob - * @param node Device index (0-3) - * @param host Structure to fill in (reg, width, mmc_id) - */ -static int mmc_get_config(const void *blob, int node, struct mmc_host *host, - bool *removablep) -{ - debug("%s: node = %d\n", __func__, node); + priv->cfg.f_min = 375000; + priv->cfg.f_max = 48000000; - host->enabled = fdtdec_get_is_enabled(blob, node); + priv->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; - host->reg = (struct tegra_mmc *)fdtdec_get_addr(blob, node, "reg"); - if ((fdt_addr_t)host->reg == FDT_ADDR_T_NONE) { - debug("%s: no sdmmc base reg info found\n", __func__); - return -FDT_ERR_NOTFOUND; - } + priv->reg = (void *)dev_get_addr(dev); -#ifdef CONFIG_TEGRA186 - { - /* - * FIXME: This variable should go away when the MMC device - * actually is a udevice. - */ - struct udevice dev; - int ret; - dev.of_offset = node; - ret = reset_get_by_name(&dev, "sdhci", &host->reset_ctl); - if (ret) { - debug("reset_get_by_name() failed: %d\n", ret); - return ret; - } - ret = clk_get_by_index(&dev, 0, &host->clk); - if (ret) { - debug("clk_get_by_index() failed: %d\n", ret); - return ret; - } + ret = reset_get_by_name(dev, "sdhci", &priv->reset_ctl); + if (ret) { + debug("reset_get_by_name() failed: %d\n", ret); + return ret; } -#else - host->mmc_id = clock_decode_periph_id(blob, node); - if (host->mmc_id == PERIPH_ID_NONE) { - debug("%s: could not decode periph id\n", __func__); - return -FDT_ERR_NOTFOUND; + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret) { + debug("clk_get_by_index() failed: %d\n", ret); + return ret; } -#endif - /* - * NOTE: mmc->bus_width is determined by mmc.c dynamically. - * TBD: Override it with this value? - */ - host->width = fdtdec_get_int(blob, node, "bus-width", 0); - if (!host->width) - debug("%s: no sdmmc width found\n", __func__); + ret = reset_assert(&priv->reset_ctl); + if (ret) + return ret; + ret = clk_enable(&priv->clk); + if (ret) + return ret; + ret = clk_set_rate(&priv->clk, 20000000); + if (IS_ERR_VALUE(ret)) + return ret; + ret = reset_deassert(&priv->reset_ctl); + if (ret) + return ret; /* These GPIOs are optional */ - gpio_request_by_name_nodev(blob, node, "cd-gpios", 0, &host->cd_gpio, - GPIOD_IS_IN); - gpio_request_by_name_nodev(blob, node, "wp-gpios", 0, &host->wp_gpio, - GPIOD_IS_IN); - gpio_request_by_name_nodev(blob, node, "power-gpios", 0, - &host->pwr_gpio, GPIOD_IS_OUT); - *removablep = !fdtdec_get_bool(blob, node, "non-removable"); - - debug("%s: found controller at %p, width = %d, periph_id = %d\n", - __func__, host->reg, host->width, -#ifndef CONFIG_TEGRA186 - host->mmc_id -#else - -1 -#endif - ); - return 0; -} - -/* - * Process a list of nodes, adding them to our list of SDMMC ports. - * - * @param blob fdt blob - * @param node_list list of nodes to process (any <=0 are ignored) - * @param count number of nodes to process - * @return 0 if ok, -1 on error - */ -static int process_nodes(const void *blob, int node_list[], int count) -{ - struct mmc_host *host; - bool removable; - int i, node; - - debug("%s: count = %d\n", __func__, count); - - /* build mmc_host[] for each controller */ - for (i = 0; i < count; i++) { - node = node_list[i]; - if (node <= 0) - continue; + gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, + GPIOD_IS_IN); + gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, + GPIOD_IS_IN); + gpio_request_by_name(dev, "power-gpios", 0, + &priv->pwr_gpio, GPIOD_IS_OUT); + if (dm_gpio_is_valid(&priv->pwr_gpio)) + dm_gpio_set_value(&priv->pwr_gpio, 1); + + priv->mmc = mmc_create(&priv->cfg, priv); + if (priv->mmc == NULL) + return -1; - host = &mmc_host[i]; - host->id = i; + priv->mmc->dev = dev; + upriv->mmc = priv->mmc; - if (mmc_get_config(blob, node, host, &removable)) { - printf("%s: failed to decode dev %d\n", __func__, i); - return -1; - } - do_mmc_init(i, removable); - } return 0; } -void tegra_mmc_init(void) -{ - int node_list[CONFIG_SYS_MMC_MAX_DEVICE], count; - const void *blob = gd->fdt_blob; - debug("%s entry\n", __func__); - - /* See if any Tegra186 MMC controllers are present */ - count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA186_SDMMC, node_list, - CONFIG_SYS_MMC_MAX_DEVICE); - debug("%s: count of Tegra186 sdhci nodes is %d\n", __func__, count); - if (process_nodes(blob, node_list, count)) { - printf("%s: Error processing T186 mmc node(s)!\n", __func__); - return; - } - - /* See if any Tegra210 MMC controllers are present */ - count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA210_SDMMC, node_list, - CONFIG_SYS_MMC_MAX_DEVICE); - debug("%s: count of Tegra210 sdhci nodes is %d\n", __func__, count); - if (process_nodes(blob, node_list, count)) { - printf("%s: Error processing T210 mmc node(s)!\n", __func__); - return; - } - - /* See if any Tegra124 MMC controllers are present */ - count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA124_SDMMC, node_list, - CONFIG_SYS_MMC_MAX_DEVICE); - debug("%s: count of Tegra124 sdhci nodes is %d\n", __func__, count); - if (process_nodes(blob, node_list, count)) { - printf("%s: Error processing T124 mmc node(s)!\n", __func__); - return; - } - - /* See if any Tegra30 MMC controllers are present */ - count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, - CONFIG_SYS_MMC_MAX_DEVICE); - debug("%s: count of T30 sdhci nodes is %d\n", __func__, count); - if (process_nodes(blob, node_list, count)) { - printf("%s: Error processing T30 mmc node(s)!\n", __func__); - return; - } +static const struct udevice_id tegra_mmc_ids[] = { + { .compatible = "nvidia,tegra20-sdhci" }, + { .compatible = "nvidia,tegra30-sdhci" }, + { .compatible = "nvidia,tegra114-sdhci" }, + { .compatible = "nvidia,tegra124-sdhci" }, + { .compatible = "nvidia,tegra210-sdhci" }, + { .compatible = "nvidia,tegra186-sdhci" }, + { } +}; - /* Now look for any Tegra20 MMC controllers */ - count = fdtdec_find_aliases_for_id(blob, "sdhci", - COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, - CONFIG_SYS_MMC_MAX_DEVICE); - debug("%s: count of T20 sdhci nodes is %d\n", __func__, count); - if (process_nodes(blob, node_list, count)) { - printf("%s: Error processing T20 mmc node(s)!\n", __func__); - return; - } -} +U_BOOT_DRIVER(tegra_mmc_drv) = { + .name = "tegra_mmc", + .id = UCLASS_MMC, + .of_match = tegra_mmc_ids, + .probe = tegra_mmc_probe, + .priv_auto_alloc_size = sizeof(struct tegra_mmc_priv), +}; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 5b84f21..4fcc0d9 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -20,6 +20,13 @@ config SANDBOX_RESET simply accepts requests to reset various HW modules without actually doing anything beyond a little error checking. +config TEGRA_CAR_RESET + bool "Enable Tegra CAR-based reset driver" + depends on TEGRA_CAR + help + Enable support for manipulating Tegra's on-SoC reset signals via + direct register access to the Tegra CAR (Clock And Reset controller). + config TEGRA186_RESET bool "Enable Tegra186 BPMP-based reset driver" depends on TEGRA186_BPMP diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index ff0e090..5d4ea3d 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_DM_RESET) += reset-uclass.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset-test.o +obj-$(CONFIG_TEGRA_CAR_RESET) += tegra-car-reset.o obj-$(CONFIG_TEGRA186_RESET) += tegra186-reset.o diff --git a/drivers/reset/tegra-car-reset.c b/drivers/reset/tegra-car-reset.c new file mode 100644 index 0000000..3147a50 --- /dev/null +++ b/drivers/reset/tegra-car-reset.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <dm.h> +#include <reset-uclass.h> +#include <asm/arch/clock.h> +#include <asm/arch-tegra/clk_rst.h> + +static int tegra_car_reset_request(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + /* PERIPH_ID_COUNT varies per SoC */ + if (reset_ctl->id >= PERIPH_ID_COUNT) + return -EINVAL; + + return 0; +} + +static int tegra_car_reset_free(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + return 0; +} + +static int tegra_car_reset_assert(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + reset_set_enable(reset_ctl->id, 1); + + return 0; +} + +static int tegra_car_reset_deassert(struct reset_ctl *reset_ctl) +{ + debug("%s(reset_ctl=%p) (dev=%p, id=%lu)\n", __func__, reset_ctl, + reset_ctl->dev, reset_ctl->id); + + reset_set_enable(reset_ctl->id, 0); + + return 0; +} + +struct reset_ops tegra_car_reset_ops = { + .request = tegra_car_reset_request, + .free = tegra_car_reset_free, + .rst_assert = tegra_car_reset_assert, + .rst_deassert = tegra_car_reset_deassert, +}; + +static int tegra_car_reset_probe(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +U_BOOT_DRIVER(tegra_car_reset) = { + .name = "tegra_car_reset", + .id = UCLASS_RESET, + .probe = tegra_car_reset_probe, + .ops = &tegra_car_reset_ops, +}; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index eb54df4..e3620da 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -600,9 +600,18 @@ static int init_ulpi_usb_controller(struct fdt_usb *config, /* reset ULPI phy */ if (dm_gpio_is_valid(&config->phy_reset_gpio)) { - dm_gpio_set_value(&config->phy_reset_gpio, 0); - mdelay(5); + /* + * This GPIO is typically active-low, and marked as such in + * device tree. dm_gpio_set_value() takes this into account + * and inverts the value we pass here if required. In other + * words, this first call logically asserts the reset signal, + * which typically results in driving the physical GPIO low, + * and the second call logically de-asserts the reset signal, + * which typically results in driver the GPIO high. + */ dm_gpio_set_value(&config->phy_reset_gpio, 1); + mdelay(5); + dm_gpio_set_value(&config->phy_reset_gpio, 0); } /* Reset the usb controller */ |