linux/drivers/phy/marvell/phy-pxa-28nm-hsic.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2015 Linaro, Ltd.
   4 * Rob Herring <robh@kernel.org>
   5 *
   6 * Based on vendor driver:
   7 * Copyright (C) 2013 Marvell Inc.
   8 * Author: Chao Xie <xiechao.mail@gmail.com>
   9 */
  10
  11#include <linux/delay.h>
  12#include <linux/slab.h>
  13#include <linux/of.h>
  14#include <linux/io.h>
  15#include <linux/err.h>
  16#include <linux/clk.h>
  17#include <linux/module.h>
  18#include <linux/platform_device.h>
  19#include <linux/phy/phy.h>
  20
  21#define PHY_28NM_HSIC_CTRL                      0x08
  22#define PHY_28NM_HSIC_IMPCAL_CAL                0x18
  23#define PHY_28NM_HSIC_PLL_CTRL01                0x1c
  24#define PHY_28NM_HSIC_PLL_CTRL2                 0x20
  25#define PHY_28NM_HSIC_INT                       0x28
  26
  27#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT         26
  28#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT           0
  29#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT          9
  30
  31#define PHY_28NM_HSIC_S2H_PU_PLL                BIT(10)
  32#define PHY_28NM_HSIC_H2S_PLL_LOCK              BIT(15)
  33#define PHY_28NM_HSIC_S2H_HSIC_EN               BIT(7)
  34#define S2H_DRV_SE0_4RESUME                     BIT(14)
  35#define PHY_28NM_HSIC_H2S_IMPCAL_DONE           BIT(27)
  36
  37#define PHY_28NM_HSIC_CONNECT_INT               BIT(1)
  38#define PHY_28NM_HSIC_HS_READY_INT              BIT(2)
  39
  40struct mv_hsic_phy {
  41        struct phy              *phy;
  42        struct platform_device  *pdev;
  43        void __iomem            *base;
  44        struct clk              *clk;
  45};
  46
  47static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout)
  48{
  49        timeout += jiffies;
  50        while (time_is_after_eq_jiffies(timeout)) {
  51                if ((readl(reg) & mask) == mask)
  52                        return true;
  53                msleep(1);
  54        }
  55        return false;
  56}
  57
  58static int mv_hsic_phy_init(struct phy *phy)
  59{
  60        struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
  61        struct platform_device *pdev = mv_phy->pdev;
  62        void __iomem *base = mv_phy->base;
  63
  64        clk_prepare_enable(mv_phy->clk);
  65
  66        /* Set reference clock */
  67        writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT |
  68                0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT |
  69                0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT,
  70                base + PHY_28NM_HSIC_PLL_CTRL01);
  71
  72        /* Turn on PLL */
  73        writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) |
  74                PHY_28NM_HSIC_S2H_PU_PLL,
  75                base + PHY_28NM_HSIC_PLL_CTRL2);
  76
  77        /* Make sure PHY PLL is locked */
  78        if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2,
  79            PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) {
  80                dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS.");
  81                clk_disable_unprepare(mv_phy->clk);
  82                return -ETIMEDOUT;
  83        }
  84
  85        return 0;
  86}
  87
  88static int mv_hsic_phy_power_on(struct phy *phy)
  89{
  90        struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
  91        struct platform_device *pdev = mv_phy->pdev;
  92        void __iomem *base = mv_phy->base;
  93        u32 reg;
  94
  95        reg = readl(base + PHY_28NM_HSIC_CTRL);
  96        /* Avoid SE0 state when resume for some device will take it as reset */
  97        reg &= ~S2H_DRV_SE0_4RESUME;
  98        reg |= PHY_28NM_HSIC_S2H_HSIC_EN;       /* Enable HSIC PHY */
  99        writel(reg, base + PHY_28NM_HSIC_CTRL);
 100
 101        /*
 102         *  Calibration Timing
 103         *                 ____________________________
 104         *  CAL START   ___|
 105         *                         ____________________
 106         *  CAL_DONE    ___________|
 107         *                 | 400us |
 108         */
 109
 110        /* Make sure PHY Calibration is ready */
 111        if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL,
 112            PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) {
 113                dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS.");
 114                return -ETIMEDOUT;
 115        }
 116
 117        /* Waiting for HSIC connect int*/
 118        if (!wait_for_reg(base + PHY_28NM_HSIC_INT,
 119            PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) {
 120                dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout.");
 121                return -ETIMEDOUT;
 122        }
 123
 124        return 0;
 125}
 126
 127static int mv_hsic_phy_power_off(struct phy *phy)
 128{
 129        struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
 130        void __iomem *base = mv_phy->base;
 131
 132        writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN,
 133                base + PHY_28NM_HSIC_CTRL);
 134
 135        return 0;
 136}
 137
 138static int mv_hsic_phy_exit(struct phy *phy)
 139{
 140        struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
 141        void __iomem *base = mv_phy->base;
 142
 143        /* Turn off PLL */
 144        writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) &
 145                ~PHY_28NM_HSIC_S2H_PU_PLL,
 146                base + PHY_28NM_HSIC_PLL_CTRL2);
 147
 148        clk_disable_unprepare(mv_phy->clk);
 149        return 0;
 150}
 151
 152
 153static const struct phy_ops hsic_ops = {
 154        .init           = mv_hsic_phy_init,
 155        .power_on       = mv_hsic_phy_power_on,
 156        .power_off      = mv_hsic_phy_power_off,
 157        .exit           = mv_hsic_phy_exit,
 158        .owner          = THIS_MODULE,
 159};
 160
 161static int mv_hsic_phy_probe(struct platform_device *pdev)
 162{
 163        struct phy_provider *phy_provider;
 164        struct mv_hsic_phy *mv_phy;
 165        struct resource *r;
 166
 167        mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL);
 168        if (!mv_phy)
 169                return -ENOMEM;
 170
 171        mv_phy->pdev = pdev;
 172
 173        mv_phy->clk = devm_clk_get(&pdev->dev, NULL);
 174        if (IS_ERR(mv_phy->clk)) {
 175                dev_err(&pdev->dev, "failed to get clock.\n");
 176                return PTR_ERR(mv_phy->clk);
 177        }
 178
 179        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 180        mv_phy->base = devm_ioremap_resource(&pdev->dev, r);
 181        if (IS_ERR(mv_phy->base))
 182                return PTR_ERR(mv_phy->base);
 183
 184        mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops);
 185        if (IS_ERR(mv_phy->phy))
 186                return PTR_ERR(mv_phy->phy);
 187
 188        phy_set_drvdata(mv_phy->phy, mv_phy);
 189
 190        phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
 191        return PTR_ERR_OR_ZERO(phy_provider);
 192}
 193
 194static const struct of_device_id mv_hsic_phy_dt_match[] = {
 195        { .compatible = "marvell,pxa1928-hsic-phy", },
 196        {},
 197};
 198MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match);
 199
 200static struct platform_driver mv_hsic_phy_driver = {
 201        .probe  = mv_hsic_phy_probe,
 202        .driver = {
 203                .name   = "mv-hsic-phy",
 204                .of_match_table = of_match_ptr(mv_hsic_phy_dt_match),
 205        },
 206};
 207module_platform_driver(mv_hsic_phy_driver);
 208
 209MODULE_AUTHOR("Rob Herring <robh@kernel.org>");
 210MODULE_DESCRIPTION("Marvell HSIC phy driver");
 211MODULE_LICENSE("GPL v2");
 212