root/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pll_lock_stat
  2. ns2_drd_phy_init
  3. ns2_drd_phy_poweroff
  4. ns2_drd_phy_poweron
  5. connect_change
  6. extcon_work
  7. gpio_irq_handler
  8. ns2_drd_phy_probe

   1 /*
   2  * Copyright (C) 2017 Broadcom
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU General Public License as
   6  * published by the Free Software Foundation version 2.
   7  *
   8  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
   9  * kind, whether express or implied; without even the implied warranty
  10  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11  * GNU General Public License for more details.
  12  */
  13 
  14 #include <linux/delay.h>
  15 #include <linux/extcon-provider.h>
  16 #include <linux/gpio.h>
  17 #include <linux/gpio/consumer.h>
  18 #include <linux/init.h>
  19 #include <linux/interrupt.h>
  20 #include <linux/io.h>
  21 #include <linux/irq.h>
  22 #include <linux/mfd/syscon.h>
  23 #include <linux/module.h>
  24 #include <linux/of.h>
  25 #include <linux/of_address.h>
  26 #include <linux/phy/phy.h>
  27 #include <linux/platform_device.h>
  28 #include <linux/regmap.h>
  29 #include <linux/slab.h>
  30 #include <linux/workqueue.h>
  31 
  32 #define ICFG_DRD_AFE            0x0
  33 #define ICFG_MISC_STAT          0x18
  34 #define ICFG_DRD_P0CTL          0x1C
  35 #define ICFG_STRAP_CTRL         0x20
  36 #define ICFG_FSM_CTRL           0x24
  37 
  38 #define ICFG_DEV_BIT            BIT(2)
  39 #define IDM_RST_BIT             BIT(0)
  40 #define AFE_CORERDY_VDDC        BIT(18)
  41 #define PHY_PLL_RESETB          BIT(15)
  42 #define PHY_RESETB              BIT(14)
  43 #define PHY_PLL_LOCK            BIT(0)
  44 
  45 #define DRD_DEV_MODE            BIT(20)
  46 #define OHCI_OVRCUR_POL         BIT(11)
  47 #define ICFG_OFF_MODE           BIT(6)
  48 #define PLL_LOCK_RETRY          1000
  49 
  50 #define EVT_DEVICE              0
  51 #define EVT_HOST                1
  52 
  53 #define DRD_HOST_MODE           (BIT(2) | BIT(3))
  54 #define DRD_DEVICE_MODE         (BIT(4) | BIT(5))
  55 #define DRD_HOST_VAL            0x803
  56 #define DRD_DEV_VAL             0x807
  57 #define GPIO_DELAY              20
  58 
  59 struct ns2_phy_data;
  60 struct ns2_phy_driver {
  61         void __iomem *icfgdrd_regs;
  62         void __iomem *idmdrd_rst_ctrl;
  63         void __iomem *crmu_usb2_ctrl;
  64         void __iomem *usb2h_strap_reg;
  65         struct ns2_phy_data *data;
  66         struct extcon_dev *edev;
  67         struct gpio_desc *vbus_gpiod;
  68         struct gpio_desc *id_gpiod;
  69         int id_irq;
  70         int vbus_irq;
  71         unsigned long debounce_jiffies;
  72         struct delayed_work wq_extcon;
  73 };
  74 
  75 struct ns2_phy_data {
  76         struct ns2_phy_driver *driver;
  77         struct phy *phy;
  78         int new_state;
  79 };
  80 
  81 static const unsigned int usb_extcon_cable[] = {
  82         EXTCON_USB,
  83         EXTCON_USB_HOST,
  84         EXTCON_NONE,
  85 };
  86 
  87 static inline int pll_lock_stat(u32 usb_reg, int reg_mask,
  88                                 struct ns2_phy_driver *driver)
  89 {
  90         int retry = PLL_LOCK_RETRY;
  91         u32 val;
  92 
  93         do {
  94                 udelay(1);
  95                 val = readl(driver->icfgdrd_regs + usb_reg);
  96                 if (val & reg_mask)
  97                         return 0;
  98         } while (--retry > 0);
  99 
 100         return -EBUSY;
 101 }
 102 
 103 static int ns2_drd_phy_init(struct phy *phy)
 104 {
 105         struct ns2_phy_data *data = phy_get_drvdata(phy);
 106         struct ns2_phy_driver *driver = data->driver;
 107         u32 val;
 108 
 109         val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
 110 
 111         if (data->new_state == EVT_HOST) {
 112                 val &= ~DRD_DEVICE_MODE;
 113                 val |= DRD_HOST_MODE;
 114         } else {
 115                 val &= ~DRD_HOST_MODE;
 116                 val |= DRD_DEVICE_MODE;
 117         }
 118         writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
 119 
 120         return 0;
 121 }
 122 
 123 static int ns2_drd_phy_poweroff(struct phy *phy)
 124 {
 125         struct ns2_phy_data *data = phy_get_drvdata(phy);
 126         struct ns2_phy_driver *driver = data->driver;
 127         u32 val;
 128 
 129         val = readl(driver->crmu_usb2_ctrl);
 130         val &= ~AFE_CORERDY_VDDC;
 131         writel(val, driver->crmu_usb2_ctrl);
 132 
 133         val = readl(driver->crmu_usb2_ctrl);
 134         val &= ~DRD_DEV_MODE;
 135         writel(val, driver->crmu_usb2_ctrl);
 136 
 137         /* Disable Host and Device Mode */
 138         val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
 139         val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE | ICFG_OFF_MODE);
 140         writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
 141 
 142         return 0;
 143 }
 144 
 145 static int ns2_drd_phy_poweron(struct phy *phy)
 146 {
 147         struct ns2_phy_data *data = phy_get_drvdata(phy);
 148         struct ns2_phy_driver *driver = data->driver;
 149         u32 extcon_event = data->new_state;
 150         int ret;
 151         u32 val;
 152 
 153         if (extcon_event == EVT_DEVICE) {
 154                 writel(DRD_DEV_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
 155 
 156                 val = readl(driver->idmdrd_rst_ctrl);
 157                 val &= ~IDM_RST_BIT;
 158                 writel(val, driver->idmdrd_rst_ctrl);
 159 
 160                 val = readl(driver->crmu_usb2_ctrl);
 161                 val |= (AFE_CORERDY_VDDC | DRD_DEV_MODE);
 162                 writel(val, driver->crmu_usb2_ctrl);
 163 
 164                 /* Bring PHY and PHY_PLL out of Reset */
 165                 val = readl(driver->crmu_usb2_ctrl);
 166                 val |= (PHY_PLL_RESETB | PHY_RESETB);
 167                 writel(val, driver->crmu_usb2_ctrl);
 168 
 169                 ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver);
 170                 if (ret < 0) {
 171                         dev_err(&phy->dev, "Phy PLL lock failed\n");
 172                         return ret;
 173                 }
 174         } else {
 175                 writel(DRD_HOST_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
 176 
 177                 val = readl(driver->crmu_usb2_ctrl);
 178                 val |= AFE_CORERDY_VDDC;
 179                 writel(val, driver->crmu_usb2_ctrl);
 180 
 181                 ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver);
 182                 if (ret < 0) {
 183                         dev_err(&phy->dev, "Phy PLL lock failed\n");
 184                         return ret;
 185                 }
 186 
 187                 val = readl(driver->idmdrd_rst_ctrl);
 188                 val &= ~IDM_RST_BIT;
 189                 writel(val, driver->idmdrd_rst_ctrl);
 190 
 191                 /* port over current Polarity */
 192                 val = readl(driver->usb2h_strap_reg);
 193                 val |= OHCI_OVRCUR_POL;
 194                 writel(val, driver->usb2h_strap_reg);
 195         }
 196 
 197         return 0;
 198 }
 199 
 200 static void connect_change(struct ns2_phy_driver *driver)
 201 {
 202         u32 extcon_event;
 203         u32 val;
 204 
 205         extcon_event = driver->data->new_state;
 206         val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
 207 
 208         switch (extcon_event) {
 209         case EVT_DEVICE:
 210                 val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE);
 211                 writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
 212 
 213                 val = (val & ~DRD_HOST_MODE) | DRD_DEVICE_MODE;
 214                 writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
 215 
 216                 val = readl(driver->icfgdrd_regs + ICFG_DRD_P0CTL);
 217                 val |= ICFG_DEV_BIT;
 218                 writel(val, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
 219                 break;
 220 
 221         case EVT_HOST:
 222                 val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE);
 223                 writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
 224 
 225                 val = (val & ~DRD_DEVICE_MODE) | DRD_HOST_MODE;
 226                 writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
 227 
 228                 val = readl(driver->usb2h_strap_reg);
 229                 val |= OHCI_OVRCUR_POL;
 230                 writel(val, driver->usb2h_strap_reg);
 231 
 232                 val = readl(driver->icfgdrd_regs + ICFG_DRD_P0CTL);
 233                 val &= ~ICFG_DEV_BIT;
 234                 writel(val, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
 235                 break;
 236 
 237         default:
 238                 pr_err("Invalid extcon event\n");
 239                 break;
 240         }
 241 }
 242 
 243 static void extcon_work(struct work_struct *work)
 244 {
 245         struct ns2_phy_driver *driver;
 246         int vbus;
 247         int id;
 248 
 249         driver  = container_of(to_delayed_work(work),
 250                                struct ns2_phy_driver, wq_extcon);
 251 
 252         id = gpiod_get_value_cansleep(driver->id_gpiod);
 253         vbus = gpiod_get_value_cansleep(driver->vbus_gpiod);
 254 
 255         if (!id && vbus) { /* Host connected */
 256                 extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, true);
 257                 pr_debug("Host cable connected\n");
 258                 driver->data->new_state = EVT_HOST;
 259                 connect_change(driver);
 260         } else if (id && !vbus) { /* Disconnected */
 261                 extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, false);
 262                 extcon_set_state_sync(driver->edev, EXTCON_USB, false);
 263                 pr_debug("Cable disconnected\n");
 264         } else if (id && vbus) { /* Device connected */
 265                 extcon_set_state_sync(driver->edev, EXTCON_USB, true);
 266                 pr_debug("Device cable connected\n");
 267                 driver->data->new_state = EVT_DEVICE;
 268                 connect_change(driver);
 269         }
 270 }
 271 
 272 static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
 273 {
 274         struct ns2_phy_driver *driver = dev_id;
 275 
 276         queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon,
 277                            driver->debounce_jiffies);
 278 
 279         return IRQ_HANDLED;
 280 }
 281 
 282 static struct phy_ops ops = {
 283         .init           = ns2_drd_phy_init,
 284         .power_on       = ns2_drd_phy_poweron,
 285         .power_off      = ns2_drd_phy_poweroff,
 286         .owner          = THIS_MODULE,
 287 };
 288 
 289 static const struct of_device_id ns2_drd_phy_dt_ids[] = {
 290         { .compatible = "brcm,ns2-drd-phy", },
 291         { }
 292 };
 293 MODULE_DEVICE_TABLE(of, ns2_drd_phy_dt_ids);
 294 
 295 static int ns2_drd_phy_probe(struct platform_device *pdev)
 296 {
 297         struct phy_provider *phy_provider;
 298         struct device *dev = &pdev->dev;
 299         struct ns2_phy_driver *driver;
 300         struct ns2_phy_data *data;
 301         struct resource *res;
 302         int ret;
 303         u32 val;
 304 
 305         driver = devm_kzalloc(dev, sizeof(struct ns2_phy_driver),
 306                               GFP_KERNEL);
 307         if (!driver)
 308                 return -ENOMEM;
 309 
 310         driver->data = devm_kzalloc(dev, sizeof(struct ns2_phy_data),
 311                                   GFP_KERNEL);
 312         if (!driver->data)
 313                 return -ENOMEM;
 314 
 315         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "icfg");
 316         driver->icfgdrd_regs = devm_ioremap_resource(dev, res);
 317         if (IS_ERR(driver->icfgdrd_regs))
 318                 return PTR_ERR(driver->icfgdrd_regs);
 319 
 320         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rst-ctrl");
 321         driver->idmdrd_rst_ctrl = devm_ioremap_resource(dev, res);
 322         if (IS_ERR(driver->idmdrd_rst_ctrl))
 323                 return PTR_ERR(driver->idmdrd_rst_ctrl);
 324 
 325         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "crmu-ctrl");
 326         driver->crmu_usb2_ctrl = devm_ioremap_resource(dev, res);
 327         if (IS_ERR(driver->crmu_usb2_ctrl))
 328                 return PTR_ERR(driver->crmu_usb2_ctrl);
 329 
 330         res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2-strap");
 331         driver->usb2h_strap_reg = devm_ioremap_resource(dev, res);
 332         if (IS_ERR(driver->usb2h_strap_reg))
 333                 return PTR_ERR(driver->usb2h_strap_reg);
 334 
 335          /* create extcon */
 336         driver->id_gpiod = devm_gpiod_get(&pdev->dev, "id", GPIOD_IN);
 337         if (IS_ERR(driver->id_gpiod)) {
 338                 dev_err(dev, "failed to get ID GPIO\n");
 339                 return PTR_ERR(driver->id_gpiod);
 340         }
 341         driver->vbus_gpiod = devm_gpiod_get(&pdev->dev, "vbus", GPIOD_IN);
 342         if (IS_ERR(driver->vbus_gpiod)) {
 343                 dev_err(dev, "failed to get VBUS GPIO\n");
 344                 return PTR_ERR(driver->vbus_gpiod);
 345         }
 346 
 347         driver->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
 348         if (IS_ERR(driver->edev)) {
 349                 dev_err(dev, "failed to allocate extcon device\n");
 350                 return -ENOMEM;
 351         }
 352 
 353         ret = devm_extcon_dev_register(dev, driver->edev);
 354         if (ret < 0) {
 355                 dev_err(dev, "failed to register extcon device\n");
 356                 return ret;
 357         }
 358 
 359         ret = gpiod_set_debounce(driver->id_gpiod, GPIO_DELAY * 1000);
 360         if (ret < 0)
 361                 driver->debounce_jiffies = msecs_to_jiffies(GPIO_DELAY);
 362 
 363         INIT_DELAYED_WORK(&driver->wq_extcon, extcon_work);
 364 
 365         driver->id_irq = gpiod_to_irq(driver->id_gpiod);
 366         if (driver->id_irq < 0) {
 367                 dev_err(dev, "failed to get ID IRQ\n");
 368                 return driver->id_irq;
 369         }
 370 
 371         driver->vbus_irq = gpiod_to_irq(driver->vbus_gpiod);
 372         if (driver->vbus_irq < 0) {
 373                 dev_err(dev, "failed to get ID IRQ\n");
 374                 return driver->vbus_irq;
 375         }
 376 
 377         ret = devm_request_irq(dev, driver->id_irq, gpio_irq_handler,
 378                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 379                                "usb_id", driver);
 380         if (ret < 0) {
 381                 dev_err(dev, "failed to request handler for ID IRQ\n");
 382                 return ret;
 383         }
 384 
 385         ret = devm_request_irq(dev, driver->vbus_irq, gpio_irq_handler,
 386                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 387                                "usb_vbus", driver);
 388         if (ret < 0) {
 389                 dev_err(dev, "failed to request handler for VBUS IRQ\n");
 390                 return ret;
 391         }
 392 
 393         dev_set_drvdata(dev, driver);
 394 
 395         /* Shutdown all ports. They can be powered up as required */
 396         val = readl(driver->crmu_usb2_ctrl);
 397         val &= ~(AFE_CORERDY_VDDC | PHY_RESETB);
 398         writel(val, driver->crmu_usb2_ctrl);
 399 
 400         data = driver->data;
 401         data->phy = devm_phy_create(dev, dev->of_node, &ops);
 402         if (IS_ERR(data->phy)) {
 403                 dev_err(dev, "Failed to create usb drd phy\n");
 404                 return PTR_ERR(data->phy);
 405         }
 406 
 407         data->driver = driver;
 408         phy_set_drvdata(data->phy, data);
 409 
 410         phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 411         if (IS_ERR(phy_provider)) {
 412                 dev_err(dev, "Failed to register as phy provider\n");
 413                 return PTR_ERR(phy_provider);
 414         }
 415 
 416         platform_set_drvdata(pdev, driver);
 417 
 418         dev_info(dev, "Registered NS2 DRD Phy device\n");
 419         queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon,
 420                            driver->debounce_jiffies);
 421 
 422         return 0;
 423 }
 424 
 425 static struct platform_driver ns2_drd_phy_driver = {
 426         .probe = ns2_drd_phy_probe,
 427         .driver = {
 428                 .name = "bcm-ns2-usbphy",
 429                 .of_match_table = of_match_ptr(ns2_drd_phy_dt_ids),
 430         },
 431 };
 432 module_platform_driver(ns2_drd_phy_driver);
 433 
 434 MODULE_ALIAS("platform:bcm-ns2-drd-phy");
 435 MODULE_AUTHOR("Broadcom");
 436 MODULE_DESCRIPTION("Broadcom NS2 USB2 PHY driver");
 437 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */