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