linux/drivers/phy/hisilicon/phy-hi6220-usb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (c) 2015 Linaro Ltd.
   4 * Copyright (c) 2015 HiSilicon Limited.
   5 */
   6
   7#include <linux/mfd/syscon.h>
   8#include <linux/module.h>
   9#include <linux/platform_device.h>
  10#include <linux/phy/phy.h>
  11#include <linux/regmap.h>
  12
  13#define SC_PERIPH_CTRL4                 0x00c
  14
  15#define CTRL4_PICO_SIDDQ                BIT(6)
  16#define CTRL4_PICO_OGDISABLE            BIT(8)
  17#define CTRL4_PICO_VBUSVLDEXT           BIT(10)
  18#define CTRL4_PICO_VBUSVLDEXTSEL        BIT(11)
  19#define CTRL4_OTG_PHY_SEL               BIT(21)
  20
  21#define SC_PERIPH_CTRL5                 0x010
  22
  23#define CTRL5_USBOTG_RES_SEL            BIT(3)
  24#define CTRL5_PICOPHY_ACAENB            BIT(4)
  25#define CTRL5_PICOPHY_BC_MODE           BIT(5)
  26#define CTRL5_PICOPHY_CHRGSEL           BIT(6)
  27#define CTRL5_PICOPHY_VDATSRCEND        BIT(7)
  28#define CTRL5_PICOPHY_VDATDETENB        BIT(8)
  29#define CTRL5_PICOPHY_DCDENB            BIT(9)
  30#define CTRL5_PICOPHY_IDDIG             BIT(10)
  31
  32#define SC_PERIPH_CTRL8                 0x018
  33#define SC_PERIPH_RSTEN0                0x300
  34#define SC_PERIPH_RSTDIS0               0x304
  35
  36#define RST0_USBOTG_BUS                 BIT(4)
  37#define RST0_POR_PICOPHY                BIT(5)
  38#define RST0_USBOTG                     BIT(6)
  39#define RST0_USBOTG_32K                 BIT(7)
  40
  41#define EYE_PATTERN_PARA                0x7053348c
  42
  43struct hi6220_priv {
  44        struct regmap *reg;
  45        struct device *dev;
  46};
  47
  48static void hi6220_phy_init(struct hi6220_priv *priv)
  49{
  50        struct regmap *reg = priv->reg;
  51        u32 val, mask;
  52
  53        val = RST0_USBOTG_BUS | RST0_POR_PICOPHY |
  54              RST0_USBOTG | RST0_USBOTG_32K;
  55        mask = val;
  56        regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val);
  57        regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val);
  58}
  59
  60static int hi6220_phy_setup(struct hi6220_priv *priv, bool on)
  61{
  62        struct regmap *reg = priv->reg;
  63        u32 val, mask;
  64        int ret;
  65
  66        if (on) {
  67                val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB;
  68                mask = val | CTRL5_PICOPHY_BC_MODE;
  69                ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val);
  70                if (ret)
  71                        goto out;
  72
  73                val =  CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL |
  74                       CTRL4_OTG_PHY_SEL;
  75                mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE;
  76                ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
  77                if (ret)
  78                        goto out;
  79
  80                ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA);
  81                if (ret)
  82                        goto out;
  83        } else {
  84                val = CTRL4_PICO_SIDDQ;
  85                mask = val;
  86                ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val);
  87                if (ret)
  88                        goto out;
  89        }
  90
  91        return 0;
  92out:
  93        dev_err(priv->dev, "failed to setup phy ret: %d\n", ret);
  94        return ret;
  95}
  96
  97static int hi6220_phy_start(struct phy *phy)
  98{
  99        struct hi6220_priv *priv = phy_get_drvdata(phy);
 100
 101        return hi6220_phy_setup(priv, true);
 102}
 103
 104static int hi6220_phy_exit(struct phy *phy)
 105{
 106        struct hi6220_priv *priv = phy_get_drvdata(phy);
 107
 108        return hi6220_phy_setup(priv, false);
 109}
 110
 111static const struct phy_ops hi6220_phy_ops = {
 112        .init           = hi6220_phy_start,
 113        .exit           = hi6220_phy_exit,
 114        .owner          = THIS_MODULE,
 115};
 116
 117static int hi6220_phy_probe(struct platform_device *pdev)
 118{
 119        struct phy_provider *phy_provider;
 120        struct device *dev = &pdev->dev;
 121        struct phy *phy;
 122        struct hi6220_priv *priv;
 123
 124        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 125        if (!priv)
 126                return -ENOMEM;
 127
 128        priv->dev = dev;
 129        priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node,
 130                                        "hisilicon,peripheral-syscon");
 131        if (IS_ERR(priv->reg)) {
 132                dev_err(dev, "no hisilicon,peripheral-syscon\n");
 133                return PTR_ERR(priv->reg);
 134        }
 135
 136        hi6220_phy_init(priv);
 137
 138        phy = devm_phy_create(dev, NULL, &hi6220_phy_ops);
 139        if (IS_ERR(phy))
 140                return PTR_ERR(phy);
 141
 142        phy_set_drvdata(phy, priv);
 143        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 144        return PTR_ERR_OR_ZERO(phy_provider);
 145}
 146
 147static const struct of_device_id hi6220_phy_of_match[] = {
 148        {.compatible = "hisilicon,hi6220-usb-phy",},
 149        { },
 150};
 151MODULE_DEVICE_TABLE(of, hi6220_phy_of_match);
 152
 153static struct platform_driver hi6220_phy_driver = {
 154        .probe  = hi6220_phy_probe,
 155        .driver = {
 156                .name   = "hi6220-usb-phy",
 157                .of_match_table = hi6220_phy_of_match,
 158        }
 159};
 160module_platform_driver(hi6220_phy_driver);
 161
 162MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver");
 163MODULE_ALIAS("platform:hi6220-usb-phy");
 164MODULE_LICENSE("GPL");
 165