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        } else {
 143                pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
 144                pwr |= phypwr;
 145                writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
 146        }
 147}
 148
 149static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
 150{
 151        s5pv210_isol(inst, 0);
 152        s5pv210_phy_pwr(inst, 1);
 153
 154        return 0;
 155}
 156
 157static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
 158{
 159        s5pv210_phy_pwr(inst, 0);
 160        s5pv210_isol(inst, 1);
 161
 162        return 0;
 163}
 164
 165static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = {
 166        [S5PV210_DEVICE] = {
 167                .label          = "device",
 168                .id             = S5PV210_DEVICE,
 169                .power_on       = s5pv210_power_on,
 170                .power_off      = s5pv210_power_off,
 171        },
 172        [S5PV210_HOST] = {
 173                .label          = "host",
 174                .id             = S5PV210_HOST,
 175                .power_on       = s5pv210_power_on,
 176                .power_off      = s5pv210_power_off,
 177        },
 178};
 179
 180const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
 181        .num_phys       = ARRAY_SIZE(s5pv210_phys),
 182        .phys           = s5pv210_phys,
 183        .rate_to_clk    = s5pv210_rate_to_clk,
 184};
 185