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 */
89struct arasan_host {
90 u32 chg_clk;
91};
92
93static 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
109static 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
116static 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
129static 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 */
149static 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 */
189static 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
244static 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
300static 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
311static 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
319static 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
327const 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

source code of linux/drivers/mmc/host/sdhci-pci-arasan.c