linux/drivers/phy/samsung/phy-exynos4210-usb2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
   4 *
   5 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
   6 * Author: 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 <linux/regmap.h>
  13#include "phy-samsung-usb2.h"
  14
  15/* Exynos USB PHY registers */
  16
  17/* PHY power control */
  18#define EXYNOS_4210_UPHYPWR                     0x0
  19
  20#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND        BIT(0)
  21#define EXYNOS_4210_UPHYPWR_PHY0_PWR            BIT(3)
  22#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR        BIT(4)
  23#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP          BIT(5)
  24#define EXYNOS_4210_UPHYPWR_PHY0        ( \
  25        EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
  26        EXYNOS_4210_UPHYPWR_PHY0_PWR | \
  27        EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
  28        EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
  29
  30#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND        BIT(6)
  31#define EXYNOS_4210_UPHYPWR_PHY1_PWR            BIT(7)
  32#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP          BIT(8)
  33#define EXYNOS_4210_UPHYPWR_PHY1 ( \
  34        EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
  35        EXYNOS_4210_UPHYPWR_PHY1_PWR | \
  36        EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
  37
  38#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND       BIT(9)
  39#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP         BIT(10)
  40#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
  41        EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
  42        EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
  43
  44#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND       BIT(11)
  45#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP         BIT(12)
  46#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
  47        EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
  48        EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
  49
  50/* PHY clock control */
  51#define EXYNOS_4210_UPHYCLK                     0x4
  52
  53#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK        (0x3 << 0)
  54#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET      0
  55#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ       (0x0 << 0)
  56#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ       (0x3 << 0)
  57#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ       (0x2 << 0)
  58
  59#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP      BIT(2)
  60#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON      BIT(4)
  61#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON      BIT(7)
  62
  63/* PHY reset control */
  64#define EXYNOS_4210_UPHYRST                     0x8
  65
  66#define EXYNOS_4210_URSTCON_PHY0                BIT(0)
  67#define EXYNOS_4210_URSTCON_OTG_HLINK           BIT(1)
  68#define EXYNOS_4210_URSTCON_OTG_PHYLINK         BIT(2)
  69#define EXYNOS_4210_URSTCON_PHY1_ALL            BIT(3)
  70#define EXYNOS_4210_URSTCON_PHY1_P0             BIT(4)
  71#define EXYNOS_4210_URSTCON_PHY1_P1P2           BIT(5)
  72#define EXYNOS_4210_URSTCON_HOST_LINK_ALL       BIT(6)
  73#define EXYNOS_4210_URSTCON_HOST_LINK_P0        BIT(7)
  74#define EXYNOS_4210_URSTCON_HOST_LINK_P1        BIT(8)
  75#define EXYNOS_4210_URSTCON_HOST_LINK_P2        BIT(9)
  76
  77/* Isolation, configured in the power management unit */
  78#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET      0x704
  79#define EXYNOS_4210_USB_ISOL_DEVICE             BIT(0)
  80#define EXYNOS_4210_USB_ISOL_HOST_OFFSET        0x708
  81#define EXYNOS_4210_USB_ISOL_HOST               BIT(0)
  82
  83/* USBYPHY1 Floating prevention */
  84#define EXYNOS_4210_UPHY1CON                    0x34
  85#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION   0x1
  86
  87/* Mode switching SUB Device <-> Host */
  88#define EXYNOS_4210_MODE_SWITCH_OFFSET          0x21c
  89#define EXYNOS_4210_MODE_SWITCH_MASK            1
  90#define EXYNOS_4210_MODE_SWITCH_DEVICE          0
  91#define EXYNOS_4210_MODE_SWITCH_HOST            1
  92
  93enum exynos4210_phy_id {
  94        EXYNOS4210_DEVICE,
  95        EXYNOS4210_HOST,
  96        EXYNOS4210_HSIC0,
  97        EXYNOS4210_HSIC1,
  98        EXYNOS4210_NUM_PHYS,
  99};
 100
 101/*
 102 * exynos4210_rate_to_clk() converts the supplied clock rate to the value that
 103 * can be written to the phy register.
 104 */
 105static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
 106{
 107        switch (rate) {
 108        case 12 * MHZ:
 109                *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
 110                break;
 111        case 24 * MHZ:
 112                *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
 113                break;
 114        case 48 * MHZ:
 115                *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
 116                break;
 117        default:
 118                return -EINVAL;
 119        }
 120
 121        return 0;
 122}
 123
 124static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
 125{
 126        struct samsung_usb2_phy_driver *drv = inst->drv;
 127        u32 offset;
 128        u32 mask;
 129
 130        switch (inst->cfg->id) {
 131        case EXYNOS4210_DEVICE:
 132                offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
 133                mask = EXYNOS_4210_USB_ISOL_DEVICE;
 134                break;
 135        case EXYNOS4210_HOST:
 136                offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
 137                mask = EXYNOS_4210_USB_ISOL_HOST;
 138                break;
 139        default:
 140                return;
 141        }
 142
 143        regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
 144}
 145
 146static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
 147{
 148        struct samsung_usb2_phy_driver *drv = inst->drv;
 149        u32 rstbits = 0;
 150        u32 phypwr = 0;
 151        u32 rst;
 152        u32 pwr;
 153        u32 clk;
 154
 155        switch (inst->cfg->id) {
 156        case EXYNOS4210_DEVICE:
 157                phypwr =        EXYNOS_4210_UPHYPWR_PHY0;
 158                rstbits =       EXYNOS_4210_URSTCON_PHY0;
 159                break;
 160        case EXYNOS4210_HOST:
 161                phypwr =        EXYNOS_4210_UPHYPWR_PHY1;
 162                rstbits =       EXYNOS_4210_URSTCON_PHY1_ALL |
 163                                EXYNOS_4210_URSTCON_PHY1_P0 |
 164                                EXYNOS_4210_URSTCON_PHY1_P1P2 |
 165                                EXYNOS_4210_URSTCON_HOST_LINK_ALL |
 166                                EXYNOS_4210_URSTCON_HOST_LINK_P0;
 167                writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
 168                break;
 169        case EXYNOS4210_HSIC0:
 170                phypwr =        EXYNOS_4210_UPHYPWR_HSIC0;
 171                rstbits =       EXYNOS_4210_URSTCON_PHY1_P1P2 |
 172                                EXYNOS_4210_URSTCON_HOST_LINK_P1;
 173                break;
 174        case EXYNOS4210_HSIC1:
 175                phypwr =        EXYNOS_4210_UPHYPWR_HSIC1;
 176                rstbits =       EXYNOS_4210_URSTCON_PHY1_P1P2 |
 177                                EXYNOS_4210_URSTCON_HOST_LINK_P2;
 178                break;
 179        }
 180
 181        if (on) {
 182                clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK);
 183                clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK;
 184                clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET;
 185                writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK);
 186
 187                pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
 188                pwr &= ~phypwr;
 189                writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
 190
 191                rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
 192                rst |= rstbits;
 193                writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
 194                udelay(10);
 195                rst &= ~rstbits;
 196                writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
 197                /* The following delay is necessary for the reset sequence to be
 198                 * completed */
 199                udelay(80);
 200        } else {
 201                pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
 202                pwr |= phypwr;
 203                writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
 204        }
 205}
 206
 207static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
 208{
 209        /* Order of initialisation is important - first power then isolation */
 210        exynos4210_phy_pwr(inst, 1);
 211        exynos4210_isol(inst, 0);
 212
 213        return 0;
 214}
 215
 216static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
 217{
 218        exynos4210_isol(inst, 1);
 219        exynos4210_phy_pwr(inst, 0);
 220
 221        return 0;
 222}
 223
 224
 225static const struct samsung_usb2_common_phy exynos4210_phys[] = {
 226        {
 227                .label          = "device",
 228                .id             = EXYNOS4210_DEVICE,
 229                .power_on       = exynos4210_power_on,
 230                .power_off      = exynos4210_power_off,
 231        },
 232        {
 233                .label          = "host",
 234                .id             = EXYNOS4210_HOST,
 235                .power_on       = exynos4210_power_on,
 236                .power_off      = exynos4210_power_off,
 237        },
 238        {
 239                .label          = "hsic0",
 240                .id             = EXYNOS4210_HSIC0,
 241                .power_on       = exynos4210_power_on,
 242                .power_off      = exynos4210_power_off,
 243        },
 244        {
 245                .label          = "hsic1",
 246                .id             = EXYNOS4210_HSIC1,
 247                .power_on       = exynos4210_power_on,
 248                .power_off      = exynos4210_power_off,
 249        },
 250};
 251
 252const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
 253        .has_mode_switch        = 0,
 254        .num_phys               = EXYNOS4210_NUM_PHYS,
 255        .phys                   = exynos4210_phys,
 256        .rate_to_clk            = exynos4210_rate_to_clk,
 257};
 258