linux/drivers/mmc/host/sdhci-pci-arasan.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * sdhci-pci-arasan.c - Driver for Arasan PCI Controller with
   4 * 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    0x300
  19#define PHY_DAT_REG     0x304
  20
  21#define PHY_WRITE       BIT(8)
  22#define PHY_BUSY        BIT(9)
  23#define DATA_MASK       0xFF
  24
  25/* PHY Specific Registers */
  26#define DLL_STATUS      0x00
  27#define IPAD_CTRL1      0x01
  28#define IPAD_CTRL2      0x02
  29#define IPAD_STS        0x03
  30#define IOREN_CTRL1     0x06
  31#define IOREN_CTRL2     0x07
  32#define IOPU_CTRL1      0x08
  33#define IOPU_CTRL2      0x09
  34#define ITAP_DELAY      0x0C
  35#define OTAP_DELAY      0x0D
  36#define STRB_SEL        0x0E
  37#define CLKBUF_SEL      0x0F
  38#define MODE_CTRL       0x11
  39#define DLL_TRIM        0x12
  40#define CMD_CTRL        0x20
  41#define DATA_CTRL       0x21
  42#define STRB_CTRL       0x22
  43#define CLK_CTRL        0x23
  44#define PHY_CTRL        0x24
  45
  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        0xFF
  52#define REN_STRB        BIT(0)
  53#define REN_CMND        BIT(1)
  54#define REN_DATA        0xFF
  55#define PU_CMD          BIT(1)
  56#define PU_DAT          0xFF
  57#define ITAPDLY_EN      BIT(0)
  58#define OTAPDLY_EN      BIT(0)
  59#define OD_REL_CMD      BIT(1)
  60#define OD_REL_DAT      0xFF
  61#define DLLTRM_ICP      0x8
  62#define PDB_CMND        BIT(0)
  63#define PDB_DATA        0xFF
  64#define PDB_STRB        BIT(0)
  65#define PDB_CLOCK       BIT(0)
  66#define CALDONE_MASK    0x10
  67#define DLL_RDY_MASK    0x10
  68#define MAX_CLK_BUF     0x7
  69
  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        else
 198                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                else
 221                        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        else
 261                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