mtd: Add TI HyperBus Memory Controller driver

AM654/J721e has HyperBus Memory Controller that supports HyperFlash and
HyperRAM devices. It provides a memory mapped interface to interact with
these devices. Add a driver to support the same.
Driver calibrates the controller, setups up for MMIO access and probes
HyperFlash child node.

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
Reviewed-by: Stefan Roese <sr@denx.de>
This commit is contained in:
Vignesh Raghavendra 2019-10-23 13:30:01 +05:30 committed by Stefan Roese
parent d75eacf9b8
commit c2dfd0a4d8
3 changed files with 113 additions and 0 deletions

View File

@ -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"

View File

@ -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/

105
drivers/mtd/hbmc-am654.c Normal file
View File

@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
// Author: Vignesh Raghavendra <vigneshr@ti.com>
#include <common.h>
#include <asm/io.h>
#include <dm.h>
#include <regmap.h>
#include <syscon.h>
#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),
};