diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 0050fb2b9b..37f379d478 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -94,6 +94,13 @@ config RENESAS_RPC_HF This enables access to Hyperflash memory through the Renesas RCar Gen3 RPC controller. +config HBMC_AM654 + bool "HyperBus controller driver for AM65x SoC" + depends on SYSCON + help + This is the driver for HyperBus controller on TI's AM65x and + other SoCs + source "drivers/mtd/nand/Kconfig" source "drivers/mtd/spi/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 22ceda93c0..293079d709 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -18,5 +18,6 @@ obj-$(CONFIG_FLASH_PIC32) += pic32_flash.o obj-$(CONFIG_ST_SMI) += st_smi.o obj-$(CONFIG_STM32_FLASH) += stm32_flash.o obj-$(CONFIG_RENESAS_RPC_HF) += renesas_rpc_hf.o +obj-$(CONFIG_HBMC_AM654) += hbmc-am654.o obj-y += nand/ diff --git a/drivers/mtd/hbmc-am654.c b/drivers/mtd/hbmc-am654.c new file mode 100644 index 0000000000..5a560f1253 --- /dev/null +++ b/drivers/mtd/hbmc-am654.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ +// Author: Vignesh Raghavendra + +#include +#include +#include +#include +#include + +#define FSS_SYSC_REG 0x4 + +#define HYPERBUS_CALIB_COUNT 25 + +struct am654_hbmc_priv { + void __iomem *mmiobase; + bool calibrated; +}; + +/* Calibrate by looking for "QRY" string within the CFI space */ +static int am654_hyperbus_calibrate(struct udevice *dev) +{ + struct am654_hbmc_priv *priv = dev_get_priv(dev); + int count = HYPERBUS_CALIB_COUNT; + int pass_count = 0; + u16 qry[3]; + + if (priv->calibrated) + return 0; + + writew(0xF0, priv->mmiobase); + writew(0x98, priv->mmiobase + 0xaa); + + while (count--) { + qry[0] = readw(priv->mmiobase + 0x20); + qry[1] = readw(priv->mmiobase + 0x22); + qry[2] = readw(priv->mmiobase + 0x24); + + if (qry[0] == 'Q' && qry[1] == 'R' && qry[2] == 'Y') + pass_count++; + else + pass_count = 0; + if (pass_count == 5) + break; + } + writew(0xF0, priv->mmiobase); + writew(0xFF, priv->mmiobase); + + return pass_count == 5; +} + +static int am654_select_hbmc(struct udevice *dev) +{ + struct regmap *regmap = syscon_get_regmap(dev_get_parent(dev)); + + return regmap_update_bits(regmap, FSS_SYSC_REG, 0x2, 0x2); +} + +static int am654_hbmc_bind(struct udevice *dev) +{ + return dm_scan_fdt_dev(dev); +} + +static int am654_hbmc_probe(struct udevice *dev) +{ + struct am654_hbmc_priv *priv = dev_get_priv(dev); + int ret; + + priv->mmiobase = devfdt_remap_addr_index(dev, 1); + if (dev_read_bool(dev, "mux-controls")) { + ret = am654_select_hbmc(dev); + if (ret) { + dev_err(dev, "Failed to select HBMC mux\n"); + return ret; + } + } + + if (!priv->calibrated) { + ret = am654_hyperbus_calibrate(dev); + if (!ret) { + dev_err(dev, "Calibration Failed\n"); + return -EIO; + } + } + priv->calibrated = true; + + return 0; +} + +static const struct udevice_id am654_hbmc_dt_ids[] = { + { + .compatible = "ti,am654-hbmc", + }, + { /* end of table */ } +}; + +U_BOOT_DRIVER(hbmc_am654) = { + .name = "hbmc-am654", + .id = UCLASS_MTD, + .of_match = am654_hbmc_dt_ids, + .probe = am654_hbmc_probe, + .bind = am654_hbmc_bind, + .priv_auto_alloc_size = sizeof(struct am654_hbmc_priv), +};