Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Allwinner sun50i(H6) USB 3.0 phy driver
0004  *
0005  * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io>
0006  *
0007  * Based on phy-sun9i-usb.c, which is:
0008  *
0009  * Copyright (C) 2014-2015 Chen-Yu Tsai <wens@csie.org>
0010  *
0011  * Based on code from Allwinner BSP, which is:
0012  *
0013  * Copyright (c) 2010-2015 Allwinner Technology Co., Ltd.
0014  */
0015 
0016 #include <linux/clk.h>
0017 #include <linux/err.h>
0018 #include <linux/io.h>
0019 #include <linux/module.h>
0020 #include <linux/phy/phy.h>
0021 #include <linux/platform_device.h>
0022 #include <linux/reset.h>
0023 
0024 /* Interface Status and Control Registers */
0025 #define SUNXI_ISCR          0x00
0026 #define SUNXI_PIPE_CLOCK_CONTROL    0x14
0027 #define SUNXI_PHY_TUNE_LOW      0x18
0028 #define SUNXI_PHY_TUNE_HIGH     0x1c
0029 #define SUNXI_PHY_EXTERNAL_CONTROL  0x20
0030 
0031 /* USB2.0 Interface Status and Control Register */
0032 #define SUNXI_ISCR_FORCE_VBUS       (3 << 12)
0033 
0034 /* PIPE Clock Control Register */
0035 #define SUNXI_PCC_PIPE_CLK_OPEN     (1 << 6)
0036 
0037 /* PHY External Control Register */
0038 #define SUNXI_PEC_EXTERN_VBUS       (3 << 1)
0039 #define SUNXI_PEC_SSC_EN        (1 << 24)
0040 #define SUNXI_PEC_REF_SSP_EN        (1 << 26)
0041 
0042 /* PHY Tune High Register */
0043 #define SUNXI_TX_DEEMPH_3P5DB(n)    ((n) << 19)
0044 #define SUNXI_TX_DEEMPH_3P5DB_MASK  GENMASK(24, 19)
0045 #define SUNXI_TX_DEEMPH_6DB(n)      ((n) << 13)
0046 #define SUNXI_TX_DEEMPH_6GB_MASK    GENMASK(18, 13)
0047 #define SUNXI_TX_SWING_FULL(n)      ((n) << 6)
0048 #define SUNXI_TX_SWING_FULL_MASK    GENMASK(12, 6)
0049 #define SUNXI_LOS_BIAS(n)       ((n) << 3)
0050 #define SUNXI_LOS_BIAS_MASK     GENMASK(5, 3)
0051 #define SUNXI_TXVBOOSTLVL(n)        ((n) << 0)
0052 #define SUNXI_TXVBOOSTLVL_MASK      GENMASK(2, 0)
0053 
0054 struct sun50i_usb3_phy {
0055     struct phy *phy;
0056     void __iomem *regs;
0057     struct reset_control *reset;
0058     struct clk *clk;
0059 };
0060 
0061 static void sun50i_usb3_phy_open(struct sun50i_usb3_phy *phy)
0062 {
0063     u32 val;
0064 
0065     val = readl(phy->regs + SUNXI_PHY_EXTERNAL_CONTROL);
0066     val |= SUNXI_PEC_EXTERN_VBUS;
0067     val |= SUNXI_PEC_SSC_EN | SUNXI_PEC_REF_SSP_EN;
0068     writel(val, phy->regs + SUNXI_PHY_EXTERNAL_CONTROL);
0069 
0070     val = readl(phy->regs + SUNXI_PIPE_CLOCK_CONTROL);
0071     val |= SUNXI_PCC_PIPE_CLK_OPEN;
0072     writel(val, phy->regs + SUNXI_PIPE_CLOCK_CONTROL);
0073 
0074     val = readl(phy->regs + SUNXI_ISCR);
0075     val |= SUNXI_ISCR_FORCE_VBUS;
0076     writel(val, phy->regs + SUNXI_ISCR);
0077 
0078     /*
0079      * All the magic numbers written to the PHY_TUNE_{LOW_HIGH}
0080      * registers are directly taken from the BSP USB3 driver from
0081      * Allwiner.
0082      */
0083     writel(0x0047fc87, phy->regs + SUNXI_PHY_TUNE_LOW);
0084 
0085     val = readl(phy->regs + SUNXI_PHY_TUNE_HIGH);
0086     val &= ~(SUNXI_TXVBOOSTLVL_MASK | SUNXI_LOS_BIAS_MASK |
0087          SUNXI_TX_SWING_FULL_MASK | SUNXI_TX_DEEMPH_6GB_MASK |
0088          SUNXI_TX_DEEMPH_3P5DB_MASK);
0089     val |= SUNXI_TXVBOOSTLVL(0x7);
0090     val |= SUNXI_LOS_BIAS(0x7);
0091     val |= SUNXI_TX_SWING_FULL(0x55);
0092     val |= SUNXI_TX_DEEMPH_6DB(0x20);
0093     val |= SUNXI_TX_DEEMPH_3P5DB(0x15);
0094     writel(val, phy->regs + SUNXI_PHY_TUNE_HIGH);
0095 }
0096 
0097 static int sun50i_usb3_phy_init(struct phy *_phy)
0098 {
0099     struct sun50i_usb3_phy *phy = phy_get_drvdata(_phy);
0100     int ret;
0101 
0102     ret = clk_prepare_enable(phy->clk);
0103     if (ret)
0104         return ret;
0105 
0106     ret = reset_control_deassert(phy->reset);
0107     if (ret) {
0108         clk_disable_unprepare(phy->clk);
0109         return ret;
0110     }
0111 
0112     sun50i_usb3_phy_open(phy);
0113     return 0;
0114 }
0115 
0116 static int sun50i_usb3_phy_exit(struct phy *_phy)
0117 {
0118     struct sun50i_usb3_phy *phy = phy_get_drvdata(_phy);
0119 
0120     reset_control_assert(phy->reset);
0121     clk_disable_unprepare(phy->clk);
0122 
0123     return 0;
0124 }
0125 
0126 static const struct phy_ops sun50i_usb3_phy_ops = {
0127     .init       = sun50i_usb3_phy_init,
0128     .exit       = sun50i_usb3_phy_exit,
0129     .owner      = THIS_MODULE,
0130 };
0131 
0132 static int sun50i_usb3_phy_probe(struct platform_device *pdev)
0133 {
0134     struct sun50i_usb3_phy *phy;
0135     struct device *dev = &pdev->dev;
0136     struct phy_provider *phy_provider;
0137 
0138     phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
0139     if (!phy)
0140         return -ENOMEM;
0141 
0142     phy->clk = devm_clk_get(dev, NULL);
0143     if (IS_ERR(phy->clk)) {
0144         if (PTR_ERR(phy->clk) != -EPROBE_DEFER)
0145             dev_err(dev, "failed to get phy clock\n");
0146         return PTR_ERR(phy->clk);
0147     }
0148 
0149     phy->reset = devm_reset_control_get(dev, NULL);
0150     if (IS_ERR(phy->reset)) {
0151         dev_err(dev, "failed to get reset control\n");
0152         return PTR_ERR(phy->reset);
0153     }
0154 
0155     phy->regs = devm_platform_ioremap_resource(pdev, 0);
0156     if (IS_ERR(phy->regs))
0157         return PTR_ERR(phy->regs);
0158 
0159     phy->phy = devm_phy_create(dev, NULL, &sun50i_usb3_phy_ops);
0160     if (IS_ERR(phy->phy)) {
0161         dev_err(dev, "failed to create PHY\n");
0162         return PTR_ERR(phy->phy);
0163     }
0164 
0165     phy_set_drvdata(phy->phy, phy);
0166     phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
0167 
0168     return PTR_ERR_OR_ZERO(phy_provider);
0169 }
0170 
0171 static const struct of_device_id sun50i_usb3_phy_of_match[] = {
0172     { .compatible = "allwinner,sun50i-h6-usb3-phy" },
0173     { },
0174 };
0175 MODULE_DEVICE_TABLE(of, sun50i_usb3_phy_of_match);
0176 
0177 static struct platform_driver sun50i_usb3_phy_driver = {
0178     .probe  = sun50i_usb3_phy_probe,
0179     .driver = {
0180         .of_match_table = sun50i_usb3_phy_of_match,
0181         .name  = "sun50i-usb3-phy",
0182     }
0183 };
0184 module_platform_driver(sun50i_usb3_phy_driver);
0185 
0186 MODULE_DESCRIPTION("Allwinner H6 USB 3.0 phy driver");
0187 MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
0188 MODULE_LICENSE("GPL");