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>
diff --git a/drivers/mtd/hbmc-am654.c b/drivers/mtd/hbmc-am654.c
new file mode 100644
index 0000000..5a560f1
--- /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 <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),
+};