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