1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
4 // Author: Vignesh Raghavendra <vigneshr@ti.com>
5 
6 #include <common.h>
7 #include <asm/io.h>
8 #include <dm.h>
9 #include <regmap.h>
10 #include <syscon.h>
11 #include <dm/device_compat.h>
12 
13 #define FSS_SYSC_REG	0x4
14 
15 #define HYPERBUS_CALIB_COUNT 25
16 
17 struct am654_hbmc_priv {
18 	void __iomem *mmiobase;
19 	bool calibrated;
20 };
21 
22 /* Calibrate by looking for "QRY" string within the CFI space */
am654_hyperbus_calibrate(struct udevice * dev)23 static int am654_hyperbus_calibrate(struct udevice *dev)
24 {
25 	struct am654_hbmc_priv *priv = dev_get_priv(dev);
26 	int count = HYPERBUS_CALIB_COUNT;
27 	int pass_count = 0;
28 	u16 qry[3];
29 
30 	if (priv->calibrated)
31 		return 0;
32 
33 	writew(0xF0, priv->mmiobase);
34 	writew(0x98, priv->mmiobase + 0xaa);
35 
36 	while (count--) {
37 		qry[0] = readw(priv->mmiobase + 0x20);
38 		qry[1] = readw(priv->mmiobase + 0x22);
39 		qry[2] = readw(priv->mmiobase + 0x24);
40 
41 		if (qry[0] == 'Q' && qry[1] == 'R' && qry[2] == 'Y')
42 			pass_count++;
43 		else
44 			pass_count = 0;
45 		if (pass_count == 5)
46 			break;
47 	}
48 	writew(0xF0, priv->mmiobase);
49 	writew(0xFF, priv->mmiobase);
50 
51 	return pass_count == 5;
52 }
53 
am654_select_hbmc(struct udevice * dev)54 static int am654_select_hbmc(struct udevice *dev)
55 {
56 	struct regmap *regmap = syscon_get_regmap(dev_get_parent(dev));
57 
58 	return regmap_update_bits(regmap, FSS_SYSC_REG, 0x2, 0x2);
59 }
60 
am654_hbmc_bind(struct udevice * dev)61 static int am654_hbmc_bind(struct udevice *dev)
62 {
63 	return dm_scan_fdt_dev(dev);
64 }
65 
am654_hbmc_probe(struct udevice * dev)66 static int am654_hbmc_probe(struct udevice *dev)
67 {
68 	struct am654_hbmc_priv *priv = dev_get_priv(dev);
69 	int ret;
70 
71 	priv->mmiobase = devfdt_remap_addr_index(dev, 1);
72 	if (dev_read_bool(dev, "mux-controls")) {
73 		ret = am654_select_hbmc(dev);
74 		if (ret) {
75 			dev_err(dev, "Failed to select HBMC mux\n");
76 			return ret;
77 		}
78 	}
79 
80 	if (!priv->calibrated) {
81 		ret = am654_hyperbus_calibrate(dev);
82 		if (!ret) {
83 			dev_err(dev, "Calibration Failed\n");
84 			return -EIO;
85 		}
86 	}
87 	priv->calibrated = true;
88 
89 	return 0;
90 }
91 
92 static const struct udevice_id am654_hbmc_dt_ids[] = {
93 	{
94 		.compatible = "ti,am654-hbmc",
95 	},
96 	{ /* end of table */ }
97 };
98 
99 U_BOOT_DRIVER(hbmc_am654) = {
100 	.name	= "hbmc-am654",
101 	.id	= UCLASS_MTD,
102 	.of_match = am654_hbmc_dt_ids,
103 	.probe = am654_hbmc_probe,
104 	.bind = am654_hbmc_bind,
105 	.priv_auto	= sizeof(struct am654_hbmc_priv),
106 };
107