Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 // Copyright (C) 2017 Broadcom
0003 
0004 #include <linux/delay.h>
0005 #include <linux/extcon-provider.h>
0006 #include <linux/gpio.h>
0007 #include <linux/gpio/consumer.h>
0008 #include <linux/init.h>
0009 #include <linux/interrupt.h>
0010 #include <linux/io.h>
0011 #include <linux/iopoll.h>
0012 #include <linux/irq.h>
0013 #include <linux/mfd/syscon.h>
0014 #include <linux/module.h>
0015 #include <linux/of.h>
0016 #include <linux/of_address.h>
0017 #include <linux/phy/phy.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/regmap.h>
0020 #include <linux/slab.h>
0021 #include <linux/workqueue.h>
0022 
0023 #define ICFG_DRD_AFE        0x0
0024 #define ICFG_MISC_STAT      0x18
0025 #define ICFG_DRD_P0CTL      0x1C
0026 #define ICFG_STRAP_CTRL     0x20
0027 #define ICFG_FSM_CTRL       0x24
0028 
0029 #define ICFG_DEV_BIT        BIT(2)
0030 #define IDM_RST_BIT     BIT(0)
0031 #define AFE_CORERDY_VDDC    BIT(18)
0032 #define PHY_PLL_RESETB      BIT(15)
0033 #define PHY_RESETB      BIT(14)
0034 #define PHY_PLL_LOCK        BIT(0)
0035 
0036 #define DRD_DEV_MODE        BIT(20)
0037 #define OHCI_OVRCUR_POL     BIT(11)
0038 #define ICFG_OFF_MODE       BIT(6)
0039 #define PLL_LOCK_RETRY      1000
0040 
0041 #define EVT_DEVICE      0
0042 #define EVT_HOST        1
0043 
0044 #define DRD_HOST_MODE       (BIT(2) | BIT(3))
0045 #define DRD_DEVICE_MODE     (BIT(4) | BIT(5))
0046 #define DRD_HOST_VAL        0x803
0047 #define DRD_DEV_VAL     0x807
0048 #define GPIO_DELAY      20
0049 
0050 struct ns2_phy_data;
0051 struct ns2_phy_driver {
0052     void __iomem *icfgdrd_regs;
0053     void __iomem *idmdrd_rst_ctrl;
0054     void __iomem *crmu_usb2_ctrl;
0055     void __iomem *usb2h_strap_reg;
0056     struct ns2_phy_data *data;
0057     struct extcon_dev *edev;
0058     struct gpio_desc *vbus_gpiod;
0059     struct gpio_desc *id_gpiod;
0060     int id_irq;
0061     int vbus_irq;
0062     unsigned long debounce_jiffies;
0063     struct delayed_work wq_extcon;
0064 };
0065 
0066 struct ns2_phy_data {
0067     struct ns2_phy_driver *driver;
0068     struct phy *phy;
0069     int new_state;
0070 };
0071 
0072 static const unsigned int usb_extcon_cable[] = {
0073     EXTCON_USB,
0074     EXTCON_USB_HOST,
0075     EXTCON_NONE,
0076 };
0077 
0078 static inline int pll_lock_stat(u32 usb_reg, int reg_mask,
0079                 struct ns2_phy_driver *driver)
0080 {
0081     u32 val;
0082 
0083     return readl_poll_timeout_atomic(driver->icfgdrd_regs + usb_reg,
0084                      val, (val & reg_mask), 1,
0085                      PLL_LOCK_RETRY);
0086 }
0087 
0088 static int ns2_drd_phy_init(struct phy *phy)
0089 {
0090     struct ns2_phy_data *data = phy_get_drvdata(phy);
0091     struct ns2_phy_driver *driver = data->driver;
0092     u32 val;
0093 
0094     val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
0095 
0096     if (data->new_state == EVT_HOST) {
0097         val &= ~DRD_DEVICE_MODE;
0098         val |= DRD_HOST_MODE;
0099     } else {
0100         val &= ~DRD_HOST_MODE;
0101         val |= DRD_DEVICE_MODE;
0102     }
0103     writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
0104 
0105     return 0;
0106 }
0107 
0108 static int ns2_drd_phy_poweroff(struct phy *phy)
0109 {
0110     struct ns2_phy_data *data = phy_get_drvdata(phy);
0111     struct ns2_phy_driver *driver = data->driver;
0112     u32 val;
0113 
0114     val = readl(driver->crmu_usb2_ctrl);
0115     val &= ~AFE_CORERDY_VDDC;
0116     writel(val, driver->crmu_usb2_ctrl);
0117 
0118     val = readl(driver->crmu_usb2_ctrl);
0119     val &= ~DRD_DEV_MODE;
0120     writel(val, driver->crmu_usb2_ctrl);
0121 
0122     /* Disable Host and Device Mode */
0123     val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
0124     val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE | ICFG_OFF_MODE);
0125     writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
0126 
0127     return 0;
0128 }
0129 
0130 static int ns2_drd_phy_poweron(struct phy *phy)
0131 {
0132     struct ns2_phy_data *data = phy_get_drvdata(phy);
0133     struct ns2_phy_driver *driver = data->driver;
0134     u32 extcon_event = data->new_state;
0135     int ret;
0136     u32 val;
0137 
0138     if (extcon_event == EVT_DEVICE) {
0139         writel(DRD_DEV_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
0140 
0141         val = readl(driver->idmdrd_rst_ctrl);
0142         val &= ~IDM_RST_BIT;
0143         writel(val, driver->idmdrd_rst_ctrl);
0144 
0145         val = readl(driver->crmu_usb2_ctrl);
0146         val |= (AFE_CORERDY_VDDC | DRD_DEV_MODE);
0147         writel(val, driver->crmu_usb2_ctrl);
0148 
0149         /* Bring PHY and PHY_PLL out of Reset */
0150         val = readl(driver->crmu_usb2_ctrl);
0151         val |= (PHY_PLL_RESETB | PHY_RESETB);
0152         writel(val, driver->crmu_usb2_ctrl);
0153 
0154         ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver);
0155         if (ret < 0) {
0156             dev_err(&phy->dev, "Phy PLL lock failed\n");
0157             return ret;
0158         }
0159     } else {
0160         writel(DRD_HOST_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
0161 
0162         val = readl(driver->crmu_usb2_ctrl);
0163         val |= AFE_CORERDY_VDDC;
0164         writel(val, driver->crmu_usb2_ctrl);
0165 
0166         ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver);
0167         if (ret < 0) {
0168             dev_err(&phy->dev, "Phy PLL lock failed\n");
0169             return ret;
0170         }
0171 
0172         val = readl(driver->idmdrd_rst_ctrl);
0173         val &= ~IDM_RST_BIT;
0174         writel(val, driver->idmdrd_rst_ctrl);
0175 
0176         /* port over current Polarity */
0177         val = readl(driver->usb2h_strap_reg);
0178         val |= OHCI_OVRCUR_POL;
0179         writel(val, driver->usb2h_strap_reg);
0180     }
0181 
0182     return 0;
0183 }
0184 
0185 static void connect_change(struct ns2_phy_driver *driver)
0186 {
0187     u32 extcon_event;
0188     u32 val;
0189 
0190     extcon_event = driver->data->new_state;
0191     val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
0192 
0193     switch (extcon_event) {
0194     case EVT_DEVICE:
0195         val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE);
0196         writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
0197 
0198         val = (val & ~DRD_HOST_MODE) | DRD_DEVICE_MODE;
0199         writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
0200 
0201         val = readl(driver->icfgdrd_regs + ICFG_DRD_P0CTL);
0202         val |= ICFG_DEV_BIT;
0203         writel(val, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
0204         break;
0205 
0206     case EVT_HOST:
0207         val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE);
0208         writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
0209 
0210         val = (val & ~DRD_DEVICE_MODE) | DRD_HOST_MODE;
0211         writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
0212 
0213         val = readl(driver->usb2h_strap_reg);
0214         val |= OHCI_OVRCUR_POL;
0215         writel(val, driver->usb2h_strap_reg);
0216 
0217         val = readl(driver->icfgdrd_regs + ICFG_DRD_P0CTL);
0218         val &= ~ICFG_DEV_BIT;
0219         writel(val, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
0220         break;
0221 
0222     default:
0223         pr_err("Invalid extcon event\n");
0224         break;
0225     }
0226 }
0227 
0228 static void extcon_work(struct work_struct *work)
0229 {
0230     struct ns2_phy_driver *driver;
0231     int vbus;
0232     int id;
0233 
0234     driver  = container_of(to_delayed_work(work),
0235                    struct ns2_phy_driver, wq_extcon);
0236 
0237     id = gpiod_get_value_cansleep(driver->id_gpiod);
0238     vbus = gpiod_get_value_cansleep(driver->vbus_gpiod);
0239 
0240     if (!id && vbus) { /* Host connected */
0241         extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, true);
0242         pr_debug("Host cable connected\n");
0243         driver->data->new_state = EVT_HOST;
0244         connect_change(driver);
0245     } else if (id && !vbus) { /* Disconnected */
0246         extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, false);
0247         extcon_set_state_sync(driver->edev, EXTCON_USB, false);
0248         pr_debug("Cable disconnected\n");
0249     } else if (id && vbus) { /* Device connected */
0250         extcon_set_state_sync(driver->edev, EXTCON_USB, true);
0251         pr_debug("Device cable connected\n");
0252         driver->data->new_state = EVT_DEVICE;
0253         connect_change(driver);
0254     }
0255 }
0256 
0257 static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
0258 {
0259     struct ns2_phy_driver *driver = dev_id;
0260 
0261     queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon,
0262                driver->debounce_jiffies);
0263 
0264     return IRQ_HANDLED;
0265 }
0266 
0267 static const struct phy_ops ops = {
0268     .init       = ns2_drd_phy_init,
0269     .power_on   = ns2_drd_phy_poweron,
0270     .power_off  = ns2_drd_phy_poweroff,
0271     .owner      = THIS_MODULE,
0272 };
0273 
0274 static const struct of_device_id ns2_drd_phy_dt_ids[] = {
0275     { .compatible = "brcm,ns2-drd-phy", },
0276     { }
0277 };
0278 MODULE_DEVICE_TABLE(of, ns2_drd_phy_dt_ids);
0279 
0280 static int ns2_drd_phy_probe(struct platform_device *pdev)
0281 {
0282     struct phy_provider *phy_provider;
0283     struct device *dev = &pdev->dev;
0284     struct ns2_phy_driver *driver;
0285     struct ns2_phy_data *data;
0286     int ret;
0287     u32 val;
0288 
0289     driver = devm_kzalloc(dev, sizeof(struct ns2_phy_driver),
0290                   GFP_KERNEL);
0291     if (!driver)
0292         return -ENOMEM;
0293 
0294     driver->data = devm_kzalloc(dev, sizeof(struct ns2_phy_data),
0295                   GFP_KERNEL);
0296     if (!driver->data)
0297         return -ENOMEM;
0298 
0299     driver->icfgdrd_regs = devm_platform_ioremap_resource_byname(pdev, "icfg");
0300     if (IS_ERR(driver->icfgdrd_regs))
0301         return PTR_ERR(driver->icfgdrd_regs);
0302 
0303     driver->idmdrd_rst_ctrl = devm_platform_ioremap_resource_byname(pdev, "rst-ctrl");
0304     if (IS_ERR(driver->idmdrd_rst_ctrl))
0305         return PTR_ERR(driver->idmdrd_rst_ctrl);
0306 
0307     driver->crmu_usb2_ctrl = devm_platform_ioremap_resource_byname(pdev, "crmu-ctrl");
0308     if (IS_ERR(driver->crmu_usb2_ctrl))
0309         return PTR_ERR(driver->crmu_usb2_ctrl);
0310 
0311     driver->usb2h_strap_reg = devm_platform_ioremap_resource_byname(pdev, "usb2-strap");
0312     if (IS_ERR(driver->usb2h_strap_reg))
0313         return PTR_ERR(driver->usb2h_strap_reg);
0314 
0315      /* create extcon */
0316     driver->id_gpiod = devm_gpiod_get(&pdev->dev, "id", GPIOD_IN);
0317     if (IS_ERR(driver->id_gpiod)) {
0318         dev_err(dev, "failed to get ID GPIO\n");
0319         return PTR_ERR(driver->id_gpiod);
0320     }
0321     driver->vbus_gpiod = devm_gpiod_get(&pdev->dev, "vbus", GPIOD_IN);
0322     if (IS_ERR(driver->vbus_gpiod)) {
0323         dev_err(dev, "failed to get VBUS GPIO\n");
0324         return PTR_ERR(driver->vbus_gpiod);
0325     }
0326 
0327     driver->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
0328     if (IS_ERR(driver->edev)) {
0329         dev_err(dev, "failed to allocate extcon device\n");
0330         return -ENOMEM;
0331     }
0332 
0333     ret = devm_extcon_dev_register(dev, driver->edev);
0334     if (ret < 0) {
0335         dev_err(dev, "failed to register extcon device\n");
0336         return ret;
0337     }
0338 
0339     ret = gpiod_set_debounce(driver->id_gpiod, GPIO_DELAY * 1000);
0340     if (ret < 0)
0341         driver->debounce_jiffies = msecs_to_jiffies(GPIO_DELAY);
0342 
0343     INIT_DELAYED_WORK(&driver->wq_extcon, extcon_work);
0344 
0345     driver->id_irq = gpiod_to_irq(driver->id_gpiod);
0346     if (driver->id_irq < 0) {
0347         dev_err(dev, "failed to get ID IRQ\n");
0348         return driver->id_irq;
0349     }
0350 
0351     driver->vbus_irq = gpiod_to_irq(driver->vbus_gpiod);
0352     if (driver->vbus_irq < 0) {
0353         dev_err(dev, "failed to get ID IRQ\n");
0354         return driver->vbus_irq;
0355     }
0356 
0357     ret = devm_request_irq(dev, driver->id_irq, gpio_irq_handler,
0358                    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
0359                    "usb_id", driver);
0360     if (ret < 0) {
0361         dev_err(dev, "failed to request handler for ID IRQ\n");
0362         return ret;
0363     }
0364 
0365     ret = devm_request_irq(dev, driver->vbus_irq, gpio_irq_handler,
0366                    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
0367                    "usb_vbus", driver);
0368     if (ret < 0) {
0369         dev_err(dev, "failed to request handler for VBUS IRQ\n");
0370         return ret;
0371     }
0372 
0373     dev_set_drvdata(dev, driver);
0374 
0375     /* Shutdown all ports. They can be powered up as required */
0376     val = readl(driver->crmu_usb2_ctrl);
0377     val &= ~(AFE_CORERDY_VDDC | PHY_RESETB);
0378     writel(val, driver->crmu_usb2_ctrl);
0379 
0380     data = driver->data;
0381     data->phy = devm_phy_create(dev, dev->of_node, &ops);
0382     if (IS_ERR(data->phy)) {
0383         dev_err(dev, "Failed to create usb drd phy\n");
0384         return PTR_ERR(data->phy);
0385     }
0386 
0387     data->driver = driver;
0388     phy_set_drvdata(data->phy, data);
0389 
0390     phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
0391     if (IS_ERR(phy_provider)) {
0392         dev_err(dev, "Failed to register as phy provider\n");
0393         return PTR_ERR(phy_provider);
0394     }
0395 
0396     platform_set_drvdata(pdev, driver);
0397 
0398     dev_info(dev, "Registered NS2 DRD Phy device\n");
0399     queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon,
0400                driver->debounce_jiffies);
0401 
0402     return 0;
0403 }
0404 
0405 static struct platform_driver ns2_drd_phy_driver = {
0406     .probe = ns2_drd_phy_probe,
0407     .driver = {
0408         .name = "bcm-ns2-usbphy",
0409         .of_match_table = of_match_ptr(ns2_drd_phy_dt_ids),
0410     },
0411 };
0412 module_platform_driver(ns2_drd_phy_driver);
0413 
0414 MODULE_ALIAS("platform:bcm-ns2-drd-phy");
0415 MODULE_AUTHOR("Broadcom");
0416 MODULE_DESCRIPTION("Broadcom NS2 USB2 PHY driver");
0417 MODULE_LICENSE("GPL v2");