brintos

brintos / linux-shallow public Read only

0
0
Text · 8.3 KiB · 499f320 Raw
332 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * sdhci-pci-arasan.c - Driver for Arasan PCI Controller with4 * 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	0x30019#define PHY_DAT_REG	0x30420 21#define PHY_WRITE	BIT(8)22#define PHY_BUSY	BIT(9)23#define DATA_MASK	0xFF24 25/* PHY Specific Registers */26#define DLL_STATUS	0x0027#define IPAD_CTRL1	0x0128#define IPAD_CTRL2	0x0229#define IPAD_STS	0x0330#define IOREN_CTRL1	0x0631#define IOREN_CTRL2	0x0732#define IOPU_CTRL1	0x0833#define IOPU_CTRL2	0x0934#define ITAP_DELAY	0x0C35#define OTAP_DELAY	0x0D36#define STRB_SEL	0x0E37#define CLKBUF_SEL	0x0F38#define MODE_CTRL	0x1139#define DLL_TRIM	0x1240#define CMD_CTRL	0x2041#define DATA_CTRL	0x2142#define STRB_CTRL	0x2243#define CLK_CTRL	0x2344#define PHY_CTRL	0x2445 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	0xFF52#define REN_STRB	BIT(0)53#define REN_CMND	BIT(1)54#define REN_DATA	0xFF55#define PU_CMD		BIT(1)56#define PU_DAT		0xFF57#define ITAPDLY_EN	BIT(0)58#define OTAPDLY_EN	BIT(0)59#define OD_REL_CMD	BIT(1)60#define OD_REL_DAT	0xFF61#define DLLTRM_ICP	0x862#define PDB_CMND	BIT(0)63#define PDB_DATA	0xFF64#define PDB_STRB	BIT(0)65#define PDB_CLOCK	BIT(0)66#define CALDONE_MASK	0x1067#define DLL_RDY_MASK	0x1068#define MAX_CLK_BUF	0x769 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(ktime_get(), 100);96	bool failed;97	u8 val = 0;98 99	while (1) {100		failed = ktime_after(ktime_get(), 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, data, PHY_DAT_REG);112	sdhci_writew(host, (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, 0, PHY_DAT_REG);121	sdhci_writew(host, 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(ktime_get(), 100);133	bool failed;134	u8 val = 0;135 136	while (1) {137		failed = ktime_after(ktime_get(), timeout);138		ret = arasan_phy_read(host, offset, &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, &val) ||156	    arasan_phy_write(host, val | RETB_ENBL | PDB_ENBL, IPAD_CTRL1) ||157	    arasan_phy_read(host, IPAD_CTRL2, &val) ||158	    arasan_phy_write(host, 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, &val) ||166	    arasan_phy_write(host, val | REN_CMND | REN_STRB, IOREN_CTRL1) ||167	    arasan_phy_read(host, IOPU_CTRL1, &val) ||168	    arasan_phy_write(host, val | PU_CMD, IOPU_CTRL1) ||169	    arasan_phy_read(host, CMD_CTRL, &val) ||170	    arasan_phy_write(host, val | PDB_CMND, CMD_CTRL) ||171	    arasan_phy_read(host, IOREN_CTRL2, &val) ||172	    arasan_phy_write(host, val | REN_DATA, IOREN_CTRL2) ||173	    arasan_phy_read(host, IOPU_CTRL2, &val) ||174	    arasan_phy_write(host, val | PU_DAT, IOPU_CTRL2) ||175	    arasan_phy_read(host, DATA_CTRL, &val) ||176	    arasan_phy_write(host, val | PDB_DATA, DATA_CTRL) ||177	    arasan_phy_read(host, STRB_CTRL, &val) ||178	    arasan_phy_write(host, val | PDB_STRB, STRB_CTRL) ||179	    arasan_phy_read(host, CLK_CTRL, &val) ||180	    arasan_phy_write(host, val | PDB_CLOCK, CLK_CTRL) ||181	    arasan_phy_read(host, CLKBUF_SEL, &val) ||182	    arasan_phy_write(host, 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, 0x0, MODE_CTRL);197	else198		ret = arasan_phy_write(host, 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, &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, 0x0, OTAP_DELAY);211		if (ret)212			return ret;213		ret = arasan_phy_write(host, 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		else221			ret = arasan_phy_write(host, 0x0, ITAP_DELAY);222	}223	if (ret)224		return ret;225	if (mode != LEGACY_MODE) {226		ret = arasan_phy_write(host, trim, DLL_TRIM);227		if (ret)228			return ret;229	}230	ret = arasan_phy_write(host, 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	else261		clk = 0x0;262 263	if (host->mmc_host_ops.hs400_enhanced_strobe) {264		arasan_phy_set(host, ENHSTRB_MODE, 1, 0x0, 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, 0x0, 0x0, 0x0,270				       0x0, 0x0);271			break;272		case MMC_TIMING_MMC_HS:273		case MMC_TIMING_SD_HS:274			arasan_phy_set(host, HISPD_MODE, 0x3, 0x0, 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, 0x2,280				       host->mmc->ios.drv_type, 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, 0x1, 0x0,286				       0x0, DLLTRM_ICP, clk);287			break;288		case MMC_TIMING_MMC_HS400:289			arasan_phy_set(host, HS400_MODE, 0x1,290				       host->mmc->ios.drv_type, 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(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