[Feature] add GA346 baseline version

Change-Id: Ic62933698569507dcf98240cdf5d9931ae34348f
diff --git a/src/kernel/linux/v4.19/drivers/mmc/host/sdhci-pci-arasan.c b/src/kernel/linux/v4.19/drivers/mmc/host/sdhci-pci-arasan.c
new file mode 100644
index 0000000..499f320
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/mmc/host/sdhci-pci-arasan.c
@@ -0,0 +1,331 @@
+// 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;
+	}
+}
+
+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);
+}
+
+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;
+}
+
+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;
+	}
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+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;
+}
+
+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;
+}
+
+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);
+}
+
+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),
+};