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