18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Marvell Berlin SATA PHY driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Marvell Technology Group Ltd. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Antoine Ténart <antoine.tenart@free-electrons.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define HOST_VSA_ADDR 0x0 178c2ecf20Sopenharmony_ci#define HOST_VSA_DATA 0x4 188c2ecf20Sopenharmony_ci#define PORT_SCR_CTL 0x2c 198c2ecf20Sopenharmony_ci#define PORT_VSR_ADDR 0x78 208c2ecf20Sopenharmony_ci#define PORT_VSR_DATA 0x7c 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define CONTROL_REGISTER 0x0 238c2ecf20Sopenharmony_ci#define MBUS_SIZE_CONTROL 0x4 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define POWER_DOWN_PHY0 BIT(6) 268c2ecf20Sopenharmony_ci#define POWER_DOWN_PHY1 BIT(14) 278c2ecf20Sopenharmony_ci#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) 288c2ecf20Sopenharmony_ci#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define BG2_PHY_BASE 0x080 318c2ecf20Sopenharmony_ci#define BG2Q_PHY_BASE 0x200 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* register 0x01 */ 348c2ecf20Sopenharmony_ci#define REF_FREF_SEL_25 BIT(0) 358c2ecf20Sopenharmony_ci#define PHY_BERLIN_MODE_SATA (0x0 << 5) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* register 0x02 */ 388c2ecf20Sopenharmony_ci#define USE_MAX_PLL_RATE BIT(12) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* register 0x23 */ 418c2ecf20Sopenharmony_ci#define DATA_BIT_WIDTH_10 (0x0 << 10) 428c2ecf20Sopenharmony_ci#define DATA_BIT_WIDTH_20 (0x1 << 10) 438c2ecf20Sopenharmony_ci#define DATA_BIT_WIDTH_40 (0x2 << 10) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* register 0x25 */ 468c2ecf20Sopenharmony_ci#define PHY_GEN_MAX_1_5 (0x0 << 10) 478c2ecf20Sopenharmony_ci#define PHY_GEN_MAX_3_0 (0x1 << 10) 488c2ecf20Sopenharmony_ci#define PHY_GEN_MAX_6_0 (0x2 << 10) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct phy_berlin_desc { 518c2ecf20Sopenharmony_ci struct phy *phy; 528c2ecf20Sopenharmony_ci u32 power_bit; 538c2ecf20Sopenharmony_ci unsigned index; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct phy_berlin_priv { 578c2ecf20Sopenharmony_ci void __iomem *base; 588c2ecf20Sopenharmony_ci spinlock_t lock; 598c2ecf20Sopenharmony_ci struct clk *clk; 608c2ecf20Sopenharmony_ci struct phy_berlin_desc **phys; 618c2ecf20Sopenharmony_ci unsigned nphys; 628c2ecf20Sopenharmony_ci u32 phy_base; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, 668c2ecf20Sopenharmony_ci u32 phy_base, u32 reg, u32 mask, u32 val) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci u32 regval; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* select register */ 718c2ecf20Sopenharmony_ci writel(phy_base + reg, ctrl_reg + PORT_VSR_ADDR); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* set bits */ 748c2ecf20Sopenharmony_ci regval = readl(ctrl_reg + PORT_VSR_DATA); 758c2ecf20Sopenharmony_ci regval &= ~mask; 768c2ecf20Sopenharmony_ci regval |= val; 778c2ecf20Sopenharmony_ci writel(regval, ctrl_reg + PORT_VSR_DATA); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int phy_berlin_sata_power_on(struct phy *phy) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct phy_berlin_desc *desc = phy_get_drvdata(phy); 838c2ecf20Sopenharmony_ci struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); 848c2ecf20Sopenharmony_ci void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80); 858c2ecf20Sopenharmony_ci u32 regval; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci clk_prepare_enable(priv->clk); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci spin_lock(&priv->lock); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* Power on PHY */ 928c2ecf20Sopenharmony_ci writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); 938c2ecf20Sopenharmony_ci regval = readl(priv->base + HOST_VSA_DATA); 948c2ecf20Sopenharmony_ci regval &= ~desc->power_bit; 958c2ecf20Sopenharmony_ci writel(regval, priv->base + HOST_VSA_DATA); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* Configure MBus */ 988c2ecf20Sopenharmony_ci writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR); 998c2ecf20Sopenharmony_ci regval = readl(priv->base + HOST_VSA_DATA); 1008c2ecf20Sopenharmony_ci regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128; 1018c2ecf20Sopenharmony_ci writel(regval, priv->base + HOST_VSA_DATA); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* set PHY mode and ref freq to 25 MHz */ 1048c2ecf20Sopenharmony_ci phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x01, 1058c2ecf20Sopenharmony_ci 0x00ff, 1068c2ecf20Sopenharmony_ci REF_FREF_SEL_25 | PHY_BERLIN_MODE_SATA); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* set PHY up to 6 Gbps */ 1098c2ecf20Sopenharmony_ci phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x25, 1108c2ecf20Sopenharmony_ci 0x0c00, PHY_GEN_MAX_6_0); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* set 40 bits width */ 1138c2ecf20Sopenharmony_ci phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x23, 1148c2ecf20Sopenharmony_ci 0x0c00, DATA_BIT_WIDTH_40); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* use max pll rate */ 1178c2ecf20Sopenharmony_ci phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x02, 1188c2ecf20Sopenharmony_ci 0x0000, USE_MAX_PLL_RATE); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* set Gen3 controller speed */ 1218c2ecf20Sopenharmony_ci regval = readl(ctrl_reg + PORT_SCR_CTL); 1228c2ecf20Sopenharmony_ci regval &= ~GENMASK(7, 4); 1238c2ecf20Sopenharmony_ci regval |= 0x30; 1248c2ecf20Sopenharmony_ci writel(regval, ctrl_reg + PORT_SCR_CTL); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci spin_unlock(&priv->lock); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int phy_berlin_sata_power_off(struct phy *phy) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct phy_berlin_desc *desc = phy_get_drvdata(phy); 1368c2ecf20Sopenharmony_ci struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); 1378c2ecf20Sopenharmony_ci u32 regval; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci clk_prepare_enable(priv->clk); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci spin_lock(&priv->lock); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Power down PHY */ 1448c2ecf20Sopenharmony_ci writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); 1458c2ecf20Sopenharmony_ci regval = readl(priv->base + HOST_VSA_DATA); 1468c2ecf20Sopenharmony_ci regval |= desc->power_bit; 1478c2ecf20Sopenharmony_ci writel(regval, priv->base + HOST_VSA_DATA); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci spin_unlock(&priv->lock); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic struct phy *phy_berlin_sata_phy_xlate(struct device *dev, 1578c2ecf20Sopenharmony_ci struct of_phandle_args *args) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct phy_berlin_priv *priv = dev_get_drvdata(dev); 1608c2ecf20Sopenharmony_ci int i; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (WARN_ON(args->args[0] >= priv->nphys)) 1638c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci for (i = 0; i < priv->nphys; i++) { 1668c2ecf20Sopenharmony_ci if (priv->phys[i]->index == args->args[0]) 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (i == priv->nphys) 1718c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return priv->phys[i]->phy; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic const struct phy_ops phy_berlin_sata_ops = { 1778c2ecf20Sopenharmony_ci .power_on = phy_berlin_sata_power_on, 1788c2ecf20Sopenharmony_ci .power_off = phy_berlin_sata_power_off, 1798c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic u32 phy_berlin_power_down_bits[] = { 1838c2ecf20Sopenharmony_ci POWER_DOWN_PHY0, 1848c2ecf20Sopenharmony_ci POWER_DOWN_PHY1, 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int phy_berlin_sata_probe(struct platform_device *pdev) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1908c2ecf20Sopenharmony_ci struct device_node *child; 1918c2ecf20Sopenharmony_ci struct phy *phy; 1928c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 1938c2ecf20Sopenharmony_ci struct phy_berlin_priv *priv; 1948c2ecf20Sopenharmony_ci struct resource *res; 1958c2ecf20Sopenharmony_ci int ret, i = 0; 1968c2ecf20Sopenharmony_ci u32 phy_id; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 1998c2ecf20Sopenharmony_ci if (!priv) 2008c2ecf20Sopenharmony_ci return -ENOMEM; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2038c2ecf20Sopenharmony_ci if (!res) 2048c2ecf20Sopenharmony_ci return -EINVAL; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci priv->base = devm_ioremap(dev, res->start, resource_size(res)); 2078c2ecf20Sopenharmony_ci if (!priv->base) 2088c2ecf20Sopenharmony_ci return -ENOMEM; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci priv->clk = devm_clk_get(dev, NULL); 2118c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) 2128c2ecf20Sopenharmony_ci return PTR_ERR(priv->clk); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci priv->nphys = of_get_child_count(dev->of_node); 2158c2ecf20Sopenharmony_ci if (priv->nphys == 0) 2168c2ecf20Sopenharmony_ci return -ENODEV; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys), 2198c2ecf20Sopenharmony_ci GFP_KERNEL); 2208c2ecf20Sopenharmony_ci if (!priv->phys) 2218c2ecf20Sopenharmony_ci return -ENOMEM; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (of_device_is_compatible(dev->of_node, "marvell,berlin2-sata-phy")) 2248c2ecf20Sopenharmony_ci priv->phy_base = BG2_PHY_BASE; 2258c2ecf20Sopenharmony_ci else 2268c2ecf20Sopenharmony_ci priv->phy_base = BG2Q_PHY_BASE; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci dev_set_drvdata(dev, priv); 2298c2ecf20Sopenharmony_ci spin_lock_init(&priv->lock); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci for_each_available_child_of_node(dev->of_node, child) { 2328c2ecf20Sopenharmony_ci struct phy_berlin_desc *phy_desc; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (of_property_read_u32(child, "reg", &phy_id)) { 2358c2ecf20Sopenharmony_ci dev_err(dev, "missing reg property in node %pOFn\n", 2368c2ecf20Sopenharmony_ci child); 2378c2ecf20Sopenharmony_ci ret = -EINVAL; 2388c2ecf20Sopenharmony_ci goto put_child; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) { 2428c2ecf20Sopenharmony_ci dev_err(dev, "invalid reg in node %pOFn\n", child); 2438c2ecf20Sopenharmony_ci ret = -EINVAL; 2448c2ecf20Sopenharmony_ci goto put_child; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL); 2488c2ecf20Sopenharmony_ci if (!phy_desc) { 2498c2ecf20Sopenharmony_ci ret = -ENOMEM; 2508c2ecf20Sopenharmony_ci goto put_child; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops); 2548c2ecf20Sopenharmony_ci if (IS_ERR(phy)) { 2558c2ecf20Sopenharmony_ci dev_err(dev, "failed to create PHY %d\n", phy_id); 2568c2ecf20Sopenharmony_ci ret = PTR_ERR(phy); 2578c2ecf20Sopenharmony_ci goto put_child; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci phy_desc->phy = phy; 2618c2ecf20Sopenharmony_ci phy_desc->power_bit = phy_berlin_power_down_bits[phy_id]; 2628c2ecf20Sopenharmony_ci phy_desc->index = phy_id; 2638c2ecf20Sopenharmony_ci phy_set_drvdata(phy, phy_desc); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci priv->phys[i++] = phy_desc; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* Make sure the PHY is off */ 2688c2ecf20Sopenharmony_ci phy_berlin_sata_power_off(phy); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci phy_provider = 2728c2ecf20Sopenharmony_ci devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate); 2738c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 2748c2ecf20Sopenharmony_ciput_child: 2758c2ecf20Sopenharmony_ci of_node_put(child); 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic const struct of_device_id phy_berlin_sata_of_match[] = { 2808c2ecf20Sopenharmony_ci { .compatible = "marvell,berlin2-sata-phy" }, 2818c2ecf20Sopenharmony_ci { .compatible = "marvell,berlin2q-sata-phy" }, 2828c2ecf20Sopenharmony_ci { }, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic struct platform_driver phy_berlin_sata_driver = { 2878c2ecf20Sopenharmony_ci .probe = phy_berlin_sata_probe, 2888c2ecf20Sopenharmony_ci .driver = { 2898c2ecf20Sopenharmony_ci .name = "phy-berlin-sata", 2908c2ecf20Sopenharmony_ci .of_match_table = phy_berlin_sata_of_match, 2918c2ecf20Sopenharmony_ci }, 2928c2ecf20Sopenharmony_ci}; 2938c2ecf20Sopenharmony_cimodule_platform_driver(phy_berlin_sata_driver); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell Berlin SATA PHY driver"); 2968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>"); 2978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 298