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 
wait_for_reg_status(void __iomem * base,u32 reg,u32 checkbit,u32 status)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 
exynos_sata_phy_power_on(struct phy * phy)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 
exynos_sata_phy_power_off(struct phy * phy)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 
exynos_sata_phy_init(struct phy * phy)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 
exynos_sata_phy_probe(struct platform_device * pdev)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 phy_provider *phy_provider;
1660b56e9a7SVivek Gautam 	struct device_node *node;
1670b56e9a7SVivek Gautam 	int ret = 0;
1680b56e9a7SVivek Gautam 
1690b56e9a7SVivek Gautam 	sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL);
1700b56e9a7SVivek Gautam 	if (!sata_phy)
1710b56e9a7SVivek Gautam 		return -ENOMEM;
1720b56e9a7SVivek Gautam 
1732f0c9facSChunfeng Yun 	sata_phy->regs = devm_platform_ioremap_resource(pdev, 0);
1740b56e9a7SVivek Gautam 	if (IS_ERR(sata_phy->regs))
1750b56e9a7SVivek Gautam 		return PTR_ERR(sata_phy->regs);
1760b56e9a7SVivek Gautam 
1770b56e9a7SVivek Gautam 	sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
1780b56e9a7SVivek Gautam 					"samsung,syscon-phandle");
1790b56e9a7SVivek Gautam 	if (IS_ERR(sata_phy->pmureg)) {
1800b56e9a7SVivek Gautam 		dev_err(dev, "syscon regmap lookup failed.\n");
1810b56e9a7SVivek Gautam 		return PTR_ERR(sata_phy->pmureg);
1820b56e9a7SVivek Gautam 	}
1830b56e9a7SVivek Gautam 
1840b56e9a7SVivek Gautam 	node = of_parse_phandle(dev->of_node,
1850b56e9a7SVivek Gautam 			"samsung,exynos-sataphy-i2c-phandle", 0);
1860b56e9a7SVivek Gautam 	if (!node)
1870b56e9a7SVivek Gautam 		return -EINVAL;
1880b56e9a7SVivek Gautam 
1890b56e9a7SVivek Gautam 	sata_phy->client = of_find_i2c_device_by_node(node);
1903c8a0234SMiaoqian Lin 	of_node_put(node);
1910b56e9a7SVivek Gautam 	if (!sata_phy->client)
1920b56e9a7SVivek Gautam 		return -EPROBE_DEFER;
1930b56e9a7SVivek Gautam 
1940b56e9a7SVivek Gautam 	dev_set_drvdata(dev, sata_phy);
1950b56e9a7SVivek Gautam 
1960b56e9a7SVivek Gautam 	sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl");
1970b56e9a7SVivek Gautam 	if (IS_ERR(sata_phy->phyclk)) {
1980b56e9a7SVivek Gautam 		dev_err(dev, "failed to get clk for PHY\n");
199*a933ee69SKrzysztof Kozlowski 		ret = PTR_ERR(sata_phy->phyclk);
200*a933ee69SKrzysztof Kozlowski 		goto put_dev;
2010b56e9a7SVivek Gautam 	}
2020b56e9a7SVivek Gautam 
2030b56e9a7SVivek Gautam 	ret = clk_prepare_enable(sata_phy->phyclk);
2040b56e9a7SVivek Gautam 	if (ret < 0) {
2050b56e9a7SVivek Gautam 		dev_err(dev, "failed to enable source clk\n");
206*a933ee69SKrzysztof Kozlowski 		goto put_dev;
2070b56e9a7SVivek Gautam 	}
2080b56e9a7SVivek Gautam 
2090b56e9a7SVivek Gautam 	sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops);
2100b56e9a7SVivek Gautam 	if (IS_ERR(sata_phy->phy)) {
2110b56e9a7SVivek Gautam 		dev_err(dev, "failed to create PHY\n");
212*a933ee69SKrzysztof Kozlowski 		ret = PTR_ERR(sata_phy->phy);
213*a933ee69SKrzysztof Kozlowski 		goto clk_disable;
2140b56e9a7SVivek Gautam 	}
2150b56e9a7SVivek Gautam 
2160b56e9a7SVivek Gautam 	phy_set_drvdata(sata_phy->phy, sata_phy);
2170b56e9a7SVivek Gautam 
2180b56e9a7SVivek Gautam 	phy_provider = devm_of_phy_provider_register(dev,
2190b56e9a7SVivek Gautam 					of_phy_simple_xlate);
2200b56e9a7SVivek Gautam 	if (IS_ERR(phy_provider)) {
221*a933ee69SKrzysztof Kozlowski 		ret = PTR_ERR(phy_provider);
222*a933ee69SKrzysztof Kozlowski 		goto clk_disable;
2230b56e9a7SVivek Gautam 	}
2240b56e9a7SVivek Gautam 
2250b56e9a7SVivek Gautam 	return 0;
226*a933ee69SKrzysztof Kozlowski 
227*a933ee69SKrzysztof Kozlowski clk_disable:
228*a933ee69SKrzysztof Kozlowski 	clk_disable_unprepare(sata_phy->phyclk);
229*a933ee69SKrzysztof Kozlowski put_dev:
230*a933ee69SKrzysztof Kozlowski 	put_device(&sata_phy->client->dev);
231*a933ee69SKrzysztof Kozlowski 
232*a933ee69SKrzysztof Kozlowski 	return ret;
2330b56e9a7SVivek Gautam }
2340b56e9a7SVivek Gautam 
2350b56e9a7SVivek Gautam static const struct of_device_id exynos_sata_phy_of_match[] = {
2360b56e9a7SVivek Gautam 	{ .compatible = "samsung,exynos5250-sata-phy" },
2370b56e9a7SVivek Gautam 	{ },
2380b56e9a7SVivek Gautam };
2390b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match);
2400b56e9a7SVivek Gautam 
2410b56e9a7SVivek Gautam static struct platform_driver exynos_sata_phy_driver = {
2420b56e9a7SVivek Gautam 	.probe	= exynos_sata_phy_probe,
2430b56e9a7SVivek Gautam 	.driver = {
2440b56e9a7SVivek Gautam 		.of_match_table	= exynos_sata_phy_of_match,
2450b56e9a7SVivek Gautam 		.name  = "samsung,sata-phy",
2466aeec986SMarek Szyprowski 		.suppress_bind_attrs = true,
2470b56e9a7SVivek Gautam 	}
2480b56e9a7SVivek Gautam };
2490b56e9a7SVivek Gautam module_platform_driver(exynos_sata_phy_driver);
2500b56e9a7SVivek Gautam 
2510b56e9a7SVivek Gautam MODULE_DESCRIPTION("Samsung SerDes PHY driver");
2520b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2");
2530b56e9a7SVivek Gautam MODULE_AUTHOR("Girish K S <ks.giri@samsung.com>");
2540b56e9a7SVivek Gautam MODULE_AUTHOR("Yuvaraj C D <yuvaraj.cd@samsung.com>");
255