1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20b56e9a7SVivek Gautam /*
30b56e9a7SVivek Gautam  * Samsung SATA SerDes(PHY) driver
40b56e9a7SVivek Gautam  *
50b56e9a7SVivek Gautam  * Copyright (C) 2013 Samsung Electronics Co., Ltd.
60b56e9a7SVivek Gautam  * Authors: Girish K S <ks.giri@samsung.com>
70b56e9a7SVivek Gautam  *         Yuvaraj Kumar C D <yuvaraj.cd@samsung.com>
80b56e9a7SVivek Gautam  */
90b56e9a7SVivek Gautam 
100b56e9a7SVivek Gautam #include <linux/clk.h>
110b56e9a7SVivek Gautam #include <linux/delay.h>
120b56e9a7SVivek Gautam #include <linux/io.h>
130b56e9a7SVivek Gautam #include <linux/i2c.h>
140b56e9a7SVivek Gautam #include <linux/kernel.h>
150b56e9a7SVivek Gautam #include <linux/module.h>
160b56e9a7SVivek Gautam #include <linux/of.h>
170b56e9a7SVivek Gautam #include <linux/of_address.h>
180b56e9a7SVivek Gautam #include <linux/phy/phy.h>
190b56e9a7SVivek Gautam #include <linux/platform_device.h>
200b56e9a7SVivek Gautam #include <linux/regmap.h>
210b56e9a7SVivek Gautam #include <linux/spinlock.h>
220b56e9a7SVivek Gautam #include <linux/mfd/syscon.h>
230b56e9a7SVivek Gautam 
240b56e9a7SVivek Gautam #define SATAPHY_CONTROL_OFFSET		0x0724
250b56e9a7SVivek Gautam #define EXYNOS5_SATAPHY_PMU_ENABLE	BIT(0)
260b56e9a7SVivek Gautam #define EXYNOS5_SATA_RESET		0x4
270b56e9a7SVivek Gautam #define RESET_GLOBAL_RST_N		BIT(0)
280b56e9a7SVivek Gautam #define RESET_CMN_RST_N			BIT(1)
290b56e9a7SVivek Gautam #define RESET_CMN_BLOCK_RST_N		BIT(2)
300b56e9a7SVivek Gautam #define RESET_CMN_I2C_RST_N		BIT(3)
310b56e9a7SVivek Gautam #define RESET_TX_RX_PIPE_RST_N		BIT(4)
320b56e9a7SVivek Gautam #define RESET_TX_RX_BLOCK_RST_N		BIT(5)
330b56e9a7SVivek Gautam #define RESET_TX_RX_I2C_RST_N		(BIT(6) | BIT(7))
340b56e9a7SVivek Gautam #define LINK_RESET			0xf0000
350b56e9a7SVivek Gautam #define EXYNOS5_SATA_MODE0		0x10
360b56e9a7SVivek Gautam #define SATA_SPD_GEN3			BIT(1)
370b56e9a7SVivek Gautam #define EXYNOS5_SATA_CTRL0		0x14
380b56e9a7SVivek Gautam #define CTRL0_P0_PHY_CALIBRATED_SEL	BIT(9)
390b56e9a7SVivek Gautam #define CTRL0_P0_PHY_CALIBRATED		BIT(8)
400b56e9a7SVivek Gautam #define EXYNOS5_SATA_PHSATA_CTRLM	0xe0
410b56e9a7SVivek Gautam #define PHCTRLM_REF_RATE		BIT(1)
420b56e9a7SVivek Gautam #define PHCTRLM_HIGH_SPEED		BIT(0)
430b56e9a7SVivek Gautam #define EXYNOS5_SATA_PHSATA_STATM	0xf0
440b56e9a7SVivek Gautam #define PHSTATM_PLL_LOCKED		BIT(0)
450b56e9a7SVivek Gautam 
460b56e9a7SVivek Gautam #define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000))
470b56e9a7SVivek Gautam 
480b56e9a7SVivek Gautam struct exynos_sata_phy {
490b56e9a7SVivek Gautam 	struct phy *phy;
500b56e9a7SVivek Gautam 	struct clk *phyclk;
510b56e9a7SVivek Gautam 	void __iomem *regs;
520b56e9a7SVivek Gautam 	struct regmap *pmureg;
530b56e9a7SVivek Gautam 	struct i2c_client *client;
540b56e9a7SVivek Gautam };
550b56e9a7SVivek Gautam 
560b56e9a7SVivek Gautam static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit,
570b56e9a7SVivek Gautam 				u32 status)
580b56e9a7SVivek Gautam {
590b56e9a7SVivek Gautam 	unsigned long timeout = jiffies + PHY_PLL_TIMEOUT;
600b56e9a7SVivek Gautam 
610b56e9a7SVivek Gautam 	while (time_before(jiffies, timeout)) {
620b56e9a7SVivek Gautam 		if ((readl(base + reg) & checkbit) == status)
630b56e9a7SVivek Gautam 			return 0;
640b56e9a7SVivek Gautam 	}
650b56e9a7SVivek Gautam 
660b56e9a7SVivek Gautam 	return -EFAULT;
670b56e9a7SVivek Gautam }
680b56e9a7SVivek Gautam 
690b56e9a7SVivek Gautam static int exynos_sata_phy_power_on(struct phy *phy)
700b56e9a7SVivek Gautam {
710b56e9a7SVivek Gautam 	struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
720b56e9a7SVivek Gautam 
730b56e9a7SVivek Gautam 	return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
740b56e9a7SVivek Gautam 			EXYNOS5_SATAPHY_PMU_ENABLE, true);
750b56e9a7SVivek Gautam 
760b56e9a7SVivek Gautam }
770b56e9a7SVivek Gautam 
780b56e9a7SVivek Gautam static int exynos_sata_phy_power_off(struct phy *phy)
790b56e9a7SVivek Gautam {
800b56e9a7SVivek Gautam 	struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
810b56e9a7SVivek Gautam 
820b56e9a7SVivek Gautam 	return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
830b56e9a7SVivek Gautam 			EXYNOS5_SATAPHY_PMU_ENABLE, false);
840b56e9a7SVivek Gautam 
850b56e9a7SVivek Gautam }
860b56e9a7SVivek Gautam 
870b56e9a7SVivek Gautam static int exynos_sata_phy_init(struct phy *phy)
880b56e9a7SVivek Gautam {
890b56e9a7SVivek Gautam 	u32 val = 0;
900b56e9a7SVivek Gautam 	int ret = 0;
910b56e9a7SVivek Gautam 	u8 buf[] = { 0x3a, 0x0b };
920b56e9a7SVivek Gautam 	struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
930b56e9a7SVivek Gautam 
940b56e9a7SVivek Gautam 	ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
950b56e9a7SVivek Gautam 			EXYNOS5_SATAPHY_PMU_ENABLE, true);
960b56e9a7SVivek Gautam 	if (ret != 0)
970b56e9a7SVivek Gautam 		dev_err(&sata_phy->phy->dev, "phy init failed\n");
980b56e9a7SVivek Gautam 
990b56e9a7SVivek Gautam 	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1000b56e9a7SVivek Gautam 
1010b56e9a7SVivek Gautam 	val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
1020b56e9a7SVivek Gautam 	val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N
1030b56e9a7SVivek Gautam 		| RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N
1040b56e9a7SVivek Gautam 		| RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N;
1050b56e9a7SVivek Gautam 	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1060b56e9a7SVivek Gautam 
1070b56e9a7SVivek Gautam 	val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
1080b56e9a7SVivek Gautam 	val |= LINK_RESET;
1090b56e9a7SVivek Gautam 	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1100b56e9a7SVivek Gautam 
1110b56e9a7SVivek Gautam 	val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
1120b56e9a7SVivek Gautam 	val |= RESET_CMN_RST_N;
1130b56e9a7SVivek Gautam 	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1140b56e9a7SVivek Gautam 
1150b56e9a7SVivek Gautam 	val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
1160b56e9a7SVivek Gautam 	val &= ~PHCTRLM_REF_RATE;
1170b56e9a7SVivek Gautam 	writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
1180b56e9a7SVivek Gautam 
1190b56e9a7SVivek Gautam 	/* High speed enable for Gen3 */
1200b56e9a7SVivek Gautam 	val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
1210b56e9a7SVivek Gautam 	val |= PHCTRLM_HIGH_SPEED;
1220b56e9a7SVivek Gautam 	writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
1230b56e9a7SVivek Gautam 
1240b56e9a7SVivek Gautam 	val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0);
1250b56e9a7SVivek Gautam 	val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED;
1260b56e9a7SVivek Gautam 	writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0);
1270b56e9a7SVivek Gautam 
1280b56e9a7SVivek Gautam 	val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0);
1290b56e9a7SVivek Gautam 	val |= SATA_SPD_GEN3;
1300b56e9a7SVivek Gautam 	writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0);
1310b56e9a7SVivek Gautam 
1320b56e9a7SVivek Gautam 	ret = i2c_master_send(sata_phy->client, buf, sizeof(buf));
1330b56e9a7SVivek Gautam 	if (ret < 0)
1340b56e9a7SVivek Gautam 		return ret;
1350b56e9a7SVivek Gautam 
1360b56e9a7SVivek Gautam 	/* release cmu reset */
1370b56e9a7SVivek Gautam 	val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
1380b56e9a7SVivek Gautam 	val &= ~RESET_CMN_RST_N;
1390b56e9a7SVivek Gautam 	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1400b56e9a7SVivek Gautam 
1410b56e9a7SVivek Gautam 	val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
1420b56e9a7SVivek Gautam 	val |= RESET_CMN_RST_N;
1430b56e9a7SVivek Gautam 	writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
1440b56e9a7SVivek Gautam 
1450b56e9a7SVivek Gautam 	ret = wait_for_reg_status(sata_phy->regs,
1460b56e9a7SVivek Gautam 				EXYNOS5_SATA_PHSATA_STATM,
1470b56e9a7SVivek Gautam 				PHSTATM_PLL_LOCKED, 1);
1480b56e9a7SVivek Gautam 	if (ret < 0)
1490b56e9a7SVivek Gautam 		dev_err(&sata_phy->phy->dev,
1500b56e9a7SVivek Gautam 			"PHY PLL locking failed\n");
1510b56e9a7SVivek Gautam 	return ret;
1520b56e9a7SVivek Gautam }
1530b56e9a7SVivek Gautam 
1540b56e9a7SVivek Gautam static const struct phy_ops exynos_sata_phy_ops = {
1550b56e9a7SVivek Gautam 	.init		= exynos_sata_phy_init,
1560b56e9a7SVivek Gautam 	.power_on	= exynos_sata_phy_power_on,
1570b56e9a7SVivek Gautam 	.power_off	= exynos_sata_phy_power_off,
1580b56e9a7SVivek Gautam 	.owner		= THIS_MODULE,
1590b56e9a7SVivek Gautam };
1600b56e9a7SVivek Gautam 
1610b56e9a7SVivek Gautam static int exynos_sata_phy_probe(struct platform_device *pdev)
1620b56e9a7SVivek Gautam {
1630b56e9a7SVivek Gautam 	struct exynos_sata_phy *sata_phy;
1640b56e9a7SVivek Gautam 	struct device *dev = &pdev->dev;
1650b56e9a7SVivek Gautam 	struct resource *res;
1660b56e9a7SVivek Gautam 	struct phy_provider *phy_provider;
1670b56e9a7SVivek Gautam 	struct device_node *node;
1680b56e9a7SVivek Gautam 	int ret = 0;
1690b56e9a7SVivek Gautam 
1700b56e9a7SVivek Gautam 	sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL);
1710b56e9a7SVivek Gautam 	if (!sata_phy)
1720b56e9a7SVivek Gautam 		return -ENOMEM;
1730b56e9a7SVivek Gautam 
1740b56e9a7SVivek Gautam 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1750b56e9a7SVivek Gautam 
1760b56e9a7SVivek Gautam 	sata_phy->regs = devm_ioremap_resource(dev, res);
1770b56e9a7SVivek Gautam 	if (IS_ERR(sata_phy->regs))
1780b56e9a7SVivek Gautam 		return PTR_ERR(sata_phy->regs);
1790b56e9a7SVivek Gautam 
1800b56e9a7SVivek Gautam 	sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1810b56e9a7SVivek Gautam 					"samsung,syscon-phandle");
1820b56e9a7SVivek Gautam 	if (IS_ERR(sata_phy->pmureg)) {
1830b56e9a7SVivek Gautam 		dev_err(dev, "syscon regmap lookup failed.\n");
1840b56e9a7SVivek Gautam 		return PTR_ERR(sata_phy->pmureg);
1850b56e9a7SVivek Gautam 	}
1860b56e9a7SVivek Gautam 
1870b56e9a7SVivek Gautam 	node = of_parse_phandle(dev->of_node,
1880b56e9a7SVivek Gautam 			"samsung,exynos-sataphy-i2c-phandle", 0);
1890b56e9a7SVivek Gautam 	if (!node)
1900b56e9a7SVivek Gautam 		return -EINVAL;
1910b56e9a7SVivek Gautam 
1920b56e9a7SVivek Gautam 	sata_phy->client = of_find_i2c_device_by_node(node);
1930b56e9a7SVivek Gautam 	if (!sata_phy->client)
1940b56e9a7SVivek Gautam 		return -EPROBE_DEFER;
1950b56e9a7SVivek Gautam 
1960b56e9a7SVivek Gautam 	dev_set_drvdata(dev, sata_phy);
1970b56e9a7SVivek Gautam 
1980b56e9a7SVivek Gautam 	sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl");
1990b56e9a7SVivek Gautam 	if (IS_ERR(sata_phy->phyclk)) {
2000b56e9a7SVivek Gautam 		dev_err(dev, "failed to get clk for PHY\n");
2010b56e9a7SVivek Gautam 		return PTR_ERR(sata_phy->phyclk);
2020b56e9a7SVivek Gautam 	}
2030b56e9a7SVivek Gautam 
2040b56e9a7SVivek Gautam 	ret = clk_prepare_enable(sata_phy->phyclk);
2050b56e9a7SVivek Gautam 	if (ret < 0) {
2060b56e9a7SVivek Gautam 		dev_err(dev, "failed to enable source clk\n");
2070b56e9a7SVivek Gautam 		return ret;
2080b56e9a7SVivek Gautam 	}
2090b56e9a7SVivek Gautam 
2100b56e9a7SVivek Gautam 	sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops);
2110b56e9a7SVivek Gautam 	if (IS_ERR(sata_phy->phy)) {
2120b56e9a7SVivek Gautam 		clk_disable_unprepare(sata_phy->phyclk);
2130b56e9a7SVivek Gautam 		dev_err(dev, "failed to create PHY\n");
2140b56e9a7SVivek Gautam 		return PTR_ERR(sata_phy->phy);
2150b56e9a7SVivek Gautam 	}
2160b56e9a7SVivek Gautam 
2170b56e9a7SVivek Gautam 	phy_set_drvdata(sata_phy->phy, sata_phy);
2180b56e9a7SVivek Gautam 
2190b56e9a7SVivek Gautam 	phy_provider = devm_of_phy_provider_register(dev,
2200b56e9a7SVivek Gautam 					of_phy_simple_xlate);
2210b56e9a7SVivek Gautam 	if (IS_ERR(phy_provider)) {
2220b56e9a7SVivek Gautam 		clk_disable_unprepare(sata_phy->phyclk);
2230b56e9a7SVivek Gautam 		return PTR_ERR(phy_provider);
2240b56e9a7SVivek Gautam 	}
2250b56e9a7SVivek Gautam 
2260b56e9a7SVivek Gautam 	return 0;
2270b56e9a7SVivek Gautam }
2280b56e9a7SVivek Gautam 
2290b56e9a7SVivek Gautam static const struct of_device_id exynos_sata_phy_of_match[] = {
2300b56e9a7SVivek Gautam 	{ .compatible = "samsung,exynos5250-sata-phy" },
2310b56e9a7SVivek Gautam 	{ },
2320b56e9a7SVivek Gautam };
2330b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match);
2340b56e9a7SVivek Gautam 
2350b56e9a7SVivek Gautam static struct platform_driver exynos_sata_phy_driver = {
2360b56e9a7SVivek Gautam 	.probe	= exynos_sata_phy_probe,
2370b56e9a7SVivek Gautam 	.driver = {
2380b56e9a7SVivek Gautam 		.of_match_table	= exynos_sata_phy_of_match,
2390b56e9a7SVivek Gautam 		.name  = "samsung,sata-phy",
2400b56e9a7SVivek Gautam 	}
2410b56e9a7SVivek Gautam };
2420b56e9a7SVivek Gautam module_platform_driver(exynos_sata_phy_driver);
2430b56e9a7SVivek Gautam 
2440b56e9a7SVivek Gautam MODULE_DESCRIPTION("Samsung SerDes PHY driver");
2450b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2");
2460b56e9a7SVivek Gautam MODULE_AUTHOR("Girish K S <ks.giri@samsung.com>");
2470b56e9a7SVivek Gautam MODULE_AUTHOR("Yuvaraj C D <yuvaraj.cd@samsung.com>");
248