linux/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c
<<
>>
Prefs
   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.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
  59struct ns2_phy_data;
  60struct 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
  75struct ns2_phy_data {
  76        struct ns2_phy_driver *driver;
  77        struct phy *phy;
  78        int new_state;
  79};
  80
  81static const unsigned int usb_extcon_cable[] = {
  82        EXTCON_USB,
  83        EXTCON_USB_HOST,
  84        EXTCON_NONE,
  85};
  86
  87static 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
 103static 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
 123static 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
 145static 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
 200static 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
 243static 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_cable_state_(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_cable_state_(driver->edev, EXTCON_USB_HOST, false);
 262                extcon_set_cable_state_(driver->edev, EXTCON_USB, false);
 263                pr_debug("Cable disconnected\n");
 264        } else if (id && vbus) { /* Device connected */
 265                extcon_set_cable_state_(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
 272static 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
 282static 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
 289static const struct of_device_id ns2_drd_phy_dt_ids[] = {
 290        { .compatible = "brcm,ns2-drd-phy", },
 291        { }
 292};
 293MODULE_DEVICE_TABLE(of, ns2_drd_phy_dt_ids);
 294
 295static 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
 425static 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};
 432module_platform_driver(ns2_drd_phy_driver);
 433
 434MODULE_ALIAS("platform:bcm-ns2-drd-phy");
 435MODULE_AUTHOR("Broadcom");
 436MODULE_DESCRIPTION("Broadcom NS2 USB2 PHY driver");
 437MODULE_LICENSE("GPL v2");
 438