1787f2454SRaviteja Garimella /* 2787f2454SRaviteja Garimella * Copyright (C) 2017 Broadcom 3787f2454SRaviteja Garimella * 4787f2454SRaviteja Garimella * This program is free software; you can redistribute it and/or 5787f2454SRaviteja Garimella * modify it under the terms of the GNU General Public License as 6787f2454SRaviteja Garimella * published by the Free Software Foundation version 2. 7787f2454SRaviteja Garimella * 8787f2454SRaviteja Garimella * This program is distributed "as is" WITHOUT ANY WARRANTY of any 9787f2454SRaviteja Garimella * kind, whether express or implied; without even the implied warranty 10787f2454SRaviteja Garimella * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11787f2454SRaviteja Garimella * GNU General Public License for more details. 12787f2454SRaviteja Garimella */ 13787f2454SRaviteja Garimella 14787f2454SRaviteja Garimella #include <linux/delay.h> 15787f2454SRaviteja Garimella #include <linux/extcon.h> 16787f2454SRaviteja Garimella #include <linux/gpio.h> 17787f2454SRaviteja Garimella #include <linux/gpio/consumer.h> 18787f2454SRaviteja Garimella #include <linux/init.h> 19787f2454SRaviteja Garimella #include <linux/interrupt.h> 20787f2454SRaviteja Garimella #include <linux/io.h> 21787f2454SRaviteja Garimella #include <linux/irq.h> 22787f2454SRaviteja Garimella #include <linux/mfd/syscon.h> 23787f2454SRaviteja Garimella #include <linux/module.h> 24787f2454SRaviteja Garimella #include <linux/of.h> 25787f2454SRaviteja Garimella #include <linux/of_address.h> 26787f2454SRaviteja Garimella #include <linux/phy/phy.h> 27787f2454SRaviteja Garimella #include <linux/platform_device.h> 28787f2454SRaviteja Garimella #include <linux/regmap.h> 29787f2454SRaviteja Garimella #include <linux/slab.h> 30787f2454SRaviteja Garimella #include <linux/workqueue.h> 31787f2454SRaviteja Garimella 32787f2454SRaviteja Garimella #define ICFG_DRD_AFE 0x0 33787f2454SRaviteja Garimella #define ICFG_MISC_STAT 0x18 34787f2454SRaviteja Garimella #define ICFG_DRD_P0CTL 0x1C 35787f2454SRaviteja Garimella #define ICFG_STRAP_CTRL 0x20 36787f2454SRaviteja Garimella #define ICFG_FSM_CTRL 0x24 37787f2454SRaviteja Garimella 38787f2454SRaviteja Garimella #define ICFG_DEV_BIT BIT(2) 39787f2454SRaviteja Garimella #define IDM_RST_BIT BIT(0) 40787f2454SRaviteja Garimella #define AFE_CORERDY_VDDC BIT(18) 41787f2454SRaviteja Garimella #define PHY_PLL_RESETB BIT(15) 42787f2454SRaviteja Garimella #define PHY_RESETB BIT(14) 43787f2454SRaviteja Garimella #define PHY_PLL_LOCK BIT(0) 44787f2454SRaviteja Garimella 45787f2454SRaviteja Garimella #define DRD_DEV_MODE BIT(20) 46787f2454SRaviteja Garimella #define OHCI_OVRCUR_POL BIT(11) 47787f2454SRaviteja Garimella #define ICFG_OFF_MODE BIT(6) 48787f2454SRaviteja Garimella #define PLL_LOCK_RETRY 1000 49787f2454SRaviteja Garimella 50787f2454SRaviteja Garimella #define EVT_DEVICE 0 51787f2454SRaviteja Garimella #define EVT_HOST 1 52787f2454SRaviteja Garimella 53787f2454SRaviteja Garimella #define DRD_HOST_MODE (BIT(2) | BIT(3)) 54787f2454SRaviteja Garimella #define DRD_DEVICE_MODE (BIT(4) | BIT(5)) 55787f2454SRaviteja Garimella #define DRD_HOST_VAL 0x803 56787f2454SRaviteja Garimella #define DRD_DEV_VAL 0x807 57787f2454SRaviteja Garimella #define GPIO_DELAY 20 58787f2454SRaviteja Garimella 59787f2454SRaviteja Garimella struct ns2_phy_data; 60787f2454SRaviteja Garimella struct ns2_phy_driver { 61787f2454SRaviteja Garimella void __iomem *icfgdrd_regs; 62787f2454SRaviteja Garimella void __iomem *idmdrd_rst_ctrl; 63787f2454SRaviteja Garimella void __iomem *crmu_usb2_ctrl; 64787f2454SRaviteja Garimella void __iomem *usb2h_strap_reg; 65787f2454SRaviteja Garimella struct ns2_phy_data *data; 66787f2454SRaviteja Garimella struct extcon_dev *edev; 67787f2454SRaviteja Garimella struct gpio_desc *vbus_gpiod; 68787f2454SRaviteja Garimella struct gpio_desc *id_gpiod; 69787f2454SRaviteja Garimella int id_irq; 70787f2454SRaviteja Garimella int vbus_irq; 71787f2454SRaviteja Garimella unsigned long debounce_jiffies; 72787f2454SRaviteja Garimella struct delayed_work wq_extcon; 73787f2454SRaviteja Garimella }; 74787f2454SRaviteja Garimella 75787f2454SRaviteja Garimella struct ns2_phy_data { 76787f2454SRaviteja Garimella struct ns2_phy_driver *driver; 77787f2454SRaviteja Garimella struct phy *phy; 78787f2454SRaviteja Garimella int new_state; 79787f2454SRaviteja Garimella }; 80787f2454SRaviteja Garimella 81787f2454SRaviteja Garimella static const unsigned int usb_extcon_cable[] = { 82787f2454SRaviteja Garimella EXTCON_USB, 83787f2454SRaviteja Garimella EXTCON_USB_HOST, 84787f2454SRaviteja Garimella EXTCON_NONE, 85787f2454SRaviteja Garimella }; 86787f2454SRaviteja Garimella 87787f2454SRaviteja Garimella static inline int pll_lock_stat(u32 usb_reg, int reg_mask, 88787f2454SRaviteja Garimella struct ns2_phy_driver *driver) 89787f2454SRaviteja Garimella { 90787f2454SRaviteja Garimella int retry = PLL_LOCK_RETRY; 91787f2454SRaviteja Garimella u32 val; 92787f2454SRaviteja Garimella 93787f2454SRaviteja Garimella do { 94787f2454SRaviteja Garimella udelay(1); 95787f2454SRaviteja Garimella val = readl(driver->icfgdrd_regs + usb_reg); 96787f2454SRaviteja Garimella if (val & reg_mask) 97787f2454SRaviteja Garimella return 0; 98787f2454SRaviteja Garimella } while (--retry > 0); 99787f2454SRaviteja Garimella 100787f2454SRaviteja Garimella return -EBUSY; 101787f2454SRaviteja Garimella } 102787f2454SRaviteja Garimella 103787f2454SRaviteja Garimella static int ns2_drd_phy_init(struct phy *phy) 104787f2454SRaviteja Garimella { 105787f2454SRaviteja Garimella struct ns2_phy_data *data = phy_get_drvdata(phy); 106787f2454SRaviteja Garimella struct ns2_phy_driver *driver = data->driver; 107787f2454SRaviteja Garimella u32 val; 108787f2454SRaviteja Garimella 109787f2454SRaviteja Garimella val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL); 110787f2454SRaviteja Garimella 111787f2454SRaviteja Garimella if (data->new_state == EVT_HOST) { 112787f2454SRaviteja Garimella val &= ~DRD_DEVICE_MODE; 113787f2454SRaviteja Garimella val |= DRD_HOST_MODE; 114787f2454SRaviteja Garimella } else { 115787f2454SRaviteja Garimella val &= ~DRD_HOST_MODE; 116787f2454SRaviteja Garimella val |= DRD_DEVICE_MODE; 117787f2454SRaviteja Garimella } 118787f2454SRaviteja Garimella writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 119787f2454SRaviteja Garimella 120787f2454SRaviteja Garimella return 0; 121787f2454SRaviteja Garimella } 122787f2454SRaviteja Garimella 123787f2454SRaviteja Garimella static int ns2_drd_phy_poweroff(struct phy *phy) 124787f2454SRaviteja Garimella { 125787f2454SRaviteja Garimella struct ns2_phy_data *data = phy_get_drvdata(phy); 126787f2454SRaviteja Garimella struct ns2_phy_driver *driver = data->driver; 127787f2454SRaviteja Garimella u32 val; 128787f2454SRaviteja Garimella 129787f2454SRaviteja Garimella val = readl(driver->crmu_usb2_ctrl); 130787f2454SRaviteja Garimella val &= ~AFE_CORERDY_VDDC; 131787f2454SRaviteja Garimella writel(val, driver->crmu_usb2_ctrl); 132787f2454SRaviteja Garimella 133787f2454SRaviteja Garimella val = readl(driver->crmu_usb2_ctrl); 134787f2454SRaviteja Garimella val &= ~DRD_DEV_MODE; 135787f2454SRaviteja Garimella writel(val, driver->crmu_usb2_ctrl); 136787f2454SRaviteja Garimella 137787f2454SRaviteja Garimella /* Disable Host and Device Mode */ 138787f2454SRaviteja Garimella val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL); 139787f2454SRaviteja Garimella val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE | ICFG_OFF_MODE); 140787f2454SRaviteja Garimella writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 141787f2454SRaviteja Garimella 142787f2454SRaviteja Garimella return 0; 143787f2454SRaviteja Garimella } 144787f2454SRaviteja Garimella 145787f2454SRaviteja Garimella static int ns2_drd_phy_poweron(struct phy *phy) 146787f2454SRaviteja Garimella { 147787f2454SRaviteja Garimella struct ns2_phy_data *data = phy_get_drvdata(phy); 148787f2454SRaviteja Garimella struct ns2_phy_driver *driver = data->driver; 149787f2454SRaviteja Garimella u32 extcon_event = data->new_state; 150787f2454SRaviteja Garimella int ret; 151787f2454SRaviteja Garimella u32 val; 152787f2454SRaviteja Garimella 153787f2454SRaviteja Garimella if (extcon_event == EVT_DEVICE) { 154787f2454SRaviteja Garimella writel(DRD_DEV_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL); 155787f2454SRaviteja Garimella 156787f2454SRaviteja Garimella val = readl(driver->idmdrd_rst_ctrl); 157787f2454SRaviteja Garimella val &= ~IDM_RST_BIT; 158787f2454SRaviteja Garimella writel(val, driver->idmdrd_rst_ctrl); 159787f2454SRaviteja Garimella 160787f2454SRaviteja Garimella val = readl(driver->crmu_usb2_ctrl); 161787f2454SRaviteja Garimella val |= (AFE_CORERDY_VDDC | DRD_DEV_MODE); 162787f2454SRaviteja Garimella writel(val, driver->crmu_usb2_ctrl); 163787f2454SRaviteja Garimella 164787f2454SRaviteja Garimella /* Bring PHY and PHY_PLL out of Reset */ 165787f2454SRaviteja Garimella val = readl(driver->crmu_usb2_ctrl); 166787f2454SRaviteja Garimella val |= (PHY_PLL_RESETB | PHY_RESETB); 167787f2454SRaviteja Garimella writel(val, driver->crmu_usb2_ctrl); 168787f2454SRaviteja Garimella 169787f2454SRaviteja Garimella ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver); 170787f2454SRaviteja Garimella if (ret < 0) { 171787f2454SRaviteja Garimella dev_err(&phy->dev, "Phy PLL lock failed\n"); 172787f2454SRaviteja Garimella return ret; 173787f2454SRaviteja Garimella } 174787f2454SRaviteja Garimella } else { 175787f2454SRaviteja Garimella writel(DRD_HOST_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL); 176787f2454SRaviteja Garimella 177787f2454SRaviteja Garimella val = readl(driver->crmu_usb2_ctrl); 178787f2454SRaviteja Garimella val |= AFE_CORERDY_VDDC; 179787f2454SRaviteja Garimella writel(val, driver->crmu_usb2_ctrl); 180787f2454SRaviteja Garimella 181787f2454SRaviteja Garimella ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver); 182787f2454SRaviteja Garimella if (ret < 0) { 183787f2454SRaviteja Garimella dev_err(&phy->dev, "Phy PLL lock failed\n"); 184787f2454SRaviteja Garimella return ret; 185787f2454SRaviteja Garimella } 186787f2454SRaviteja Garimella 187787f2454SRaviteja Garimella val = readl(driver->idmdrd_rst_ctrl); 188787f2454SRaviteja Garimella val &= ~IDM_RST_BIT; 189787f2454SRaviteja Garimella writel(val, driver->idmdrd_rst_ctrl); 190787f2454SRaviteja Garimella 191787f2454SRaviteja Garimella /* port over current Polarity */ 192787f2454SRaviteja Garimella val = readl(driver->usb2h_strap_reg); 193787f2454SRaviteja Garimella val |= OHCI_OVRCUR_POL; 194787f2454SRaviteja Garimella writel(val, driver->usb2h_strap_reg); 195787f2454SRaviteja Garimella } 196787f2454SRaviteja Garimella 197787f2454SRaviteja Garimella return 0; 198787f2454SRaviteja Garimella } 199787f2454SRaviteja Garimella 200787f2454SRaviteja Garimella static void connect_change(struct ns2_phy_driver *driver) 201787f2454SRaviteja Garimella { 202787f2454SRaviteja Garimella u32 extcon_event; 203787f2454SRaviteja Garimella u32 val; 204787f2454SRaviteja Garimella 205787f2454SRaviteja Garimella extcon_event = driver->data->new_state; 206787f2454SRaviteja Garimella val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL); 207787f2454SRaviteja Garimella 208787f2454SRaviteja Garimella switch (extcon_event) { 209787f2454SRaviteja Garimella case EVT_DEVICE: 210787f2454SRaviteja Garimella val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE); 211787f2454SRaviteja Garimella writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 212787f2454SRaviteja Garimella 213787f2454SRaviteja Garimella val = (val & ~DRD_HOST_MODE) | DRD_DEVICE_MODE; 214787f2454SRaviteja Garimella writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 215787f2454SRaviteja Garimella 216787f2454SRaviteja Garimella val = readl(driver->icfgdrd_regs + ICFG_DRD_P0CTL); 217787f2454SRaviteja Garimella val |= ICFG_DEV_BIT; 218787f2454SRaviteja Garimella writel(val, driver->icfgdrd_regs + ICFG_DRD_P0CTL); 219787f2454SRaviteja Garimella break; 220787f2454SRaviteja Garimella 221787f2454SRaviteja Garimella case EVT_HOST: 222787f2454SRaviteja Garimella val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE); 223787f2454SRaviteja Garimella writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 224787f2454SRaviteja Garimella 225787f2454SRaviteja Garimella val = (val & ~DRD_DEVICE_MODE) | DRD_HOST_MODE; 226787f2454SRaviteja Garimella writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 227787f2454SRaviteja Garimella 228787f2454SRaviteja Garimella val = readl(driver->usb2h_strap_reg); 229787f2454SRaviteja Garimella val |= OHCI_OVRCUR_POL; 230787f2454SRaviteja Garimella writel(val, driver->usb2h_strap_reg); 231787f2454SRaviteja Garimella 232787f2454SRaviteja Garimella val = readl(driver->icfgdrd_regs + ICFG_DRD_P0CTL); 233787f2454SRaviteja Garimella val &= ~ICFG_DEV_BIT; 234787f2454SRaviteja Garimella writel(val, driver->icfgdrd_regs + ICFG_DRD_P0CTL); 235787f2454SRaviteja Garimella break; 236787f2454SRaviteja Garimella 237787f2454SRaviteja Garimella default: 238787f2454SRaviteja Garimella pr_err("Invalid extcon event\n"); 239787f2454SRaviteja Garimella break; 240787f2454SRaviteja Garimella } 241787f2454SRaviteja Garimella } 242787f2454SRaviteja Garimella 243787f2454SRaviteja Garimella static void extcon_work(struct work_struct *work) 244787f2454SRaviteja Garimella { 245787f2454SRaviteja Garimella struct ns2_phy_driver *driver; 246787f2454SRaviteja Garimella int vbus; 247787f2454SRaviteja Garimella int id; 248787f2454SRaviteja Garimella 249787f2454SRaviteja Garimella driver = container_of(to_delayed_work(work), 250787f2454SRaviteja Garimella struct ns2_phy_driver, wq_extcon); 251787f2454SRaviteja Garimella 252787f2454SRaviteja Garimella id = gpiod_get_value_cansleep(driver->id_gpiod); 253787f2454SRaviteja Garimella vbus = gpiod_get_value_cansleep(driver->vbus_gpiod); 254787f2454SRaviteja Garimella 255787f2454SRaviteja Garimella if (!id && vbus) { /* Host connected */ 256*02026de8SChanwoo Choi extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, true); 257787f2454SRaviteja Garimella pr_debug("Host cable connected\n"); 258787f2454SRaviteja Garimella driver->data->new_state = EVT_HOST; 259787f2454SRaviteja Garimella connect_change(driver); 260787f2454SRaviteja Garimella } else if (id && !vbus) { /* Disconnected */ 261*02026de8SChanwoo Choi extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, false); 262*02026de8SChanwoo Choi extcon_set_state_sync(driver->edev, EXTCON_USB, false); 263787f2454SRaviteja Garimella pr_debug("Cable disconnected\n"); 264787f2454SRaviteja Garimella } else if (id && vbus) { /* Device connected */ 265*02026de8SChanwoo Choi extcon_set_state_sync(driver->edev, EXTCON_USB, true); 266787f2454SRaviteja Garimella pr_debug("Device cable connected\n"); 267787f2454SRaviteja Garimella driver->data->new_state = EVT_DEVICE; 268787f2454SRaviteja Garimella connect_change(driver); 269787f2454SRaviteja Garimella } 270787f2454SRaviteja Garimella } 271787f2454SRaviteja Garimella 272787f2454SRaviteja Garimella static irqreturn_t gpio_irq_handler(int irq, void *dev_id) 273787f2454SRaviteja Garimella { 274787f2454SRaviteja Garimella struct ns2_phy_driver *driver = dev_id; 275787f2454SRaviteja Garimella 276787f2454SRaviteja Garimella queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon, 277787f2454SRaviteja Garimella driver->debounce_jiffies); 278787f2454SRaviteja Garimella 279787f2454SRaviteja Garimella return IRQ_HANDLED; 280787f2454SRaviteja Garimella } 281787f2454SRaviteja Garimella 282787f2454SRaviteja Garimella static struct phy_ops ops = { 283787f2454SRaviteja Garimella .init = ns2_drd_phy_init, 284787f2454SRaviteja Garimella .power_on = ns2_drd_phy_poweron, 285787f2454SRaviteja Garimella .power_off = ns2_drd_phy_poweroff, 286787f2454SRaviteja Garimella .owner = THIS_MODULE, 287787f2454SRaviteja Garimella }; 288787f2454SRaviteja Garimella 289787f2454SRaviteja Garimella static const struct of_device_id ns2_drd_phy_dt_ids[] = { 290787f2454SRaviteja Garimella { .compatible = "brcm,ns2-drd-phy", }, 291787f2454SRaviteja Garimella { } 292787f2454SRaviteja Garimella }; 293787f2454SRaviteja Garimella MODULE_DEVICE_TABLE(of, ns2_drd_phy_dt_ids); 294787f2454SRaviteja Garimella 295787f2454SRaviteja Garimella static int ns2_drd_phy_probe(struct platform_device *pdev) 296787f2454SRaviteja Garimella { 297787f2454SRaviteja Garimella struct phy_provider *phy_provider; 298787f2454SRaviteja Garimella struct device *dev = &pdev->dev; 299787f2454SRaviteja Garimella struct ns2_phy_driver *driver; 300787f2454SRaviteja Garimella struct ns2_phy_data *data; 301787f2454SRaviteja Garimella struct resource *res; 302787f2454SRaviteja Garimella int ret; 303787f2454SRaviteja Garimella u32 val; 304787f2454SRaviteja Garimella 305787f2454SRaviteja Garimella driver = devm_kzalloc(dev, sizeof(struct ns2_phy_driver), 306787f2454SRaviteja Garimella GFP_KERNEL); 307787f2454SRaviteja Garimella if (!driver) 308787f2454SRaviteja Garimella return -ENOMEM; 309787f2454SRaviteja Garimella 310787f2454SRaviteja Garimella driver->data = devm_kzalloc(dev, sizeof(struct ns2_phy_data), 311787f2454SRaviteja Garimella GFP_KERNEL); 312787f2454SRaviteja Garimella if (!driver->data) 313787f2454SRaviteja Garimella return -ENOMEM; 314787f2454SRaviteja Garimella 315787f2454SRaviteja Garimella res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "icfg"); 316787f2454SRaviteja Garimella driver->icfgdrd_regs = devm_ioremap_resource(dev, res); 317787f2454SRaviteja Garimella if (IS_ERR(driver->icfgdrd_regs)) 318787f2454SRaviteja Garimella return PTR_ERR(driver->icfgdrd_regs); 319787f2454SRaviteja Garimella 320787f2454SRaviteja Garimella res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rst-ctrl"); 321787f2454SRaviteja Garimella driver->idmdrd_rst_ctrl = devm_ioremap_resource(dev, res); 322787f2454SRaviteja Garimella if (IS_ERR(driver->idmdrd_rst_ctrl)) 323787f2454SRaviteja Garimella return PTR_ERR(driver->idmdrd_rst_ctrl); 324787f2454SRaviteja Garimella 325787f2454SRaviteja Garimella res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "crmu-ctrl"); 326787f2454SRaviteja Garimella driver->crmu_usb2_ctrl = devm_ioremap_resource(dev, res); 327787f2454SRaviteja Garimella if (IS_ERR(driver->crmu_usb2_ctrl)) 328787f2454SRaviteja Garimella return PTR_ERR(driver->crmu_usb2_ctrl); 329787f2454SRaviteja Garimella 330787f2454SRaviteja Garimella res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2-strap"); 331787f2454SRaviteja Garimella driver->usb2h_strap_reg = devm_ioremap_resource(dev, res); 332787f2454SRaviteja Garimella if (IS_ERR(driver->usb2h_strap_reg)) 333787f2454SRaviteja Garimella return PTR_ERR(driver->usb2h_strap_reg); 334787f2454SRaviteja Garimella 335787f2454SRaviteja Garimella /* create extcon */ 336787f2454SRaviteja Garimella driver->id_gpiod = devm_gpiod_get(&pdev->dev, "id", GPIOD_IN); 337787f2454SRaviteja Garimella if (IS_ERR(driver->id_gpiod)) { 338787f2454SRaviteja Garimella dev_err(dev, "failed to get ID GPIO\n"); 339787f2454SRaviteja Garimella return PTR_ERR(driver->id_gpiod); 340787f2454SRaviteja Garimella } 341787f2454SRaviteja Garimella driver->vbus_gpiod = devm_gpiod_get(&pdev->dev, "vbus", GPIOD_IN); 342787f2454SRaviteja Garimella if (IS_ERR(driver->vbus_gpiod)) { 343787f2454SRaviteja Garimella dev_err(dev, "failed to get VBUS GPIO\n"); 344787f2454SRaviteja Garimella return PTR_ERR(driver->vbus_gpiod); 345787f2454SRaviteja Garimella } 346787f2454SRaviteja Garimella 347787f2454SRaviteja Garimella driver->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable); 348787f2454SRaviteja Garimella if (IS_ERR(driver->edev)) { 349787f2454SRaviteja Garimella dev_err(dev, "failed to allocate extcon device\n"); 350787f2454SRaviteja Garimella return -ENOMEM; 351787f2454SRaviteja Garimella } 352787f2454SRaviteja Garimella 353787f2454SRaviteja Garimella ret = devm_extcon_dev_register(dev, driver->edev); 354787f2454SRaviteja Garimella if (ret < 0) { 355787f2454SRaviteja Garimella dev_err(dev, "failed to register extcon device\n"); 356787f2454SRaviteja Garimella return ret; 357787f2454SRaviteja Garimella } 358787f2454SRaviteja Garimella 359787f2454SRaviteja Garimella ret = gpiod_set_debounce(driver->id_gpiod, GPIO_DELAY * 1000); 360787f2454SRaviteja Garimella if (ret < 0) 361787f2454SRaviteja Garimella driver->debounce_jiffies = msecs_to_jiffies(GPIO_DELAY); 362787f2454SRaviteja Garimella 363787f2454SRaviteja Garimella INIT_DELAYED_WORK(&driver->wq_extcon, extcon_work); 364787f2454SRaviteja Garimella 365787f2454SRaviteja Garimella driver->id_irq = gpiod_to_irq(driver->id_gpiod); 366787f2454SRaviteja Garimella if (driver->id_irq < 0) { 367787f2454SRaviteja Garimella dev_err(dev, "failed to get ID IRQ\n"); 368787f2454SRaviteja Garimella return driver->id_irq; 369787f2454SRaviteja Garimella } 370787f2454SRaviteja Garimella 371787f2454SRaviteja Garimella driver->vbus_irq = gpiod_to_irq(driver->vbus_gpiod); 372787f2454SRaviteja Garimella if (driver->vbus_irq < 0) { 373787f2454SRaviteja Garimella dev_err(dev, "failed to get ID IRQ\n"); 374787f2454SRaviteja Garimella return driver->vbus_irq; 375787f2454SRaviteja Garimella } 376787f2454SRaviteja Garimella 377787f2454SRaviteja Garimella ret = devm_request_irq(dev, driver->id_irq, gpio_irq_handler, 378787f2454SRaviteja Garimella IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 379787f2454SRaviteja Garimella "usb_id", driver); 380787f2454SRaviteja Garimella if (ret < 0) { 381787f2454SRaviteja Garimella dev_err(dev, "failed to request handler for ID IRQ\n"); 382787f2454SRaviteja Garimella return ret; 383787f2454SRaviteja Garimella } 384787f2454SRaviteja Garimella 385787f2454SRaviteja Garimella ret = devm_request_irq(dev, driver->vbus_irq, gpio_irq_handler, 386787f2454SRaviteja Garimella IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 387787f2454SRaviteja Garimella "usb_vbus", driver); 388787f2454SRaviteja Garimella if (ret < 0) { 389787f2454SRaviteja Garimella dev_err(dev, "failed to request handler for VBUS IRQ\n"); 390787f2454SRaviteja Garimella return ret; 391787f2454SRaviteja Garimella } 392787f2454SRaviteja Garimella 393787f2454SRaviteja Garimella dev_set_drvdata(dev, driver); 394787f2454SRaviteja Garimella 395787f2454SRaviteja Garimella /* Shutdown all ports. They can be powered up as required */ 396787f2454SRaviteja Garimella val = readl(driver->crmu_usb2_ctrl); 397787f2454SRaviteja Garimella val &= ~(AFE_CORERDY_VDDC | PHY_RESETB); 398787f2454SRaviteja Garimella writel(val, driver->crmu_usb2_ctrl); 399787f2454SRaviteja Garimella 400787f2454SRaviteja Garimella data = driver->data; 401787f2454SRaviteja Garimella data->phy = devm_phy_create(dev, dev->of_node, &ops); 402787f2454SRaviteja Garimella if (IS_ERR(data->phy)) { 403787f2454SRaviteja Garimella dev_err(dev, "Failed to create usb drd phy\n"); 404787f2454SRaviteja Garimella return PTR_ERR(data->phy); 405787f2454SRaviteja Garimella } 406787f2454SRaviteja Garimella 407787f2454SRaviteja Garimella data->driver = driver; 408787f2454SRaviteja Garimella phy_set_drvdata(data->phy, data); 409787f2454SRaviteja Garimella 410787f2454SRaviteja Garimella phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 411787f2454SRaviteja Garimella if (IS_ERR(phy_provider)) { 412787f2454SRaviteja Garimella dev_err(dev, "Failed to register as phy provider\n"); 413787f2454SRaviteja Garimella return PTR_ERR(phy_provider); 414787f2454SRaviteja Garimella } 415787f2454SRaviteja Garimella 416787f2454SRaviteja Garimella platform_set_drvdata(pdev, driver); 417787f2454SRaviteja Garimella 418787f2454SRaviteja Garimella dev_info(dev, "Registered NS2 DRD Phy device\n"); 419787f2454SRaviteja Garimella queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon, 420787f2454SRaviteja Garimella driver->debounce_jiffies); 421787f2454SRaviteja Garimella 422787f2454SRaviteja Garimella return 0; 423787f2454SRaviteja Garimella } 424787f2454SRaviteja Garimella 425787f2454SRaviteja Garimella static struct platform_driver ns2_drd_phy_driver = { 426787f2454SRaviteja Garimella .probe = ns2_drd_phy_probe, 427787f2454SRaviteja Garimella .driver = { 428787f2454SRaviteja Garimella .name = "bcm-ns2-usbphy", 429787f2454SRaviteja Garimella .of_match_table = of_match_ptr(ns2_drd_phy_dt_ids), 430787f2454SRaviteja Garimella }, 431787f2454SRaviteja Garimella }; 432787f2454SRaviteja Garimella module_platform_driver(ns2_drd_phy_driver); 433787f2454SRaviteja Garimella 434787f2454SRaviteja Garimella MODULE_ALIAS("platform:bcm-ns2-drd-phy"); 435787f2454SRaviteja Garimella MODULE_AUTHOR("Broadcom"); 436787f2454SRaviteja Garimella MODULE_DESCRIPTION("Broadcom NS2 USB2 PHY driver"); 437787f2454SRaviteja Garimella MODULE_LICENSE("GPL v2"); 438