1*0b56e9a7SVivek Gautam /* 2*0b56e9a7SVivek Gautam * Allwinner sun4i USB phy driver 3*0b56e9a7SVivek Gautam * 4*0b56e9a7SVivek Gautam * Copyright (C) 2014-2015 Hans de Goede <hdegoede@redhat.com> 5*0b56e9a7SVivek Gautam * 6*0b56e9a7SVivek Gautam * Based on code from 7*0b56e9a7SVivek Gautam * Allwinner Technology Co., Ltd. <www.allwinnertech.com> 8*0b56e9a7SVivek Gautam * 9*0b56e9a7SVivek Gautam * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver 10*0b56e9a7SVivek Gautam * Copyright (C) 2013 Samsung Electronics Co., Ltd. 11*0b56e9a7SVivek Gautam * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 12*0b56e9a7SVivek Gautam * 13*0b56e9a7SVivek Gautam * This program is free software; you can redistribute it and/or modify 14*0b56e9a7SVivek Gautam * it under the terms of the GNU General Public License as published by 15*0b56e9a7SVivek Gautam * the Free Software Foundation; either version 2 of the License, or 16*0b56e9a7SVivek Gautam * (at your option) any later version. 17*0b56e9a7SVivek Gautam * 18*0b56e9a7SVivek Gautam * This program is distributed in the hope that it will be useful, 19*0b56e9a7SVivek Gautam * but WITHOUT ANY WARRANTY; without even the implied warranty of 20*0b56e9a7SVivek Gautam * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21*0b56e9a7SVivek Gautam * GNU General Public License for more details. 22*0b56e9a7SVivek Gautam */ 23*0b56e9a7SVivek Gautam 24*0b56e9a7SVivek Gautam #include <linux/clk.h> 25*0b56e9a7SVivek Gautam #include <linux/delay.h> 26*0b56e9a7SVivek Gautam #include <linux/err.h> 27*0b56e9a7SVivek Gautam #include <linux/extcon.h> 28*0b56e9a7SVivek Gautam #include <linux/io.h> 29*0b56e9a7SVivek Gautam #include <linux/interrupt.h> 30*0b56e9a7SVivek Gautam #include <linux/kernel.h> 31*0b56e9a7SVivek Gautam #include <linux/module.h> 32*0b56e9a7SVivek Gautam #include <linux/mutex.h> 33*0b56e9a7SVivek Gautam #include <linux/of.h> 34*0b56e9a7SVivek Gautam #include <linux/of_address.h> 35*0b56e9a7SVivek Gautam #include <linux/of_device.h> 36*0b56e9a7SVivek Gautam #include <linux/of_gpio.h> 37*0b56e9a7SVivek Gautam #include <linux/phy/phy.h> 38*0b56e9a7SVivek Gautam #include <linux/phy/phy-sun4i-usb.h> 39*0b56e9a7SVivek Gautam #include <linux/platform_device.h> 40*0b56e9a7SVivek Gautam #include <linux/power_supply.h> 41*0b56e9a7SVivek Gautam #include <linux/regulator/consumer.h> 42*0b56e9a7SVivek Gautam #include <linux/reset.h> 43*0b56e9a7SVivek Gautam #include <linux/spinlock.h> 44*0b56e9a7SVivek Gautam #include <linux/usb/of.h> 45*0b56e9a7SVivek Gautam #include <linux/workqueue.h> 46*0b56e9a7SVivek Gautam 47*0b56e9a7SVivek Gautam #define REG_ISCR 0x00 48*0b56e9a7SVivek Gautam #define REG_PHYCTL_A10 0x04 49*0b56e9a7SVivek Gautam #define REG_PHYBIST 0x08 50*0b56e9a7SVivek Gautam #define REG_PHYTUNE 0x0c 51*0b56e9a7SVivek Gautam #define REG_PHYCTL_A33 0x10 52*0b56e9a7SVivek Gautam #define REG_PHY_OTGCTL 0x20 53*0b56e9a7SVivek Gautam 54*0b56e9a7SVivek Gautam #define REG_PMU_UNK1 0x10 55*0b56e9a7SVivek Gautam 56*0b56e9a7SVivek Gautam #define PHYCTL_DATA BIT(7) 57*0b56e9a7SVivek Gautam 58*0b56e9a7SVivek Gautam #define OTGCTL_ROUTE_MUSB BIT(0) 59*0b56e9a7SVivek Gautam 60*0b56e9a7SVivek Gautam #define SUNXI_AHB_ICHR8_EN BIT(10) 61*0b56e9a7SVivek Gautam #define SUNXI_AHB_INCR4_BURST_EN BIT(9) 62*0b56e9a7SVivek Gautam #define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) 63*0b56e9a7SVivek Gautam #define SUNXI_ULPI_BYPASS_EN BIT(0) 64*0b56e9a7SVivek Gautam 65*0b56e9a7SVivek Gautam /* ISCR, Interface Status and Control bits */ 66*0b56e9a7SVivek Gautam #define ISCR_ID_PULLUP_EN (1 << 17) 67*0b56e9a7SVivek Gautam #define ISCR_DPDM_PULLUP_EN (1 << 16) 68*0b56e9a7SVivek Gautam /* sunxi has the phy id/vbus pins not connected, so we use the force bits */ 69*0b56e9a7SVivek Gautam #define ISCR_FORCE_ID_MASK (3 << 14) 70*0b56e9a7SVivek Gautam #define ISCR_FORCE_ID_LOW (2 << 14) 71*0b56e9a7SVivek Gautam #define ISCR_FORCE_ID_HIGH (3 << 14) 72*0b56e9a7SVivek Gautam #define ISCR_FORCE_VBUS_MASK (3 << 12) 73*0b56e9a7SVivek Gautam #define ISCR_FORCE_VBUS_LOW (2 << 12) 74*0b56e9a7SVivek Gautam #define ISCR_FORCE_VBUS_HIGH (3 << 12) 75*0b56e9a7SVivek Gautam 76*0b56e9a7SVivek Gautam /* Common Control Bits for Both PHYs */ 77*0b56e9a7SVivek Gautam #define PHY_PLL_BW 0x03 78*0b56e9a7SVivek Gautam #define PHY_RES45_CAL_EN 0x0c 79*0b56e9a7SVivek Gautam 80*0b56e9a7SVivek Gautam /* Private Control Bits for Each PHY */ 81*0b56e9a7SVivek Gautam #define PHY_TX_AMPLITUDE_TUNE 0x20 82*0b56e9a7SVivek Gautam #define PHY_TX_SLEWRATE_TUNE 0x22 83*0b56e9a7SVivek Gautam #define PHY_VBUSVALID_TH_SEL 0x25 84*0b56e9a7SVivek Gautam #define PHY_PULLUP_RES_SEL 0x27 85*0b56e9a7SVivek Gautam #define PHY_OTG_FUNC_EN 0x28 86*0b56e9a7SVivek Gautam #define PHY_VBUS_DET_EN 0x29 87*0b56e9a7SVivek Gautam #define PHY_DISCON_TH_SEL 0x2a 88*0b56e9a7SVivek Gautam #define PHY_SQUELCH_DETECT 0x3c 89*0b56e9a7SVivek Gautam 90*0b56e9a7SVivek Gautam #define MAX_PHYS 4 91*0b56e9a7SVivek Gautam 92*0b56e9a7SVivek Gautam /* 93*0b56e9a7SVivek Gautam * Note do not raise the debounce time, we must report Vusb high within 100ms 94*0b56e9a7SVivek Gautam * otherwise we get Vbus errors 95*0b56e9a7SVivek Gautam */ 96*0b56e9a7SVivek Gautam #define DEBOUNCE_TIME msecs_to_jiffies(50) 97*0b56e9a7SVivek Gautam #define POLL_TIME msecs_to_jiffies(250) 98*0b56e9a7SVivek Gautam 99*0b56e9a7SVivek Gautam enum sun4i_usb_phy_type { 100*0b56e9a7SVivek Gautam sun4i_a10_phy, 101*0b56e9a7SVivek Gautam sun6i_a31_phy, 102*0b56e9a7SVivek Gautam sun8i_a33_phy, 103*0b56e9a7SVivek Gautam sun8i_h3_phy, 104*0b56e9a7SVivek Gautam sun8i_v3s_phy, 105*0b56e9a7SVivek Gautam sun50i_a64_phy, 106*0b56e9a7SVivek Gautam }; 107*0b56e9a7SVivek Gautam 108*0b56e9a7SVivek Gautam struct sun4i_usb_phy_cfg { 109*0b56e9a7SVivek Gautam int num_phys; 110*0b56e9a7SVivek Gautam enum sun4i_usb_phy_type type; 111*0b56e9a7SVivek Gautam u32 disc_thresh; 112*0b56e9a7SVivek Gautam u8 phyctl_offset; 113*0b56e9a7SVivek Gautam bool dedicated_clocks; 114*0b56e9a7SVivek Gautam bool enable_pmu_unk1; 115*0b56e9a7SVivek Gautam bool phy0_dual_route; 116*0b56e9a7SVivek Gautam }; 117*0b56e9a7SVivek Gautam 118*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data { 119*0b56e9a7SVivek Gautam void __iomem *base; 120*0b56e9a7SVivek Gautam const struct sun4i_usb_phy_cfg *cfg; 121*0b56e9a7SVivek Gautam enum usb_dr_mode dr_mode; 122*0b56e9a7SVivek Gautam spinlock_t reg_lock; /* guard access to phyctl reg */ 123*0b56e9a7SVivek Gautam struct sun4i_usb_phy { 124*0b56e9a7SVivek Gautam struct phy *phy; 125*0b56e9a7SVivek Gautam void __iomem *pmu; 126*0b56e9a7SVivek Gautam struct regulator *vbus; 127*0b56e9a7SVivek Gautam struct reset_control *reset; 128*0b56e9a7SVivek Gautam struct clk *clk; 129*0b56e9a7SVivek Gautam bool regulator_on; 130*0b56e9a7SVivek Gautam int index; 131*0b56e9a7SVivek Gautam } phys[MAX_PHYS]; 132*0b56e9a7SVivek Gautam /* phy0 / otg related variables */ 133*0b56e9a7SVivek Gautam struct extcon_dev *extcon; 134*0b56e9a7SVivek Gautam bool phy0_init; 135*0b56e9a7SVivek Gautam struct gpio_desc *id_det_gpio; 136*0b56e9a7SVivek Gautam struct gpio_desc *vbus_det_gpio; 137*0b56e9a7SVivek Gautam struct power_supply *vbus_power_supply; 138*0b56e9a7SVivek Gautam struct notifier_block vbus_power_nb; 139*0b56e9a7SVivek Gautam bool vbus_power_nb_registered; 140*0b56e9a7SVivek Gautam bool force_session_end; 141*0b56e9a7SVivek Gautam int id_det_irq; 142*0b56e9a7SVivek Gautam int vbus_det_irq; 143*0b56e9a7SVivek Gautam int id_det; 144*0b56e9a7SVivek Gautam int vbus_det; 145*0b56e9a7SVivek Gautam struct delayed_work detect; 146*0b56e9a7SVivek Gautam }; 147*0b56e9a7SVivek Gautam 148*0b56e9a7SVivek Gautam #define to_sun4i_usb_phy_data(phy) \ 149*0b56e9a7SVivek Gautam container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index]) 150*0b56e9a7SVivek Gautam 151*0b56e9a7SVivek Gautam static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set) 152*0b56e9a7SVivek Gautam { 153*0b56e9a7SVivek Gautam struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 154*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 155*0b56e9a7SVivek Gautam u32 iscr; 156*0b56e9a7SVivek Gautam 157*0b56e9a7SVivek Gautam iscr = readl(data->base + REG_ISCR); 158*0b56e9a7SVivek Gautam iscr &= ~clr; 159*0b56e9a7SVivek Gautam iscr |= set; 160*0b56e9a7SVivek Gautam writel(iscr, data->base + REG_ISCR); 161*0b56e9a7SVivek Gautam } 162*0b56e9a7SVivek Gautam 163*0b56e9a7SVivek Gautam static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val) 164*0b56e9a7SVivek Gautam { 165*0b56e9a7SVivek Gautam if (val) 166*0b56e9a7SVivek Gautam val = ISCR_FORCE_ID_HIGH; 167*0b56e9a7SVivek Gautam else 168*0b56e9a7SVivek Gautam val = ISCR_FORCE_ID_LOW; 169*0b56e9a7SVivek Gautam 170*0b56e9a7SVivek Gautam sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val); 171*0b56e9a7SVivek Gautam } 172*0b56e9a7SVivek Gautam 173*0b56e9a7SVivek Gautam static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val) 174*0b56e9a7SVivek Gautam { 175*0b56e9a7SVivek Gautam if (val) 176*0b56e9a7SVivek Gautam val = ISCR_FORCE_VBUS_HIGH; 177*0b56e9a7SVivek Gautam else 178*0b56e9a7SVivek Gautam val = ISCR_FORCE_VBUS_LOW; 179*0b56e9a7SVivek Gautam 180*0b56e9a7SVivek Gautam sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val); 181*0b56e9a7SVivek Gautam } 182*0b56e9a7SVivek Gautam 183*0b56e9a7SVivek Gautam static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, 184*0b56e9a7SVivek Gautam int len) 185*0b56e9a7SVivek Gautam { 186*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy); 187*0b56e9a7SVivek Gautam u32 temp, usbc_bit = BIT(phy->index * 2); 188*0b56e9a7SVivek Gautam void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset; 189*0b56e9a7SVivek Gautam unsigned long flags; 190*0b56e9a7SVivek Gautam int i; 191*0b56e9a7SVivek Gautam 192*0b56e9a7SVivek Gautam spin_lock_irqsave(&phy_data->reg_lock, flags); 193*0b56e9a7SVivek Gautam 194*0b56e9a7SVivek Gautam if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) { 195*0b56e9a7SVivek Gautam /* SoCs newer than A33 need us to set phyctl to 0 explicitly */ 196*0b56e9a7SVivek Gautam writel(0, phyctl); 197*0b56e9a7SVivek Gautam } 198*0b56e9a7SVivek Gautam 199*0b56e9a7SVivek Gautam for (i = 0; i < len; i++) { 200*0b56e9a7SVivek Gautam temp = readl(phyctl); 201*0b56e9a7SVivek Gautam 202*0b56e9a7SVivek Gautam /* clear the address portion */ 203*0b56e9a7SVivek Gautam temp &= ~(0xff << 8); 204*0b56e9a7SVivek Gautam 205*0b56e9a7SVivek Gautam /* set the address */ 206*0b56e9a7SVivek Gautam temp |= ((addr + i) << 8); 207*0b56e9a7SVivek Gautam writel(temp, phyctl); 208*0b56e9a7SVivek Gautam 209*0b56e9a7SVivek Gautam /* set the data bit and clear usbc bit*/ 210*0b56e9a7SVivek Gautam temp = readb(phyctl); 211*0b56e9a7SVivek Gautam if (data & 0x1) 212*0b56e9a7SVivek Gautam temp |= PHYCTL_DATA; 213*0b56e9a7SVivek Gautam else 214*0b56e9a7SVivek Gautam temp &= ~PHYCTL_DATA; 215*0b56e9a7SVivek Gautam temp &= ~usbc_bit; 216*0b56e9a7SVivek Gautam writeb(temp, phyctl); 217*0b56e9a7SVivek Gautam 218*0b56e9a7SVivek Gautam /* pulse usbc_bit */ 219*0b56e9a7SVivek Gautam temp = readb(phyctl); 220*0b56e9a7SVivek Gautam temp |= usbc_bit; 221*0b56e9a7SVivek Gautam writeb(temp, phyctl); 222*0b56e9a7SVivek Gautam 223*0b56e9a7SVivek Gautam temp = readb(phyctl); 224*0b56e9a7SVivek Gautam temp &= ~usbc_bit; 225*0b56e9a7SVivek Gautam writeb(temp, phyctl); 226*0b56e9a7SVivek Gautam 227*0b56e9a7SVivek Gautam data >>= 1; 228*0b56e9a7SVivek Gautam } 229*0b56e9a7SVivek Gautam 230*0b56e9a7SVivek Gautam spin_unlock_irqrestore(&phy_data->reg_lock, flags); 231*0b56e9a7SVivek Gautam } 232*0b56e9a7SVivek Gautam 233*0b56e9a7SVivek Gautam static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable) 234*0b56e9a7SVivek Gautam { 235*0b56e9a7SVivek Gautam u32 bits, reg_value; 236*0b56e9a7SVivek Gautam 237*0b56e9a7SVivek Gautam if (!phy->pmu) 238*0b56e9a7SVivek Gautam return; 239*0b56e9a7SVivek Gautam 240*0b56e9a7SVivek Gautam bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN | 241*0b56e9a7SVivek Gautam SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN; 242*0b56e9a7SVivek Gautam 243*0b56e9a7SVivek Gautam reg_value = readl(phy->pmu); 244*0b56e9a7SVivek Gautam 245*0b56e9a7SVivek Gautam if (enable) 246*0b56e9a7SVivek Gautam reg_value |= bits; 247*0b56e9a7SVivek Gautam else 248*0b56e9a7SVivek Gautam reg_value &= ~bits; 249*0b56e9a7SVivek Gautam 250*0b56e9a7SVivek Gautam writel(reg_value, phy->pmu); 251*0b56e9a7SVivek Gautam } 252*0b56e9a7SVivek Gautam 253*0b56e9a7SVivek Gautam static int sun4i_usb_phy_init(struct phy *_phy) 254*0b56e9a7SVivek Gautam { 255*0b56e9a7SVivek Gautam struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 256*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 257*0b56e9a7SVivek Gautam int ret; 258*0b56e9a7SVivek Gautam u32 val; 259*0b56e9a7SVivek Gautam 260*0b56e9a7SVivek Gautam ret = clk_prepare_enable(phy->clk); 261*0b56e9a7SVivek Gautam if (ret) 262*0b56e9a7SVivek Gautam return ret; 263*0b56e9a7SVivek Gautam 264*0b56e9a7SVivek Gautam ret = reset_control_deassert(phy->reset); 265*0b56e9a7SVivek Gautam if (ret) { 266*0b56e9a7SVivek Gautam clk_disable_unprepare(phy->clk); 267*0b56e9a7SVivek Gautam return ret; 268*0b56e9a7SVivek Gautam } 269*0b56e9a7SVivek Gautam 270*0b56e9a7SVivek Gautam if (phy->pmu && data->cfg->enable_pmu_unk1) { 271*0b56e9a7SVivek Gautam val = readl(phy->pmu + REG_PMU_UNK1); 272*0b56e9a7SVivek Gautam writel(val & ~2, phy->pmu + REG_PMU_UNK1); 273*0b56e9a7SVivek Gautam } 274*0b56e9a7SVivek Gautam 275*0b56e9a7SVivek Gautam /* Enable USB 45 Ohm resistor calibration */ 276*0b56e9a7SVivek Gautam if (phy->index == 0) 277*0b56e9a7SVivek Gautam sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); 278*0b56e9a7SVivek Gautam 279*0b56e9a7SVivek Gautam /* Adjust PHY's magnitude and rate */ 280*0b56e9a7SVivek Gautam sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); 281*0b56e9a7SVivek Gautam 282*0b56e9a7SVivek Gautam /* Disconnect threshold adjustment */ 283*0b56e9a7SVivek Gautam sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, 284*0b56e9a7SVivek Gautam data->cfg->disc_thresh, 2); 285*0b56e9a7SVivek Gautam 286*0b56e9a7SVivek Gautam sun4i_usb_phy_passby(phy, 1); 287*0b56e9a7SVivek Gautam 288*0b56e9a7SVivek Gautam if (phy->index == 0) { 289*0b56e9a7SVivek Gautam data->phy0_init = true; 290*0b56e9a7SVivek Gautam 291*0b56e9a7SVivek Gautam /* Enable pull-ups */ 292*0b56e9a7SVivek Gautam sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN); 293*0b56e9a7SVivek Gautam sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN); 294*0b56e9a7SVivek Gautam 295*0b56e9a7SVivek Gautam /* Force ISCR and cable state updates */ 296*0b56e9a7SVivek Gautam data->id_det = -1; 297*0b56e9a7SVivek Gautam data->vbus_det = -1; 298*0b56e9a7SVivek Gautam queue_delayed_work(system_wq, &data->detect, 0); 299*0b56e9a7SVivek Gautam } 300*0b56e9a7SVivek Gautam 301*0b56e9a7SVivek Gautam return 0; 302*0b56e9a7SVivek Gautam } 303*0b56e9a7SVivek Gautam 304*0b56e9a7SVivek Gautam static int sun4i_usb_phy_exit(struct phy *_phy) 305*0b56e9a7SVivek Gautam { 306*0b56e9a7SVivek Gautam struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 307*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 308*0b56e9a7SVivek Gautam 309*0b56e9a7SVivek Gautam if (phy->index == 0) { 310*0b56e9a7SVivek Gautam /* Disable pull-ups */ 311*0b56e9a7SVivek Gautam sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0); 312*0b56e9a7SVivek Gautam sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0); 313*0b56e9a7SVivek Gautam data->phy0_init = false; 314*0b56e9a7SVivek Gautam } 315*0b56e9a7SVivek Gautam 316*0b56e9a7SVivek Gautam sun4i_usb_phy_passby(phy, 0); 317*0b56e9a7SVivek Gautam reset_control_assert(phy->reset); 318*0b56e9a7SVivek Gautam clk_disable_unprepare(phy->clk); 319*0b56e9a7SVivek Gautam 320*0b56e9a7SVivek Gautam return 0; 321*0b56e9a7SVivek Gautam } 322*0b56e9a7SVivek Gautam 323*0b56e9a7SVivek Gautam static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data) 324*0b56e9a7SVivek Gautam { 325*0b56e9a7SVivek Gautam switch (data->dr_mode) { 326*0b56e9a7SVivek Gautam case USB_DR_MODE_OTG: 327*0b56e9a7SVivek Gautam if (data->id_det_gpio) 328*0b56e9a7SVivek Gautam return gpiod_get_value_cansleep(data->id_det_gpio); 329*0b56e9a7SVivek Gautam else 330*0b56e9a7SVivek Gautam return 1; /* Fallback to peripheral mode */ 331*0b56e9a7SVivek Gautam case USB_DR_MODE_HOST: 332*0b56e9a7SVivek Gautam return 0; 333*0b56e9a7SVivek Gautam case USB_DR_MODE_PERIPHERAL: 334*0b56e9a7SVivek Gautam default: 335*0b56e9a7SVivek Gautam return 1; 336*0b56e9a7SVivek Gautam } 337*0b56e9a7SVivek Gautam } 338*0b56e9a7SVivek Gautam 339*0b56e9a7SVivek Gautam static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) 340*0b56e9a7SVivek Gautam { 341*0b56e9a7SVivek Gautam if (data->vbus_det_gpio) 342*0b56e9a7SVivek Gautam return gpiod_get_value_cansleep(data->vbus_det_gpio); 343*0b56e9a7SVivek Gautam 344*0b56e9a7SVivek Gautam if (data->vbus_power_supply) { 345*0b56e9a7SVivek Gautam union power_supply_propval val; 346*0b56e9a7SVivek Gautam int r; 347*0b56e9a7SVivek Gautam 348*0b56e9a7SVivek Gautam r = power_supply_get_property(data->vbus_power_supply, 349*0b56e9a7SVivek Gautam POWER_SUPPLY_PROP_PRESENT, &val); 350*0b56e9a7SVivek Gautam if (r == 0) 351*0b56e9a7SVivek Gautam return val.intval; 352*0b56e9a7SVivek Gautam } 353*0b56e9a7SVivek Gautam 354*0b56e9a7SVivek Gautam /* Fallback: report vbus as high */ 355*0b56e9a7SVivek Gautam return 1; 356*0b56e9a7SVivek Gautam } 357*0b56e9a7SVivek Gautam 358*0b56e9a7SVivek Gautam static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data) 359*0b56e9a7SVivek Gautam { 360*0b56e9a7SVivek Gautam return data->vbus_det_gpio || data->vbus_power_supply; 361*0b56e9a7SVivek Gautam } 362*0b56e9a7SVivek Gautam 363*0b56e9a7SVivek Gautam static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data) 364*0b56e9a7SVivek Gautam { 365*0b56e9a7SVivek Gautam if ((data->id_det_gpio && data->id_det_irq <= 0) || 366*0b56e9a7SVivek Gautam (data->vbus_det_gpio && data->vbus_det_irq <= 0)) 367*0b56e9a7SVivek Gautam return true; 368*0b56e9a7SVivek Gautam 369*0b56e9a7SVivek Gautam /* 370*0b56e9a7SVivek Gautam * The A31 companion pmic (axp221) does not generate vbus change 371*0b56e9a7SVivek Gautam * interrupts when the board is driving vbus, so we must poll 372*0b56e9a7SVivek Gautam * when using the pmic for vbus-det _and_ we're driving vbus. 373*0b56e9a7SVivek Gautam */ 374*0b56e9a7SVivek Gautam if (data->cfg->type == sun6i_a31_phy && 375*0b56e9a7SVivek Gautam data->vbus_power_supply && data->phys[0].regulator_on) 376*0b56e9a7SVivek Gautam return true; 377*0b56e9a7SVivek Gautam 378*0b56e9a7SVivek Gautam return false; 379*0b56e9a7SVivek Gautam } 380*0b56e9a7SVivek Gautam 381*0b56e9a7SVivek Gautam static int sun4i_usb_phy_power_on(struct phy *_phy) 382*0b56e9a7SVivek Gautam { 383*0b56e9a7SVivek Gautam struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 384*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 385*0b56e9a7SVivek Gautam int ret; 386*0b56e9a7SVivek Gautam 387*0b56e9a7SVivek Gautam if (!phy->vbus || phy->regulator_on) 388*0b56e9a7SVivek Gautam return 0; 389*0b56e9a7SVivek Gautam 390*0b56e9a7SVivek Gautam /* For phy0 only turn on Vbus if we don't have an ext. Vbus */ 391*0b56e9a7SVivek Gautam if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) && 392*0b56e9a7SVivek Gautam data->vbus_det) { 393*0b56e9a7SVivek Gautam dev_warn(&_phy->dev, "External vbus detected, not enabling our own vbus\n"); 394*0b56e9a7SVivek Gautam return 0; 395*0b56e9a7SVivek Gautam } 396*0b56e9a7SVivek Gautam 397*0b56e9a7SVivek Gautam ret = regulator_enable(phy->vbus); 398*0b56e9a7SVivek Gautam if (ret) 399*0b56e9a7SVivek Gautam return ret; 400*0b56e9a7SVivek Gautam 401*0b56e9a7SVivek Gautam phy->regulator_on = true; 402*0b56e9a7SVivek Gautam 403*0b56e9a7SVivek Gautam /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */ 404*0b56e9a7SVivek Gautam if (phy->index == 0 && sun4i_usb_phy0_poll(data)) 405*0b56e9a7SVivek Gautam mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); 406*0b56e9a7SVivek Gautam 407*0b56e9a7SVivek Gautam return 0; 408*0b56e9a7SVivek Gautam } 409*0b56e9a7SVivek Gautam 410*0b56e9a7SVivek Gautam static int sun4i_usb_phy_power_off(struct phy *_phy) 411*0b56e9a7SVivek Gautam { 412*0b56e9a7SVivek Gautam struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 413*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 414*0b56e9a7SVivek Gautam 415*0b56e9a7SVivek Gautam if (!phy->vbus || !phy->regulator_on) 416*0b56e9a7SVivek Gautam return 0; 417*0b56e9a7SVivek Gautam 418*0b56e9a7SVivek Gautam regulator_disable(phy->vbus); 419*0b56e9a7SVivek Gautam phy->regulator_on = false; 420*0b56e9a7SVivek Gautam 421*0b56e9a7SVivek Gautam /* 422*0b56e9a7SVivek Gautam * phy0 vbus typically slowly discharges, sometimes this causes the 423*0b56e9a7SVivek Gautam * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan. 424*0b56e9a7SVivek Gautam */ 425*0b56e9a7SVivek Gautam if (phy->index == 0 && !sun4i_usb_phy0_poll(data)) 426*0b56e9a7SVivek Gautam mod_delayed_work(system_wq, &data->detect, POLL_TIME); 427*0b56e9a7SVivek Gautam 428*0b56e9a7SVivek Gautam return 0; 429*0b56e9a7SVivek Gautam } 430*0b56e9a7SVivek Gautam 431*0b56e9a7SVivek Gautam static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode) 432*0b56e9a7SVivek Gautam { 433*0b56e9a7SVivek Gautam struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 434*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); 435*0b56e9a7SVivek Gautam int new_mode; 436*0b56e9a7SVivek Gautam 437*0b56e9a7SVivek Gautam if (phy->index != 0) 438*0b56e9a7SVivek Gautam return -EINVAL; 439*0b56e9a7SVivek Gautam 440*0b56e9a7SVivek Gautam switch (mode) { 441*0b56e9a7SVivek Gautam case PHY_MODE_USB_HOST: 442*0b56e9a7SVivek Gautam new_mode = USB_DR_MODE_HOST; 443*0b56e9a7SVivek Gautam break; 444*0b56e9a7SVivek Gautam case PHY_MODE_USB_DEVICE: 445*0b56e9a7SVivek Gautam new_mode = USB_DR_MODE_PERIPHERAL; 446*0b56e9a7SVivek Gautam break; 447*0b56e9a7SVivek Gautam case PHY_MODE_USB_OTG: 448*0b56e9a7SVivek Gautam new_mode = USB_DR_MODE_OTG; 449*0b56e9a7SVivek Gautam break; 450*0b56e9a7SVivek Gautam default: 451*0b56e9a7SVivek Gautam return -EINVAL; 452*0b56e9a7SVivek Gautam } 453*0b56e9a7SVivek Gautam 454*0b56e9a7SVivek Gautam if (new_mode != data->dr_mode) { 455*0b56e9a7SVivek Gautam dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode); 456*0b56e9a7SVivek Gautam data->dr_mode = new_mode; 457*0b56e9a7SVivek Gautam } 458*0b56e9a7SVivek Gautam 459*0b56e9a7SVivek Gautam data->id_det = -1; /* Force reprocessing of id */ 460*0b56e9a7SVivek Gautam data->force_session_end = true; 461*0b56e9a7SVivek Gautam queue_delayed_work(system_wq, &data->detect, 0); 462*0b56e9a7SVivek Gautam 463*0b56e9a7SVivek Gautam return 0; 464*0b56e9a7SVivek Gautam } 465*0b56e9a7SVivek Gautam 466*0b56e9a7SVivek Gautam void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled) 467*0b56e9a7SVivek Gautam { 468*0b56e9a7SVivek Gautam struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); 469*0b56e9a7SVivek Gautam 470*0b56e9a7SVivek Gautam sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); 471*0b56e9a7SVivek Gautam } 472*0b56e9a7SVivek Gautam EXPORT_SYMBOL_GPL(sun4i_usb_phy_set_squelch_detect); 473*0b56e9a7SVivek Gautam 474*0b56e9a7SVivek Gautam static const struct phy_ops sun4i_usb_phy_ops = { 475*0b56e9a7SVivek Gautam .init = sun4i_usb_phy_init, 476*0b56e9a7SVivek Gautam .exit = sun4i_usb_phy_exit, 477*0b56e9a7SVivek Gautam .power_on = sun4i_usb_phy_power_on, 478*0b56e9a7SVivek Gautam .power_off = sun4i_usb_phy_power_off, 479*0b56e9a7SVivek Gautam .set_mode = sun4i_usb_phy_set_mode, 480*0b56e9a7SVivek Gautam .owner = THIS_MODULE, 481*0b56e9a7SVivek Gautam }; 482*0b56e9a7SVivek Gautam 483*0b56e9a7SVivek Gautam static void sun4i_usb_phy0_reroute(struct sun4i_usb_phy_data *data, int id_det) 484*0b56e9a7SVivek Gautam { 485*0b56e9a7SVivek Gautam u32 regval; 486*0b56e9a7SVivek Gautam 487*0b56e9a7SVivek Gautam regval = readl(data->base + REG_PHY_OTGCTL); 488*0b56e9a7SVivek Gautam if (id_det == 0) { 489*0b56e9a7SVivek Gautam /* Host mode. Route phy0 to EHCI/OHCI */ 490*0b56e9a7SVivek Gautam regval &= ~OTGCTL_ROUTE_MUSB; 491*0b56e9a7SVivek Gautam } else { 492*0b56e9a7SVivek Gautam /* Peripheral mode. Route phy0 to MUSB */ 493*0b56e9a7SVivek Gautam regval |= OTGCTL_ROUTE_MUSB; 494*0b56e9a7SVivek Gautam } 495*0b56e9a7SVivek Gautam writel(regval, data->base + REG_PHY_OTGCTL); 496*0b56e9a7SVivek Gautam } 497*0b56e9a7SVivek Gautam 498*0b56e9a7SVivek Gautam static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) 499*0b56e9a7SVivek Gautam { 500*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data = 501*0b56e9a7SVivek Gautam container_of(work, struct sun4i_usb_phy_data, detect.work); 502*0b56e9a7SVivek Gautam struct phy *phy0 = data->phys[0].phy; 503*0b56e9a7SVivek Gautam bool force_session_end, id_notify = false, vbus_notify = false; 504*0b56e9a7SVivek Gautam int id_det, vbus_det; 505*0b56e9a7SVivek Gautam 506*0b56e9a7SVivek Gautam if (phy0 == NULL) 507*0b56e9a7SVivek Gautam return; 508*0b56e9a7SVivek Gautam 509*0b56e9a7SVivek Gautam id_det = sun4i_usb_phy0_get_id_det(data); 510*0b56e9a7SVivek Gautam vbus_det = sun4i_usb_phy0_get_vbus_det(data); 511*0b56e9a7SVivek Gautam 512*0b56e9a7SVivek Gautam mutex_lock(&phy0->mutex); 513*0b56e9a7SVivek Gautam 514*0b56e9a7SVivek Gautam if (!data->phy0_init) { 515*0b56e9a7SVivek Gautam mutex_unlock(&phy0->mutex); 516*0b56e9a7SVivek Gautam return; 517*0b56e9a7SVivek Gautam } 518*0b56e9a7SVivek Gautam 519*0b56e9a7SVivek Gautam force_session_end = data->force_session_end; 520*0b56e9a7SVivek Gautam data->force_session_end = false; 521*0b56e9a7SVivek Gautam 522*0b56e9a7SVivek Gautam if (id_det != data->id_det) { 523*0b56e9a7SVivek Gautam /* id-change, force session end if we've no vbus detection */ 524*0b56e9a7SVivek Gautam if (data->dr_mode == USB_DR_MODE_OTG && 525*0b56e9a7SVivek Gautam !sun4i_usb_phy0_have_vbus_det(data)) 526*0b56e9a7SVivek Gautam force_session_end = true; 527*0b56e9a7SVivek Gautam 528*0b56e9a7SVivek Gautam /* When entering host mode (id = 0) force end the session now */ 529*0b56e9a7SVivek Gautam if (force_session_end && id_det == 0) { 530*0b56e9a7SVivek Gautam sun4i_usb_phy0_set_vbus_detect(phy0, 0); 531*0b56e9a7SVivek Gautam msleep(200); 532*0b56e9a7SVivek Gautam sun4i_usb_phy0_set_vbus_detect(phy0, 1); 533*0b56e9a7SVivek Gautam } 534*0b56e9a7SVivek Gautam sun4i_usb_phy0_set_id_detect(phy0, id_det); 535*0b56e9a7SVivek Gautam data->id_det = id_det; 536*0b56e9a7SVivek Gautam id_notify = true; 537*0b56e9a7SVivek Gautam } 538*0b56e9a7SVivek Gautam 539*0b56e9a7SVivek Gautam if (vbus_det != data->vbus_det) { 540*0b56e9a7SVivek Gautam sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det); 541*0b56e9a7SVivek Gautam data->vbus_det = vbus_det; 542*0b56e9a7SVivek Gautam vbus_notify = true; 543*0b56e9a7SVivek Gautam } 544*0b56e9a7SVivek Gautam 545*0b56e9a7SVivek Gautam mutex_unlock(&phy0->mutex); 546*0b56e9a7SVivek Gautam 547*0b56e9a7SVivek Gautam if (id_notify) { 548*0b56e9a7SVivek Gautam extcon_set_state_sync(data->extcon, EXTCON_USB_HOST, 549*0b56e9a7SVivek Gautam !id_det); 550*0b56e9a7SVivek Gautam /* When leaving host mode force end the session here */ 551*0b56e9a7SVivek Gautam if (force_session_end && id_det == 1) { 552*0b56e9a7SVivek Gautam mutex_lock(&phy0->mutex); 553*0b56e9a7SVivek Gautam sun4i_usb_phy0_set_vbus_detect(phy0, 0); 554*0b56e9a7SVivek Gautam msleep(1000); 555*0b56e9a7SVivek Gautam sun4i_usb_phy0_set_vbus_detect(phy0, 1); 556*0b56e9a7SVivek Gautam mutex_unlock(&phy0->mutex); 557*0b56e9a7SVivek Gautam } 558*0b56e9a7SVivek Gautam 559*0b56e9a7SVivek Gautam /* Re-route PHY0 if necessary */ 560*0b56e9a7SVivek Gautam if (data->cfg->phy0_dual_route) 561*0b56e9a7SVivek Gautam sun4i_usb_phy0_reroute(data, id_det); 562*0b56e9a7SVivek Gautam } 563*0b56e9a7SVivek Gautam 564*0b56e9a7SVivek Gautam if (vbus_notify) 565*0b56e9a7SVivek Gautam extcon_set_state_sync(data->extcon, EXTCON_USB, vbus_det); 566*0b56e9a7SVivek Gautam 567*0b56e9a7SVivek Gautam if (sun4i_usb_phy0_poll(data)) 568*0b56e9a7SVivek Gautam queue_delayed_work(system_wq, &data->detect, POLL_TIME); 569*0b56e9a7SVivek Gautam } 570*0b56e9a7SVivek Gautam 571*0b56e9a7SVivek Gautam static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id) 572*0b56e9a7SVivek Gautam { 573*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data = dev_id; 574*0b56e9a7SVivek Gautam 575*0b56e9a7SVivek Gautam /* vbus or id changed, let the pins settle and then scan them */ 576*0b56e9a7SVivek Gautam mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); 577*0b56e9a7SVivek Gautam 578*0b56e9a7SVivek Gautam return IRQ_HANDLED; 579*0b56e9a7SVivek Gautam } 580*0b56e9a7SVivek Gautam 581*0b56e9a7SVivek Gautam static int sun4i_usb_phy0_vbus_notify(struct notifier_block *nb, 582*0b56e9a7SVivek Gautam unsigned long val, void *v) 583*0b56e9a7SVivek Gautam { 584*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data = 585*0b56e9a7SVivek Gautam container_of(nb, struct sun4i_usb_phy_data, vbus_power_nb); 586*0b56e9a7SVivek Gautam struct power_supply *psy = v; 587*0b56e9a7SVivek Gautam 588*0b56e9a7SVivek Gautam /* Properties on the vbus_power_supply changed, scan vbus_det */ 589*0b56e9a7SVivek Gautam if (val == PSY_EVENT_PROP_CHANGED && psy == data->vbus_power_supply) 590*0b56e9a7SVivek Gautam mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); 591*0b56e9a7SVivek Gautam 592*0b56e9a7SVivek Gautam return NOTIFY_OK; 593*0b56e9a7SVivek Gautam } 594*0b56e9a7SVivek Gautam 595*0b56e9a7SVivek Gautam static struct phy *sun4i_usb_phy_xlate(struct device *dev, 596*0b56e9a7SVivek Gautam struct of_phandle_args *args) 597*0b56e9a7SVivek Gautam { 598*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); 599*0b56e9a7SVivek Gautam 600*0b56e9a7SVivek Gautam if (args->args[0] >= data->cfg->num_phys) 601*0b56e9a7SVivek Gautam return ERR_PTR(-ENODEV); 602*0b56e9a7SVivek Gautam 603*0b56e9a7SVivek Gautam return data->phys[args->args[0]].phy; 604*0b56e9a7SVivek Gautam } 605*0b56e9a7SVivek Gautam 606*0b56e9a7SVivek Gautam static int sun4i_usb_phy_remove(struct platform_device *pdev) 607*0b56e9a7SVivek Gautam { 608*0b56e9a7SVivek Gautam struct device *dev = &pdev->dev; 609*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); 610*0b56e9a7SVivek Gautam 611*0b56e9a7SVivek Gautam if (data->vbus_power_nb_registered) 612*0b56e9a7SVivek Gautam power_supply_unreg_notifier(&data->vbus_power_nb); 613*0b56e9a7SVivek Gautam if (data->id_det_irq > 0) 614*0b56e9a7SVivek Gautam devm_free_irq(dev, data->id_det_irq, data); 615*0b56e9a7SVivek Gautam if (data->vbus_det_irq > 0) 616*0b56e9a7SVivek Gautam devm_free_irq(dev, data->vbus_det_irq, data); 617*0b56e9a7SVivek Gautam 618*0b56e9a7SVivek Gautam cancel_delayed_work_sync(&data->detect); 619*0b56e9a7SVivek Gautam 620*0b56e9a7SVivek Gautam return 0; 621*0b56e9a7SVivek Gautam } 622*0b56e9a7SVivek Gautam 623*0b56e9a7SVivek Gautam static const unsigned int sun4i_usb_phy0_cable[] = { 624*0b56e9a7SVivek Gautam EXTCON_USB, 625*0b56e9a7SVivek Gautam EXTCON_USB_HOST, 626*0b56e9a7SVivek Gautam EXTCON_NONE, 627*0b56e9a7SVivek Gautam }; 628*0b56e9a7SVivek Gautam 629*0b56e9a7SVivek Gautam static int sun4i_usb_phy_probe(struct platform_device *pdev) 630*0b56e9a7SVivek Gautam { 631*0b56e9a7SVivek Gautam struct sun4i_usb_phy_data *data; 632*0b56e9a7SVivek Gautam struct device *dev = &pdev->dev; 633*0b56e9a7SVivek Gautam struct device_node *np = dev->of_node; 634*0b56e9a7SVivek Gautam struct phy_provider *phy_provider; 635*0b56e9a7SVivek Gautam struct resource *res; 636*0b56e9a7SVivek Gautam int i, ret; 637*0b56e9a7SVivek Gautam 638*0b56e9a7SVivek Gautam data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 639*0b56e9a7SVivek Gautam if (!data) 640*0b56e9a7SVivek Gautam return -ENOMEM; 641*0b56e9a7SVivek Gautam 642*0b56e9a7SVivek Gautam spin_lock_init(&data->reg_lock); 643*0b56e9a7SVivek Gautam INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan); 644*0b56e9a7SVivek Gautam dev_set_drvdata(dev, data); 645*0b56e9a7SVivek Gautam data->cfg = of_device_get_match_data(dev); 646*0b56e9a7SVivek Gautam if (!data->cfg) 647*0b56e9a7SVivek Gautam return -EINVAL; 648*0b56e9a7SVivek Gautam 649*0b56e9a7SVivek Gautam res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); 650*0b56e9a7SVivek Gautam data->base = devm_ioremap_resource(dev, res); 651*0b56e9a7SVivek Gautam if (IS_ERR(data->base)) 652*0b56e9a7SVivek Gautam return PTR_ERR(data->base); 653*0b56e9a7SVivek Gautam 654*0b56e9a7SVivek Gautam data->id_det_gpio = devm_gpiod_get_optional(dev, "usb0_id_det", 655*0b56e9a7SVivek Gautam GPIOD_IN); 656*0b56e9a7SVivek Gautam if (IS_ERR(data->id_det_gpio)) 657*0b56e9a7SVivek Gautam return PTR_ERR(data->id_det_gpio); 658*0b56e9a7SVivek Gautam 659*0b56e9a7SVivek Gautam data->vbus_det_gpio = devm_gpiod_get_optional(dev, "usb0_vbus_det", 660*0b56e9a7SVivek Gautam GPIOD_IN); 661*0b56e9a7SVivek Gautam if (IS_ERR(data->vbus_det_gpio)) 662*0b56e9a7SVivek Gautam return PTR_ERR(data->vbus_det_gpio); 663*0b56e9a7SVivek Gautam 664*0b56e9a7SVivek Gautam if (of_find_property(np, "usb0_vbus_power-supply", NULL)) { 665*0b56e9a7SVivek Gautam data->vbus_power_supply = devm_power_supply_get_by_phandle(dev, 666*0b56e9a7SVivek Gautam "usb0_vbus_power-supply"); 667*0b56e9a7SVivek Gautam if (IS_ERR(data->vbus_power_supply)) 668*0b56e9a7SVivek Gautam return PTR_ERR(data->vbus_power_supply); 669*0b56e9a7SVivek Gautam 670*0b56e9a7SVivek Gautam if (!data->vbus_power_supply) 671*0b56e9a7SVivek Gautam return -EPROBE_DEFER; 672*0b56e9a7SVivek Gautam } 673*0b56e9a7SVivek Gautam 674*0b56e9a7SVivek Gautam data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0); 675*0b56e9a7SVivek Gautam 676*0b56e9a7SVivek Gautam data->extcon = devm_extcon_dev_allocate(dev, sun4i_usb_phy0_cable); 677*0b56e9a7SVivek Gautam if (IS_ERR(data->extcon)) 678*0b56e9a7SVivek Gautam return PTR_ERR(data->extcon); 679*0b56e9a7SVivek Gautam 680*0b56e9a7SVivek Gautam ret = devm_extcon_dev_register(dev, data->extcon); 681*0b56e9a7SVivek Gautam if (ret) { 682*0b56e9a7SVivek Gautam dev_err(dev, "failed to register extcon: %d\n", ret); 683*0b56e9a7SVivek Gautam return ret; 684*0b56e9a7SVivek Gautam } 685*0b56e9a7SVivek Gautam 686*0b56e9a7SVivek Gautam for (i = 0; i < data->cfg->num_phys; i++) { 687*0b56e9a7SVivek Gautam struct sun4i_usb_phy *phy = data->phys + i; 688*0b56e9a7SVivek Gautam char name[16]; 689*0b56e9a7SVivek Gautam 690*0b56e9a7SVivek Gautam snprintf(name, sizeof(name), "usb%d_vbus", i); 691*0b56e9a7SVivek Gautam phy->vbus = devm_regulator_get_optional(dev, name); 692*0b56e9a7SVivek Gautam if (IS_ERR(phy->vbus)) { 693*0b56e9a7SVivek Gautam if (PTR_ERR(phy->vbus) == -EPROBE_DEFER) 694*0b56e9a7SVivek Gautam return -EPROBE_DEFER; 695*0b56e9a7SVivek Gautam phy->vbus = NULL; 696*0b56e9a7SVivek Gautam } 697*0b56e9a7SVivek Gautam 698*0b56e9a7SVivek Gautam if (data->cfg->dedicated_clocks) 699*0b56e9a7SVivek Gautam snprintf(name, sizeof(name), "usb%d_phy", i); 700*0b56e9a7SVivek Gautam else 701*0b56e9a7SVivek Gautam strlcpy(name, "usb_phy", sizeof(name)); 702*0b56e9a7SVivek Gautam 703*0b56e9a7SVivek Gautam phy->clk = devm_clk_get(dev, name); 704*0b56e9a7SVivek Gautam if (IS_ERR(phy->clk)) { 705*0b56e9a7SVivek Gautam dev_err(dev, "failed to get clock %s\n", name); 706*0b56e9a7SVivek Gautam return PTR_ERR(phy->clk); 707*0b56e9a7SVivek Gautam } 708*0b56e9a7SVivek Gautam 709*0b56e9a7SVivek Gautam snprintf(name, sizeof(name), "usb%d_reset", i); 710*0b56e9a7SVivek Gautam phy->reset = devm_reset_control_get(dev, name); 711*0b56e9a7SVivek Gautam if (IS_ERR(phy->reset)) { 712*0b56e9a7SVivek Gautam dev_err(dev, "failed to get reset %s\n", name); 713*0b56e9a7SVivek Gautam return PTR_ERR(phy->reset); 714*0b56e9a7SVivek Gautam } 715*0b56e9a7SVivek Gautam 716*0b56e9a7SVivek Gautam if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */ 717*0b56e9a7SVivek Gautam snprintf(name, sizeof(name), "pmu%d", i); 718*0b56e9a7SVivek Gautam res = platform_get_resource_byname(pdev, 719*0b56e9a7SVivek Gautam IORESOURCE_MEM, name); 720*0b56e9a7SVivek Gautam phy->pmu = devm_ioremap_resource(dev, res); 721*0b56e9a7SVivek Gautam if (IS_ERR(phy->pmu)) 722*0b56e9a7SVivek Gautam return PTR_ERR(phy->pmu); 723*0b56e9a7SVivek Gautam } 724*0b56e9a7SVivek Gautam 725*0b56e9a7SVivek Gautam phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops); 726*0b56e9a7SVivek Gautam if (IS_ERR(phy->phy)) { 727*0b56e9a7SVivek Gautam dev_err(dev, "failed to create PHY %d\n", i); 728*0b56e9a7SVivek Gautam return PTR_ERR(phy->phy); 729*0b56e9a7SVivek Gautam } 730*0b56e9a7SVivek Gautam 731*0b56e9a7SVivek Gautam phy->index = i; 732*0b56e9a7SVivek Gautam phy_set_drvdata(phy->phy, &data->phys[i]); 733*0b56e9a7SVivek Gautam } 734*0b56e9a7SVivek Gautam 735*0b56e9a7SVivek Gautam data->id_det_irq = gpiod_to_irq(data->id_det_gpio); 736*0b56e9a7SVivek Gautam if (data->id_det_irq > 0) { 737*0b56e9a7SVivek Gautam ret = devm_request_irq(dev, data->id_det_irq, 738*0b56e9a7SVivek Gautam sun4i_usb_phy0_id_vbus_det_irq, 739*0b56e9a7SVivek Gautam IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 740*0b56e9a7SVivek Gautam "usb0-id-det", data); 741*0b56e9a7SVivek Gautam if (ret) { 742*0b56e9a7SVivek Gautam dev_err(dev, "Err requesting id-det-irq: %d\n", ret); 743*0b56e9a7SVivek Gautam return ret; 744*0b56e9a7SVivek Gautam } 745*0b56e9a7SVivek Gautam } 746*0b56e9a7SVivek Gautam 747*0b56e9a7SVivek Gautam data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio); 748*0b56e9a7SVivek Gautam if (data->vbus_det_irq > 0) { 749*0b56e9a7SVivek Gautam ret = devm_request_irq(dev, data->vbus_det_irq, 750*0b56e9a7SVivek Gautam sun4i_usb_phy0_id_vbus_det_irq, 751*0b56e9a7SVivek Gautam IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 752*0b56e9a7SVivek Gautam "usb0-vbus-det", data); 753*0b56e9a7SVivek Gautam if (ret) { 754*0b56e9a7SVivek Gautam dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret); 755*0b56e9a7SVivek Gautam data->vbus_det_irq = -1; 756*0b56e9a7SVivek Gautam sun4i_usb_phy_remove(pdev); /* Stop detect work */ 757*0b56e9a7SVivek Gautam return ret; 758*0b56e9a7SVivek Gautam } 759*0b56e9a7SVivek Gautam } 760*0b56e9a7SVivek Gautam 761*0b56e9a7SVivek Gautam if (data->vbus_power_supply) { 762*0b56e9a7SVivek Gautam data->vbus_power_nb.notifier_call = sun4i_usb_phy0_vbus_notify; 763*0b56e9a7SVivek Gautam data->vbus_power_nb.priority = 0; 764*0b56e9a7SVivek Gautam ret = power_supply_reg_notifier(&data->vbus_power_nb); 765*0b56e9a7SVivek Gautam if (ret) { 766*0b56e9a7SVivek Gautam sun4i_usb_phy_remove(pdev); /* Stop detect work */ 767*0b56e9a7SVivek Gautam return ret; 768*0b56e9a7SVivek Gautam } 769*0b56e9a7SVivek Gautam data->vbus_power_nb_registered = true; 770*0b56e9a7SVivek Gautam } 771*0b56e9a7SVivek Gautam 772*0b56e9a7SVivek Gautam phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate); 773*0b56e9a7SVivek Gautam if (IS_ERR(phy_provider)) { 774*0b56e9a7SVivek Gautam sun4i_usb_phy_remove(pdev); /* Stop detect work */ 775*0b56e9a7SVivek Gautam return PTR_ERR(phy_provider); 776*0b56e9a7SVivek Gautam } 777*0b56e9a7SVivek Gautam 778*0b56e9a7SVivek Gautam return 0; 779*0b56e9a7SVivek Gautam } 780*0b56e9a7SVivek Gautam 781*0b56e9a7SVivek Gautam static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { 782*0b56e9a7SVivek Gautam .num_phys = 3, 783*0b56e9a7SVivek Gautam .type = sun4i_a10_phy, 784*0b56e9a7SVivek Gautam .disc_thresh = 3, 785*0b56e9a7SVivek Gautam .phyctl_offset = REG_PHYCTL_A10, 786*0b56e9a7SVivek Gautam .dedicated_clocks = false, 787*0b56e9a7SVivek Gautam .enable_pmu_unk1 = false, 788*0b56e9a7SVivek Gautam }; 789*0b56e9a7SVivek Gautam 790*0b56e9a7SVivek Gautam static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { 791*0b56e9a7SVivek Gautam .num_phys = 2, 792*0b56e9a7SVivek Gautam .type = sun4i_a10_phy, 793*0b56e9a7SVivek Gautam .disc_thresh = 2, 794*0b56e9a7SVivek Gautam .phyctl_offset = REG_PHYCTL_A10, 795*0b56e9a7SVivek Gautam .dedicated_clocks = false, 796*0b56e9a7SVivek Gautam .enable_pmu_unk1 = false, 797*0b56e9a7SVivek Gautam }; 798*0b56e9a7SVivek Gautam 799*0b56e9a7SVivek Gautam static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { 800*0b56e9a7SVivek Gautam .num_phys = 3, 801*0b56e9a7SVivek Gautam .type = sun6i_a31_phy, 802*0b56e9a7SVivek Gautam .disc_thresh = 3, 803*0b56e9a7SVivek Gautam .phyctl_offset = REG_PHYCTL_A10, 804*0b56e9a7SVivek Gautam .dedicated_clocks = true, 805*0b56e9a7SVivek Gautam .enable_pmu_unk1 = false, 806*0b56e9a7SVivek Gautam }; 807*0b56e9a7SVivek Gautam 808*0b56e9a7SVivek Gautam static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { 809*0b56e9a7SVivek Gautam .num_phys = 3, 810*0b56e9a7SVivek Gautam .type = sun4i_a10_phy, 811*0b56e9a7SVivek Gautam .disc_thresh = 2, 812*0b56e9a7SVivek Gautam .phyctl_offset = REG_PHYCTL_A10, 813*0b56e9a7SVivek Gautam .dedicated_clocks = false, 814*0b56e9a7SVivek Gautam .enable_pmu_unk1 = false, 815*0b56e9a7SVivek Gautam }; 816*0b56e9a7SVivek Gautam 817*0b56e9a7SVivek Gautam static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { 818*0b56e9a7SVivek Gautam .num_phys = 2, 819*0b56e9a7SVivek Gautam .type = sun4i_a10_phy, 820*0b56e9a7SVivek Gautam .disc_thresh = 3, 821*0b56e9a7SVivek Gautam .phyctl_offset = REG_PHYCTL_A10, 822*0b56e9a7SVivek Gautam .dedicated_clocks = true, 823*0b56e9a7SVivek Gautam .enable_pmu_unk1 = false, 824*0b56e9a7SVivek Gautam }; 825*0b56e9a7SVivek Gautam 826*0b56e9a7SVivek Gautam static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { 827*0b56e9a7SVivek Gautam .num_phys = 2, 828*0b56e9a7SVivek Gautam .type = sun8i_a33_phy, 829*0b56e9a7SVivek Gautam .disc_thresh = 3, 830*0b56e9a7SVivek Gautam .phyctl_offset = REG_PHYCTL_A33, 831*0b56e9a7SVivek Gautam .dedicated_clocks = true, 832*0b56e9a7SVivek Gautam .enable_pmu_unk1 = false, 833*0b56e9a7SVivek Gautam }; 834*0b56e9a7SVivek Gautam 835*0b56e9a7SVivek Gautam static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { 836*0b56e9a7SVivek Gautam .num_phys = 4, 837*0b56e9a7SVivek Gautam .type = sun8i_h3_phy, 838*0b56e9a7SVivek Gautam .disc_thresh = 3, 839*0b56e9a7SVivek Gautam .phyctl_offset = REG_PHYCTL_A33, 840*0b56e9a7SVivek Gautam .dedicated_clocks = true, 841*0b56e9a7SVivek Gautam .enable_pmu_unk1 = true, 842*0b56e9a7SVivek Gautam .phy0_dual_route = true, 843*0b56e9a7SVivek Gautam }; 844*0b56e9a7SVivek Gautam 845*0b56e9a7SVivek Gautam static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { 846*0b56e9a7SVivek Gautam .num_phys = 1, 847*0b56e9a7SVivek Gautam .type = sun8i_v3s_phy, 848*0b56e9a7SVivek Gautam .disc_thresh = 3, 849*0b56e9a7SVivek Gautam .phyctl_offset = REG_PHYCTL_A33, 850*0b56e9a7SVivek Gautam .dedicated_clocks = true, 851*0b56e9a7SVivek Gautam .enable_pmu_unk1 = true, 852*0b56e9a7SVivek Gautam }; 853*0b56e9a7SVivek Gautam 854*0b56e9a7SVivek Gautam static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { 855*0b56e9a7SVivek Gautam .num_phys = 2, 856*0b56e9a7SVivek Gautam .type = sun50i_a64_phy, 857*0b56e9a7SVivek Gautam .disc_thresh = 3, 858*0b56e9a7SVivek Gautam .phyctl_offset = REG_PHYCTL_A33, 859*0b56e9a7SVivek Gautam .dedicated_clocks = true, 860*0b56e9a7SVivek Gautam .enable_pmu_unk1 = true, 861*0b56e9a7SVivek Gautam .phy0_dual_route = true, 862*0b56e9a7SVivek Gautam }; 863*0b56e9a7SVivek Gautam 864*0b56e9a7SVivek Gautam static const struct of_device_id sun4i_usb_phy_of_match[] = { 865*0b56e9a7SVivek Gautam { .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg }, 866*0b56e9a7SVivek Gautam { .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg }, 867*0b56e9a7SVivek Gautam { .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg }, 868*0b56e9a7SVivek Gautam { .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg }, 869*0b56e9a7SVivek Gautam { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg }, 870*0b56e9a7SVivek Gautam { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg }, 871*0b56e9a7SVivek Gautam { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, 872*0b56e9a7SVivek Gautam { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, 873*0b56e9a7SVivek Gautam { .compatible = "allwinner,sun50i-a64-usb-phy", 874*0b56e9a7SVivek Gautam .data = &sun50i_a64_cfg}, 875*0b56e9a7SVivek Gautam { }, 876*0b56e9a7SVivek Gautam }; 877*0b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match); 878*0b56e9a7SVivek Gautam 879*0b56e9a7SVivek Gautam static struct platform_driver sun4i_usb_phy_driver = { 880*0b56e9a7SVivek Gautam .probe = sun4i_usb_phy_probe, 881*0b56e9a7SVivek Gautam .remove = sun4i_usb_phy_remove, 882*0b56e9a7SVivek Gautam .driver = { 883*0b56e9a7SVivek Gautam .of_match_table = sun4i_usb_phy_of_match, 884*0b56e9a7SVivek Gautam .name = "sun4i-usb-phy", 885*0b56e9a7SVivek Gautam } 886*0b56e9a7SVivek Gautam }; 887*0b56e9a7SVivek Gautam module_platform_driver(sun4i_usb_phy_driver); 888*0b56e9a7SVivek Gautam 889*0b56e9a7SVivek Gautam MODULE_DESCRIPTION("Allwinner sun4i USB phy driver"); 890*0b56e9a7SVivek Gautam MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 891*0b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2"); 892