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