Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support
0004  *
0005  * Copyright (C) 2013 Samsung Electronics Co., Ltd.
0006  * Authors: Kamil Debski <k.debski@samsung.com>
0007  */
0008 
0009 #include <linux/delay.h>
0010 #include <linux/io.h>
0011 #include <linux/phy/phy.h>
0012 #include "phy-samsung-usb2.h"
0013 
0014 /* Exynos USB PHY registers */
0015 
0016 /* PHY power control */
0017 #define S5PV210_UPHYPWR         0x0
0018 
0019 #define S5PV210_UPHYPWR_PHY0_SUSPEND    BIT(0)
0020 #define S5PV210_UPHYPWR_PHY0_PWR    BIT(3)
0021 #define S5PV210_UPHYPWR_PHY0_OTG_PWR    BIT(4)
0022 #define S5PV210_UPHYPWR_PHY0    ( \
0023     S5PV210_UPHYPWR_PHY0_SUSPEND | \
0024     S5PV210_UPHYPWR_PHY0_PWR | \
0025     S5PV210_UPHYPWR_PHY0_OTG_PWR)
0026 
0027 #define S5PV210_UPHYPWR_PHY1_SUSPEND    BIT(6)
0028 #define S5PV210_UPHYPWR_PHY1_PWR    BIT(7)
0029 #define S5PV210_UPHYPWR_PHY1 ( \
0030     S5PV210_UPHYPWR_PHY1_SUSPEND | \
0031     S5PV210_UPHYPWR_PHY1_PWR)
0032 
0033 /* PHY clock control */
0034 #define S5PV210_UPHYCLK         0x4
0035 
0036 #define S5PV210_UPHYCLK_PHYFSEL_MASK    (0x3 << 0)
0037 #define S5PV210_UPHYCLK_PHYFSEL_48MHZ   (0x0 << 0)
0038 #define S5PV210_UPHYCLK_PHYFSEL_24MHZ   (0x3 << 0)
0039 #define S5PV210_UPHYCLK_PHYFSEL_12MHZ   (0x2 << 0)
0040 
0041 #define S5PV210_UPHYCLK_PHY0_ID_PULLUP  BIT(2)
0042 #define S5PV210_UPHYCLK_PHY0_COMMON_ON  BIT(4)
0043 #define S5PV210_UPHYCLK_PHY1_COMMON_ON  BIT(7)
0044 
0045 /* PHY reset control */
0046 #define S5PV210_UPHYRST         0x8
0047 
0048 #define S5PV210_URSTCON_PHY0        BIT(0)
0049 #define S5PV210_URSTCON_OTG_HLINK   BIT(1)
0050 #define S5PV210_URSTCON_OTG_PHYLINK BIT(2)
0051 #define S5PV210_URSTCON_PHY1_ALL    BIT(3)
0052 #define S5PV210_URSTCON_HOST_LINK_ALL   BIT(4)
0053 
0054 /* Isolation, configured in the power management unit */
0055 #define S5PV210_USB_ISOL_OFFSET     0x680c
0056 #define S5PV210_USB_ISOL_DEVICE     BIT(0)
0057 #define S5PV210_USB_ISOL_HOST       BIT(1)
0058 
0059 
0060 enum s5pv210_phy_id {
0061     S5PV210_DEVICE,
0062     S5PV210_HOST,
0063     S5PV210_NUM_PHYS,
0064 };
0065 
0066 /*
0067  * s5pv210_rate_to_clk() converts the supplied clock rate to the value that
0068  * can be written to the phy register.
0069  */
0070 static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg)
0071 {
0072     switch (rate) {
0073     case 12 * MHZ:
0074         *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
0075         break;
0076     case 24 * MHZ:
0077         *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
0078         break;
0079     case 48 * MHZ:
0080         *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
0081         break;
0082     default:
0083         return -EINVAL;
0084     }
0085 
0086     return 0;
0087 }
0088 
0089 static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on)
0090 {
0091     struct samsung_usb2_phy_driver *drv = inst->drv;
0092     u32 mask;
0093 
0094     switch (inst->cfg->id) {
0095     case S5PV210_DEVICE:
0096         mask = S5PV210_USB_ISOL_DEVICE;
0097         break;
0098     case S5PV210_HOST:
0099         mask = S5PV210_USB_ISOL_HOST;
0100         break;
0101     default:
0102         return;
0103     }
0104 
0105     regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET,
0106                             mask, on ? 0 : mask);
0107 }
0108 
0109 static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
0110 {
0111     struct samsung_usb2_phy_driver *drv = inst->drv;
0112     u32 rstbits = 0;
0113     u32 phypwr = 0;
0114     u32 rst;
0115     u32 pwr;
0116 
0117     switch (inst->cfg->id) {
0118     case S5PV210_DEVICE:
0119         phypwr =    S5PV210_UPHYPWR_PHY0;
0120         rstbits =   S5PV210_URSTCON_PHY0;
0121         break;
0122     case S5PV210_HOST:
0123         phypwr =    S5PV210_UPHYPWR_PHY1;
0124         rstbits =   S5PV210_URSTCON_PHY1_ALL |
0125                 S5PV210_URSTCON_HOST_LINK_ALL;
0126         break;
0127     }
0128 
0129     if (on) {
0130         writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK);
0131 
0132         pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
0133         pwr &= ~phypwr;
0134         writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
0135 
0136         rst = readl(drv->reg_phy + S5PV210_UPHYRST);
0137         rst |= rstbits;
0138         writel(rst, drv->reg_phy + S5PV210_UPHYRST);
0139         udelay(10);
0140         rst &= ~rstbits;
0141         writel(rst, drv->reg_phy + S5PV210_UPHYRST);
0142         /* The following delay is necessary for the reset sequence to be
0143          * completed
0144          */
0145         udelay(80);
0146     } else {
0147         pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
0148         pwr |= phypwr;
0149         writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
0150     }
0151 }
0152 
0153 static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
0154 {
0155     s5pv210_isol(inst, 0);
0156     s5pv210_phy_pwr(inst, 1);
0157 
0158     return 0;
0159 }
0160 
0161 static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
0162 {
0163     s5pv210_phy_pwr(inst, 0);
0164     s5pv210_isol(inst, 1);
0165 
0166     return 0;
0167 }
0168 
0169 static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = {
0170     [S5PV210_DEVICE] = {
0171         .label      = "device",
0172         .id     = S5PV210_DEVICE,
0173         .power_on   = s5pv210_power_on,
0174         .power_off  = s5pv210_power_off,
0175     },
0176     [S5PV210_HOST] = {
0177         .label      = "host",
0178         .id     = S5PV210_HOST,
0179         .power_on   = s5pv210_power_on,
0180         .power_off  = s5pv210_power_off,
0181     },
0182 };
0183 
0184 const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
0185     .num_phys   = ARRAY_SIZE(s5pv210_phys),
0186     .phys       = s5pv210_phys,
0187     .rate_to_clk    = s5pv210_rate_to_clk,
0188 };