linux/drivers/phy/samsung/phy-s5pv210-usb2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support
   4 *
   5 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
   6 * Authors: Kamil Debski <k.debski@samsung.com>
   7 */
   8
   9#include <linux/delay.h>
  10#include <linux/io.h>
  11#include <linux/phy/phy.h>
  12#include "phy-samsung-usb2.h"
  13
  14/* Exynos USB PHY registers */
  15
  16/* PHY power control */
  17#define S5PV210_UPHYPWR                 0x0
  18
  19#define S5PV210_UPHYPWR_PHY0_SUSPEND    BIT(0)
  20#define S5PV210_UPHYPWR_PHY0_PWR        BIT(3)
  21#define S5PV210_UPHYPWR_PHY0_OTG_PWR    BIT(4)
  22#define S5PV210_UPHYPWR_PHY0    ( \
  23        S5PV210_UPHYPWR_PHY0_SUSPEND | \
  24        S5PV210_UPHYPWR_PHY0_PWR | \
  25        S5PV210_UPHYPWR_PHY0_OTG_PWR)
  26
  27#define S5PV210_UPHYPWR_PHY1_SUSPEND    BIT(6)
  28#define S5PV210_UPHYPWR_PHY1_PWR        BIT(7)
  29#define S5PV210_UPHYPWR_PHY1 ( \
  30        S5PV210_UPHYPWR_PHY1_SUSPEND | \
  31        S5PV210_UPHYPWR_PHY1_PWR)
  32
  33/* PHY clock control */
  34#define S5PV210_UPHYCLK                 0x4
  35
  36#define S5PV210_UPHYCLK_PHYFSEL_MASK    (0x3 << 0)
  37#define S5PV210_UPHYCLK_PHYFSEL_48MHZ   (0x0 << 0)
  38#define S5PV210_UPHYCLK_PHYFSEL_24MHZ   (0x3 << 0)
  39#define S5PV210_UPHYCLK_PHYFSEL_12MHZ   (0x2 << 0)
  40
  41#define S5PV210_UPHYCLK_PHY0_ID_PULLUP  BIT(2)
  42#define S5PV210_UPHYCLK_PHY0_COMMON_ON  BIT(4)
  43#define S5PV210_UPHYCLK_PHY1_COMMON_ON  BIT(7)
  44
  45/* PHY reset control */
  46#define S5PV210_UPHYRST                 0x8
  47
  48#define S5PV210_URSTCON_PHY0            BIT(0)
  49#define S5PV210_URSTCON_OTG_HLINK       BIT(1)
  50#define S5PV210_URSTCON_OTG_PHYLINK     BIT(2)
  51#define S5PV210_URSTCON_PHY1_ALL        BIT(3)
  52#define S5PV210_URSTCON_HOST_LINK_ALL   BIT(4)
  53
  54/* Isolation, configured in the power management unit */
  55#define S5PV210_USB_ISOL_OFFSET         0x680c
  56#define S5PV210_USB_ISOL_DEVICE         BIT(0)
  57#define S5PV210_USB_ISOL_HOST           BIT(1)
  58
  59
  60enum s5pv210_phy_id {
  61        S5PV210_DEVICE,
  62        S5PV210_HOST,
  63        S5PV210_NUM_PHYS,
  64};
  65
  66/*
  67 * s5pv210_rate_to_clk() converts the supplied clock rate to the value that
  68 * can be written to the phy register.
  69 */
  70static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg)
  71{
  72        switch (rate) {
  73        case 12 * MHZ:
  74                *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
  75                break;
  76        case 24 * MHZ:
  77                *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
  78                break;
  79        case 48 * MHZ:
  80                *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
  81                break;
  82        default:
  83                return -EINVAL;
  84        }
  85
  86        return 0;
  87}
  88
  89static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on)
  90{
  91        struct samsung_usb2_phy_driver *drv = inst->drv;
  92        u32 mask;
  93
  94        switch (inst->cfg->id) {
  95        case S5PV210_DEVICE:
  96                mask = S5PV210_USB_ISOL_DEVICE;
  97                break;
  98        case S5PV210_HOST:
  99                mask = S5PV210_USB_ISOL_HOST;
 100                break;
 101        default:
 102                return;
 103        }
 104
 105        regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET,
 106                                                        mask, on ? 0 : mask);
 107}
 108
 109static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
 110{
 111        struct samsung_usb2_phy_driver *drv = inst->drv;
 112        u32 rstbits = 0;
 113        u32 phypwr = 0;
 114        u32 rst;
 115        u32 pwr;
 116
 117        switch (inst->cfg->id) {
 118        case S5PV210_DEVICE:
 119                phypwr =        S5PV210_UPHYPWR_PHY0;
 120                rstbits =       S5PV210_URSTCON_PHY0;
 121                break;
 122        case S5PV210_HOST:
 123                phypwr =        S5PV210_UPHYPWR_PHY1;
 124                rstbits =       S5PV210_URSTCON_PHY1_ALL |
 125                                S5PV210_URSTCON_HOST_LINK_ALL;
 126                break;
 127        }
 128
 129        if (on) {
 130                writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK);
 131
 132                pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
 133                pwr &= ~phypwr;
 134                writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
 135
 136                rst = readl(drv->reg_phy + S5PV210_UPHYRST);
 137                rst |= rstbits;
 138                writel(rst, drv->reg_phy + S5PV210_UPHYRST);
 139                udelay(10);
 140                rst &= ~rstbits;
 141                writel(rst, drv->reg_phy + S5PV210_UPHYRST);
 142                /* The following delay is necessary for the reset sequence to be
 143                 * completed
 144                 */
 145                udelay(80);
 146        } else {
 147                pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
 148                pwr |= phypwr;
 149                writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
 150        }
 151}
 152
 153static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
 154{
 155        s5pv210_isol(inst, 0);
 156        s5pv210_phy_pwr(inst, 1);
 157
 158        return 0;
 159}
 160
 161static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
 162{
 163        s5pv210_phy_pwr(inst, 0);
 164        s5pv210_isol(inst, 1);
 165
 166        return 0;
 167}
 168
 169static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = {
 170        [S5PV210_DEVICE] = {
 171                .label          = "device",
 172                .id             = S5PV210_DEVICE,
 173                .power_on       = s5pv210_power_on,
 174                .power_off      = s5pv210_power_off,
 175        },
 176        [S5PV210_HOST] = {
 177                .label          = "host",
 178                .id             = S5PV210_HOST,
 179                .power_on       = s5pv210_power_on,
 180                .power_off      = s5pv210_power_off,
 181        },
 182};
 183
 184const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
 185        .num_phys       = ARRAY_SIZE(s5pv210_phys),
 186        .phys           = s5pv210_phys,
 187        .rate_to_clk    = s5pv210_rate_to_clk,
 188};
 189