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