10b56e9a7SVivek Gautam /* 20b56e9a7SVivek Gautam * Copyright (C) 2015 Linaro, Ltd. 30b56e9a7SVivek Gautam * Rob Herring <robh@kernel.org> 40b56e9a7SVivek Gautam * 50b56e9a7SVivek Gautam * Based on vendor driver: 60b56e9a7SVivek Gautam * Copyright (C) 2013 Marvell Inc. 70b56e9a7SVivek Gautam * Author: Chao Xie <xiechao.mail@gmail.com> 80b56e9a7SVivek Gautam * 90b56e9a7SVivek Gautam * This software is licensed under the terms of the GNU General Public 100b56e9a7SVivek Gautam * License version 2, as published by the Free Software Foundation, and 110b56e9a7SVivek Gautam * may be copied, distributed, and modified under those terms. 120b56e9a7SVivek Gautam * 130b56e9a7SVivek Gautam * This program is distributed in the hope that it will be useful, 140b56e9a7SVivek Gautam * but WITHOUT ANY WARRANTY; without even the implied warranty of 150b56e9a7SVivek Gautam * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 160b56e9a7SVivek Gautam * GNU General Public License for more details. 170b56e9a7SVivek Gautam * 180b56e9a7SVivek Gautam */ 190b56e9a7SVivek Gautam 200b56e9a7SVivek Gautam #include <linux/delay.h> 210b56e9a7SVivek Gautam #include <linux/slab.h> 220b56e9a7SVivek Gautam #include <linux/of.h> 230b56e9a7SVivek Gautam #include <linux/io.h> 240b56e9a7SVivek Gautam #include <linux/err.h> 250b56e9a7SVivek Gautam #include <linux/clk.h> 260b56e9a7SVivek Gautam #include <linux/module.h> 270b56e9a7SVivek Gautam #include <linux/platform_device.h> 280b56e9a7SVivek Gautam #include <linux/phy/phy.h> 290b56e9a7SVivek Gautam 300b56e9a7SVivek Gautam #define PHY_28NM_HSIC_CTRL 0x08 310b56e9a7SVivek Gautam #define PHY_28NM_HSIC_IMPCAL_CAL 0x18 320b56e9a7SVivek Gautam #define PHY_28NM_HSIC_PLL_CTRL01 0x1c 330b56e9a7SVivek Gautam #define PHY_28NM_HSIC_PLL_CTRL2 0x20 340b56e9a7SVivek Gautam #define PHY_28NM_HSIC_INT 0x28 350b56e9a7SVivek Gautam 360b56e9a7SVivek Gautam #define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26 370b56e9a7SVivek Gautam #define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0 380b56e9a7SVivek Gautam #define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9 390b56e9a7SVivek Gautam 400b56e9a7SVivek Gautam #define PHY_28NM_HSIC_S2H_PU_PLL BIT(10) 410b56e9a7SVivek Gautam #define PHY_28NM_HSIC_H2S_PLL_LOCK BIT(15) 420b56e9a7SVivek Gautam #define PHY_28NM_HSIC_S2H_HSIC_EN BIT(7) 430b56e9a7SVivek Gautam #define S2H_DRV_SE0_4RESUME BIT(14) 440b56e9a7SVivek Gautam #define PHY_28NM_HSIC_H2S_IMPCAL_DONE BIT(27) 450b56e9a7SVivek Gautam 460b56e9a7SVivek Gautam #define PHY_28NM_HSIC_CONNECT_INT BIT(1) 470b56e9a7SVivek Gautam #define PHY_28NM_HSIC_HS_READY_INT BIT(2) 480b56e9a7SVivek Gautam 490b56e9a7SVivek Gautam struct mv_hsic_phy { 500b56e9a7SVivek Gautam struct phy *phy; 510b56e9a7SVivek Gautam struct platform_device *pdev; 520b56e9a7SVivek Gautam void __iomem *base; 530b56e9a7SVivek Gautam struct clk *clk; 540b56e9a7SVivek Gautam }; 550b56e9a7SVivek Gautam 560b56e9a7SVivek Gautam static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) 570b56e9a7SVivek Gautam { 580b56e9a7SVivek Gautam timeout += jiffies; 590b56e9a7SVivek Gautam while (time_is_after_eq_jiffies(timeout)) { 600b56e9a7SVivek Gautam if ((readl(reg) & mask) == mask) 610b56e9a7SVivek Gautam return true; 620b56e9a7SVivek Gautam msleep(1); 630b56e9a7SVivek Gautam } 640b56e9a7SVivek Gautam return false; 650b56e9a7SVivek Gautam } 660b56e9a7SVivek Gautam 670b56e9a7SVivek Gautam static int mv_hsic_phy_init(struct phy *phy) 680b56e9a7SVivek Gautam { 690b56e9a7SVivek Gautam struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); 700b56e9a7SVivek Gautam struct platform_device *pdev = mv_phy->pdev; 710b56e9a7SVivek Gautam void __iomem *base = mv_phy->base; 720b56e9a7SVivek Gautam 730b56e9a7SVivek Gautam clk_prepare_enable(mv_phy->clk); 740b56e9a7SVivek Gautam 750b56e9a7SVivek Gautam /* Set reference clock */ 760b56e9a7SVivek Gautam writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT | 770b56e9a7SVivek Gautam 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT | 780b56e9a7SVivek Gautam 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT, 790b56e9a7SVivek Gautam base + PHY_28NM_HSIC_PLL_CTRL01); 800b56e9a7SVivek Gautam 810b56e9a7SVivek Gautam /* Turn on PLL */ 820b56e9a7SVivek Gautam writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | 830b56e9a7SVivek Gautam PHY_28NM_HSIC_S2H_PU_PLL, 840b56e9a7SVivek Gautam base + PHY_28NM_HSIC_PLL_CTRL2); 850b56e9a7SVivek Gautam 860b56e9a7SVivek Gautam /* Make sure PHY PLL is locked */ 870b56e9a7SVivek Gautam if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2, 880b56e9a7SVivek Gautam PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) { 890b56e9a7SVivek Gautam dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS."); 900b56e9a7SVivek Gautam clk_disable_unprepare(mv_phy->clk); 910b56e9a7SVivek Gautam return -ETIMEDOUT; 920b56e9a7SVivek Gautam } 930b56e9a7SVivek Gautam 940b56e9a7SVivek Gautam return 0; 950b56e9a7SVivek Gautam } 960b56e9a7SVivek Gautam 970b56e9a7SVivek Gautam static int mv_hsic_phy_power_on(struct phy *phy) 980b56e9a7SVivek Gautam { 990b56e9a7SVivek Gautam struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); 1000b56e9a7SVivek Gautam struct platform_device *pdev = mv_phy->pdev; 1010b56e9a7SVivek Gautam void __iomem *base = mv_phy->base; 1020b56e9a7SVivek Gautam u32 reg; 1030b56e9a7SVivek Gautam 1040b56e9a7SVivek Gautam reg = readl(base + PHY_28NM_HSIC_CTRL); 1050b56e9a7SVivek Gautam /* Avoid SE0 state when resume for some device will take it as reset */ 1060b56e9a7SVivek Gautam reg &= ~S2H_DRV_SE0_4RESUME; 1070b56e9a7SVivek Gautam reg |= PHY_28NM_HSIC_S2H_HSIC_EN; /* Enable HSIC PHY */ 1080b56e9a7SVivek Gautam writel(reg, base + PHY_28NM_HSIC_CTRL); 1090b56e9a7SVivek Gautam 1100b56e9a7SVivek Gautam /* 1110b56e9a7SVivek Gautam * Calibration Timing 1120b56e9a7SVivek Gautam * ____________________________ 1130b56e9a7SVivek Gautam * CAL START ___| 1140b56e9a7SVivek Gautam * ____________________ 1150b56e9a7SVivek Gautam * CAL_DONE ___________| 1160b56e9a7SVivek Gautam * | 400us | 1170b56e9a7SVivek Gautam */ 1180b56e9a7SVivek Gautam 1190b56e9a7SVivek Gautam /* Make sure PHY Calibration is ready */ 1200b56e9a7SVivek Gautam if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL, 1210b56e9a7SVivek Gautam PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) { 1220b56e9a7SVivek Gautam dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS."); 1230b56e9a7SVivek Gautam return -ETIMEDOUT; 1240b56e9a7SVivek Gautam } 1250b56e9a7SVivek Gautam 1260b56e9a7SVivek Gautam /* Waiting for HSIC connect int*/ 1270b56e9a7SVivek Gautam if (!wait_for_reg(base + PHY_28NM_HSIC_INT, 1280b56e9a7SVivek Gautam PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) { 1290b56e9a7SVivek Gautam dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout."); 1300b56e9a7SVivek Gautam return -ETIMEDOUT; 1310b56e9a7SVivek Gautam } 1320b56e9a7SVivek Gautam 1330b56e9a7SVivek Gautam return 0; 1340b56e9a7SVivek Gautam } 1350b56e9a7SVivek Gautam 1360b56e9a7SVivek Gautam static int mv_hsic_phy_power_off(struct phy *phy) 1370b56e9a7SVivek Gautam { 1380b56e9a7SVivek Gautam struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); 1390b56e9a7SVivek Gautam void __iomem *base = mv_phy->base; 1400b56e9a7SVivek Gautam 1410b56e9a7SVivek Gautam writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN, 1420b56e9a7SVivek Gautam base + PHY_28NM_HSIC_CTRL); 1430b56e9a7SVivek Gautam 1440b56e9a7SVivek Gautam return 0; 1450b56e9a7SVivek Gautam } 1460b56e9a7SVivek Gautam 1470b56e9a7SVivek Gautam static int mv_hsic_phy_exit(struct phy *phy) 1480b56e9a7SVivek Gautam { 1490b56e9a7SVivek Gautam struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); 1500b56e9a7SVivek Gautam void __iomem *base = mv_phy->base; 1510b56e9a7SVivek Gautam 1520b56e9a7SVivek Gautam /* Turn off PLL */ 1530b56e9a7SVivek Gautam writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) & 1540b56e9a7SVivek Gautam ~PHY_28NM_HSIC_S2H_PU_PLL, 1550b56e9a7SVivek Gautam base + PHY_28NM_HSIC_PLL_CTRL2); 1560b56e9a7SVivek Gautam 1570b56e9a7SVivek Gautam clk_disable_unprepare(mv_phy->clk); 1580b56e9a7SVivek Gautam return 0; 1590b56e9a7SVivek Gautam } 1600b56e9a7SVivek Gautam 1610b56e9a7SVivek Gautam 1620b56e9a7SVivek Gautam static const struct phy_ops hsic_ops = { 1630b56e9a7SVivek Gautam .init = mv_hsic_phy_init, 1640b56e9a7SVivek Gautam .power_on = mv_hsic_phy_power_on, 1650b56e9a7SVivek Gautam .power_off = mv_hsic_phy_power_off, 1660b56e9a7SVivek Gautam .exit = mv_hsic_phy_exit, 1670b56e9a7SVivek Gautam .owner = THIS_MODULE, 1680b56e9a7SVivek Gautam }; 1690b56e9a7SVivek Gautam 1700b56e9a7SVivek Gautam static int mv_hsic_phy_probe(struct platform_device *pdev) 1710b56e9a7SVivek Gautam { 1720b56e9a7SVivek Gautam struct phy_provider *phy_provider; 1730b56e9a7SVivek Gautam struct mv_hsic_phy *mv_phy; 1740b56e9a7SVivek Gautam struct resource *r; 1750b56e9a7SVivek Gautam 1760b56e9a7SVivek Gautam mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); 1770b56e9a7SVivek Gautam if (!mv_phy) 1780b56e9a7SVivek Gautam return -ENOMEM; 1790b56e9a7SVivek Gautam 1800b56e9a7SVivek Gautam mv_phy->pdev = pdev; 1810b56e9a7SVivek Gautam 1820b56e9a7SVivek Gautam mv_phy->clk = devm_clk_get(&pdev->dev, NULL); 1830b56e9a7SVivek Gautam if (IS_ERR(mv_phy->clk)) { 1840b56e9a7SVivek Gautam dev_err(&pdev->dev, "failed to get clock.\n"); 1850b56e9a7SVivek Gautam return PTR_ERR(mv_phy->clk); 1860b56e9a7SVivek Gautam } 1870b56e9a7SVivek Gautam 1880b56e9a7SVivek Gautam r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1890b56e9a7SVivek Gautam mv_phy->base = devm_ioremap_resource(&pdev->dev, r); 1900b56e9a7SVivek Gautam if (IS_ERR(mv_phy->base)) 1910b56e9a7SVivek Gautam return PTR_ERR(mv_phy->base); 1920b56e9a7SVivek Gautam 1930b56e9a7SVivek Gautam mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops); 1940b56e9a7SVivek Gautam if (IS_ERR(mv_phy->phy)) 1950b56e9a7SVivek Gautam return PTR_ERR(mv_phy->phy); 1960b56e9a7SVivek Gautam 1970b56e9a7SVivek Gautam phy_set_drvdata(mv_phy->phy, mv_phy); 1980b56e9a7SVivek Gautam 1990b56e9a7SVivek Gautam phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); 2000b56e9a7SVivek Gautam return PTR_ERR_OR_ZERO(phy_provider); 2010b56e9a7SVivek Gautam } 2020b56e9a7SVivek Gautam 2030b56e9a7SVivek Gautam static const struct of_device_id mv_hsic_phy_dt_match[] = { 2040b56e9a7SVivek Gautam { .compatible = "marvell,pxa1928-hsic-phy", }, 2050b56e9a7SVivek Gautam {}, 2060b56e9a7SVivek Gautam }; 2070b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match); 2080b56e9a7SVivek Gautam 2090b56e9a7SVivek Gautam static struct platform_driver mv_hsic_phy_driver = { 2100b56e9a7SVivek Gautam .probe = mv_hsic_phy_probe, 2110b56e9a7SVivek Gautam .driver = { 2120b56e9a7SVivek Gautam .name = "mv-hsic-phy", 2130b56e9a7SVivek Gautam .of_match_table = of_match_ptr(mv_hsic_phy_dt_match), 2140b56e9a7SVivek Gautam }, 2150b56e9a7SVivek Gautam }; 2160b56e9a7SVivek Gautam module_platform_driver(mv_hsic_phy_driver); 2170b56e9a7SVivek Gautam 2180b56e9a7SVivek Gautam MODULE_AUTHOR("Rob Herring <robh@kernel.org>"); 2190b56e9a7SVivek Gautam MODULE_DESCRIPTION("Marvell HSIC phy driver"); 2200b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2"); 221