]> git.karo-electronics.de Git - karo-tx-uboot.git/blob - board/highbank/ahci.c
dm: Add a system reset uclass
[karo-tx-uboot.git] / board / highbank / ahci.c
1 /*
2  * Copyright 2012 Calxeda, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <common.h>
19 #include <ahci.h>
20 #include <asm/io.h>
21
22 #define CPHY_MAP(dev, addr) ((((dev) & 0x1f) << 7) | (((addr) >> 9) & 0x7f))
23 #define CPHY_ADDR(base, dev, addr) ((base) | (((addr) & 0x1ff) << 2))
24 #define CPHY_BASE                       0xfff58000
25 #define CPHY_WIDTH                      0x1000
26 #define CPHY_DTE_XS                     5
27 #define CPHY_MII                        31
28 #define SERDES_CR_CTL                   0x80a0
29 #define SERDES_CR_ADDR                  0x80a1
30 #define SERDES_CR_DATA                  0x80a2
31 #define CR_BUSY                         0x0001
32 #define CR_START                        0x0001
33 #define CR_WR_RDN                       0x0002
34 #define CPHY_TX_INPUT_STS               0x2001
35 #define CPHY_RX_INPUT_STS               0x2002
36 #define CPHY_SATA_TX_OVERRIDE_BIT       0x8000
37 #define CPHY_SATA_RX_OVERRIDE_BIT       0x4000
38 #define CPHY_TX_INPUT_OVERRIDE          0x2004
39 #define CPHY_RX_INPUT_OVERRIDE          0x2005
40 #define SPHY_LANE                       0x100
41 #define SPHY_HALF_RATE                  0x0001
42 #define CPHY_SATA_DPLL_MODE             0x0700
43 #define CPHY_SATA_DPLL_SHIFT            8
44 #define CPHY_SATA_TX_ATTEN              0x1c00
45 #define CPHY_SATA_TX_ATTEN_SHIFT        10
46
47 #define HB_SREG_SATA_ATTEN              0xfff3cf24
48
49 #define SATA_PORT_BASE                  0xffe08000
50 #define SATA_VERSIONR                   0xf8
51 #define SATA_HB_VERSION                 0x3332302a
52
53 static u32 __combo_phy_reg_read(u8 phy, u8 dev, u32 addr)
54 {
55         u32 data;
56         writel(CPHY_MAP(dev, addr), CPHY_BASE + 0x800 + CPHY_WIDTH * phy);
57         data = readl(CPHY_ADDR(CPHY_BASE + CPHY_WIDTH * phy, dev, addr));
58         return data;
59 }
60
61 static void __combo_phy_reg_write(u8 phy, u8 dev, u32 addr, u32 data)
62 {
63         writel(CPHY_MAP(dev, addr), CPHY_BASE + 0x800 + CPHY_WIDTH * phy);
64         writel(data, CPHY_ADDR(CPHY_BASE + CPHY_WIDTH * phy, dev, addr));
65 }
66
67 static u32 combo_phy_read(u8 phy, u32 addr)
68 {
69         u8 dev = CPHY_DTE_XS;
70         if (phy == 5)
71                 dev = CPHY_MII;
72         while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
73                 udelay(5);
74         __combo_phy_reg_write(phy, dev, SERDES_CR_ADDR, addr);
75         __combo_phy_reg_write(phy, dev, SERDES_CR_CTL, CR_START);
76         while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
77                 udelay(5);
78         return __combo_phy_reg_read(phy, dev, SERDES_CR_DATA);
79 }
80
81 static void combo_phy_write(u8 phy, u32 addr, u32 data)
82 {
83         u8 dev = CPHY_DTE_XS;
84         if (phy == 5)
85                 dev = CPHY_MII;
86         while (__combo_phy_reg_read(phy, dev, SERDES_CR_CTL) & CR_BUSY)
87                 udelay(5);
88         __combo_phy_reg_write(phy, dev, SERDES_CR_ADDR, addr);
89         __combo_phy_reg_write(phy, dev, SERDES_CR_DATA, data);
90         __combo_phy_reg_write(phy, dev, SERDES_CR_CTL, CR_WR_RDN | CR_START);
91 }
92
93 static void cphy_spread_spectrum_override(u8 phy, u8 lane, u32 val)
94 {
95         u32 tmp;
96         tmp = combo_phy_read(phy, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
97         tmp &= ~CPHY_SATA_RX_OVERRIDE_BIT;
98         combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
99
100         tmp |= CPHY_SATA_RX_OVERRIDE_BIT;
101         combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
102
103         tmp &= ~CPHY_SATA_DPLL_MODE;
104         tmp |= (val << CPHY_SATA_DPLL_SHIFT) & CPHY_SATA_DPLL_MODE;
105         combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
106 }
107
108 static void cphy_tx_attenuation_override(u8 phy, u8 lane)
109 {
110         u32 val;
111         u32 tmp;
112         u8  shift;
113
114         shift = ((phy == 5) ? 4 : lane) * 4;
115
116         val = (readl(HB_SREG_SATA_ATTEN) >> shift) & 0xf;
117
118         if (val & 0x8)
119                 return;
120
121         tmp = combo_phy_read(phy, CPHY_TX_INPUT_STS + lane * SPHY_LANE);
122         tmp &= ~CPHY_SATA_TX_OVERRIDE_BIT;
123         combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
124
125         tmp |= CPHY_SATA_TX_OVERRIDE_BIT;
126         combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
127
128         tmp |= (val << CPHY_SATA_TX_ATTEN_SHIFT) & CPHY_SATA_TX_ATTEN;
129         combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
130 }
131
132 static void cphy_disable_port_overrides(u8 port)
133 {
134         u32 tmp;
135         u8 lane = 0, phy = 0;
136
137         if (port == 0)
138                 phy = 5;
139         else if (port < 5)
140                 lane = port - 1;
141         else
142                 return;
143         tmp = combo_phy_read(phy, CPHY_RX_INPUT_STS + lane * SPHY_LANE);
144         tmp &= ~CPHY_SATA_RX_OVERRIDE_BIT;
145         combo_phy_write(phy, CPHY_RX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
146
147         tmp = combo_phy_read(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE);
148         tmp &= ~CPHY_SATA_TX_OVERRIDE_BIT;
149         combo_phy_write(phy, CPHY_TX_INPUT_OVERRIDE + lane * SPHY_LANE, tmp);
150 }
151
152 void cphy_disable_overrides(void)
153 {
154         int i;
155         u32 port_map;
156
157         port_map = readl(0xffe08000 + HOST_PORTS_IMPL);
158         for (i = 0; i < 5; i++) {
159                 if (port_map & (1 << i))
160                         cphy_disable_port_overrides(i);
161         }
162 }
163
164 static void cphy_override_lane(u8 port)
165 {
166         u32 tmp, k = 0;
167         u8 lane = 0, phy = 0;
168
169         if (port == 0)
170                 phy = 5;
171         else if (port < 5)
172                 lane = port - 1;
173         else
174                 return;
175
176         do {
177                 tmp = combo_phy_read(0, CPHY_RX_INPUT_STS +
178                                         lane * SPHY_LANE);
179         } while ((tmp & SPHY_HALF_RATE) && (k++ < 1000));
180         cphy_spread_spectrum_override(phy, lane, 3);
181         cphy_tx_attenuation_override(phy, lane);
182 }
183
184 #define WAIT_MS_LINKUP  4
185
186 int ahci_link_up(struct ahci_probe_ent *probe_ent, int port)
187 {
188         u32 tmp;
189         int j = 0;
190         u8 *port_mmio = (u8 *)probe_ent->port[port].port_mmio;
191         u32 is_highbank = readl(SATA_PORT_BASE + SATA_VERSIONR) ==
192                                 SATA_HB_VERSION ? 1 : 0;
193
194         /* Bring up SATA link.
195          * SATA link bringup time is usually less than 1 ms; only very
196          * rarely has it taken between 1-2 ms. Never seen it above 2 ms.
197          */
198         while (j < WAIT_MS_LINKUP) {
199                 if (is_highbank && (j == 0)) {
200                         cphy_disable_port_overrides(port);
201                         writel(0x301, port_mmio + PORT_SCR_CTL);
202                         udelay(1000);
203                         writel(0x300, port_mmio + PORT_SCR_CTL);
204                         udelay(1000);
205                         cphy_override_lane(port);
206                 }
207
208                 tmp = readl(port_mmio + PORT_SCR_STAT);
209                 if ((tmp & 0xf) == 0x3)
210                         return 0;
211                 udelay(1000);
212                 j++;
213
214                 if ((j == WAIT_MS_LINKUP) && (tmp & 0xf))
215                         j = 0;  /* retry phy reset */
216         }
217         return 1;
218 }