blob: 9064774e6614d7915b6ab4906ac29b823bad664f [file] [log] [blame]
Ian Campbella6e50a82014-07-18 20:38:41 +01001#include <common.h>
2#include <ahci.h>
Simon Glasscf7b2e12017-07-04 13:31:31 -06003#include <dm.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -06004#include <log.h>
Ian Campbella6e50a82014-07-18 20:38:41 +01005#include <scsi.h>
6#include <errno.h>
7#include <asm/io.h>
8#include <asm/gpio.h>
Simon Glassc05ed002020-05-10 11:40:11 -06009#include <linux/delay.h>
Andre Przywaraae79c1d2022-07-15 16:52:14 +010010#include <power/regulator.h>
Ian Campbella6e50a82014-07-18 20:38:41 +010011
12#define AHCI_PHYCS0R 0x00c0
13#define AHCI_PHYCS1R 0x00c4
14#define AHCI_PHYCS2R 0x00c8
15#define AHCI_RWCR 0x00fc
16
17/* This magic PHY initialisation was taken from the Allwinner releases
18 * and Linux driver, but is completely undocumented.
19 */
Simon Glasscf7b2e12017-07-04 13:31:31 -060020static int sunxi_ahci_phy_init(u8 *reg_base)
Ian Campbella6e50a82014-07-18 20:38:41 +010021{
Ian Campbella6e50a82014-07-18 20:38:41 +010022 u32 reg_val;
23 int timeout;
24
25 writel(0, reg_base + AHCI_RWCR);
26 mdelay(5);
27
28 setbits_le32(reg_base + AHCI_PHYCS1R, 0x1 << 19);
29 clrsetbits_le32(reg_base + AHCI_PHYCS0R,
30 (0x7 << 24),
31 (0x5 << 24) | (0x1 << 23) | (0x1 << 18));
32 clrsetbits_le32(reg_base + AHCI_PHYCS1R,
33 (0x3 << 16) | (0x1f << 8) | (0x3 << 6),
34 (0x2 << 16) | (0x6 << 8) | (0x2 << 6));
35 setbits_le32(reg_base + AHCI_PHYCS1R, (0x1 << 28) | (0x1 << 15));
36 clrbits_le32(reg_base + AHCI_PHYCS1R, (0x1 << 19));
37 clrsetbits_le32(reg_base + AHCI_PHYCS0R, (0x7 << 20), (0x3 << 20));
38 clrsetbits_le32(reg_base + AHCI_PHYCS2R, (0x1f << 5), (0x19 << 5));
39 mdelay(5);
40
41 setbits_le32(reg_base + AHCI_PHYCS0R, (0x1 << 19));
42
43 timeout = 250; /* Power up takes approx 50 us */
44 for (;;) {
45 reg_val = readl(reg_base + AHCI_PHYCS0R) & (0x7 << 28);
46 if (reg_val == (0x2 << 28))
47 break;
48 if (--timeout == 0) {
49 printf("AHCI PHY power up failed.\n");
50 return -EIO;
51 }
52 udelay(1);
53 };
54
55 setbits_le32(reg_base + AHCI_PHYCS2R, (0x1 << 24));
56
57 timeout = 100; /* Calibration takes approx 10 us */
58 for (;;) {
59 reg_val = readl(reg_base + AHCI_PHYCS2R) & (0x1 << 24);
60 if (reg_val == 0x0)
61 break;
62 if (--timeout == 0) {
63 printf("AHCI PHY calibration failed.\n");
64 return -EIO;
65 }
66 udelay(1);
67 }
68
69 mdelay(15);
70
71 writel(0x7, reg_base + AHCI_RWCR);
72
73 return 0;
74}
75
Simon Glasscf7b2e12017-07-04 13:31:31 -060076static int sunxi_sata_probe(struct udevice *dev)
77{
Andre Przywaraae79c1d2022-07-15 16:52:14 +010078 struct udevice *reg_dev;
Simon Glasscf7b2e12017-07-04 13:31:31 -060079 ulong base;
80 u8 *reg;
81 int ret;
82
83 base = dev_read_addr(dev);
84 if (base == FDT_ADDR_T_NONE) {
Dario Binacchia38bb0d2021-01-24 19:13:10 +010085 debug("%s: Failed to find address\n", __func__);
Simon Glasscf7b2e12017-07-04 13:31:31 -060086 return -EINVAL;
87 }
88 reg = (u8 *)base;
89 ret = sunxi_ahci_phy_init(reg);
90 if (ret) {
Dario Binacchia38bb0d2021-01-24 19:13:10 +010091 debug("%s: Failed to init phy (err=%d)\n", __func__, ret);
Simon Glasscf7b2e12017-07-04 13:31:31 -060092 return ret;
93 }
Andre Przywaraae79c1d2022-07-15 16:52:14 +010094
95 ret = device_get_supply_regulator(dev, "target-supply", &reg_dev);
96 if (ret == 0) {
97 regulator_set_enable(reg_dev, true);
98 mdelay(500);
99 }
100
Simon Glasscf7b2e12017-07-04 13:31:31 -0600101 ret = ahci_probe_scsi(dev, base);
102 if (ret) {
Dario Binacchia38bb0d2021-01-24 19:13:10 +0100103 debug("%s: Failed to probe (err=%d)\n", __func__, ret);
Simon Glasscf7b2e12017-07-04 13:31:31 -0600104 return ret;
105 }
106
107 return 0;
108}
109
110static int sunxi_sata_bind(struct udevice *dev)
111{
112 struct udevice *scsi_dev;
113 int ret;
114
115 ret = ahci_bind_scsi(dev, &scsi_dev);
116 if (ret) {
Dario Binacchia38bb0d2021-01-24 19:13:10 +0100117 debug("%s: Failed to bind (err=%d)\n", __func__, ret);
Simon Glasscf7b2e12017-07-04 13:31:31 -0600118 return ret;
119 }
120
121 return 0;
122}
123
124static const struct udevice_id sunxi_ahci_ids[] = {
125 { .compatible = "allwinner,sun4i-a10-ahci" },
Jagan Tekicbb9cdc2019-04-12 16:47:56 +0530126 { .compatible = "allwinner,sun8i-r40-ahci" },
Simon Glasscf7b2e12017-07-04 13:31:31 -0600127 { }
128};
129
130U_BOOT_DRIVER(ahci_sunxi_drv) = {
131 .name = "ahci_sunxi",
132 .id = UCLASS_AHCI,
133 .of_match = sunxi_ahci_ids,
134 .bind = sunxi_sata_bind,
135 .probe = sunxi_sata_probe,
136};