258 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support4 *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 0x019 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 0x452 53#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0)54#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 055#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 0x865 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 0x70479#define EXYNOS_4210_USB_ISOL_DEVICE BIT(0)80#define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x70881#define EXYNOS_4210_USB_ISOL_HOST BIT(0)82 83/* USBYPHY1 Floating prevention */84#define EXYNOS_4210_UPHY1CON 0x3485#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x186 87/* Mode switching SUB Device <-> Host */88#define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c89#define EXYNOS_4210_MODE_SWITCH_MASK 190#define EXYNOS_4210_MODE_SWITCH_DEVICE 091#define EXYNOS_4210_MODE_SWITCH_HOST 192 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 that103 * 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 be198 * 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