162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * sdhci-pci-arasan.c - Driver for Arasan PCI Controller with
462306a36Sopenharmony_ci * integrated phy.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2017 Arasan Chip Systems Inc.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: Atul Garg <agarg@arasan.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/pci.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "sdhci.h"
1562306a36Sopenharmony_ci#include "sdhci-pci.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Extra registers for Arasan SD/SDIO/MMC Host Controller with PHY */
1862306a36Sopenharmony_ci#define PHY_ADDR_REG	0x300
1962306a36Sopenharmony_ci#define PHY_DAT_REG	0x304
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define PHY_WRITE	BIT(8)
2262306a36Sopenharmony_ci#define PHY_BUSY	BIT(9)
2362306a36Sopenharmony_ci#define DATA_MASK	0xFF
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* PHY Specific Registers */
2662306a36Sopenharmony_ci#define DLL_STATUS	0x00
2762306a36Sopenharmony_ci#define IPAD_CTRL1	0x01
2862306a36Sopenharmony_ci#define IPAD_CTRL2	0x02
2962306a36Sopenharmony_ci#define IPAD_STS	0x03
3062306a36Sopenharmony_ci#define IOREN_CTRL1	0x06
3162306a36Sopenharmony_ci#define IOREN_CTRL2	0x07
3262306a36Sopenharmony_ci#define IOPU_CTRL1	0x08
3362306a36Sopenharmony_ci#define IOPU_CTRL2	0x09
3462306a36Sopenharmony_ci#define ITAP_DELAY	0x0C
3562306a36Sopenharmony_ci#define OTAP_DELAY	0x0D
3662306a36Sopenharmony_ci#define STRB_SEL	0x0E
3762306a36Sopenharmony_ci#define CLKBUF_SEL	0x0F
3862306a36Sopenharmony_ci#define MODE_CTRL	0x11
3962306a36Sopenharmony_ci#define DLL_TRIM	0x12
4062306a36Sopenharmony_ci#define CMD_CTRL	0x20
4162306a36Sopenharmony_ci#define DATA_CTRL	0x21
4262306a36Sopenharmony_ci#define STRB_CTRL	0x22
4362306a36Sopenharmony_ci#define CLK_CTRL	0x23
4462306a36Sopenharmony_ci#define PHY_CTRL	0x24
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define DLL_ENBL	BIT(3)
4762306a36Sopenharmony_ci#define RTRIM_EN	BIT(1)
4862306a36Sopenharmony_ci#define PDB_ENBL	BIT(1)
4962306a36Sopenharmony_ci#define RETB_ENBL	BIT(6)
5062306a36Sopenharmony_ci#define ODEN_CMD	BIT(1)
5162306a36Sopenharmony_ci#define ODEN_DAT	0xFF
5262306a36Sopenharmony_ci#define REN_STRB	BIT(0)
5362306a36Sopenharmony_ci#define REN_CMND	BIT(1)
5462306a36Sopenharmony_ci#define REN_DATA	0xFF
5562306a36Sopenharmony_ci#define PU_CMD		BIT(1)
5662306a36Sopenharmony_ci#define PU_DAT		0xFF
5762306a36Sopenharmony_ci#define ITAPDLY_EN	BIT(0)
5862306a36Sopenharmony_ci#define OTAPDLY_EN	BIT(0)
5962306a36Sopenharmony_ci#define OD_REL_CMD	BIT(1)
6062306a36Sopenharmony_ci#define OD_REL_DAT	0xFF
6162306a36Sopenharmony_ci#define DLLTRM_ICP	0x8
6262306a36Sopenharmony_ci#define PDB_CMND	BIT(0)
6362306a36Sopenharmony_ci#define PDB_DATA	0xFF
6462306a36Sopenharmony_ci#define PDB_STRB	BIT(0)
6562306a36Sopenharmony_ci#define PDB_CLOCK	BIT(0)
6662306a36Sopenharmony_ci#define CALDONE_MASK	0x10
6762306a36Sopenharmony_ci#define DLL_RDY_MASK	0x10
6862306a36Sopenharmony_ci#define MAX_CLK_BUF	0x7
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Mode Controls */
7162306a36Sopenharmony_ci#define ENHSTRB_MODE	BIT(0)
7262306a36Sopenharmony_ci#define HS400_MODE	BIT(1)
7362306a36Sopenharmony_ci#define LEGACY_MODE	BIT(2)
7462306a36Sopenharmony_ci#define DDR50_MODE	BIT(3)
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * Controller has no specific bits for HS200/HS.
7862306a36Sopenharmony_ci * Used BIT(4), BIT(5) for software programming.
7962306a36Sopenharmony_ci */
8062306a36Sopenharmony_ci#define HS200_MODE	BIT(4)
8162306a36Sopenharmony_ci#define HISPD_MODE	BIT(5)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define OTAPDLY(x)	(((x) << 1) | OTAPDLY_EN)
8462306a36Sopenharmony_ci#define ITAPDLY(x)	(((x) << 1) | ITAPDLY_EN)
8562306a36Sopenharmony_ci#define FREQSEL(x)	(((x) << 5) | DLL_ENBL)
8662306a36Sopenharmony_ci#define IOPAD(x, y)	((x) | ((y) << 2))
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* Arasan private data */
8962306a36Sopenharmony_cistruct arasan_host {
9062306a36Sopenharmony_ci	u32 chg_clk;
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int arasan_phy_addr_poll(struct sdhci_host *host, u32 offset, u32 mask)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	ktime_t timeout = ktime_add_us(ktime_get(), 100);
9662306a36Sopenharmony_ci	bool failed;
9762306a36Sopenharmony_ci	u8 val = 0;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	while (1) {
10062306a36Sopenharmony_ci		failed = ktime_after(ktime_get(), timeout);
10162306a36Sopenharmony_ci		val = sdhci_readw(host, PHY_ADDR_REG);
10262306a36Sopenharmony_ci		if (!(val & mask))
10362306a36Sopenharmony_ci			return 0;
10462306a36Sopenharmony_ci		if (failed)
10562306a36Sopenharmony_ci			return -EBUSY;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int arasan_phy_write(struct sdhci_host *host, u8 data, u8 offset)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	sdhci_writew(host, data, PHY_DAT_REG);
11262306a36Sopenharmony_ci	sdhci_writew(host, (PHY_WRITE | offset), PHY_ADDR_REG);
11362306a36Sopenharmony_ci	return arasan_phy_addr_poll(host, PHY_ADDR_REG, PHY_BUSY);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int arasan_phy_read(struct sdhci_host *host, u8 offset, u8 *data)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	int ret;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	sdhci_writew(host, 0, PHY_DAT_REG);
12162306a36Sopenharmony_ci	sdhci_writew(host, offset, PHY_ADDR_REG);
12262306a36Sopenharmony_ci	ret = arasan_phy_addr_poll(host, PHY_ADDR_REG, PHY_BUSY);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/* Masking valid data bits */
12562306a36Sopenharmony_ci	*data = sdhci_readw(host, PHY_DAT_REG) & DATA_MASK;
12662306a36Sopenharmony_ci	return ret;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int arasan_phy_sts_poll(struct sdhci_host *host, u32 offset, u32 mask)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	int ret;
13262306a36Sopenharmony_ci	ktime_t timeout = ktime_add_us(ktime_get(), 100);
13362306a36Sopenharmony_ci	bool failed;
13462306a36Sopenharmony_ci	u8 val = 0;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	while (1) {
13762306a36Sopenharmony_ci		failed = ktime_after(ktime_get(), timeout);
13862306a36Sopenharmony_ci		ret = arasan_phy_read(host, offset, &val);
13962306a36Sopenharmony_ci		if (ret)
14062306a36Sopenharmony_ci			return -EBUSY;
14162306a36Sopenharmony_ci		else if (val & mask)
14262306a36Sopenharmony_ci			return 0;
14362306a36Sopenharmony_ci		if (failed)
14462306a36Sopenharmony_ci			return -EBUSY;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/* Initialize the Arasan PHY */
14962306a36Sopenharmony_cistatic int arasan_phy_init(struct sdhci_host *host)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	int ret;
15262306a36Sopenharmony_ci	u8 val;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* Program IOPADs and wait for calibration to be done */
15562306a36Sopenharmony_ci	if (arasan_phy_read(host, IPAD_CTRL1, &val) ||
15662306a36Sopenharmony_ci	    arasan_phy_write(host, val | RETB_ENBL | PDB_ENBL, IPAD_CTRL1) ||
15762306a36Sopenharmony_ci	    arasan_phy_read(host, IPAD_CTRL2, &val) ||
15862306a36Sopenharmony_ci	    arasan_phy_write(host, val | RTRIM_EN, IPAD_CTRL2))
15962306a36Sopenharmony_ci		return -EBUSY;
16062306a36Sopenharmony_ci	ret = arasan_phy_sts_poll(host, IPAD_STS, CALDONE_MASK);
16162306a36Sopenharmony_ci	if (ret)
16262306a36Sopenharmony_ci		return -EBUSY;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Program CMD/Data lines */
16562306a36Sopenharmony_ci	if (arasan_phy_read(host, IOREN_CTRL1, &val) ||
16662306a36Sopenharmony_ci	    arasan_phy_write(host, val | REN_CMND | REN_STRB, IOREN_CTRL1) ||
16762306a36Sopenharmony_ci	    arasan_phy_read(host, IOPU_CTRL1, &val) ||
16862306a36Sopenharmony_ci	    arasan_phy_write(host, val | PU_CMD, IOPU_CTRL1) ||
16962306a36Sopenharmony_ci	    arasan_phy_read(host, CMD_CTRL, &val) ||
17062306a36Sopenharmony_ci	    arasan_phy_write(host, val | PDB_CMND, CMD_CTRL) ||
17162306a36Sopenharmony_ci	    arasan_phy_read(host, IOREN_CTRL2, &val) ||
17262306a36Sopenharmony_ci	    arasan_phy_write(host, val | REN_DATA, IOREN_CTRL2) ||
17362306a36Sopenharmony_ci	    arasan_phy_read(host, IOPU_CTRL2, &val) ||
17462306a36Sopenharmony_ci	    arasan_phy_write(host, val | PU_DAT, IOPU_CTRL2) ||
17562306a36Sopenharmony_ci	    arasan_phy_read(host, DATA_CTRL, &val) ||
17662306a36Sopenharmony_ci	    arasan_phy_write(host, val | PDB_DATA, DATA_CTRL) ||
17762306a36Sopenharmony_ci	    arasan_phy_read(host, STRB_CTRL, &val) ||
17862306a36Sopenharmony_ci	    arasan_phy_write(host, val | PDB_STRB, STRB_CTRL) ||
17962306a36Sopenharmony_ci	    arasan_phy_read(host, CLK_CTRL, &val) ||
18062306a36Sopenharmony_ci	    arasan_phy_write(host, val | PDB_CLOCK, CLK_CTRL) ||
18162306a36Sopenharmony_ci	    arasan_phy_read(host, CLKBUF_SEL, &val) ||
18262306a36Sopenharmony_ci	    arasan_phy_write(host, val | MAX_CLK_BUF, CLKBUF_SEL) ||
18362306a36Sopenharmony_ci	    arasan_phy_write(host, LEGACY_MODE, MODE_CTRL))
18462306a36Sopenharmony_ci		return -EBUSY;
18562306a36Sopenharmony_ci	return 0;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/* Set Arasan PHY for different modes */
18962306a36Sopenharmony_cistatic int arasan_phy_set(struct sdhci_host *host, u8 mode, u8 otap,
19062306a36Sopenharmony_ci			  u8 drv_type, u8 itap, u8 trim, u8 clk)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	u8 val;
19362306a36Sopenharmony_ci	int ret;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (mode == HISPD_MODE || mode == HS200_MODE)
19662306a36Sopenharmony_ci		ret = arasan_phy_write(host, 0x0, MODE_CTRL);
19762306a36Sopenharmony_ci	else
19862306a36Sopenharmony_ci		ret = arasan_phy_write(host, mode, MODE_CTRL);
19962306a36Sopenharmony_ci	if (ret)
20062306a36Sopenharmony_ci		return ret;
20162306a36Sopenharmony_ci	if (mode == HS400_MODE || mode == HS200_MODE) {
20262306a36Sopenharmony_ci		ret = arasan_phy_read(host, IPAD_CTRL1, &val);
20362306a36Sopenharmony_ci		if (ret)
20462306a36Sopenharmony_ci			return ret;
20562306a36Sopenharmony_ci		ret = arasan_phy_write(host, IOPAD(val, drv_type), IPAD_CTRL1);
20662306a36Sopenharmony_ci		if (ret)
20762306a36Sopenharmony_ci			return ret;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci	if (mode == LEGACY_MODE) {
21062306a36Sopenharmony_ci		ret = arasan_phy_write(host, 0x0, OTAP_DELAY);
21162306a36Sopenharmony_ci		if (ret)
21262306a36Sopenharmony_ci			return ret;
21362306a36Sopenharmony_ci		ret = arasan_phy_write(host, 0x0, ITAP_DELAY);
21462306a36Sopenharmony_ci	} else {
21562306a36Sopenharmony_ci		ret = arasan_phy_write(host, OTAPDLY(otap), OTAP_DELAY);
21662306a36Sopenharmony_ci		if (ret)
21762306a36Sopenharmony_ci			return ret;
21862306a36Sopenharmony_ci		if (mode != HS200_MODE)
21962306a36Sopenharmony_ci			ret = arasan_phy_write(host, ITAPDLY(itap), ITAP_DELAY);
22062306a36Sopenharmony_ci		else
22162306a36Sopenharmony_ci			ret = arasan_phy_write(host, 0x0, ITAP_DELAY);
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci	if (ret)
22462306a36Sopenharmony_ci		return ret;
22562306a36Sopenharmony_ci	if (mode != LEGACY_MODE) {
22662306a36Sopenharmony_ci		ret = arasan_phy_write(host, trim, DLL_TRIM);
22762306a36Sopenharmony_ci		if (ret)
22862306a36Sopenharmony_ci			return ret;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci	ret = arasan_phy_write(host, 0, DLL_STATUS);
23162306a36Sopenharmony_ci	if (ret)
23262306a36Sopenharmony_ci		return ret;
23362306a36Sopenharmony_ci	if (mode != LEGACY_MODE) {
23462306a36Sopenharmony_ci		ret = arasan_phy_write(host, FREQSEL(clk), DLL_STATUS);
23562306a36Sopenharmony_ci		if (ret)
23662306a36Sopenharmony_ci			return ret;
23762306a36Sopenharmony_ci		ret = arasan_phy_sts_poll(host, DLL_STATUS, DLL_RDY_MASK);
23862306a36Sopenharmony_ci		if (ret)
23962306a36Sopenharmony_ci			return -EBUSY;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	return 0;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int arasan_select_phy_clock(struct sdhci_host *host)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct sdhci_pci_slot *slot = sdhci_priv(host);
24762306a36Sopenharmony_ci	struct arasan_host *arasan_host = sdhci_pci_priv(slot);
24862306a36Sopenharmony_ci	u8 clk;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (arasan_host->chg_clk == host->mmc->ios.clock)
25162306a36Sopenharmony_ci		return 0;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	arasan_host->chg_clk = host->mmc->ios.clock;
25462306a36Sopenharmony_ci	if (host->mmc->ios.clock == 200000000)
25562306a36Sopenharmony_ci		clk = 0x0;
25662306a36Sopenharmony_ci	else if (host->mmc->ios.clock == 100000000)
25762306a36Sopenharmony_ci		clk = 0x2;
25862306a36Sopenharmony_ci	else if (host->mmc->ios.clock == 50000000)
25962306a36Sopenharmony_ci		clk = 0x1;
26062306a36Sopenharmony_ci	else
26162306a36Sopenharmony_ci		clk = 0x0;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (host->mmc_host_ops.hs400_enhanced_strobe) {
26462306a36Sopenharmony_ci		arasan_phy_set(host, ENHSTRB_MODE, 1, 0x0, 0x0,
26562306a36Sopenharmony_ci			       DLLTRM_ICP, clk);
26662306a36Sopenharmony_ci	} else {
26762306a36Sopenharmony_ci		switch (host->mmc->ios.timing) {
26862306a36Sopenharmony_ci		case MMC_TIMING_LEGACY:
26962306a36Sopenharmony_ci			arasan_phy_set(host, LEGACY_MODE, 0x0, 0x0, 0x0,
27062306a36Sopenharmony_ci				       0x0, 0x0);
27162306a36Sopenharmony_ci			break;
27262306a36Sopenharmony_ci		case MMC_TIMING_MMC_HS:
27362306a36Sopenharmony_ci		case MMC_TIMING_SD_HS:
27462306a36Sopenharmony_ci			arasan_phy_set(host, HISPD_MODE, 0x3, 0x0, 0x2,
27562306a36Sopenharmony_ci				       DLLTRM_ICP, clk);
27662306a36Sopenharmony_ci			break;
27762306a36Sopenharmony_ci		case MMC_TIMING_MMC_HS200:
27862306a36Sopenharmony_ci		case MMC_TIMING_UHS_SDR104:
27962306a36Sopenharmony_ci			arasan_phy_set(host, HS200_MODE, 0x2,
28062306a36Sopenharmony_ci				       host->mmc->ios.drv_type, 0x0,
28162306a36Sopenharmony_ci				       DLLTRM_ICP, clk);
28262306a36Sopenharmony_ci			break;
28362306a36Sopenharmony_ci		case MMC_TIMING_MMC_DDR52:
28462306a36Sopenharmony_ci		case MMC_TIMING_UHS_DDR50:
28562306a36Sopenharmony_ci			arasan_phy_set(host, DDR50_MODE, 0x1, 0x0,
28662306a36Sopenharmony_ci				       0x0, DLLTRM_ICP, clk);
28762306a36Sopenharmony_ci			break;
28862306a36Sopenharmony_ci		case MMC_TIMING_MMC_HS400:
28962306a36Sopenharmony_ci			arasan_phy_set(host, HS400_MODE, 0x1,
29062306a36Sopenharmony_ci				       host->mmc->ios.drv_type, 0xa,
29162306a36Sopenharmony_ci				       DLLTRM_ICP, clk);
29262306a36Sopenharmony_ci			break;
29362306a36Sopenharmony_ci		default:
29462306a36Sopenharmony_ci			break;
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci	return 0;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic int arasan_pci_probe_slot(struct sdhci_pci_slot *slot)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	int err;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE | MMC_CAP_8_BIT_DATA;
30562306a36Sopenharmony_ci	err = arasan_phy_init(slot->host);
30662306a36Sopenharmony_ci	if (err)
30762306a36Sopenharmony_ci		return -ENODEV;
30862306a36Sopenharmony_ci	return 0;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic void arasan_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	sdhci_set_clock(host, clock);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* Change phy settings for the new clock */
31662306a36Sopenharmony_ci	arasan_select_phy_clock(host);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic const struct sdhci_ops arasan_sdhci_pci_ops = {
32062306a36Sopenharmony_ci	.set_clock	= arasan_sdhci_set_clock,
32162306a36Sopenharmony_ci	.enable_dma	= sdhci_pci_enable_dma,
32262306a36Sopenharmony_ci	.set_bus_width	= sdhci_set_bus_width,
32362306a36Sopenharmony_ci	.reset		= sdhci_reset,
32462306a36Sopenharmony_ci	.set_uhs_signaling	= sdhci_set_uhs_signaling,
32562306a36Sopenharmony_ci};
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ciconst struct sdhci_pci_fixes sdhci_arasan = {
32862306a36Sopenharmony_ci	.probe_slot = arasan_pci_probe_slot,
32962306a36Sopenharmony_ci	.ops        = &arasan_sdhci_pci_ops,
33062306a36Sopenharmony_ci	.priv_size  = sizeof(struct arasan_host),
33162306a36Sopenharmony_ci};
332