162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Palmas USB transceiver driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com 662306a36Sopenharmony_ci * Author: Graeme Gregory <gg@slimlogic.co.uk> 762306a36Sopenharmony_ci * Author: Kishon Vijay Abraham I <kishon@ti.com> 862306a36Sopenharmony_ci * Based on twl6030_usb.c 962306a36Sopenharmony_ci * Author: Hema HK <hemahk@ti.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/devm-helpers.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/mfd/palmas.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/of_platform.h> 2162306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2262306a36Sopenharmony_ci#include <linux/workqueue.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define USB_GPIO_DEBOUNCE_MS 20 /* ms */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic const unsigned int palmas_extcon_cable[] = { 2762306a36Sopenharmony_ci EXTCON_USB, 2862306a36Sopenharmony_ci EXTCON_USB_HOST, 2962306a36Sopenharmony_ci EXTCON_NONE, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void palmas_usb_wakeup(struct palmas *palmas, int enable) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci if (enable) 3562306a36Sopenharmony_ci palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP, 3662306a36Sopenharmony_ci PALMAS_USB_WAKEUP_ID_WK_UP_COMP); 3762306a36Sopenharmony_ci else 3862306a36Sopenharmony_ci palmas_write(palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_WAKEUP, 0); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct palmas_usb *palmas_usb = _palmas_usb; 4462306a36Sopenharmony_ci struct extcon_dev *edev = palmas_usb->edev; 4562306a36Sopenharmony_ci unsigned int vbus_line_state; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci palmas_read(palmas_usb->palmas, PALMAS_INTERRUPT_BASE, 4862306a36Sopenharmony_ci PALMAS_INT3_LINE_STATE, &vbus_line_state); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS) { 5162306a36Sopenharmony_ci if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) { 5262306a36Sopenharmony_ci palmas_usb->linkstat = PALMAS_USB_STATE_VBUS; 5362306a36Sopenharmony_ci extcon_set_state_sync(edev, EXTCON_USB, true); 5462306a36Sopenharmony_ci dev_dbg(palmas_usb->dev, "USB cable is attached\n"); 5562306a36Sopenharmony_ci } else { 5662306a36Sopenharmony_ci dev_dbg(palmas_usb->dev, 5762306a36Sopenharmony_ci "Spurious connect event detected\n"); 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci } else if (!(vbus_line_state & PALMAS_INT3_LINE_STATE_VBUS)) { 6062306a36Sopenharmony_ci if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) { 6162306a36Sopenharmony_ci palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; 6262306a36Sopenharmony_ci extcon_set_state_sync(edev, EXTCON_USB, false); 6362306a36Sopenharmony_ci dev_dbg(palmas_usb->dev, "USB cable is detached\n"); 6462306a36Sopenharmony_ci } else { 6562306a36Sopenharmony_ci dev_dbg(palmas_usb->dev, 6662306a36Sopenharmony_ci "Spurious disconnect event detected\n"); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return IRQ_HANDLED; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci unsigned int set, id_src; 7662306a36Sopenharmony_ci struct palmas_usb *palmas_usb = _palmas_usb; 7762306a36Sopenharmony_ci struct extcon_dev *edev = palmas_usb->edev; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE, 8062306a36Sopenharmony_ci PALMAS_USB_ID_INT_LATCH_SET, &set); 8162306a36Sopenharmony_ci palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE, 8262306a36Sopenharmony_ci PALMAS_USB_ID_INT_SRC, &id_src); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if ((set & PALMAS_USB_ID_INT_SRC_ID_GND) && 8562306a36Sopenharmony_ci (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { 8662306a36Sopenharmony_ci palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, 8762306a36Sopenharmony_ci PALMAS_USB_ID_INT_LATCH_CLR, 8862306a36Sopenharmony_ci PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND); 8962306a36Sopenharmony_ci palmas_usb->linkstat = PALMAS_USB_STATE_ID; 9062306a36Sopenharmony_ci extcon_set_state_sync(edev, EXTCON_USB_HOST, true); 9162306a36Sopenharmony_ci dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n"); 9262306a36Sopenharmony_ci } else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) && 9362306a36Sopenharmony_ci (id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) { 9462306a36Sopenharmony_ci palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, 9562306a36Sopenharmony_ci PALMAS_USB_ID_INT_LATCH_CLR, 9662306a36Sopenharmony_ci PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT); 9762306a36Sopenharmony_ci palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; 9862306a36Sopenharmony_ci extcon_set_state_sync(edev, EXTCON_USB_HOST, false); 9962306a36Sopenharmony_ci dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n"); 10062306a36Sopenharmony_ci } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) && 10162306a36Sopenharmony_ci (!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) { 10262306a36Sopenharmony_ci palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT; 10362306a36Sopenharmony_ci extcon_set_state_sync(edev, EXTCON_USB_HOST, false); 10462306a36Sopenharmony_ci dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n"); 10562306a36Sopenharmony_ci } else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) && 10662306a36Sopenharmony_ci (id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) { 10762306a36Sopenharmony_ci palmas_usb->linkstat = PALMAS_USB_STATE_ID; 10862306a36Sopenharmony_ci extcon_set_state_sync(edev, EXTCON_USB_HOST, true); 10962306a36Sopenharmony_ci dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n"); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return IRQ_HANDLED; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic void palmas_gpio_id_detect(struct work_struct *work) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int id; 11862306a36Sopenharmony_ci struct palmas_usb *palmas_usb = container_of(to_delayed_work(work), 11962306a36Sopenharmony_ci struct palmas_usb, 12062306a36Sopenharmony_ci wq_detectid); 12162306a36Sopenharmony_ci struct extcon_dev *edev = palmas_usb->edev; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!palmas_usb->id_gpiod) 12462306a36Sopenharmony_ci return; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci id = gpiod_get_value_cansleep(palmas_usb->id_gpiod); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (id) { 12962306a36Sopenharmony_ci extcon_set_state_sync(edev, EXTCON_USB_HOST, false); 13062306a36Sopenharmony_ci dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n"); 13162306a36Sopenharmony_ci } else { 13262306a36Sopenharmony_ci extcon_set_state_sync(edev, EXTCON_USB_HOST, true); 13362306a36Sopenharmony_ci dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n"); 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic irqreturn_t palmas_gpio_id_irq_handler(int irq, void *_palmas_usb) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct palmas_usb *palmas_usb = _palmas_usb; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &palmas_usb->wq_detectid, 14262306a36Sopenharmony_ci palmas_usb->sw_debounce_jiffies); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return IRQ_HANDLED; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void palmas_enable_irq(struct palmas_usb *palmas_usb) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, 15062306a36Sopenharmony_ci PALMAS_USB_VBUS_CTRL_SET, 15162306a36Sopenharmony_ci PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (palmas_usb->enable_id_detection) { 15462306a36Sopenharmony_ci palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, 15562306a36Sopenharmony_ci PALMAS_USB_ID_CTRL_SET, 15662306a36Sopenharmony_ci PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, 15962306a36Sopenharmony_ci PALMAS_USB_ID_INT_EN_HI_SET, 16062306a36Sopenharmony_ci PALMAS_USB_ID_INT_EN_HI_SET_ID_GND | 16162306a36Sopenharmony_ci PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (palmas_usb->enable_vbus_detection) 16562306a36Sopenharmony_ci palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* cold plug for host mode needs this delay */ 16862306a36Sopenharmony_ci if (palmas_usb->enable_id_detection) { 16962306a36Sopenharmony_ci msleep(30); 17062306a36Sopenharmony_ci palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int palmas_usb_probe(struct platform_device *pdev) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); 17762306a36Sopenharmony_ci struct palmas_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); 17862306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 17962306a36Sopenharmony_ci struct palmas_usb *palmas_usb; 18062306a36Sopenharmony_ci int status; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (!palmas) { 18362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get valid parent\n"); 18462306a36Sopenharmony_ci return -EINVAL; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL); 18862306a36Sopenharmony_ci if (!palmas_usb) 18962306a36Sopenharmony_ci return -ENOMEM; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (node && !pdata) { 19262306a36Sopenharmony_ci palmas_usb->wakeup = of_property_read_bool(node, "ti,wakeup"); 19362306a36Sopenharmony_ci palmas_usb->enable_id_detection = of_property_read_bool(node, 19462306a36Sopenharmony_ci "ti,enable-id-detection"); 19562306a36Sopenharmony_ci palmas_usb->enable_vbus_detection = of_property_read_bool(node, 19662306a36Sopenharmony_ci "ti,enable-vbus-detection"); 19762306a36Sopenharmony_ci } else { 19862306a36Sopenharmony_ci palmas_usb->wakeup = true; 19962306a36Sopenharmony_ci palmas_usb->enable_id_detection = true; 20062306a36Sopenharmony_ci palmas_usb->enable_vbus_detection = true; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (pdata) 20362306a36Sopenharmony_ci palmas_usb->wakeup = pdata->wakeup; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci palmas_usb->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id", 20762306a36Sopenharmony_ci GPIOD_IN); 20862306a36Sopenharmony_ci if (IS_ERR(palmas_usb->id_gpiod)) 20962306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(palmas_usb->id_gpiod), 21062306a36Sopenharmony_ci "failed to get id gpio\n"); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci palmas_usb->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus", 21362306a36Sopenharmony_ci GPIOD_IN); 21462306a36Sopenharmony_ci if (IS_ERR(palmas_usb->vbus_gpiod)) 21562306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(palmas_usb->vbus_gpiod), 21662306a36Sopenharmony_ci "failed to get id gpio\n"); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (palmas_usb->enable_id_detection && palmas_usb->id_gpiod) { 21962306a36Sopenharmony_ci palmas_usb->enable_id_detection = false; 22062306a36Sopenharmony_ci palmas_usb->enable_gpio_id_detection = true; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (palmas_usb->enable_vbus_detection && palmas_usb->vbus_gpiod) { 22462306a36Sopenharmony_ci palmas_usb->enable_vbus_detection = false; 22562306a36Sopenharmony_ci palmas_usb->enable_gpio_vbus_detection = true; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (palmas_usb->enable_gpio_id_detection) { 22962306a36Sopenharmony_ci u32 debounce; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (of_property_read_u32(node, "debounce-delay-ms", &debounce)) 23262306a36Sopenharmony_ci debounce = USB_GPIO_DEBOUNCE_MS; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci status = gpiod_set_debounce(palmas_usb->id_gpiod, 23562306a36Sopenharmony_ci debounce * 1000); 23662306a36Sopenharmony_ci if (status < 0) 23762306a36Sopenharmony_ci palmas_usb->sw_debounce_jiffies = msecs_to_jiffies(debounce); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci status = devm_delayed_work_autocancel(&pdev->dev, 24162306a36Sopenharmony_ci &palmas_usb->wq_detectid, 24262306a36Sopenharmony_ci palmas_gpio_id_detect); 24362306a36Sopenharmony_ci if (status) 24462306a36Sopenharmony_ci return status; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci palmas->usb = palmas_usb; 24762306a36Sopenharmony_ci palmas_usb->palmas = palmas; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci palmas_usb->dev = &pdev->dev; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci palmas_usb_wakeup(palmas, palmas_usb->wakeup); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci platform_set_drvdata(pdev, palmas_usb); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev, 25662306a36Sopenharmony_ci palmas_extcon_cable); 25762306a36Sopenharmony_ci if (IS_ERR(palmas_usb->edev)) { 25862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate extcon device\n"); 25962306a36Sopenharmony_ci return -ENOMEM; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev); 26362306a36Sopenharmony_ci if (status) { 26462306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register extcon device\n"); 26562306a36Sopenharmony_ci return status; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (palmas_usb->enable_id_detection) { 26962306a36Sopenharmony_ci palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data, 27062306a36Sopenharmony_ci PALMAS_ID_OTG_IRQ); 27162306a36Sopenharmony_ci palmas_usb->id_irq = regmap_irq_get_virq(palmas->irq_data, 27262306a36Sopenharmony_ci PALMAS_ID_IRQ); 27362306a36Sopenharmony_ci status = devm_request_threaded_irq(palmas_usb->dev, 27462306a36Sopenharmony_ci palmas_usb->id_irq, 27562306a36Sopenharmony_ci NULL, palmas_id_irq_handler, 27662306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | 27762306a36Sopenharmony_ci IRQF_ONESHOT, 27862306a36Sopenharmony_ci "palmas_usb_id", palmas_usb); 27962306a36Sopenharmony_ci if (status < 0) { 28062306a36Sopenharmony_ci dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", 28162306a36Sopenharmony_ci palmas_usb->id_irq, status); 28262306a36Sopenharmony_ci return status; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci } else if (palmas_usb->enable_gpio_id_detection) { 28562306a36Sopenharmony_ci palmas_usb->gpio_id_irq = gpiod_to_irq(palmas_usb->id_gpiod); 28662306a36Sopenharmony_ci if (palmas_usb->gpio_id_irq < 0) { 28762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get id irq\n"); 28862306a36Sopenharmony_ci return palmas_usb->gpio_id_irq; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci status = devm_request_threaded_irq(&pdev->dev, 29162306a36Sopenharmony_ci palmas_usb->gpio_id_irq, 29262306a36Sopenharmony_ci NULL, 29362306a36Sopenharmony_ci palmas_gpio_id_irq_handler, 29462306a36Sopenharmony_ci IRQF_TRIGGER_RISING | 29562306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | 29662306a36Sopenharmony_ci IRQF_ONESHOT, 29762306a36Sopenharmony_ci "palmas_usb_id", 29862306a36Sopenharmony_ci palmas_usb); 29962306a36Sopenharmony_ci if (status < 0) { 30062306a36Sopenharmony_ci dev_err(&pdev->dev, 30162306a36Sopenharmony_ci "failed to request handler for id irq\n"); 30262306a36Sopenharmony_ci return status; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (palmas_usb->enable_vbus_detection) { 30762306a36Sopenharmony_ci palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data, 30862306a36Sopenharmony_ci PALMAS_VBUS_OTG_IRQ); 30962306a36Sopenharmony_ci palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data, 31062306a36Sopenharmony_ci PALMAS_VBUS_IRQ); 31162306a36Sopenharmony_ci status = devm_request_threaded_irq(palmas_usb->dev, 31262306a36Sopenharmony_ci palmas_usb->vbus_irq, NULL, 31362306a36Sopenharmony_ci palmas_vbus_irq_handler, 31462306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | 31562306a36Sopenharmony_ci IRQF_ONESHOT, 31662306a36Sopenharmony_ci "palmas_usb_vbus", palmas_usb); 31762306a36Sopenharmony_ci if (status < 0) { 31862306a36Sopenharmony_ci dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", 31962306a36Sopenharmony_ci palmas_usb->vbus_irq, status); 32062306a36Sopenharmony_ci return status; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } else if (palmas_usb->enable_gpio_vbus_detection) { 32362306a36Sopenharmony_ci /* remux GPIO_1 as VBUSDET */ 32462306a36Sopenharmony_ci status = palmas_update_bits(palmas, 32562306a36Sopenharmony_ci PALMAS_PU_PD_OD_BASE, 32662306a36Sopenharmony_ci PALMAS_PRIMARY_SECONDARY_PAD1, 32762306a36Sopenharmony_ci PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_MASK, 32862306a36Sopenharmony_ci (1 << PALMAS_PRIMARY_SECONDARY_PAD1_GPIO_1_SHIFT)); 32962306a36Sopenharmony_ci if (status < 0) { 33062306a36Sopenharmony_ci dev_err(&pdev->dev, "can't remux GPIO1\n"); 33162306a36Sopenharmony_ci return status; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data, 33562306a36Sopenharmony_ci PALMAS_VBUS_OTG_IRQ); 33662306a36Sopenharmony_ci palmas_usb->gpio_vbus_irq = gpiod_to_irq(palmas_usb->vbus_gpiod); 33762306a36Sopenharmony_ci if (palmas_usb->gpio_vbus_irq < 0) { 33862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get vbus irq\n"); 33962306a36Sopenharmony_ci return palmas_usb->gpio_vbus_irq; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci status = devm_request_threaded_irq(&pdev->dev, 34262306a36Sopenharmony_ci palmas_usb->gpio_vbus_irq, 34362306a36Sopenharmony_ci NULL, 34462306a36Sopenharmony_ci palmas_vbus_irq_handler, 34562306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | 34662306a36Sopenharmony_ci IRQF_TRIGGER_RISING | 34762306a36Sopenharmony_ci IRQF_ONESHOT, 34862306a36Sopenharmony_ci "palmas_usb_vbus", 34962306a36Sopenharmony_ci palmas_usb); 35062306a36Sopenharmony_ci if (status < 0) { 35162306a36Sopenharmony_ci dev_err(&pdev->dev, 35262306a36Sopenharmony_ci "failed to request handler for vbus irq\n"); 35362306a36Sopenharmony_ci return status; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci palmas_enable_irq(palmas_usb); 35862306a36Sopenharmony_ci /* perform initial detection */ 35962306a36Sopenharmony_ci if (palmas_usb->enable_gpio_vbus_detection) 36062306a36Sopenharmony_ci palmas_vbus_irq_handler(palmas_usb->gpio_vbus_irq, palmas_usb); 36162306a36Sopenharmony_ci palmas_gpio_id_detect(&palmas_usb->wq_detectid.work); 36262306a36Sopenharmony_ci device_set_wakeup_capable(&pdev->dev, true); 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 36762306a36Sopenharmony_cistatic int palmas_usb_suspend(struct device *dev) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct palmas_usb *palmas_usb = dev_get_drvdata(dev); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (device_may_wakeup(dev)) { 37262306a36Sopenharmony_ci if (palmas_usb->enable_vbus_detection) 37362306a36Sopenharmony_ci enable_irq_wake(palmas_usb->vbus_irq); 37462306a36Sopenharmony_ci if (palmas_usb->enable_gpio_vbus_detection) 37562306a36Sopenharmony_ci enable_irq_wake(palmas_usb->gpio_vbus_irq); 37662306a36Sopenharmony_ci if (palmas_usb->enable_id_detection) 37762306a36Sopenharmony_ci enable_irq_wake(palmas_usb->id_irq); 37862306a36Sopenharmony_ci if (palmas_usb->enable_gpio_id_detection) 37962306a36Sopenharmony_ci enable_irq_wake(palmas_usb->gpio_id_irq); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int palmas_usb_resume(struct device *dev) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct palmas_usb *palmas_usb = dev_get_drvdata(dev); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (device_may_wakeup(dev)) { 38962306a36Sopenharmony_ci if (palmas_usb->enable_vbus_detection) 39062306a36Sopenharmony_ci disable_irq_wake(palmas_usb->vbus_irq); 39162306a36Sopenharmony_ci if (palmas_usb->enable_gpio_vbus_detection) 39262306a36Sopenharmony_ci disable_irq_wake(palmas_usb->gpio_vbus_irq); 39362306a36Sopenharmony_ci if (palmas_usb->enable_id_detection) 39462306a36Sopenharmony_ci disable_irq_wake(palmas_usb->id_irq); 39562306a36Sopenharmony_ci if (palmas_usb->enable_gpio_id_detection) 39662306a36Sopenharmony_ci disable_irq_wake(palmas_usb->gpio_id_irq); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* check if GPIO states changed while suspend/resume */ 40062306a36Sopenharmony_ci if (palmas_usb->enable_gpio_vbus_detection) 40162306a36Sopenharmony_ci palmas_vbus_irq_handler(palmas_usb->gpio_vbus_irq, palmas_usb); 40262306a36Sopenharmony_ci palmas_gpio_id_detect(&palmas_usb->wq_detectid.work); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci}; 40662306a36Sopenharmony_ci#endif 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_usb_suspend, palmas_usb_resume); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic const struct of_device_id of_palmas_match_tbl[] = { 41162306a36Sopenharmony_ci { .compatible = "ti,palmas-usb", }, 41262306a36Sopenharmony_ci { .compatible = "ti,palmas-usb-vid", }, 41362306a36Sopenharmony_ci { .compatible = "ti,twl6035-usb", }, 41462306a36Sopenharmony_ci { .compatible = "ti,twl6035-usb-vid", }, 41562306a36Sopenharmony_ci { /* end */ } 41662306a36Sopenharmony_ci}; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic struct platform_driver palmas_usb_driver = { 41962306a36Sopenharmony_ci .probe = palmas_usb_probe, 42062306a36Sopenharmony_ci .driver = { 42162306a36Sopenharmony_ci .name = "palmas-usb", 42262306a36Sopenharmony_ci .of_match_table = of_palmas_match_tbl, 42362306a36Sopenharmony_ci .pm = &palmas_pm_ops, 42462306a36Sopenharmony_ci }, 42562306a36Sopenharmony_ci}; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cimodule_platform_driver(palmas_usb_driver); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ciMODULE_ALIAS("platform:palmas-usb"); 43062306a36Sopenharmony_ciMODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); 43162306a36Sopenharmony_ciMODULE_DESCRIPTION("Palmas USB transceiver driver"); 43262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 43362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_palmas_match_tbl); 434