phy-twl6030-usb.c (11608B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver. 4 * 5 * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com 6 * 7 * Author: Hema HK <hemahk@ti.com> 8 */ 9 10#include <linux/module.h> 11#include <linux/init.h> 12#include <linux/interrupt.h> 13#include <linux/platform_device.h> 14#include <linux/io.h> 15#include <linux/usb/musb.h> 16#include <linux/usb/phy_companion.h> 17#include <linux/phy/omap_usb.h> 18#include <linux/mfd/twl.h> 19#include <linux/regulator/consumer.h> 20#include <linux/err.h> 21#include <linux/slab.h> 22#include <linux/delay.h> 23#include <linux/of.h> 24 25/* usb register definitions */ 26#define USB_VENDOR_ID_LSB 0x00 27#define USB_VENDOR_ID_MSB 0x01 28#define USB_PRODUCT_ID_LSB 0x02 29#define USB_PRODUCT_ID_MSB 0x03 30#define USB_VBUS_CTRL_SET 0x04 31#define USB_VBUS_CTRL_CLR 0x05 32#define USB_ID_CTRL_SET 0x06 33#define USB_ID_CTRL_CLR 0x07 34#define USB_VBUS_INT_SRC 0x08 35#define USB_VBUS_INT_LATCH_SET 0x09 36#define USB_VBUS_INT_LATCH_CLR 0x0A 37#define USB_VBUS_INT_EN_LO_SET 0x0B 38#define USB_VBUS_INT_EN_LO_CLR 0x0C 39#define USB_VBUS_INT_EN_HI_SET 0x0D 40#define USB_VBUS_INT_EN_HI_CLR 0x0E 41#define USB_ID_INT_SRC 0x0F 42#define USB_ID_INT_LATCH_SET 0x10 43#define USB_ID_INT_LATCH_CLR 0x11 44 45#define USB_ID_INT_EN_LO_SET 0x12 46#define USB_ID_INT_EN_LO_CLR 0x13 47#define USB_ID_INT_EN_HI_SET 0x14 48#define USB_ID_INT_EN_HI_CLR 0x15 49#define USB_OTG_ADP_CTRL 0x16 50#define USB_OTG_ADP_HIGH 0x17 51#define USB_OTG_ADP_LOW 0x18 52#define USB_OTG_ADP_RISE 0x19 53#define USB_OTG_REVISION 0x1A 54 55/* to be moved to LDO */ 56#define TWL6030_MISC2 0xE5 57#define TWL6030_CFG_LDO_PD2 0xF5 58#define TWL6030_BACKUP_REG 0xFA 59 60#define STS_HW_CONDITIONS 0x21 61 62/* In module TWL6030_MODULE_PM_MASTER */ 63#define STS_HW_CONDITIONS 0x21 64#define STS_USB_ID BIT(2) 65 66/* In module TWL6030_MODULE_PM_RECEIVER */ 67#define VUSB_CFG_TRANS 0x71 68#define VUSB_CFG_STATE 0x72 69#define VUSB_CFG_VOLTAGE 0x73 70 71/* in module TWL6030_MODULE_MAIN_CHARGE */ 72 73#define CHARGERUSB_CTRL1 0x8 74 75#define CONTROLLER_STAT1 0x03 76#define VBUS_DET BIT(2) 77 78struct twl6030_usb { 79 struct phy_companion comparator; 80 struct device *dev; 81 82 /* for vbus reporting with irqs disabled */ 83 spinlock_t lock; 84 85 struct regulator *usb3v3; 86 87 /* used to check initial cable status after probe */ 88 struct delayed_work get_status_work; 89 90 /* used to set vbus, in atomic path */ 91 struct work_struct set_vbus_work; 92 93 int irq1; 94 int irq2; 95 enum musb_vbus_id_status linkstat; 96 u8 asleep; 97 bool vbus_enable; 98}; 99 100#define comparator_to_twl(x) container_of((x), struct twl6030_usb, comparator) 101 102/*-------------------------------------------------------------------------*/ 103 104static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module, 105 u8 data, u8 address) 106{ 107 int ret = 0; 108 109 ret = twl_i2c_write_u8(module, data, address); 110 if (ret < 0) 111 dev_err(twl->dev, 112 "Write[0x%x] Error %d\n", address, ret); 113 return ret; 114} 115 116static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) 117{ 118 u8 data; 119 int ret; 120 121 ret = twl_i2c_read_u8(module, &data, address); 122 if (ret >= 0) 123 ret = data; 124 else 125 dev_err(twl->dev, 126 "readb[0x%x,0x%x] Error %d\n", 127 module, address, ret); 128 return ret; 129} 130 131static int twl6030_start_srp(struct phy_companion *comparator) 132{ 133 struct twl6030_usb *twl = comparator_to_twl(comparator); 134 135 twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET); 136 twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET); 137 138 mdelay(100); 139 twl6030_writeb(twl, TWL_MODULE_USB, 0xa0, USB_VBUS_CTRL_CLR); 140 141 return 0; 142} 143 144static int twl6030_usb_ldo_init(struct twl6030_usb *twl) 145{ 146 /* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */ 147 twl6030_writeb(twl, TWL6030_MODULE_ID0, 0x1, TWL6030_BACKUP_REG); 148 149 /* Program CFG_LDO_PD2 register and set VUSB bit */ 150 twl6030_writeb(twl, TWL6030_MODULE_ID0, 0x1, TWL6030_CFG_LDO_PD2); 151 152 /* Program MISC2 register and set bit VUSB_IN_VBAT */ 153 twl6030_writeb(twl, TWL6030_MODULE_ID0, 0x10, TWL6030_MISC2); 154 155 twl->usb3v3 = regulator_get(twl->dev, "usb"); 156 if (IS_ERR(twl->usb3v3)) 157 return -ENODEV; 158 159 /* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */ 160 twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET); 161 162 /* 163 * Program the USB_ID_CTRL_SET register to enable GND drive 164 * and the ID comparators 165 */ 166 twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET); 167 168 return 0; 169} 170 171static ssize_t vbus_show(struct device *dev, 172 struct device_attribute *attr, char *buf) 173{ 174 struct twl6030_usb *twl = dev_get_drvdata(dev); 175 unsigned long flags; 176 int ret = -EINVAL; 177 178 spin_lock_irqsave(&twl->lock, flags); 179 180 switch (twl->linkstat) { 181 case MUSB_VBUS_VALID: 182 ret = snprintf(buf, PAGE_SIZE, "vbus\n"); 183 break; 184 case MUSB_ID_GROUND: 185 ret = snprintf(buf, PAGE_SIZE, "id\n"); 186 break; 187 case MUSB_VBUS_OFF: 188 ret = snprintf(buf, PAGE_SIZE, "none\n"); 189 break; 190 default: 191 ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); 192 } 193 spin_unlock_irqrestore(&twl->lock, flags); 194 195 return ret; 196} 197static DEVICE_ATTR_RO(vbus); 198 199static struct attribute *twl6030_attrs[] = { 200 &dev_attr_vbus.attr, 201 NULL, 202}; 203ATTRIBUTE_GROUPS(twl6030); 204 205static irqreturn_t twl6030_usb_irq(int irq, void *_twl) 206{ 207 struct twl6030_usb *twl = _twl; 208 enum musb_vbus_id_status status = MUSB_UNKNOWN; 209 u8 vbus_state, hw_state; 210 int ret; 211 212 hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); 213 214 vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, 215 CONTROLLER_STAT1); 216 if (!(hw_state & STS_USB_ID)) { 217 if (vbus_state & VBUS_DET) { 218 ret = regulator_enable(twl->usb3v3); 219 if (ret) 220 dev_err(twl->dev, "Failed to enable usb3v3\n"); 221 222 twl->asleep = 1; 223 status = MUSB_VBUS_VALID; 224 twl->linkstat = status; 225 ret = musb_mailbox(status); 226 if (ret) 227 twl->linkstat = MUSB_UNKNOWN; 228 } else { 229 if (twl->linkstat != MUSB_UNKNOWN) { 230 status = MUSB_VBUS_OFF; 231 twl->linkstat = status; 232 ret = musb_mailbox(status); 233 if (ret) 234 twl->linkstat = MUSB_UNKNOWN; 235 if (twl->asleep) { 236 regulator_disable(twl->usb3v3); 237 twl->asleep = 0; 238 } 239 } 240 } 241 } 242 sysfs_notify(&twl->dev->kobj, NULL, "vbus"); 243 244 return IRQ_HANDLED; 245} 246 247static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) 248{ 249 struct twl6030_usb *twl = _twl; 250 enum musb_vbus_id_status status = MUSB_UNKNOWN; 251 u8 hw_state; 252 int ret; 253 254 hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); 255 256 if (hw_state & STS_USB_ID) { 257 ret = regulator_enable(twl->usb3v3); 258 if (ret) 259 dev_err(twl->dev, "Failed to enable usb3v3\n"); 260 261 twl->asleep = 1; 262 twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR); 263 twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET); 264 status = MUSB_ID_GROUND; 265 twl->linkstat = status; 266 ret = musb_mailbox(status); 267 if (ret) 268 twl->linkstat = MUSB_UNKNOWN; 269 } else { 270 twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR); 271 twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); 272 } 273 twl6030_writeb(twl, TWL_MODULE_USB, status, USB_ID_INT_LATCH_CLR); 274 275 return IRQ_HANDLED; 276} 277 278static void twl6030_status_work(struct work_struct *work) 279{ 280 struct twl6030_usb *twl = container_of(work, struct twl6030_usb, 281 get_status_work.work); 282 283 twl6030_usb_irq(twl->irq2, twl); 284 twl6030_usbotg_irq(twl->irq1, twl); 285} 286 287static int twl6030_enable_irq(struct twl6030_usb *twl) 288{ 289 twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); 290 twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C); 291 twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C); 292 293 twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, 294 REG_INT_MSK_LINE_C); 295 twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, 296 REG_INT_MSK_STS_C); 297 298 return 0; 299} 300 301static void otg_set_vbus_work(struct work_struct *data) 302{ 303 struct twl6030_usb *twl = container_of(data, struct twl6030_usb, 304 set_vbus_work); 305 306 /* 307 * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1 308 * register. This enables boost mode. 309 */ 310 311 if (twl->vbus_enable) 312 twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE, 0x40, 313 CHARGERUSB_CTRL1); 314 else 315 twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE, 0x00, 316 CHARGERUSB_CTRL1); 317} 318 319static int twl6030_set_vbus(struct phy_companion *comparator, bool enabled) 320{ 321 struct twl6030_usb *twl = comparator_to_twl(comparator); 322 323 twl->vbus_enable = enabled; 324 schedule_work(&twl->set_vbus_work); 325 326 return 0; 327} 328 329static int twl6030_usb_probe(struct platform_device *pdev) 330{ 331 u32 ret; 332 struct twl6030_usb *twl; 333 int status, err; 334 struct device_node *np = pdev->dev.of_node; 335 struct device *dev = &pdev->dev; 336 337 if (!np) { 338 dev_err(dev, "no DT info\n"); 339 return -EINVAL; 340 } 341 342 twl = devm_kzalloc(dev, sizeof(*twl), GFP_KERNEL); 343 if (!twl) 344 return -ENOMEM; 345 346 twl->dev = &pdev->dev; 347 twl->irq1 = platform_get_irq(pdev, 0); 348 twl->irq2 = platform_get_irq(pdev, 1); 349 twl->linkstat = MUSB_UNKNOWN; 350 351 if (twl->irq1 < 0) 352 return twl->irq1; 353 if (twl->irq2 < 0) 354 return twl->irq2; 355 356 twl->comparator.set_vbus = twl6030_set_vbus; 357 twl->comparator.start_srp = twl6030_start_srp; 358 359 ret = omap_usb2_set_comparator(&twl->comparator); 360 if (ret == -ENODEV) { 361 dev_info(&pdev->dev, "phy not ready, deferring probe"); 362 return -EPROBE_DEFER; 363 } 364 365 /* init spinlock for workqueue */ 366 spin_lock_init(&twl->lock); 367 368 err = twl6030_usb_ldo_init(twl); 369 if (err) { 370 dev_err(&pdev->dev, "ldo init failed\n"); 371 return err; 372 } 373 374 platform_set_drvdata(pdev, twl); 375 376 INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); 377 INIT_DELAYED_WORK(&twl->get_status_work, twl6030_status_work); 378 379 status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq, 380 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, 381 "twl6030_usb", twl); 382 if (status < 0) { 383 dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", 384 twl->irq1, status); 385 goto err_put_regulator; 386 } 387 388 status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq, 389 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, 390 "twl6030_usb", twl); 391 if (status < 0) { 392 dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", 393 twl->irq2, status); 394 goto err_free_irq1; 395 } 396 397 twl->asleep = 0; 398 twl6030_enable_irq(twl); 399 schedule_delayed_work(&twl->get_status_work, HZ); 400 dev_info(&pdev->dev, "Initialized TWL6030 USB module\n"); 401 402 return 0; 403 404err_free_irq1: 405 free_irq(twl->irq1, twl); 406err_put_regulator: 407 regulator_put(twl->usb3v3); 408 409 return status; 410} 411 412static int twl6030_usb_remove(struct platform_device *pdev) 413{ 414 struct twl6030_usb *twl = platform_get_drvdata(pdev); 415 416 cancel_delayed_work_sync(&twl->get_status_work); 417 twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, 418 REG_INT_MSK_LINE_C); 419 twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, 420 REG_INT_MSK_STS_C); 421 free_irq(twl->irq1, twl); 422 free_irq(twl->irq2, twl); 423 regulator_put(twl->usb3v3); 424 cancel_work_sync(&twl->set_vbus_work); 425 426 return 0; 427} 428 429static const struct of_device_id twl6030_usb_id_table[] = { 430 { .compatible = "ti,twl6030-usb" }, 431 {} 432}; 433MODULE_DEVICE_TABLE(of, twl6030_usb_id_table); 434 435static struct platform_driver twl6030_usb_driver = { 436 .probe = twl6030_usb_probe, 437 .remove = twl6030_usb_remove, 438 .driver = { 439 .name = "twl6030_usb", 440 .of_match_table = of_match_ptr(twl6030_usb_id_table), 441 .dev_groups = twl6030_groups, 442 }, 443}; 444 445static int __init twl6030_usb_init(void) 446{ 447 return platform_driver_register(&twl6030_usb_driver); 448} 449subsys_initcall(twl6030_usb_init); 450 451static void __exit twl6030_usb_exit(void) 452{ 453 platform_driver_unregister(&twl6030_usb_driver); 454} 455module_exit(twl6030_usb_exit); 456 457MODULE_ALIAS("platform:twl6030_usb"); 458MODULE_AUTHOR("Hema HK <hemahk@ti.com>"); 459MODULE_DESCRIPTION("TWL6030 USB transceiver driver"); 460MODULE_LICENSE("GPL");