cregit-Linux how code gets into the kernel

Release 4.16 drivers/mmc/host/sdhci-pci-arasan.c

Directory: drivers/mmc/host
// SPDX-License-Identifier: GPL-2.0
/*
 * sdhci-pci-arasan.c - Driver for Arasan PCI Controller with
 * integrated phy.
 *
 * Copyright (C) 2017 Arasan Chip Systems Inc.
 *
 * Author: Atul Garg <agarg@arasan.com>
 */

#include <linux/pci.h>
#include <linux/delay.h>

#include "sdhci.h"
#include "sdhci-pci.h"

/* Extra registers for Arasan SD/SDIO/MMC Host Controller with PHY */

#define PHY_ADDR_REG	0x300

#define PHY_DAT_REG	0x304


#define PHY_WRITE	BIT(8)

#define PHY_BUSY	BIT(9)

#define DATA_MASK	0xFF

/* PHY Specific Registers */

#define DLL_STATUS	0x00

#define IPAD_CTRL1	0x01

#define IPAD_CTRL2	0x02

#define IPAD_STS	0x03

#define IOREN_CTRL1	0x06

#define IOREN_CTRL2	0x07

#define IOPU_CTRL1	0x08

#define IOPU_CTRL2	0x09

#define ITAP_DELAY	0x0C

#define OTAP_DELAY	0x0D

#define STRB_SEL	0x0E

#define CLKBUF_SEL	0x0F

#define MODE_CTRL	0x11

#define DLL_TRIM	0x12

#define CMD_CTRL	0x20

#define DATA_CTRL	0x21

#define STRB_CTRL	0x22

#define CLK_CTRL	0x23

#define PHY_CTRL	0x24


#define DLL_ENBL	BIT(3)

#define RTRIM_EN	BIT(1)

#define PDB_ENBL	BIT(1)

#define RETB_ENBL	BIT(6)

#define ODEN_CMD	BIT(1)

#define ODEN_DAT	0xFF

#define REN_STRB	BIT(0)

#define REN_CMND	BIT(1)

#define REN_DATA	0xFF

#define PU_CMD		BIT(1)

#define PU_DAT		0xFF

#define ITAPDLY_EN	BIT(0)

#define OTAPDLY_EN	BIT(0)

#define OD_REL_CMD	BIT(1)

#define OD_REL_DAT	0xFF

#define DLLTRM_ICP	0x8

#define PDB_CMND	BIT(0)

#define PDB_DATA	0xFF

#define PDB_STRB	BIT(0)

#define PDB_CLOCK	BIT(0)

#define CALDONE_MASK	0x10

#define DLL_RDY_MASK	0x10

#define MAX_CLK_BUF	0x7

/* Mode Controls */

#define ENHSTRB_MODE	BIT(0)

#define HS400_MODE	BIT(1)

#define LEGACY_MODE	BIT(2)

#define DDR50_MODE	BIT(3)

/*
 * Controller has no specific bits for HS200/HS.
 * Used BIT(4), BIT(5) for software programming.
 */

#define HS200_MODE	BIT(4)

#define HISPD_MODE	BIT(5)


#define OTAPDLY(x)	(((x) << 1) | OTAPDLY_EN)

#define ITAPDLY(x)	(((x) << 1) | ITAPDLY_EN)

#define FREQSEL(x)	(((x) << 5) | DLL_ENBL)

#define IOPAD(x, y)	((x) | ((y) << 2))

/* Arasan private data */

struct arasan_host {
	
u32 chg_clk;
};


static int arasan_phy_addr_poll(struct sdhci_host *host, u32 offset, u32 mask) { ktime_t timeout = ktime_add_us(ktime_get(), 100); bool failed; u8 val = 0; while (1) { failed = ktime_after(ktime_get(), timeout); val = sdhci_readw(host, PHY_ADDR_REG); if (!(val & mask)) return 0; if (failed) return -EBUSY; } }

Contributors

PersonTokensPropCommitsCommitProp
Atul Garg81100.00%1100.00%
Total81100.00%1100.00%


static int arasan_phy_write(struct sdhci_host *host, u8 data, u8 offset) { sdhci_writew(host, data, PHY_DAT_REG); sdhci_writew(host, (PHY_WRITE | offset), PHY_ADDR_REG); return arasan_phy_addr_poll(host, PHY_ADDR_REG, PHY_BUSY); }

Contributors

PersonTokensPropCommitsCommitProp
Atul Garg49100.00%1100.00%
Total49100.00%1100.00%


static int arasan_phy_read(struct sdhci_host *host, u8 offset, u8 *data) { int ret; sdhci_writew(host, 0, PHY_DAT_REG); sdhci_writew(host, offset, PHY_ADDR_REG); ret = arasan_phy_addr_poll(host, PHY_ADDR_REG, PHY_BUSY); /* Masking valid data bits */ *data = sdhci_readw(host, PHY_DAT_REG) & DATA_MASK; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Atul Garg66100.00%1100.00%
Total66100.00%1100.00%


static int arasan_phy_sts_poll(struct sdhci_host *host, u32 offset, u32 mask) { int ret; ktime_t timeout = ktime_add_us(ktime_get(), 100); bool failed; u8 val = 0; while (1) { failed = ktime_after(ktime_get(), timeout); ret = arasan_phy_read(host, offset, &val); if (ret) return -EBUSY; else if (val & mask) return 0; if (failed) return -EBUSY; } }

Contributors

PersonTokensPropCommitsCommitProp
Atul Garg93100.00%1100.00%
Total93100.00%1100.00%

/* Initialize the Arasan PHY */
static int arasan_phy_init(struct sdhci_host *host) { int ret; u8 val; /* Program IOPADs and wait for calibration to be done */ if (arasan_phy_read(host, IPAD_CTRL1, &val) || arasan_phy_write(host, val | RETB_ENBL | PDB_ENBL, IPAD_CTRL1) || arasan_phy_read(host, IPAD_CTRL2, &val) || arasan_phy_write(host, val | RTRIM_EN, IPAD_CTRL2)) return -EBUSY; ret = arasan_phy_sts_poll(host, IPAD_STS, CALDONE_MASK); if (ret) return -EBUSY; /* Program CMD/Data lines */ if (arasan_phy_read(host, IOREN_CTRL1, &val) || arasan_phy_write(host, val | REN_CMND | REN_STRB, IOREN_CTRL1) || arasan_phy_read(host, IOPU_CTRL1, &val) || arasan_phy_write(host, val | PU_CMD, IOPU_CTRL1) || arasan_phy_read(host, CMD_CTRL, &val) || arasan_phy_write(host, val | PDB_CMND, CMD_CTRL) || arasan_phy_read(host, IOREN_CTRL2, &val) || arasan_phy_write(host, val | REN_DATA, IOREN_CTRL2) || arasan_phy_read(host, IOPU_CTRL2, &val) || arasan_phy_write(host, val | PU_DAT, IOPU_CTRL2) || arasan_phy_read(host, DATA_CTRL, &val) || arasan_phy_write(host, val | PDB_DATA, DATA_CTRL) || arasan_phy_read(host, STRB_CTRL, &val) || arasan_phy_write(host, val | PDB_STRB, STRB_CTRL) || arasan_phy_read(host, CLK_CTRL, &val) || arasan_phy_write(host, val | PDB_CLOCK, CLK_CTRL) || arasan_phy_read(host, CLKBUF_SEL, &val) || arasan_phy_write(host, val | MAX_CLK_BUF, CLKBUF_SEL) || arasan_phy_write(host, LEGACY_MODE, MODE_CTRL)) return -EBUSY; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Atul Garg297100.00%1100.00%
Total297100.00%1100.00%

/* Set Arasan PHY for different modes */
static int arasan_phy_set(struct sdhci_host *host, u8 mode, u8 otap, u8 drv_type, u8 itap, u8 trim, u8 clk) { u8 val; int ret; if (mode == HISPD_MODE || mode == HS200_MODE) ret = arasan_phy_write(host, 0x0, MODE_CTRL); else ret = arasan_phy_write(host, mode, MODE_CTRL); if (ret) return ret; if (mode == HS400_MODE || mode == HS200_MODE) { ret = arasan_phy_read(host, IPAD_CTRL1, &val); if (ret) return ret; ret = arasan_phy_write(host, IOPAD(val, drv_type), IPAD_CTRL1); if (ret) return ret; } if (mode == LEGACY_MODE) { ret = arasan_phy_write(host, 0x0, OTAP_DELAY); if (ret) return ret; ret = arasan_phy_write(host, 0x0, ITAP_DELAY); } else { ret = arasan_phy_write(host, OTAPDLY(otap), OTAP_DELAY); if (ret) return ret; if (mode != HS200_MODE) ret = arasan_phy_write(host, ITAPDLY(itap), ITAP_DELAY); else ret = arasan_phy_write(host, 0x0, ITAP_DELAY); } if (ret) return ret; if (mode != LEGACY_MODE) { ret = arasan_phy_write(host, trim, DLL_TRIM); if (ret) return ret; } ret = arasan_phy_write(host, 0, DLL_STATUS); if (ret) return ret; if (mode != LEGACY_MODE) { ret = arasan_phy_write(host, FREQSEL(clk), DLL_STATUS); if (ret) return ret; ret = arasan_phy_sts_poll(host, DLL_STATUS, DLL_RDY_MASK); if (ret) return -EBUSY; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Atul Garg324100.00%1100.00%
Total324100.00%1100.00%


static int arasan_select_phy_clock(struct sdhci_host *host) { struct sdhci_pci_slot *slot = sdhci_priv(host); struct arasan_host *arasan_host = sdhci_pci_priv(slot); u8 clk; if (arasan_host->chg_clk == host->mmc->ios.clock) return 0; arasan_host->chg_clk = host->mmc->ios.clock; if (host->mmc->ios.clock == 200000000) clk = 0x0; else if (host->mmc->ios.clock == 100000000) clk = 0x2; else if (host->mmc->ios.clock == 50000000) clk = 0x1; else clk = 0x0; if (host->mmc_host_ops.hs400_enhanced_strobe) { arasan_phy_set(host, ENHSTRB_MODE, 1, 0x0, 0x0, DLLTRM_ICP, clk); } else { switch (host->mmc->ios.timing) { case MMC_TIMING_LEGACY: arasan_phy_set(host, LEGACY_MODE, 0x0, 0x0, 0x0, 0x0, 0x0); break; case MMC_TIMING_MMC_HS: case MMC_TIMING_SD_HS: arasan_phy_set(host, HISPD_MODE, 0x3, 0x0, 0x2, DLLTRM_ICP, clk); break; case MMC_TIMING_MMC_HS200: case MMC_TIMING_UHS_SDR104: arasan_phy_set(host, HS200_MODE, 0x2, host->mmc->ios.drv_type, 0x0, DLLTRM_ICP, clk); break; case MMC_TIMING_MMC_DDR52: case MMC_TIMING_UHS_DDR50: arasan_phy_set(host, DDR50_MODE, 0x1, 0x0, 0x0, DLLTRM_ICP, clk); break; case MMC_TIMING_MMC_HS400: arasan_phy_set(host, HS400_MODE, 0x1, host->mmc->ios.drv_type, 0xa, DLLTRM_ICP, clk); break; default: break; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Atul Garg291100.00%1100.00%
Total291100.00%1100.00%


static int arasan_pci_probe_slot(struct sdhci_pci_slot *slot) { int err; slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE | MMC_CAP_8_BIT_DATA; err = arasan_phy_init(slot->host); if (err) return -ENODEV; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Atul Garg46100.00%1100.00%
Total46100.00%1100.00%


static void arasan_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { sdhci_set_clock(host, clock); /* Change phy settings for the new clock */ arasan_select_phy_clock(host); }

Contributors

PersonTokensPropCommitsCommitProp
Atul Garg28100.00%1100.00%
Total28100.00%1100.00%

static const struct sdhci_ops arasan_sdhci_pci_ops = { .set_clock = arasan_sdhci_set_clock, .enable_dma = sdhci_pci_enable_dma, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, }; const struct sdhci_pci_fixes sdhci_arasan = { .probe_slot = arasan_pci_probe_slot, .ops = &arasan_sdhci_pci_ops, .priv_size = sizeof(struct arasan_host), };

Overall Contributors

PersonTokensPropCommitsCommitProp
Atul Garg1606100.00%1100.00%
Total1606100.00%1100.00%
Directory: drivers/mmc/host
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.