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