linux/drivers/phy/phy-pistachio-usb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * IMG Pistachio USB PHY driver
   4 *
   5 * Copyright (C) 2015 Google, Inc.
   6 */
   7
   8#include <linux/clk.h>
   9#include <linux/delay.h>
  10#include <linux/io.h>
  11#include <linux/kernel.h>
  12#include <linux/mfd/syscon.h>
  13#include <linux/module.h>
  14#include <linux/of.h>
  15#include <linux/phy/phy.h>
  16#include <linux/platform_device.h>
  17#include <linux/regmap.h>
  18
  19#include <dt-bindings/phy/phy-pistachio-usb.h>
  20
  21#define USB_PHY_CONTROL1                                0x04
  22#define USB_PHY_CONTROL1_FSEL_SHIFT                     2
  23#define USB_PHY_CONTROL1_FSEL_MASK                      0x7
  24
  25#define USB_PHY_STRAP_CONTROL                           0x10
  26#define USB_PHY_STRAP_CONTROL_REFCLK_SHIFT              4
  27#define USB_PHY_STRAP_CONTROL_REFCLK_MASK               0x3
  28
  29#define USB_PHY_STATUS                                  0x14
  30#define USB_PHY_STATUS_RX_PHY_CLK                       BIT(9)
  31#define USB_PHY_STATUS_RX_UTMI_CLK                      BIT(8)
  32#define USB_PHY_STATUS_VBUS_FAULT                       BIT(7)
  33
  34struct pistachio_usb_phy {
  35        struct device *dev;
  36        struct regmap *cr_top;
  37        struct clk *phy_clk;
  38        unsigned int refclk;
  39};
  40
  41static const unsigned long fsel_rate_map[] = {
  42        9600000,
  43        10000000,
  44        12000000,
  45        19200000,
  46        20000000,
  47        24000000,
  48        0,
  49        50000000,
  50};
  51
  52static int pistachio_usb_phy_power_on(struct phy *phy)
  53{
  54        struct pistachio_usb_phy *p_phy = phy_get_drvdata(phy);
  55        unsigned long timeout, rate;
  56        unsigned int i;
  57        int ret;
  58
  59        ret = clk_prepare_enable(p_phy->phy_clk);
  60        if (ret < 0) {
  61                dev_err(p_phy->dev, "Failed to enable PHY clock: %d\n", ret);
  62                return ret;
  63        }
  64
  65        regmap_update_bits(p_phy->cr_top, USB_PHY_STRAP_CONTROL,
  66                           USB_PHY_STRAP_CONTROL_REFCLK_MASK <<
  67                           USB_PHY_STRAP_CONTROL_REFCLK_SHIFT,
  68                           p_phy->refclk << USB_PHY_STRAP_CONTROL_REFCLK_SHIFT);
  69
  70        rate = clk_get_rate(p_phy->phy_clk);
  71        if (p_phy->refclk == REFCLK_XO_CRYSTAL && rate != 12000000) {
  72                dev_err(p_phy->dev, "Unsupported rate for XO crystal: %ld\n",
  73                        rate);
  74                ret = -EINVAL;
  75                goto disable_clk;
  76        }
  77
  78        for (i = 0; i < ARRAY_SIZE(fsel_rate_map); i++) {
  79                if (rate == fsel_rate_map[i])
  80                        break;
  81        }
  82        if (i == ARRAY_SIZE(fsel_rate_map)) {
  83                dev_err(p_phy->dev, "Unsupported clock rate: %lu\n", rate);
  84                ret = -EINVAL;
  85                goto disable_clk;
  86        }
  87
  88        regmap_update_bits(p_phy->cr_top, USB_PHY_CONTROL1,
  89                           USB_PHY_CONTROL1_FSEL_MASK <<
  90                           USB_PHY_CONTROL1_FSEL_SHIFT,
  91                           i << USB_PHY_CONTROL1_FSEL_SHIFT);
  92
  93        timeout = jiffies + msecs_to_jiffies(200);
  94        while (time_before(jiffies, timeout)) {
  95                unsigned int val;
  96
  97                regmap_read(p_phy->cr_top, USB_PHY_STATUS, &val);
  98                if (val & USB_PHY_STATUS_VBUS_FAULT) {
  99                        dev_err(p_phy->dev, "VBUS fault detected\n");
 100                        ret = -EIO;
 101                        goto disable_clk;
 102                }
 103                if ((val & USB_PHY_STATUS_RX_PHY_CLK) &&
 104                    (val & USB_PHY_STATUS_RX_UTMI_CLK))
 105                        return 0;
 106                usleep_range(1000, 1500);
 107        }
 108
 109        dev_err(p_phy->dev, "Timed out waiting for PHY to power on\n");
 110        ret = -ETIMEDOUT;
 111
 112disable_clk:
 113        clk_disable_unprepare(p_phy->phy_clk);
 114        return ret;
 115}
 116
 117static int pistachio_usb_phy_power_off(struct phy *phy)
 118{
 119        struct pistachio_usb_phy *p_phy = phy_get_drvdata(phy);
 120
 121        clk_disable_unprepare(p_phy->phy_clk);
 122
 123        return 0;
 124}
 125
 126static const struct phy_ops pistachio_usb_phy_ops = {
 127        .power_on = pistachio_usb_phy_power_on,
 128        .power_off = pistachio_usb_phy_power_off,
 129        .owner = THIS_MODULE,
 130};
 131
 132static int pistachio_usb_phy_probe(struct platform_device *pdev)
 133{
 134        struct pistachio_usb_phy *p_phy;
 135        struct phy_provider *provider;
 136        struct phy *phy;
 137        int ret;
 138
 139        p_phy = devm_kzalloc(&pdev->dev, sizeof(*p_phy), GFP_KERNEL);
 140        if (!p_phy)
 141                return -ENOMEM;
 142        p_phy->dev = &pdev->dev;
 143        platform_set_drvdata(pdev, p_phy);
 144
 145        p_phy->cr_top = syscon_regmap_lookup_by_phandle(p_phy->dev->of_node,
 146                                                        "img,cr-top");
 147        if (IS_ERR(p_phy->cr_top)) {
 148                dev_err(p_phy->dev, "Failed to get CR_TOP registers: %ld\n",
 149                        PTR_ERR(p_phy->cr_top));
 150                return PTR_ERR(p_phy->cr_top);
 151        }
 152
 153        p_phy->phy_clk = devm_clk_get(p_phy->dev, "usb_phy");
 154        if (IS_ERR(p_phy->phy_clk)) {
 155                dev_err(p_phy->dev, "Failed to get usb_phy clock: %ld\n",
 156                        PTR_ERR(p_phy->phy_clk));
 157                return PTR_ERR(p_phy->phy_clk);
 158        }
 159
 160        ret = of_property_read_u32(p_phy->dev->of_node, "img,refclk",
 161                                   &p_phy->refclk);
 162        if (ret < 0) {
 163                dev_err(p_phy->dev, "No reference clock selector specified\n");
 164                return ret;
 165        }
 166
 167        phy = devm_phy_create(p_phy->dev, NULL, &pistachio_usb_phy_ops);
 168        if (IS_ERR(phy)) {
 169                dev_err(p_phy->dev, "Failed to create PHY: %ld\n",
 170                        PTR_ERR(phy));
 171                return PTR_ERR(phy);
 172        }
 173        phy_set_drvdata(phy, p_phy);
 174
 175        provider = devm_of_phy_provider_register(p_phy->dev,
 176                                                 of_phy_simple_xlate);
 177        if (IS_ERR(provider)) {
 178                dev_err(p_phy->dev, "Failed to register PHY provider: %ld\n",
 179                        PTR_ERR(provider));
 180                return PTR_ERR(provider);
 181        }
 182
 183        return 0;
 184}
 185
 186static const struct of_device_id pistachio_usb_phy_of_match[] = {
 187        { .compatible = "img,pistachio-usb-phy", },
 188        { },
 189};
 190MODULE_DEVICE_TABLE(of, pistachio_usb_phy_of_match);
 191
 192static struct platform_driver pistachio_usb_phy_driver = {
 193        .probe          = pistachio_usb_phy_probe,
 194        .driver         = {
 195                .name   = "pistachio-usb-phy",
 196                .of_match_table = pistachio_usb_phy_of_match,
 197        },
 198};
 199module_platform_driver(pistachio_usb_phy_driver);
 200
 201MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>");
 202MODULE_DESCRIPTION("IMG Pistachio USB2.0 PHY driver");
 203MODULE_LICENSE("GPL v2");
 204