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