// SPDX-License-Identifier: GPL-2.0 // // Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ // Author: Vignesh Raghavendra #include #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 = sizeof(struct am654_hbmc_priv), };