162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Tahvo USB transceiver driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005-2006 Nokia Corporation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Parts copied from isp1301_omap.c. 862306a36Sopenharmony_ci * Copyright (C) 2004 Texas Instruments 962306a36Sopenharmony_ci * Copyright (C) 2004 David Brownell 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs. 1262306a36Sopenharmony_ci * Modified for Retu/Tahvo MFD by Aaro Koskinen. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/clk.h> 1762306a36Sopenharmony_ci#include <linux/usb.h> 1862306a36Sopenharmony_ci#include <linux/extcon-provider.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/usb/otg.h> 2262306a36Sopenharmony_ci#include <linux/mfd/retu.h> 2362306a36Sopenharmony_ci#include <linux/usb/gadget.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DRIVER_NAME "tahvo-usb" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define TAHVO_REG_IDSR 0x02 2962306a36Sopenharmony_ci#define TAHVO_REG_USBR 0x06 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define USBR_SLAVE_CONTROL (1 << 8) 3262306a36Sopenharmony_ci#define USBR_VPPVIO_SW (1 << 7) 3362306a36Sopenharmony_ci#define USBR_SPEED (1 << 6) 3462306a36Sopenharmony_ci#define USBR_REGOUT (1 << 5) 3562306a36Sopenharmony_ci#define USBR_MASTER_SW2 (1 << 4) 3662306a36Sopenharmony_ci#define USBR_MASTER_SW1 (1 << 3) 3762306a36Sopenharmony_ci#define USBR_SLAVE_SW (1 << 2) 3862306a36Sopenharmony_ci#define USBR_NSUSPEND (1 << 1) 3962306a36Sopenharmony_ci#define USBR_SEMODE (1 << 0) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define TAHVO_MODE_HOST 0 4262306a36Sopenharmony_ci#define TAHVO_MODE_PERIPHERAL 1 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct tahvo_usb { 4562306a36Sopenharmony_ci struct platform_device *pt_dev; 4662306a36Sopenharmony_ci struct usb_phy phy; 4762306a36Sopenharmony_ci int vbus_state; 4862306a36Sopenharmony_ci struct mutex serialize; 4962306a36Sopenharmony_ci struct clk *ick; 5062306a36Sopenharmony_ci int irq; 5162306a36Sopenharmony_ci int tahvo_mode; 5262306a36Sopenharmony_ci struct extcon_dev *extcon; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic const unsigned int tahvo_cable[] = { 5662306a36Sopenharmony_ci EXTCON_USB, 5762306a36Sopenharmony_ci EXTCON_USB_HOST, 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci EXTCON_NONE, 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic ssize_t vbus_show(struct device *device, 6362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct tahvo_usb *tu = dev_get_drvdata(device); 6662306a36Sopenharmony_ci return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off"); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(vbus); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void check_vbus_state(struct tahvo_usb *tu) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 7362306a36Sopenharmony_ci int reg, prev_state; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci reg = retu_read(rdev, TAHVO_REG_IDSR); 7662306a36Sopenharmony_ci if (reg & TAHVO_STAT_VBUS) { 7762306a36Sopenharmony_ci switch (tu->phy.otg->state) { 7862306a36Sopenharmony_ci case OTG_STATE_B_IDLE: 7962306a36Sopenharmony_ci /* Enable the gadget driver */ 8062306a36Sopenharmony_ci if (tu->phy.otg->gadget) 8162306a36Sopenharmony_ci usb_gadget_vbus_connect(tu->phy.otg->gadget); 8262306a36Sopenharmony_ci tu->phy.otg->state = OTG_STATE_B_PERIPHERAL; 8362306a36Sopenharmony_ci usb_phy_set_event(&tu->phy, USB_EVENT_ENUMERATED); 8462306a36Sopenharmony_ci break; 8562306a36Sopenharmony_ci case OTG_STATE_A_IDLE: 8662306a36Sopenharmony_ci /* 8762306a36Sopenharmony_ci * Session is now valid assuming the USB hub is driving 8862306a36Sopenharmony_ci * Vbus. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci tu->phy.otg->state = OTG_STATE_A_HOST; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci default: 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci dev_info(&tu->pt_dev->dev, "USB cable connected\n"); 9662306a36Sopenharmony_ci } else { 9762306a36Sopenharmony_ci switch (tu->phy.otg->state) { 9862306a36Sopenharmony_ci case OTG_STATE_B_PERIPHERAL: 9962306a36Sopenharmony_ci if (tu->phy.otg->gadget) 10062306a36Sopenharmony_ci usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 10162306a36Sopenharmony_ci tu->phy.otg->state = OTG_STATE_B_IDLE; 10262306a36Sopenharmony_ci usb_phy_set_event(&tu->phy, USB_EVENT_NONE); 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci case OTG_STATE_A_HOST: 10562306a36Sopenharmony_ci tu->phy.otg->state = OTG_STATE_A_IDLE; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci default: 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci dev_info(&tu->pt_dev->dev, "USB cable disconnected\n"); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci prev_state = tu->vbus_state; 11462306a36Sopenharmony_ci tu->vbus_state = reg & TAHVO_STAT_VBUS; 11562306a36Sopenharmony_ci if (prev_state != tu->vbus_state) { 11662306a36Sopenharmony_ci extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state); 11762306a36Sopenharmony_ci sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state"); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void tahvo_usb_become_host(struct tahvo_usb *tu) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, true); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* Power up the transceiver in USB host mode */ 12862306a36Sopenharmony_ci retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND | 12962306a36Sopenharmony_ci USBR_MASTER_SW2 | USBR_MASTER_SW1); 13062306a36Sopenharmony_ci tu->phy.otg->state = OTG_STATE_A_IDLE; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci check_vbus_state(tu); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void tahvo_usb_stop_host(struct tahvo_usb *tu) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci tu->phy.otg->state = OTG_STATE_A_IDLE; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void tahvo_usb_become_peripheral(struct tahvo_usb *tu) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, false); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* Power up transceiver and set it in USB peripheral mode */ 14762306a36Sopenharmony_ci retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT | 14862306a36Sopenharmony_ci USBR_NSUSPEND | USBR_SLAVE_SW); 14962306a36Sopenharmony_ci tu->phy.otg->state = OTG_STATE_B_IDLE; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci check_vbus_state(tu); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void tahvo_usb_stop_peripheral(struct tahvo_usb *tu) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci if (tu->phy.otg->gadget) 15762306a36Sopenharmony_ci usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 15862306a36Sopenharmony_ci tu->phy.otg->state = OTG_STATE_B_IDLE; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void tahvo_usb_power_off(struct tahvo_usb *tu) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Disable gadget controller if any */ 16662306a36Sopenharmony_ci if (tu->phy.otg->gadget) 16762306a36Sopenharmony_ci usb_gadget_vbus_disconnect(tu->phy.otg->gadget); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Power off transceiver */ 17062306a36Sopenharmony_ci retu_write(rdev, TAHVO_REG_USBR, 0); 17162306a36Sopenharmony_ci tu->phy.otg->state = OTG_STATE_UNDEFINED; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy); 17762306a36Sopenharmony_ci struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent); 17862306a36Sopenharmony_ci u16 w; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci dev_dbg(&tu->pt_dev->dev, "%s\n", __func__); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci w = retu_read(rdev, TAHVO_REG_USBR); 18362306a36Sopenharmony_ci if (suspend) 18462306a36Sopenharmony_ci w &= ~USBR_NSUSPEND; 18562306a36Sopenharmony_ci else 18662306a36Sopenharmony_ci w |= USBR_NSUSPEND; 18762306a36Sopenharmony_ci retu_write(rdev, TAHVO_REG_USBR, w); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb, 19562306a36Sopenharmony_ci phy); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci mutex_lock(&tu->serialize); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (host == NULL) { 20062306a36Sopenharmony_ci if (tu->tahvo_mode == TAHVO_MODE_HOST) 20162306a36Sopenharmony_ci tahvo_usb_power_off(tu); 20262306a36Sopenharmony_ci otg->host = NULL; 20362306a36Sopenharmony_ci mutex_unlock(&tu->serialize); 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (tu->tahvo_mode == TAHVO_MODE_HOST) { 20862306a36Sopenharmony_ci otg->host = NULL; 20962306a36Sopenharmony_ci tahvo_usb_become_host(tu); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci otg->host = host; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci mutex_unlock(&tu->serialize); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int tahvo_usb_set_peripheral(struct usb_otg *otg, 22062306a36Sopenharmony_ci struct usb_gadget *gadget) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb, 22362306a36Sopenharmony_ci phy); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci mutex_lock(&tu->serialize); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (!gadget) { 22862306a36Sopenharmony_ci if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 22962306a36Sopenharmony_ci tahvo_usb_power_off(tu); 23062306a36Sopenharmony_ci tu->phy.otg->gadget = NULL; 23162306a36Sopenharmony_ci mutex_unlock(&tu->serialize); 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci tu->phy.otg->gadget = gadget; 23662306a36Sopenharmony_ci if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 23762306a36Sopenharmony_ci tahvo_usb_become_peripheral(tu); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci mutex_unlock(&tu->serialize); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct tahvo_usb *tu = _tu; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci mutex_lock(&tu->serialize); 24962306a36Sopenharmony_ci check_vbus_state(tu); 25062306a36Sopenharmony_ci mutex_unlock(&tu->serialize); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return IRQ_HANDLED; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic ssize_t otg_mode_show(struct device *device, 25662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct tahvo_usb *tu = dev_get_drvdata(device); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci switch (tu->tahvo_mode) { 26162306a36Sopenharmony_ci case TAHVO_MODE_HOST: 26262306a36Sopenharmony_ci return sprintf(buf, "host\n"); 26362306a36Sopenharmony_ci case TAHVO_MODE_PERIPHERAL: 26462306a36Sopenharmony_ci return sprintf(buf, "peripheral\n"); 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return -EINVAL; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic ssize_t otg_mode_store(struct device *device, 27162306a36Sopenharmony_ci struct device_attribute *attr, 27262306a36Sopenharmony_ci const char *buf, size_t count) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct tahvo_usb *tu = dev_get_drvdata(device); 27562306a36Sopenharmony_ci int r; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci mutex_lock(&tu->serialize); 27862306a36Sopenharmony_ci if (count >= 4 && strncmp(buf, "host", 4) == 0) { 27962306a36Sopenharmony_ci if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) 28062306a36Sopenharmony_ci tahvo_usb_stop_peripheral(tu); 28162306a36Sopenharmony_ci tu->tahvo_mode = TAHVO_MODE_HOST; 28262306a36Sopenharmony_ci if (tu->phy.otg->host) { 28362306a36Sopenharmony_ci dev_info(device, "HOST mode: host controller present\n"); 28462306a36Sopenharmony_ci tahvo_usb_become_host(tu); 28562306a36Sopenharmony_ci } else { 28662306a36Sopenharmony_ci dev_info(device, "HOST mode: no host controller, powering off\n"); 28762306a36Sopenharmony_ci tahvo_usb_power_off(tu); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci r = strlen(buf); 29062306a36Sopenharmony_ci } else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) { 29162306a36Sopenharmony_ci if (tu->tahvo_mode == TAHVO_MODE_HOST) 29262306a36Sopenharmony_ci tahvo_usb_stop_host(tu); 29362306a36Sopenharmony_ci tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; 29462306a36Sopenharmony_ci if (tu->phy.otg->gadget) { 29562306a36Sopenharmony_ci dev_info(device, "PERIPHERAL mode: gadget driver present\n"); 29662306a36Sopenharmony_ci tahvo_usb_become_peripheral(tu); 29762306a36Sopenharmony_ci } else { 29862306a36Sopenharmony_ci dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n"); 29962306a36Sopenharmony_ci tahvo_usb_power_off(tu); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci r = strlen(buf); 30262306a36Sopenharmony_ci } else { 30362306a36Sopenharmony_ci r = -EINVAL; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci mutex_unlock(&tu->serialize); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return r; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(otg_mode); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic struct attribute *tahvo_attrs[] = { 31262306a36Sopenharmony_ci &dev_attr_vbus.attr, 31362306a36Sopenharmony_ci &dev_attr_otg_mode.attr, 31462306a36Sopenharmony_ci NULL 31562306a36Sopenharmony_ci}; 31662306a36Sopenharmony_ciATTRIBUTE_GROUPS(tahvo); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int tahvo_usb_probe(struct platform_device *pdev) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent); 32162306a36Sopenharmony_ci struct tahvo_usb *tu; 32262306a36Sopenharmony_ci int ret; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL); 32562306a36Sopenharmony_ci if (!tu) 32662306a36Sopenharmony_ci return -ENOMEM; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg), 32962306a36Sopenharmony_ci GFP_KERNEL); 33062306a36Sopenharmony_ci if (!tu->phy.otg) 33162306a36Sopenharmony_ci return -ENOMEM; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci tu->pt_dev = pdev; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Default mode */ 33662306a36Sopenharmony_ci#ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT 33762306a36Sopenharmony_ci tu->tahvo_mode = TAHVO_MODE_HOST; 33862306a36Sopenharmony_ci#else 33962306a36Sopenharmony_ci tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; 34062306a36Sopenharmony_ci#endif 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci mutex_init(&tu->serialize); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick"); 34562306a36Sopenharmony_ci if (!IS_ERR(tu->ick)) 34662306a36Sopenharmony_ci clk_enable(tu->ick); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * Set initial state, so that we generate kevents only on state changes. 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci tu->extcon = devm_extcon_dev_allocate(&pdev->dev, tahvo_cable); 35462306a36Sopenharmony_ci if (IS_ERR(tu->extcon)) { 35562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate memory for extcon\n"); 35662306a36Sopenharmony_ci ret = PTR_ERR(tu->extcon); 35762306a36Sopenharmony_ci goto err_disable_clk; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci ret = devm_extcon_dev_register(&pdev->dev, tu->extcon); 36162306a36Sopenharmony_ci if (ret) { 36262306a36Sopenharmony_ci dev_err(&pdev->dev, "could not register extcon device: %d\n", 36362306a36Sopenharmony_ci ret); 36462306a36Sopenharmony_ci goto err_disable_clk; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Set the initial cable state. */ 36862306a36Sopenharmony_ci extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, 36962306a36Sopenharmony_ci tu->tahvo_mode == TAHVO_MODE_HOST); 37062306a36Sopenharmony_ci extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* Create OTG interface */ 37362306a36Sopenharmony_ci tahvo_usb_power_off(tu); 37462306a36Sopenharmony_ci tu->phy.dev = &pdev->dev; 37562306a36Sopenharmony_ci tu->phy.otg->state = OTG_STATE_UNDEFINED; 37662306a36Sopenharmony_ci tu->phy.label = DRIVER_NAME; 37762306a36Sopenharmony_ci tu->phy.set_suspend = tahvo_usb_set_suspend; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci tu->phy.otg->usb_phy = &tu->phy; 38062306a36Sopenharmony_ci tu->phy.otg->set_host = tahvo_usb_set_host; 38162306a36Sopenharmony_ci tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2); 38462306a36Sopenharmony_ci if (ret < 0) { 38562306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot register USB transceiver: %d\n", 38662306a36Sopenharmony_ci ret); 38762306a36Sopenharmony_ci goto err_disable_clk; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, tu); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci tu->irq = ret = platform_get_irq(pdev, 0); 39362306a36Sopenharmony_ci if (ret < 0) 39462306a36Sopenharmony_ci goto err_remove_phy; 39562306a36Sopenharmony_ci ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, 39662306a36Sopenharmony_ci IRQF_ONESHOT, 39762306a36Sopenharmony_ci "tahvo-vbus", tu); 39862306a36Sopenharmony_ci if (ret) { 39962306a36Sopenharmony_ci dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n", 40062306a36Sopenharmony_ci ret); 40162306a36Sopenharmony_ci goto err_remove_phy; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cierr_remove_phy: 40762306a36Sopenharmony_ci usb_remove_phy(&tu->phy); 40862306a36Sopenharmony_cierr_disable_clk: 40962306a36Sopenharmony_ci if (!IS_ERR(tu->ick)) 41062306a36Sopenharmony_ci clk_disable(tu->ick); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return ret; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void tahvo_usb_remove(struct platform_device *pdev) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct tahvo_usb *tu = platform_get_drvdata(pdev); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci free_irq(tu->irq, tu); 42062306a36Sopenharmony_ci usb_remove_phy(&tu->phy); 42162306a36Sopenharmony_ci if (!IS_ERR(tu->ick)) 42262306a36Sopenharmony_ci clk_disable(tu->ick); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic struct platform_driver tahvo_usb_driver = { 42662306a36Sopenharmony_ci .probe = tahvo_usb_probe, 42762306a36Sopenharmony_ci .remove_new = tahvo_usb_remove, 42862306a36Sopenharmony_ci .driver = { 42962306a36Sopenharmony_ci .name = "tahvo-usb", 43062306a36Sopenharmony_ci .dev_groups = tahvo_groups, 43162306a36Sopenharmony_ci }, 43262306a36Sopenharmony_ci}; 43362306a36Sopenharmony_cimodule_platform_driver(tahvo_usb_driver); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ciMODULE_DESCRIPTION("Tahvo USB transceiver driver"); 43662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 43762306a36Sopenharmony_ciMODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs"); 43862306a36Sopenharmony_ciMODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); 439