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