162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci * Authors: Kamil Debski <k.debski@samsung.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/phy/phy.h> 1262306a36Sopenharmony_ci#include "phy-samsung-usb2.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* Exynos USB PHY registers */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* PHY power control */ 1762306a36Sopenharmony_ci#define S5PV210_UPHYPWR 0x0 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define S5PV210_UPHYPWR_PHY0_SUSPEND BIT(0) 2062306a36Sopenharmony_ci#define S5PV210_UPHYPWR_PHY0_PWR BIT(3) 2162306a36Sopenharmony_ci#define S5PV210_UPHYPWR_PHY0_OTG_PWR BIT(4) 2262306a36Sopenharmony_ci#define S5PV210_UPHYPWR_PHY0 ( \ 2362306a36Sopenharmony_ci S5PV210_UPHYPWR_PHY0_SUSPEND | \ 2462306a36Sopenharmony_ci S5PV210_UPHYPWR_PHY0_PWR | \ 2562306a36Sopenharmony_ci S5PV210_UPHYPWR_PHY0_OTG_PWR) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define S5PV210_UPHYPWR_PHY1_SUSPEND BIT(6) 2862306a36Sopenharmony_ci#define S5PV210_UPHYPWR_PHY1_PWR BIT(7) 2962306a36Sopenharmony_ci#define S5PV210_UPHYPWR_PHY1 ( \ 3062306a36Sopenharmony_ci S5PV210_UPHYPWR_PHY1_SUSPEND | \ 3162306a36Sopenharmony_ci S5PV210_UPHYPWR_PHY1_PWR) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* PHY clock control */ 3462306a36Sopenharmony_ci#define S5PV210_UPHYCLK 0x4 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define S5PV210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) 3762306a36Sopenharmony_ci#define S5PV210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) 3862306a36Sopenharmony_ci#define S5PV210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) 3962306a36Sopenharmony_ci#define S5PV210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2) 4262306a36Sopenharmony_ci#define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4) 4362306a36Sopenharmony_ci#define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* PHY reset control */ 4662306a36Sopenharmony_ci#define S5PV210_UPHYRST 0x8 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define S5PV210_URSTCON_PHY0 BIT(0) 4962306a36Sopenharmony_ci#define S5PV210_URSTCON_OTG_HLINK BIT(1) 5062306a36Sopenharmony_ci#define S5PV210_URSTCON_OTG_PHYLINK BIT(2) 5162306a36Sopenharmony_ci#define S5PV210_URSTCON_PHY1_ALL BIT(3) 5262306a36Sopenharmony_ci#define S5PV210_URSTCON_HOST_LINK_ALL BIT(4) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* Isolation, configured in the power management unit */ 5562306a36Sopenharmony_ci#define S5PV210_USB_ISOL_OFFSET 0x680c 5662306a36Sopenharmony_ci#define S5PV210_USB_ISOL_DEVICE BIT(0) 5762306a36Sopenharmony_ci#define S5PV210_USB_ISOL_HOST BIT(1) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cienum s5pv210_phy_id { 6162306a36Sopenharmony_ci S5PV210_DEVICE, 6262306a36Sopenharmony_ci S5PV210_HOST, 6362306a36Sopenharmony_ci S5PV210_NUM_PHYS, 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * s5pv210_rate_to_clk() converts the supplied clock rate to the value that 6862306a36Sopenharmony_ci * can be written to the phy register. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_cistatic int s5pv210_rate_to_clk(unsigned long rate, u32 *reg) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci switch (rate) { 7362306a36Sopenharmony_ci case 12 * MHZ: 7462306a36Sopenharmony_ci *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ; 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci case 24 * MHZ: 7762306a36Sopenharmony_ci *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ; 7862306a36Sopenharmony_ci break; 7962306a36Sopenharmony_ci case 48 * MHZ: 8062306a36Sopenharmony_ci *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ; 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci default: 8362306a36Sopenharmony_ci return -EINVAL; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return 0; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct samsung_usb2_phy_driver *drv = inst->drv; 9262306a36Sopenharmony_ci u32 mask; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci switch (inst->cfg->id) { 9562306a36Sopenharmony_ci case S5PV210_DEVICE: 9662306a36Sopenharmony_ci mask = S5PV210_USB_ISOL_DEVICE; 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci case S5PV210_HOST: 9962306a36Sopenharmony_ci mask = S5PV210_USB_ISOL_HOST; 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci default: 10262306a36Sopenharmony_ci return; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET, 10662306a36Sopenharmony_ci mask, on ? 0 : mask); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct samsung_usb2_phy_driver *drv = inst->drv; 11262306a36Sopenharmony_ci u32 rstbits = 0; 11362306a36Sopenharmony_ci u32 phypwr = 0; 11462306a36Sopenharmony_ci u32 rst; 11562306a36Sopenharmony_ci u32 pwr; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci switch (inst->cfg->id) { 11862306a36Sopenharmony_ci case S5PV210_DEVICE: 11962306a36Sopenharmony_ci phypwr = S5PV210_UPHYPWR_PHY0; 12062306a36Sopenharmony_ci rstbits = S5PV210_URSTCON_PHY0; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci case S5PV210_HOST: 12362306a36Sopenharmony_ci phypwr = S5PV210_UPHYPWR_PHY1; 12462306a36Sopenharmony_ci rstbits = S5PV210_URSTCON_PHY1_ALL | 12562306a36Sopenharmony_ci S5PV210_URSTCON_HOST_LINK_ALL; 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (on) { 13062306a36Sopenharmony_ci writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); 13362306a36Sopenharmony_ci pwr &= ~phypwr; 13462306a36Sopenharmony_ci writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci rst = readl(drv->reg_phy + S5PV210_UPHYRST); 13762306a36Sopenharmony_ci rst |= rstbits; 13862306a36Sopenharmony_ci writel(rst, drv->reg_phy + S5PV210_UPHYRST); 13962306a36Sopenharmony_ci udelay(10); 14062306a36Sopenharmony_ci rst &= ~rstbits; 14162306a36Sopenharmony_ci writel(rst, drv->reg_phy + S5PV210_UPHYRST); 14262306a36Sopenharmony_ci /* The following delay is necessary for the reset sequence to be 14362306a36Sopenharmony_ci * completed 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci udelay(80); 14662306a36Sopenharmony_ci } else { 14762306a36Sopenharmony_ci pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); 14862306a36Sopenharmony_ci pwr |= phypwr; 14962306a36Sopenharmony_ci writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int s5pv210_power_on(struct samsung_usb2_phy_instance *inst) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci s5pv210_isol(inst, 0); 15662306a36Sopenharmony_ci s5pv210_phy_pwr(inst, 1); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int s5pv210_power_off(struct samsung_usb2_phy_instance *inst) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci s5pv210_phy_pwr(inst, 0); 16462306a36Sopenharmony_ci s5pv210_isol(inst, 1); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = { 17062306a36Sopenharmony_ci [S5PV210_DEVICE] = { 17162306a36Sopenharmony_ci .label = "device", 17262306a36Sopenharmony_ci .id = S5PV210_DEVICE, 17362306a36Sopenharmony_ci .power_on = s5pv210_power_on, 17462306a36Sopenharmony_ci .power_off = s5pv210_power_off, 17562306a36Sopenharmony_ci }, 17662306a36Sopenharmony_ci [S5PV210_HOST] = { 17762306a36Sopenharmony_ci .label = "host", 17862306a36Sopenharmony_ci .id = S5PV210_HOST, 17962306a36Sopenharmony_ci .power_on = s5pv210_power_on, 18062306a36Sopenharmony_ci .power_off = s5pv210_power_off, 18162306a36Sopenharmony_ci }, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ciconst struct samsung_usb2_phy_config s5pv210_usb2_phy_config = { 18562306a36Sopenharmony_ci .num_phys = ARRAY_SIZE(s5pv210_phys), 18662306a36Sopenharmony_ci .phys = s5pv210_phys, 18762306a36Sopenharmony_ci .rate_to_clk = s5pv210_rate_to_clk, 18862306a36Sopenharmony_ci}; 189