1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * sdhci-pci-arasan.c - Driver for Arasan PCI Controller with |
4 | * integrated phy. |
5 | * |
6 | * Copyright (C) 2017 Arasan Chip Systems Inc. |
7 | * |
8 | * Author: Atul Garg <agarg@arasan.com> |
9 | */ |
10 | |
11 | #include <linux/pci.h> |
12 | #include <linux/delay.h> |
13 | |
14 | #include "sdhci.h" |
15 | #include "sdhci-pci.h" |
16 | |
17 | /* Extra registers for Arasan SD/SDIO/MMC Host Controller with PHY */ |
18 | #define PHY_ADDR_REG 0x300 |
19 | #define PHY_DAT_REG 0x304 |
20 | |
21 | #define PHY_WRITE BIT(8) |
22 | #define PHY_BUSY BIT(9) |
23 | #define DATA_MASK 0xFF |
24 | |
25 | /* PHY Specific Registers */ |
26 | #define DLL_STATUS 0x00 |
27 | #define IPAD_CTRL1 0x01 |
28 | #define IPAD_CTRL2 0x02 |
29 | #define IPAD_STS 0x03 |
30 | #define IOREN_CTRL1 0x06 |
31 | #define IOREN_CTRL2 0x07 |
32 | #define IOPU_CTRL1 0x08 |
33 | #define IOPU_CTRL2 0x09 |
34 | #define ITAP_DELAY 0x0C |
35 | #define OTAP_DELAY 0x0D |
36 | #define STRB_SEL 0x0E |
37 | #define CLKBUF_SEL 0x0F |
38 | #define MODE_CTRL 0x11 |
39 | #define DLL_TRIM 0x12 |
40 | #define CMD_CTRL 0x20 |
41 | #define DATA_CTRL 0x21 |
42 | #define STRB_CTRL 0x22 |
43 | #define CLK_CTRL 0x23 |
44 | #define PHY_CTRL 0x24 |
45 | |
46 | #define DLL_ENBL BIT(3) |
47 | #define RTRIM_EN BIT(1) |
48 | #define PDB_ENBL BIT(1) |
49 | #define RETB_ENBL BIT(6) |
50 | #define ODEN_CMD BIT(1) |
51 | #define ODEN_DAT 0xFF |
52 | #define REN_STRB BIT(0) |
53 | #define REN_CMND BIT(1) |
54 | #define REN_DATA 0xFF |
55 | #define PU_CMD BIT(1) |
56 | #define PU_DAT 0xFF |
57 | #define ITAPDLY_EN BIT(0) |
58 | #define OTAPDLY_EN BIT(0) |
59 | #define OD_REL_CMD BIT(1) |
60 | #define OD_REL_DAT 0xFF |
61 | #define DLLTRM_ICP 0x8 |
62 | #define PDB_CMND BIT(0) |
63 | #define PDB_DATA 0xFF |
64 | #define PDB_STRB BIT(0) |
65 | #define PDB_CLOCK BIT(0) |
66 | #define CALDONE_MASK 0x10 |
67 | #define DLL_RDY_MASK 0x10 |
68 | #define MAX_CLK_BUF 0x7 |
69 | |
70 | /* Mode Controls */ |
71 | #define ENHSTRB_MODE BIT(0) |
72 | #define HS400_MODE BIT(1) |
73 | #define LEGACY_MODE BIT(2) |
74 | #define DDR50_MODE BIT(3) |
75 | |
76 | /* |
77 | * Controller has no specific bits for HS200/HS. |
78 | * Used BIT(4), BIT(5) for software programming. |
79 | */ |
80 | #define HS200_MODE BIT(4) |
81 | #define HISPD_MODE BIT(5) |
82 | |
83 | #define OTAPDLY(x) (((x) << 1) | OTAPDLY_EN) |
84 | #define ITAPDLY(x) (((x) << 1) | ITAPDLY_EN) |
85 | #define FREQSEL(x) (((x) << 5) | DLL_ENBL) |
86 | #define IOPAD(x, y) ((x) | ((y) << 2)) |
87 | |
88 | /* Arasan private data */ |
89 | struct arasan_host { |
90 | u32 chg_clk; |
91 | }; |
92 | |
93 | static int arasan_phy_addr_poll(struct sdhci_host *host, u32 offset, u32 mask) |
94 | { |
95 | ktime_t timeout = ktime_add_us(kt: ktime_get(), usec: 100); |
96 | bool failed; |
97 | u8 val = 0; |
98 | |
99 | while (1) { |
100 | failed = ktime_after(cmp1: ktime_get(), cmp2: timeout); |
101 | val = sdhci_readw(host, PHY_ADDR_REG); |
102 | if (!(val & mask)) |
103 | return 0; |
104 | if (failed) |
105 | return -EBUSY; |
106 | } |
107 | } |
108 | |
109 | static int arasan_phy_write(struct sdhci_host *host, u8 data, u8 offset) |
110 | { |
111 | sdhci_writew(host, val: data, PHY_DAT_REG); |
112 | sdhci_writew(host, val: (PHY_WRITE | offset), PHY_ADDR_REG); |
113 | return arasan_phy_addr_poll(host, PHY_ADDR_REG, PHY_BUSY); |
114 | } |
115 | |
116 | static int arasan_phy_read(struct sdhci_host *host, u8 offset, u8 *data) |
117 | { |
118 | int ret; |
119 | |
120 | sdhci_writew(host, val: 0, PHY_DAT_REG); |
121 | sdhci_writew(host, val: offset, PHY_ADDR_REG); |
122 | ret = arasan_phy_addr_poll(host, PHY_ADDR_REG, PHY_BUSY); |
123 | |
124 | /* Masking valid data bits */ |
125 | *data = sdhci_readw(host, PHY_DAT_REG) & DATA_MASK; |
126 | return ret; |
127 | } |
128 | |
129 | static int arasan_phy_sts_poll(struct sdhci_host *host, u32 offset, u32 mask) |
130 | { |
131 | int ret; |
132 | ktime_t timeout = ktime_add_us(kt: ktime_get(), usec: 100); |
133 | bool failed; |
134 | u8 val = 0; |
135 | |
136 | while (1) { |
137 | failed = ktime_after(cmp1: ktime_get(), cmp2: timeout); |
138 | ret = arasan_phy_read(host, offset, data: &val); |
139 | if (ret) |
140 | return -EBUSY; |
141 | else if (val & mask) |
142 | return 0; |
143 | if (failed) |
144 | return -EBUSY; |
145 | } |
146 | } |
147 | |
148 | /* Initialize the Arasan PHY */ |
149 | static int arasan_phy_init(struct sdhci_host *host) |
150 | { |
151 | int ret; |
152 | u8 val; |
153 | |
154 | /* Program IOPADs and wait for calibration to be done */ |
155 | if (arasan_phy_read(host, IPAD_CTRL1, data: &val) || |
156 | arasan_phy_write(host, data: val | RETB_ENBL | PDB_ENBL, IPAD_CTRL1) || |
157 | arasan_phy_read(host, IPAD_CTRL2, data: &val) || |
158 | arasan_phy_write(host, data: val | RTRIM_EN, IPAD_CTRL2)) |
159 | return -EBUSY; |
160 | ret = arasan_phy_sts_poll(host, IPAD_STS, CALDONE_MASK); |
161 | if (ret) |
162 | return -EBUSY; |
163 | |
164 | /* Program CMD/Data lines */ |
165 | if (arasan_phy_read(host, IOREN_CTRL1, data: &val) || |
166 | arasan_phy_write(host, data: val | REN_CMND | REN_STRB, IOREN_CTRL1) || |
167 | arasan_phy_read(host, IOPU_CTRL1, data: &val) || |
168 | arasan_phy_write(host, data: val | PU_CMD, IOPU_CTRL1) || |
169 | arasan_phy_read(host, CMD_CTRL, data: &val) || |
170 | arasan_phy_write(host, data: val | PDB_CMND, CMD_CTRL) || |
171 | arasan_phy_read(host, IOREN_CTRL2, data: &val) || |
172 | arasan_phy_write(host, data: val | REN_DATA, IOREN_CTRL2) || |
173 | arasan_phy_read(host, IOPU_CTRL2, data: &val) || |
174 | arasan_phy_write(host, data: val | PU_DAT, IOPU_CTRL2) || |
175 | arasan_phy_read(host, DATA_CTRL, data: &val) || |
176 | arasan_phy_write(host, data: val | PDB_DATA, DATA_CTRL) || |
177 | arasan_phy_read(host, STRB_CTRL, data: &val) || |
178 | arasan_phy_write(host, data: val | PDB_STRB, STRB_CTRL) || |
179 | arasan_phy_read(host, CLK_CTRL, data: &val) || |
180 | arasan_phy_write(host, data: val | PDB_CLOCK, CLK_CTRL) || |
181 | arasan_phy_read(host, CLKBUF_SEL, data: &val) || |
182 | arasan_phy_write(host, data: val | MAX_CLK_BUF, CLKBUF_SEL) || |
183 | arasan_phy_write(host, LEGACY_MODE, MODE_CTRL)) |
184 | return -EBUSY; |
185 | return 0; |
186 | } |
187 | |
188 | /* Set Arasan PHY for different modes */ |
189 | static int arasan_phy_set(struct sdhci_host *host, u8 mode, u8 otap, |
190 | u8 drv_type, u8 itap, u8 trim, u8 clk) |
191 | { |
192 | u8 val; |
193 | int ret; |
194 | |
195 | if (mode == HISPD_MODE || mode == HS200_MODE) |
196 | ret = arasan_phy_write(host, data: 0x0, MODE_CTRL); |
197 | else |
198 | ret = arasan_phy_write(host, data: mode, MODE_CTRL); |
199 | if (ret) |
200 | return ret; |
201 | if (mode == HS400_MODE || mode == HS200_MODE) { |
202 | ret = arasan_phy_read(host, IPAD_CTRL1, data: &val); |
203 | if (ret) |
204 | return ret; |
205 | ret = arasan_phy_write(host, IOPAD(val, drv_type), IPAD_CTRL1); |
206 | if (ret) |
207 | return ret; |
208 | } |
209 | if (mode == LEGACY_MODE) { |
210 | ret = arasan_phy_write(host, data: 0x0, OTAP_DELAY); |
211 | if (ret) |
212 | return ret; |
213 | ret = arasan_phy_write(host, data: 0x0, ITAP_DELAY); |
214 | } else { |
215 | ret = arasan_phy_write(host, OTAPDLY(otap), OTAP_DELAY); |
216 | if (ret) |
217 | return ret; |
218 | if (mode != HS200_MODE) |
219 | ret = arasan_phy_write(host, ITAPDLY(itap), ITAP_DELAY); |
220 | else |
221 | ret = arasan_phy_write(host, data: 0x0, ITAP_DELAY); |
222 | } |
223 | if (ret) |
224 | return ret; |
225 | if (mode != LEGACY_MODE) { |
226 | ret = arasan_phy_write(host, data: trim, DLL_TRIM); |
227 | if (ret) |
228 | return ret; |
229 | } |
230 | ret = arasan_phy_write(host, data: 0, DLL_STATUS); |
231 | if (ret) |
232 | return ret; |
233 | if (mode != LEGACY_MODE) { |
234 | ret = arasan_phy_write(host, FREQSEL(clk), DLL_STATUS); |
235 | if (ret) |
236 | return ret; |
237 | ret = arasan_phy_sts_poll(host, DLL_STATUS, DLL_RDY_MASK); |
238 | if (ret) |
239 | return -EBUSY; |
240 | } |
241 | return 0; |
242 | } |
243 | |
244 | static int arasan_select_phy_clock(struct sdhci_host *host) |
245 | { |
246 | struct sdhci_pci_slot *slot = sdhci_priv(host); |
247 | struct arasan_host *arasan_host = sdhci_pci_priv(slot); |
248 | u8 clk; |
249 | |
250 | if (arasan_host->chg_clk == host->mmc->ios.clock) |
251 | return 0; |
252 | |
253 | arasan_host->chg_clk = host->mmc->ios.clock; |
254 | if (host->mmc->ios.clock == 200000000) |
255 | clk = 0x0; |
256 | else if (host->mmc->ios.clock == 100000000) |
257 | clk = 0x2; |
258 | else if (host->mmc->ios.clock == 50000000) |
259 | clk = 0x1; |
260 | else |
261 | clk = 0x0; |
262 | |
263 | if (host->mmc_host_ops.hs400_enhanced_strobe) { |
264 | arasan_phy_set(host, ENHSTRB_MODE, otap: 1, drv_type: 0x0, itap: 0x0, |
265 | DLLTRM_ICP, clk); |
266 | } else { |
267 | switch (host->mmc->ios.timing) { |
268 | case MMC_TIMING_LEGACY: |
269 | arasan_phy_set(host, LEGACY_MODE, otap: 0x0, drv_type: 0x0, itap: 0x0, |
270 | trim: 0x0, clk: 0x0); |
271 | break; |
272 | case MMC_TIMING_MMC_HS: |
273 | case MMC_TIMING_SD_HS: |
274 | arasan_phy_set(host, HISPD_MODE, otap: 0x3, drv_type: 0x0, itap: 0x2, |
275 | DLLTRM_ICP, clk); |
276 | break; |
277 | case MMC_TIMING_MMC_HS200: |
278 | case MMC_TIMING_UHS_SDR104: |
279 | arasan_phy_set(host, HS200_MODE, otap: 0x2, |
280 | drv_type: host->mmc->ios.drv_type, itap: 0x0, |
281 | DLLTRM_ICP, clk); |
282 | break; |
283 | case MMC_TIMING_MMC_DDR52: |
284 | case MMC_TIMING_UHS_DDR50: |
285 | arasan_phy_set(host, DDR50_MODE, otap: 0x1, drv_type: 0x0, |
286 | itap: 0x0, DLLTRM_ICP, clk); |
287 | break; |
288 | case MMC_TIMING_MMC_HS400: |
289 | arasan_phy_set(host, HS400_MODE, otap: 0x1, |
290 | drv_type: host->mmc->ios.drv_type, itap: 0xa, |
291 | DLLTRM_ICP, clk); |
292 | break; |
293 | default: |
294 | break; |
295 | } |
296 | } |
297 | return 0; |
298 | } |
299 | |
300 | static int arasan_pci_probe_slot(struct sdhci_pci_slot *slot) |
301 | { |
302 | int err; |
303 | |
304 | slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE | MMC_CAP_8_BIT_DATA; |
305 | err = arasan_phy_init(host: slot->host); |
306 | if (err) |
307 | return -ENODEV; |
308 | return 0; |
309 | } |
310 | |
311 | static void arasan_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) |
312 | { |
313 | sdhci_set_clock(host, clock); |
314 | |
315 | /* Change phy settings for the new clock */ |
316 | arasan_select_phy_clock(host); |
317 | } |
318 | |
319 | static const struct sdhci_ops arasan_sdhci_pci_ops = { |
320 | .set_clock = arasan_sdhci_set_clock, |
321 | .enable_dma = sdhci_pci_enable_dma, |
322 | .set_bus_width = sdhci_set_bus_width, |
323 | .reset = sdhci_reset, |
324 | .set_uhs_signaling = sdhci_set_uhs_signaling, |
325 | }; |
326 | |
327 | const struct sdhci_pci_fixes sdhci_arasan = { |
328 | .probe_slot = arasan_pci_probe_slot, |
329 | .ops = &arasan_sdhci_pci_ops, |
330 | .priv_size = sizeof(struct arasan_host), |
331 | }; |
332 | |