18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Samsung SATA SerDes(PHY) driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Samsung Electronics Co., Ltd.
68c2ecf20Sopenharmony_ci * Authors: Girish K S <ks.giri@samsung.com>
78c2ecf20Sopenharmony_ci *         Yuvaraj Kumar C D <yuvaraj.cd@samsung.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/i2c.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/of_address.h>
188c2ecf20Sopenharmony_ci#include <linux/phy/phy.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/regmap.h>
218c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
228c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define SATAPHY_CONTROL_OFFSET		0x0724
258c2ecf20Sopenharmony_ci#define EXYNOS5_SATAPHY_PMU_ENABLE	BIT(0)
268c2ecf20Sopenharmony_ci#define EXYNOS5_SATA_RESET		0x4
278c2ecf20Sopenharmony_ci#define RESET_GLOBAL_RST_N		BIT(0)
288c2ecf20Sopenharmony_ci#define RESET_CMN_RST_N			BIT(1)
298c2ecf20Sopenharmony_ci#define RESET_CMN_BLOCK_RST_N		BIT(2)
308c2ecf20Sopenharmony_ci#define RESET_CMN_I2C_RST_N		BIT(3)
318c2ecf20Sopenharmony_ci#define RESET_TX_RX_PIPE_RST_N		BIT(4)
328c2ecf20Sopenharmony_ci#define RESET_TX_RX_BLOCK_RST_N		BIT(5)
338c2ecf20Sopenharmony_ci#define RESET_TX_RX_I2C_RST_N		(BIT(6) | BIT(7))
348c2ecf20Sopenharmony_ci#define LINK_RESET			0xf0000
358c2ecf20Sopenharmony_ci#define EXYNOS5_SATA_MODE0		0x10
368c2ecf20Sopenharmony_ci#define SATA_SPD_GEN3			BIT(1)
378c2ecf20Sopenharmony_ci#define EXYNOS5_SATA_CTRL0		0x14
388c2ecf20Sopenharmony_ci#define CTRL0_P0_PHY_CALIBRATED_SEL	BIT(9)
398c2ecf20Sopenharmony_ci#define CTRL0_P0_PHY_CALIBRATED		BIT(8)
408c2ecf20Sopenharmony_ci#define EXYNOS5_SATA_PHSATA_CTRLM	0xe0
418c2ecf20Sopenharmony_ci#define PHCTRLM_REF_RATE		BIT(1)
428c2ecf20Sopenharmony_ci#define PHCTRLM_HIGH_SPEED		BIT(0)
438c2ecf20Sopenharmony_ci#define EXYNOS5_SATA_PHSATA_STATM	0xf0
448c2ecf20Sopenharmony_ci#define PHSTATM_PLL_LOCKED		BIT(0)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000))
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct exynos_sata_phy {
498c2ecf20Sopenharmony_ci	struct phy *phy;
508c2ecf20Sopenharmony_ci	struct clk *phyclk;
518c2ecf20Sopenharmony_ci	void __iomem *regs;
528c2ecf20Sopenharmony_ci	struct regmap *pmureg;
538c2ecf20Sopenharmony_ci	struct i2c_client *client;
548c2ecf20Sopenharmony_ci};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit,
578c2ecf20Sopenharmony_ci				u32 status)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + PHY_PLL_TIMEOUT;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
628c2ecf20Sopenharmony_ci		if ((readl(base + reg) & checkbit) == status)
638c2ecf20Sopenharmony_ci			return 0;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return -EFAULT;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int exynos_sata_phy_power_on(struct phy *phy)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
748c2ecf20Sopenharmony_ci			EXYNOS5_SATAPHY_PMU_ENABLE, true);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int exynos_sata_phy_power_off(struct phy *phy)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
838c2ecf20Sopenharmony_ci			EXYNOS5_SATAPHY_PMU_ENABLE, false);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int exynos_sata_phy_init(struct phy *phy)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	u32 val = 0;
908c2ecf20Sopenharmony_ci	int ret = 0;
918c2ecf20Sopenharmony_ci	u8 buf[] = { 0x3a, 0x0b };
928c2ecf20Sopenharmony_ci	struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
958c2ecf20Sopenharmony_ci			EXYNOS5_SATAPHY_PMU_ENABLE, true);
968c2ecf20Sopenharmony_ci	if (ret != 0)
978c2ecf20Sopenharmony_ci		dev_err(&sata_phy->phy->dev, "phy init failed\n");
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
1028c2ecf20Sopenharmony_ci	val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N
1038c2ecf20Sopenharmony_ci		| RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N
1048c2ecf20Sopenharmony_ci		| RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N;
1058c2ecf20Sopenharmony_ci	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
1088c2ecf20Sopenharmony_ci	val |= LINK_RESET;
1098c2ecf20Sopenharmony_ci	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
1128c2ecf20Sopenharmony_ci	val |= RESET_CMN_RST_N;
1138c2ecf20Sopenharmony_ci	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
1168c2ecf20Sopenharmony_ci	val &= ~PHCTRLM_REF_RATE;
1178c2ecf20Sopenharmony_ci	writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* High speed enable for Gen3 */
1208c2ecf20Sopenharmony_ci	val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
1218c2ecf20Sopenharmony_ci	val |= PHCTRLM_HIGH_SPEED;
1228c2ecf20Sopenharmony_ci	writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0);
1258c2ecf20Sopenharmony_ci	val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED;
1268c2ecf20Sopenharmony_ci	writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0);
1298c2ecf20Sopenharmony_ci	val |= SATA_SPD_GEN3;
1308c2ecf20Sopenharmony_ci	writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	ret = i2c_master_send(sata_phy->client, buf, sizeof(buf));
1338c2ecf20Sopenharmony_ci	if (ret < 0)
1348c2ecf20Sopenharmony_ci		return ret;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* release cmu reset */
1378c2ecf20Sopenharmony_ci	val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
1388c2ecf20Sopenharmony_ci	val &= ~RESET_CMN_RST_N;
1398c2ecf20Sopenharmony_ci	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
1428c2ecf20Sopenharmony_ci	val |= RESET_CMN_RST_N;
1438c2ecf20Sopenharmony_ci	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	ret = wait_for_reg_status(sata_phy->regs,
1468c2ecf20Sopenharmony_ci				EXYNOS5_SATA_PHSATA_STATM,
1478c2ecf20Sopenharmony_ci				PHSTATM_PLL_LOCKED, 1);
1488c2ecf20Sopenharmony_ci	if (ret < 0)
1498c2ecf20Sopenharmony_ci		dev_err(&sata_phy->phy->dev,
1508c2ecf20Sopenharmony_ci			"PHY PLL locking failed\n");
1518c2ecf20Sopenharmony_ci	return ret;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic const struct phy_ops exynos_sata_phy_ops = {
1558c2ecf20Sopenharmony_ci	.init		= exynos_sata_phy_init,
1568c2ecf20Sopenharmony_ci	.power_on	= exynos_sata_phy_power_on,
1578c2ecf20Sopenharmony_ci	.power_off	= exynos_sata_phy_power_off,
1588c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1598c2ecf20Sopenharmony_ci};
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic int exynos_sata_phy_probe(struct platform_device *pdev)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct exynos_sata_phy *sata_phy;
1648c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1658c2ecf20Sopenharmony_ci	struct resource *res;
1668c2ecf20Sopenharmony_ci	struct phy_provider *phy_provider;
1678c2ecf20Sopenharmony_ci	struct device_node *node;
1688c2ecf20Sopenharmony_ci	int ret = 0;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL);
1718c2ecf20Sopenharmony_ci	if (!sata_phy)
1728c2ecf20Sopenharmony_ci		return -ENOMEM;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	sata_phy->regs = devm_ioremap_resource(dev, res);
1778c2ecf20Sopenharmony_ci	if (IS_ERR(sata_phy->regs))
1788c2ecf20Sopenharmony_ci		return PTR_ERR(sata_phy->regs);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1818c2ecf20Sopenharmony_ci					"samsung,syscon-phandle");
1828c2ecf20Sopenharmony_ci	if (IS_ERR(sata_phy->pmureg)) {
1838c2ecf20Sopenharmony_ci		dev_err(dev, "syscon regmap lookup failed.\n");
1848c2ecf20Sopenharmony_ci		return PTR_ERR(sata_phy->pmureg);
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	node = of_parse_phandle(dev->of_node,
1888c2ecf20Sopenharmony_ci			"samsung,exynos-sataphy-i2c-phandle", 0);
1898c2ecf20Sopenharmony_ci	if (!node)
1908c2ecf20Sopenharmony_ci		return -EINVAL;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	sata_phy->client = of_find_i2c_device_by_node(node);
1938c2ecf20Sopenharmony_ci	of_node_put(node);
1948c2ecf20Sopenharmony_ci	if (!sata_phy->client)
1958c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, sata_phy);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl");
2008c2ecf20Sopenharmony_ci	if (IS_ERR(sata_phy->phyclk)) {
2018c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get clk for PHY\n");
2028c2ecf20Sopenharmony_ci		ret = PTR_ERR(sata_phy->phyclk);
2038c2ecf20Sopenharmony_ci		goto put_dev;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(sata_phy->phyclk);
2078c2ecf20Sopenharmony_ci	if (ret < 0) {
2088c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable source clk\n");
2098c2ecf20Sopenharmony_ci		goto put_dev;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops);
2138c2ecf20Sopenharmony_ci	if (IS_ERR(sata_phy->phy)) {
2148c2ecf20Sopenharmony_ci		dev_err(dev, "failed to create PHY\n");
2158c2ecf20Sopenharmony_ci		ret = PTR_ERR(sata_phy->phy);
2168c2ecf20Sopenharmony_ci		goto clk_disable;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	phy_set_drvdata(sata_phy->phy, sata_phy);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	phy_provider = devm_of_phy_provider_register(dev,
2228c2ecf20Sopenharmony_ci					of_phy_simple_xlate);
2238c2ecf20Sopenharmony_ci	if (IS_ERR(phy_provider)) {
2248c2ecf20Sopenharmony_ci		ret = PTR_ERR(phy_provider);
2258c2ecf20Sopenharmony_ci		goto clk_disable;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	return 0;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ciclk_disable:
2318c2ecf20Sopenharmony_ci	clk_disable_unprepare(sata_phy->phyclk);
2328c2ecf20Sopenharmony_ciput_dev:
2338c2ecf20Sopenharmony_ci	put_device(&sata_phy->client->dev);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return ret;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_sata_phy_of_match[] = {
2398c2ecf20Sopenharmony_ci	{ .compatible = "samsung,exynos5250-sata-phy" },
2408c2ecf20Sopenharmony_ci	{ },
2418c2ecf20Sopenharmony_ci};
2428c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic struct platform_driver exynos_sata_phy_driver = {
2458c2ecf20Sopenharmony_ci	.probe	= exynos_sata_phy_probe,
2468c2ecf20Sopenharmony_ci	.driver = {
2478c2ecf20Sopenharmony_ci		.of_match_table	= exynos_sata_phy_of_match,
2488c2ecf20Sopenharmony_ci		.name  = "samsung,sata-phy",
2498c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci};
2528c2ecf20Sopenharmony_cimodule_platform_driver(exynos_sata_phy_driver);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Samsung SerDes PHY driver");
2558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
2568c2ecf20Sopenharmony_ciMODULE_AUTHOR("Girish K S <ks.giri@samsung.com>");
2578c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yuvaraj C D <yuvaraj.cd@samsung.com>");
258