]>
Commit | Line | Data |
---|---|---|
a6e50a88 IC |
1 | #include <common.h> |
2 | #include <ahci.h> | |
cf7b2e10 | 3 | #include <dm.h> |
a6e50a88 IC |
4 | #include <scsi.h> |
5 | #include <errno.h> | |
6 | #include <asm/io.h> | |
7 | #include <asm/gpio.h> | |
8 | ||
9 | #define AHCI_PHYCS0R 0x00c0 | |
10 | #define AHCI_PHYCS1R 0x00c4 | |
11 | #define AHCI_PHYCS2R 0x00c8 | |
12 | #define AHCI_RWCR 0x00fc | |
13 | ||
14 | /* This magic PHY initialisation was taken from the Allwinner releases | |
15 | * and Linux driver, but is completely undocumented. | |
16 | */ | |
cf7b2e10 | 17 | static int sunxi_ahci_phy_init(u8 *reg_base) |
a6e50a88 | 18 | { |
a6e50a88 IC |
19 | u32 reg_val; |
20 | int timeout; | |
21 | ||
22 | writel(0, reg_base + AHCI_RWCR); | |
23 | mdelay(5); | |
24 | ||
25 | setbits_le32(reg_base + AHCI_PHYCS1R, 0x1 << 19); | |
26 | clrsetbits_le32(reg_base + AHCI_PHYCS0R, | |
27 | (0x7 << 24), | |
28 | (0x5 << 24) | (0x1 << 23) | (0x1 << 18)); | |
29 | clrsetbits_le32(reg_base + AHCI_PHYCS1R, | |
30 | (0x3 << 16) | (0x1f << 8) | (0x3 << 6), | |
31 | (0x2 << 16) | (0x6 << 8) | (0x2 << 6)); | |
32 | setbits_le32(reg_base + AHCI_PHYCS1R, (0x1 << 28) | (0x1 << 15)); | |
33 | clrbits_le32(reg_base + AHCI_PHYCS1R, (0x1 << 19)); | |
34 | clrsetbits_le32(reg_base + AHCI_PHYCS0R, (0x7 << 20), (0x3 << 20)); | |
35 | clrsetbits_le32(reg_base + AHCI_PHYCS2R, (0x1f << 5), (0x19 << 5)); | |
36 | mdelay(5); | |
37 | ||
38 | setbits_le32(reg_base + AHCI_PHYCS0R, (0x1 << 19)); | |
39 | ||
40 | timeout = 250; /* Power up takes approx 50 us */ | |
41 | for (;;) { | |
42 | reg_val = readl(reg_base + AHCI_PHYCS0R) & (0x7 << 28); | |
43 | if (reg_val == (0x2 << 28)) | |
44 | break; | |
45 | if (--timeout == 0) { | |
46 | printf("AHCI PHY power up failed.\n"); | |
47 | return -EIO; | |
48 | } | |
49 | udelay(1); | |
50 | }; | |
51 | ||
52 | setbits_le32(reg_base + AHCI_PHYCS2R, (0x1 << 24)); | |
53 | ||
54 | timeout = 100; /* Calibration takes approx 10 us */ | |
55 | for (;;) { | |
56 | reg_val = readl(reg_base + AHCI_PHYCS2R) & (0x1 << 24); | |
57 | if (reg_val == 0x0) | |
58 | break; | |
59 | if (--timeout == 0) { | |
60 | printf("AHCI PHY calibration failed.\n"); | |
61 | return -EIO; | |
62 | } | |
63 | udelay(1); | |
64 | } | |
65 | ||
66 | mdelay(15); | |
67 | ||
68 | writel(0x7, reg_base + AHCI_RWCR); | |
69 | ||
70 | return 0; | |
71 | } | |
72 | ||
cf7b2e10 SG |
73 | static int sunxi_sata_probe(struct udevice *dev) |
74 | { | |
75 | ulong base; | |
76 | u8 *reg; | |
77 | int ret; | |
78 | ||
79 | base = dev_read_addr(dev); | |
80 | if (base == FDT_ADDR_T_NONE) { | |
81 | debug("%s: Failed to find address (err=%d\n)", __func__, ret); | |
82 | return -EINVAL; | |
83 | } | |
84 | reg = (u8 *)base; | |
85 | ret = sunxi_ahci_phy_init(reg); | |
86 | if (ret) { | |
87 | debug("%s: Failed to init phy (err=%d\n)", __func__, ret); | |
88 | return ret; | |
89 | } | |
90 | ret = ahci_probe_scsi(dev, base); | |
91 | if (ret) { | |
92 | debug("%s: Failed to probe (err=%d\n)", __func__, ret); | |
93 | return ret; | |
94 | } | |
95 | ||
96 | return 0; | |
97 | } | |
98 | ||
99 | static int sunxi_sata_bind(struct udevice *dev) | |
100 | { | |
101 | struct udevice *scsi_dev; | |
102 | int ret; | |
103 | ||
104 | ret = ahci_bind_scsi(dev, &scsi_dev); | |
105 | if (ret) { | |
106 | debug("%s: Failed to bind (err=%d\n)", __func__, ret); | |
107 | return ret; | |
108 | } | |
109 | ||
110 | return 0; | |
111 | } | |
112 | ||
113 | static const struct udevice_id sunxi_ahci_ids[] = { | |
114 | { .compatible = "allwinner,sun4i-a10-ahci" }, | |
cbb9cdc7 | 115 | { .compatible = "allwinner,sun8i-r40-ahci" }, |
cf7b2e10 SG |
116 | { } |
117 | }; | |
118 | ||
119 | U_BOOT_DRIVER(ahci_sunxi_drv) = { | |
120 | .name = "ahci_sunxi", | |
121 | .id = UCLASS_AHCI, | |
122 | .of_match = sunxi_ahci_ids, | |
123 | .bind = sunxi_sata_bind, | |
124 | .probe = sunxi_sata_probe, | |
125 | }; |