18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2017 Broadcom 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci * GNU General Public License for more details. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/extcon-provider.h> 168c2ecf20Sopenharmony_ci#include <linux/gpio.h> 178c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 228c2ecf20Sopenharmony_ci#include <linux/irq.h> 238c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/of.h> 268c2ecf20Sopenharmony_ci#include <linux/of_address.h> 278c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 288c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 298c2ecf20Sopenharmony_ci#include <linux/regmap.h> 308c2ecf20Sopenharmony_ci#include <linux/slab.h> 318c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define ICFG_DRD_AFE 0x0 348c2ecf20Sopenharmony_ci#define ICFG_MISC_STAT 0x18 358c2ecf20Sopenharmony_ci#define ICFG_DRD_P0CTL 0x1C 368c2ecf20Sopenharmony_ci#define ICFG_STRAP_CTRL 0x20 378c2ecf20Sopenharmony_ci#define ICFG_FSM_CTRL 0x24 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define ICFG_DEV_BIT BIT(2) 408c2ecf20Sopenharmony_ci#define IDM_RST_BIT BIT(0) 418c2ecf20Sopenharmony_ci#define AFE_CORERDY_VDDC BIT(18) 428c2ecf20Sopenharmony_ci#define PHY_PLL_RESETB BIT(15) 438c2ecf20Sopenharmony_ci#define PHY_RESETB BIT(14) 448c2ecf20Sopenharmony_ci#define PHY_PLL_LOCK BIT(0) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define DRD_DEV_MODE BIT(20) 478c2ecf20Sopenharmony_ci#define OHCI_OVRCUR_POL BIT(11) 488c2ecf20Sopenharmony_ci#define ICFG_OFF_MODE BIT(6) 498c2ecf20Sopenharmony_ci#define PLL_LOCK_RETRY 1000 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define EVT_DEVICE 0 528c2ecf20Sopenharmony_ci#define EVT_HOST 1 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define DRD_HOST_MODE (BIT(2) | BIT(3)) 558c2ecf20Sopenharmony_ci#define DRD_DEVICE_MODE (BIT(4) | BIT(5)) 568c2ecf20Sopenharmony_ci#define DRD_HOST_VAL 0x803 578c2ecf20Sopenharmony_ci#define DRD_DEV_VAL 0x807 588c2ecf20Sopenharmony_ci#define GPIO_DELAY 20 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct ns2_phy_data; 618c2ecf20Sopenharmony_cistruct ns2_phy_driver { 628c2ecf20Sopenharmony_ci void __iomem *icfgdrd_regs; 638c2ecf20Sopenharmony_ci void __iomem *idmdrd_rst_ctrl; 648c2ecf20Sopenharmony_ci void __iomem *crmu_usb2_ctrl; 658c2ecf20Sopenharmony_ci void __iomem *usb2h_strap_reg; 668c2ecf20Sopenharmony_ci struct ns2_phy_data *data; 678c2ecf20Sopenharmony_ci struct extcon_dev *edev; 688c2ecf20Sopenharmony_ci struct gpio_desc *vbus_gpiod; 698c2ecf20Sopenharmony_ci struct gpio_desc *id_gpiod; 708c2ecf20Sopenharmony_ci int id_irq; 718c2ecf20Sopenharmony_ci int vbus_irq; 728c2ecf20Sopenharmony_ci unsigned long debounce_jiffies; 738c2ecf20Sopenharmony_ci struct delayed_work wq_extcon; 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistruct ns2_phy_data { 778c2ecf20Sopenharmony_ci struct ns2_phy_driver *driver; 788c2ecf20Sopenharmony_ci struct phy *phy; 798c2ecf20Sopenharmony_ci int new_state; 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic const unsigned int usb_extcon_cable[] = { 838c2ecf20Sopenharmony_ci EXTCON_USB, 848c2ecf20Sopenharmony_ci EXTCON_USB_HOST, 858c2ecf20Sopenharmony_ci EXTCON_NONE, 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic inline int pll_lock_stat(u32 usb_reg, int reg_mask, 898c2ecf20Sopenharmony_ci struct ns2_phy_driver *driver) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci u32 val; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return readl_poll_timeout_atomic(driver->icfgdrd_regs + usb_reg, 948c2ecf20Sopenharmony_ci val, (val & reg_mask), 1, 958c2ecf20Sopenharmony_ci PLL_LOCK_RETRY); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int ns2_drd_phy_init(struct phy *phy) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct ns2_phy_data *data = phy_get_drvdata(phy); 1018c2ecf20Sopenharmony_ci struct ns2_phy_driver *driver = data->driver; 1028c2ecf20Sopenharmony_ci u32 val; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (data->new_state == EVT_HOST) { 1078c2ecf20Sopenharmony_ci val &= ~DRD_DEVICE_MODE; 1088c2ecf20Sopenharmony_ci val |= DRD_HOST_MODE; 1098c2ecf20Sopenharmony_ci } else { 1108c2ecf20Sopenharmony_ci val &= ~DRD_HOST_MODE; 1118c2ecf20Sopenharmony_ci val |= DRD_DEVICE_MODE; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int ns2_drd_phy_poweroff(struct phy *phy) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct ns2_phy_data *data = phy_get_drvdata(phy); 1218c2ecf20Sopenharmony_ci struct ns2_phy_driver *driver = data->driver; 1228c2ecf20Sopenharmony_ci u32 val; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci val = readl(driver->crmu_usb2_ctrl); 1258c2ecf20Sopenharmony_ci val &= ~AFE_CORERDY_VDDC; 1268c2ecf20Sopenharmony_ci writel(val, driver->crmu_usb2_ctrl); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci val = readl(driver->crmu_usb2_ctrl); 1298c2ecf20Sopenharmony_ci val &= ~DRD_DEV_MODE; 1308c2ecf20Sopenharmony_ci writel(val, driver->crmu_usb2_ctrl); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Disable Host and Device Mode */ 1338c2ecf20Sopenharmony_ci val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL); 1348c2ecf20Sopenharmony_ci val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE | ICFG_OFF_MODE); 1358c2ecf20Sopenharmony_ci writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int ns2_drd_phy_poweron(struct phy *phy) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct ns2_phy_data *data = phy_get_drvdata(phy); 1438c2ecf20Sopenharmony_ci struct ns2_phy_driver *driver = data->driver; 1448c2ecf20Sopenharmony_ci u32 extcon_event = data->new_state; 1458c2ecf20Sopenharmony_ci int ret; 1468c2ecf20Sopenharmony_ci u32 val; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (extcon_event == EVT_DEVICE) { 1498c2ecf20Sopenharmony_ci writel(DRD_DEV_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci val = readl(driver->idmdrd_rst_ctrl); 1528c2ecf20Sopenharmony_ci val &= ~IDM_RST_BIT; 1538c2ecf20Sopenharmony_ci writel(val, driver->idmdrd_rst_ctrl); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci val = readl(driver->crmu_usb2_ctrl); 1568c2ecf20Sopenharmony_ci val |= (AFE_CORERDY_VDDC | DRD_DEV_MODE); 1578c2ecf20Sopenharmony_ci writel(val, driver->crmu_usb2_ctrl); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* Bring PHY and PHY_PLL out of Reset */ 1608c2ecf20Sopenharmony_ci val = readl(driver->crmu_usb2_ctrl); 1618c2ecf20Sopenharmony_ci val |= (PHY_PLL_RESETB | PHY_RESETB); 1628c2ecf20Sopenharmony_ci writel(val, driver->crmu_usb2_ctrl); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver); 1658c2ecf20Sopenharmony_ci if (ret < 0) { 1668c2ecf20Sopenharmony_ci dev_err(&phy->dev, "Phy PLL lock failed\n"); 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci } else { 1708c2ecf20Sopenharmony_ci writel(DRD_HOST_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci val = readl(driver->crmu_usb2_ctrl); 1738c2ecf20Sopenharmony_ci val |= AFE_CORERDY_VDDC; 1748c2ecf20Sopenharmony_ci writel(val, driver->crmu_usb2_ctrl); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver); 1778c2ecf20Sopenharmony_ci if (ret < 0) { 1788c2ecf20Sopenharmony_ci dev_err(&phy->dev, "Phy PLL lock failed\n"); 1798c2ecf20Sopenharmony_ci return ret; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci val = readl(driver->idmdrd_rst_ctrl); 1838c2ecf20Sopenharmony_ci val &= ~IDM_RST_BIT; 1848c2ecf20Sopenharmony_ci writel(val, driver->idmdrd_rst_ctrl); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* port over current Polarity */ 1878c2ecf20Sopenharmony_ci val = readl(driver->usb2h_strap_reg); 1888c2ecf20Sopenharmony_ci val |= OHCI_OVRCUR_POL; 1898c2ecf20Sopenharmony_ci writel(val, driver->usb2h_strap_reg); 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void connect_change(struct ns2_phy_driver *driver) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci u32 extcon_event; 1988c2ecf20Sopenharmony_ci u32 val; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci extcon_event = driver->data->new_state; 2018c2ecf20Sopenharmony_ci val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci switch (extcon_event) { 2048c2ecf20Sopenharmony_ci case EVT_DEVICE: 2058c2ecf20Sopenharmony_ci val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE); 2068c2ecf20Sopenharmony_ci writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci val = (val & ~DRD_HOST_MODE) | DRD_DEVICE_MODE; 2098c2ecf20Sopenharmony_ci writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci val = readl(driver->icfgdrd_regs + ICFG_DRD_P0CTL); 2128c2ecf20Sopenharmony_ci val |= ICFG_DEV_BIT; 2138c2ecf20Sopenharmony_ci writel(val, driver->icfgdrd_regs + ICFG_DRD_P0CTL); 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci case EVT_HOST: 2178c2ecf20Sopenharmony_ci val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE); 2188c2ecf20Sopenharmony_ci writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci val = (val & ~DRD_DEVICE_MODE) | DRD_HOST_MODE; 2218c2ecf20Sopenharmony_ci writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci val = readl(driver->usb2h_strap_reg); 2248c2ecf20Sopenharmony_ci val |= OHCI_OVRCUR_POL; 2258c2ecf20Sopenharmony_ci writel(val, driver->usb2h_strap_reg); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci val = readl(driver->icfgdrd_regs + ICFG_DRD_P0CTL); 2288c2ecf20Sopenharmony_ci val &= ~ICFG_DEV_BIT; 2298c2ecf20Sopenharmony_ci writel(val, driver->icfgdrd_regs + ICFG_DRD_P0CTL); 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci default: 2338c2ecf20Sopenharmony_ci pr_err("Invalid extcon event\n"); 2348c2ecf20Sopenharmony_ci break; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void extcon_work(struct work_struct *work) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct ns2_phy_driver *driver; 2418c2ecf20Sopenharmony_ci int vbus; 2428c2ecf20Sopenharmony_ci int id; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci driver = container_of(to_delayed_work(work), 2458c2ecf20Sopenharmony_ci struct ns2_phy_driver, wq_extcon); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci id = gpiod_get_value_cansleep(driver->id_gpiod); 2488c2ecf20Sopenharmony_ci vbus = gpiod_get_value_cansleep(driver->vbus_gpiod); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (!id && vbus) { /* Host connected */ 2518c2ecf20Sopenharmony_ci extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, true); 2528c2ecf20Sopenharmony_ci pr_debug("Host cable connected\n"); 2538c2ecf20Sopenharmony_ci driver->data->new_state = EVT_HOST; 2548c2ecf20Sopenharmony_ci connect_change(driver); 2558c2ecf20Sopenharmony_ci } else if (id && !vbus) { /* Disconnected */ 2568c2ecf20Sopenharmony_ci extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, false); 2578c2ecf20Sopenharmony_ci extcon_set_state_sync(driver->edev, EXTCON_USB, false); 2588c2ecf20Sopenharmony_ci pr_debug("Cable disconnected\n"); 2598c2ecf20Sopenharmony_ci } else if (id && vbus) { /* Device connected */ 2608c2ecf20Sopenharmony_ci extcon_set_state_sync(driver->edev, EXTCON_USB, true); 2618c2ecf20Sopenharmony_ci pr_debug("Device cable connected\n"); 2628c2ecf20Sopenharmony_ci driver->data->new_state = EVT_DEVICE; 2638c2ecf20Sopenharmony_ci connect_change(driver); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic irqreturn_t gpio_irq_handler(int irq, void *dev_id) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct ns2_phy_driver *driver = dev_id; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon, 2728c2ecf20Sopenharmony_ci driver->debounce_jiffies); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic const struct phy_ops ops = { 2788c2ecf20Sopenharmony_ci .init = ns2_drd_phy_init, 2798c2ecf20Sopenharmony_ci .power_on = ns2_drd_phy_poweron, 2808c2ecf20Sopenharmony_ci .power_off = ns2_drd_phy_poweroff, 2818c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2828c2ecf20Sopenharmony_ci}; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic const struct of_device_id ns2_drd_phy_dt_ids[] = { 2858c2ecf20Sopenharmony_ci { .compatible = "brcm,ns2-drd-phy", }, 2868c2ecf20Sopenharmony_ci { } 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ns2_drd_phy_dt_ids); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int ns2_drd_phy_probe(struct platform_device *pdev) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 2938c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2948c2ecf20Sopenharmony_ci struct ns2_phy_driver *driver; 2958c2ecf20Sopenharmony_ci struct ns2_phy_data *data; 2968c2ecf20Sopenharmony_ci struct resource *res; 2978c2ecf20Sopenharmony_ci int ret; 2988c2ecf20Sopenharmony_ci u32 val; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci driver = devm_kzalloc(dev, sizeof(struct ns2_phy_driver), 3018c2ecf20Sopenharmony_ci GFP_KERNEL); 3028c2ecf20Sopenharmony_ci if (!driver) 3038c2ecf20Sopenharmony_ci return -ENOMEM; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci driver->data = devm_kzalloc(dev, sizeof(struct ns2_phy_data), 3068c2ecf20Sopenharmony_ci GFP_KERNEL); 3078c2ecf20Sopenharmony_ci if (!driver->data) 3088c2ecf20Sopenharmony_ci return -ENOMEM; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "icfg"); 3118c2ecf20Sopenharmony_ci driver->icfgdrd_regs = devm_ioremap_resource(dev, res); 3128c2ecf20Sopenharmony_ci if (IS_ERR(driver->icfgdrd_regs)) 3138c2ecf20Sopenharmony_ci return PTR_ERR(driver->icfgdrd_regs); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rst-ctrl"); 3168c2ecf20Sopenharmony_ci driver->idmdrd_rst_ctrl = devm_ioremap_resource(dev, res); 3178c2ecf20Sopenharmony_ci if (IS_ERR(driver->idmdrd_rst_ctrl)) 3188c2ecf20Sopenharmony_ci return PTR_ERR(driver->idmdrd_rst_ctrl); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "crmu-ctrl"); 3218c2ecf20Sopenharmony_ci driver->crmu_usb2_ctrl = devm_ioremap_resource(dev, res); 3228c2ecf20Sopenharmony_ci if (IS_ERR(driver->crmu_usb2_ctrl)) 3238c2ecf20Sopenharmony_ci return PTR_ERR(driver->crmu_usb2_ctrl); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2-strap"); 3268c2ecf20Sopenharmony_ci driver->usb2h_strap_reg = devm_ioremap_resource(dev, res); 3278c2ecf20Sopenharmony_ci if (IS_ERR(driver->usb2h_strap_reg)) 3288c2ecf20Sopenharmony_ci return PTR_ERR(driver->usb2h_strap_reg); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* create extcon */ 3318c2ecf20Sopenharmony_ci driver->id_gpiod = devm_gpiod_get(&pdev->dev, "id", GPIOD_IN); 3328c2ecf20Sopenharmony_ci if (IS_ERR(driver->id_gpiod)) { 3338c2ecf20Sopenharmony_ci dev_err(dev, "failed to get ID GPIO\n"); 3348c2ecf20Sopenharmony_ci return PTR_ERR(driver->id_gpiod); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci driver->vbus_gpiod = devm_gpiod_get(&pdev->dev, "vbus", GPIOD_IN); 3378c2ecf20Sopenharmony_ci if (IS_ERR(driver->vbus_gpiod)) { 3388c2ecf20Sopenharmony_ci dev_err(dev, "failed to get VBUS GPIO\n"); 3398c2ecf20Sopenharmony_ci return PTR_ERR(driver->vbus_gpiod); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci driver->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable); 3438c2ecf20Sopenharmony_ci if (IS_ERR(driver->edev)) { 3448c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate extcon device\n"); 3458c2ecf20Sopenharmony_ci return -ENOMEM; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci ret = devm_extcon_dev_register(dev, driver->edev); 3498c2ecf20Sopenharmony_ci if (ret < 0) { 3508c2ecf20Sopenharmony_ci dev_err(dev, "failed to register extcon device\n"); 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci ret = gpiod_set_debounce(driver->id_gpiod, GPIO_DELAY * 1000); 3558c2ecf20Sopenharmony_ci if (ret < 0) 3568c2ecf20Sopenharmony_ci driver->debounce_jiffies = msecs_to_jiffies(GPIO_DELAY); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&driver->wq_extcon, extcon_work); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci driver->id_irq = gpiod_to_irq(driver->id_gpiod); 3618c2ecf20Sopenharmony_ci if (driver->id_irq < 0) { 3628c2ecf20Sopenharmony_ci dev_err(dev, "failed to get ID IRQ\n"); 3638c2ecf20Sopenharmony_ci return driver->id_irq; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci driver->vbus_irq = gpiod_to_irq(driver->vbus_gpiod); 3678c2ecf20Sopenharmony_ci if (driver->vbus_irq < 0) { 3688c2ecf20Sopenharmony_ci dev_err(dev, "failed to get ID IRQ\n"); 3698c2ecf20Sopenharmony_ci return driver->vbus_irq; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, driver->id_irq, gpio_irq_handler, 3738c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 3748c2ecf20Sopenharmony_ci "usb_id", driver); 3758c2ecf20Sopenharmony_ci if (ret < 0) { 3768c2ecf20Sopenharmony_ci dev_err(dev, "failed to request handler for ID IRQ\n"); 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, driver->vbus_irq, gpio_irq_handler, 3818c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 3828c2ecf20Sopenharmony_ci "usb_vbus", driver); 3838c2ecf20Sopenharmony_ci if (ret < 0) { 3848c2ecf20Sopenharmony_ci dev_err(dev, "failed to request handler for VBUS IRQ\n"); 3858c2ecf20Sopenharmony_ci return ret; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci dev_set_drvdata(dev, driver); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* Shutdown all ports. They can be powered up as required */ 3918c2ecf20Sopenharmony_ci val = readl(driver->crmu_usb2_ctrl); 3928c2ecf20Sopenharmony_ci val &= ~(AFE_CORERDY_VDDC | PHY_RESETB); 3938c2ecf20Sopenharmony_ci writel(val, driver->crmu_usb2_ctrl); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci data = driver->data; 3968c2ecf20Sopenharmony_ci data->phy = devm_phy_create(dev, dev->of_node, &ops); 3978c2ecf20Sopenharmony_ci if (IS_ERR(data->phy)) { 3988c2ecf20Sopenharmony_ci dev_err(dev, "Failed to create usb drd phy\n"); 3998c2ecf20Sopenharmony_ci return PTR_ERR(data->phy); 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci data->driver = driver; 4038c2ecf20Sopenharmony_ci phy_set_drvdata(data->phy, data); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 4068c2ecf20Sopenharmony_ci if (IS_ERR(phy_provider)) { 4078c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register as phy provider\n"); 4088c2ecf20Sopenharmony_ci return PTR_ERR(phy_provider); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, driver); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci dev_info(dev, "Registered NS2 DRD Phy device\n"); 4148c2ecf20Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon, 4158c2ecf20Sopenharmony_ci driver->debounce_jiffies); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic struct platform_driver ns2_drd_phy_driver = { 4218c2ecf20Sopenharmony_ci .probe = ns2_drd_phy_probe, 4228c2ecf20Sopenharmony_ci .driver = { 4238c2ecf20Sopenharmony_ci .name = "bcm-ns2-usbphy", 4248c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ns2_drd_phy_dt_ids), 4258c2ecf20Sopenharmony_ci }, 4268c2ecf20Sopenharmony_ci}; 4278c2ecf20Sopenharmony_cimodule_platform_driver(ns2_drd_phy_driver); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:bcm-ns2-drd-phy"); 4308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Broadcom"); 4318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom NS2 USB2 PHY driver"); 4328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 433