cregit-Linux how code gets into the kernel

Release 4.7 drivers/usb/phy/phy-twl6030-usb.c

Directory: drivers/usb/phy
/*
 * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver.
 *
 * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Author: Hema HK <hemahk@ti.com>
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/usb/musb.h>
#include <linux/usb/phy_companion.h>
#include <linux/phy/omap_usb.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/of.h>

/* usb register definitions */

#define USB_VENDOR_ID_LSB		0x00

#define USB_VENDOR_ID_MSB		0x01

#define USB_PRODUCT_ID_LSB		0x02

#define USB_PRODUCT_ID_MSB		0x03

#define USB_VBUS_CTRL_SET		0x04

#define USB_VBUS_CTRL_CLR		0x05

#define USB_ID_CTRL_SET			0x06

#define USB_ID_CTRL_CLR			0x07

#define USB_VBUS_INT_SRC		0x08

#define USB_VBUS_INT_LATCH_SET		0x09

#define USB_VBUS_INT_LATCH_CLR		0x0A

#define USB_VBUS_INT_EN_LO_SET		0x0B

#define USB_VBUS_INT_EN_LO_CLR		0x0C

#define USB_VBUS_INT_EN_HI_SET		0x0D

#define USB_VBUS_INT_EN_HI_CLR		0x0E

#define USB_ID_INT_SRC			0x0F

#define USB_ID_INT_LATCH_SET		0x10

#define USB_ID_INT_LATCH_CLR		0x11


#define USB_ID_INT_EN_LO_SET		0x12

#define USB_ID_INT_EN_LO_CLR		0x13

#define USB_ID_INT_EN_HI_SET		0x14

#define USB_ID_INT_EN_HI_CLR		0x15

#define USB_OTG_ADP_CTRL		0x16

#define USB_OTG_ADP_HIGH		0x17

#define USB_OTG_ADP_LOW			0x18

#define USB_OTG_ADP_RISE		0x19

#define USB_OTG_REVISION		0x1A

/* to be moved to LDO */

#define TWL6030_MISC2			0xE5

#define TWL6030_CFG_LDO_PD2		0xF5

#define TWL6030_BACKUP_REG		0xFA


#define STS_HW_CONDITIONS		0x21

/* In module TWL6030_MODULE_PM_MASTER */

#define STS_HW_CONDITIONS		0x21

#define STS_USB_ID			BIT(2)

/* In module TWL6030_MODULE_PM_RECEIVER */

#define VUSB_CFG_TRANS			0x71

#define VUSB_CFG_STATE			0x72

#define VUSB_CFG_VOLTAGE		0x73

/* in module TWL6030_MODULE_MAIN_CHARGE */


#define CHARGERUSB_CTRL1		0x8


#define CONTROLLER_STAT1		0x03

#define	VBUS_DET			BIT(2)


struct twl6030_usb {
	
struct phy_companion	comparator;
	
struct device		*dev;

	/* for vbus reporting with irqs disabled */
	
spinlock_t		lock;

	
struct regulator		*usb3v3;

	/* used to check initial cable status after probe */
	
struct delayed_work	get_status_work;

	/* used to set vbus, in atomic path */
	
struct work_struct	set_vbus_work;

	
int			irq1;
	
int			irq2;
	
enum musb_vbus_id_status linkstat;
	
u8			asleep;
	
bool			vbus_enable;
	
const char		*regulator;
};


#define	comparator_to_twl(x) container_of((x), struct twl6030_usb, comparator)

/*-------------------------------------------------------------------------*/


static inline int twl6030_writeb(struct twl6030_usb *twl, u8 module, u8 data, u8 address) { int ret = 0; ret = twl_i2c_write_u8(module, data, address); if (ret < 0) dev_err(twl->dev, "Write[0x%x] Error %d\n", address, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi59100.00%1100.00%
Total59100.00%1100.00%


static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) { u8 data; int ret; ret = twl_i2c_read_u8(module, &data, address); if (ret >= 0) ret = data; else dev_err(twl->dev, "readb[0x%x,0x%x] Error %d\n", module, address, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi6396.92%150.00%
dan carpenterdan carpenter23.08%150.00%
Total65100.00%2100.00%


static int twl6030_start_srp(struct phy_companion *comparator) { struct twl6030_usb *twl = comparator_to_twl(comparator); twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET); twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET); mdelay(100); twl6030_writeb(twl, TWL_MODULE_USB, 0xa0, USB_VBUS_CTRL_CLR); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi5893.55%150.00%
kishon vijay abraham ikishon vijay abraham i46.45%150.00%
Total62100.00%2100.00%


static int twl6030_usb_ldo_init(struct twl6030_usb *twl) { /* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */ twl6030_writeb(twl, TWL6030_MODULE_ID0, 0x1, TWL6030_BACKUP_REG); /* Program CFG_LDO_PD2 register and set VUSB bit */ twl6030_writeb(twl, TWL6030_MODULE_ID0, 0x1, TWL6030_CFG_LDO_PD2); /* Program MISC2 register and set bit VUSB_IN_VBAT */ twl6030_writeb(twl, TWL6030_MODULE_ID0, 0x10, TWL6030_MISC2); twl->usb3v3 = regulator_get(twl->dev, twl->regulator); if (IS_ERR(twl->usb3v3)) return -ENODEV; /* Program the USB_VBUS_CTRL_SET and set VBUS_ACT_COMP bit */ twl6030_writeb(twl, TWL_MODULE_USB, 0x4, USB_VBUS_CTRL_SET); /* * Program the USB_ID_CTRL_SET register to enable GND drive * and the ID comparators */ twl6030_writeb(twl, TWL_MODULE_USB, 0x14, USB_ID_CTRL_SET); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi9997.06%150.00%
kishon vijay abraham ikishon vijay abraham i32.94%150.00%
Total102100.00%2100.00%


static ssize_t twl6030_usb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) { struct twl6030_usb *twl = dev_get_drvdata(dev); unsigned long flags; int ret = -EINVAL; spin_lock_irqsave(&twl->lock, flags); switch (twl->linkstat) { case MUSB_VBUS_VALID: ret = snprintf(buf, PAGE_SIZE, "vbus\n"); break; case MUSB_ID_GROUND: ret = snprintf(buf, PAGE_SIZE, "id\n"); break; case MUSB_VBUS_OFF: ret = snprintf(buf, PAGE_SIZE, "none\n"); break; default: ret = snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); } spin_unlock_irqrestore(&twl->lock, flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi12597.66%150.00%
tony lindgrentony lindgren32.34%150.00%
Total128100.00%2100.00%

static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL);
static irqreturn_t twl6030_usb_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; enum musb_vbus_id_status status = MUSB_UNKNOWN; u8 vbus_state, hw_state; int ret; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, CONTROLLER_STAT1); if (!(hw_state & STS_USB_ID)) { if (vbus_state & VBUS_DET) { ret = regulator_enable(twl->usb3v3); if (ret) dev_err(twl->dev, "Failed to enable usb3v3\n"); twl->asleep = 1; status = MUSB_VBUS_VALID; twl->linkstat = status; ret = musb_mailbox(status); if (ret) twl->linkstat = MUSB_UNKNOWN; } else { if (twl->linkstat != MUSB_UNKNOWN) { status = MUSB_VBUS_OFF; twl->linkstat = status; ret = musb_mailbox(status); if (ret) twl->linkstat = MUSB_UNKNOWN; if (twl->asleep) { regulator_disable(twl->usb3v3); twl->asleep = 0; } } } } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi14971.63%342.86%
tony lindgrentony lindgren3114.90%228.57%
fabio baltierifabio baltieri188.65%114.29%
kishon vijay abraham ikishon vijay abraham i104.81%114.29%
Total208100.00%7100.00%


static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; enum musb_vbus_id_status status = MUSB_UNKNOWN; u8 hw_state; int ret; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); if (hw_state & STS_USB_ID) { ret = regulator_enable(twl->usb3v3); if (ret) dev_err(twl->dev, "Failed to enable usb3v3\n"); twl->asleep = 1; twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR); twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET); status = MUSB_ID_GROUND; twl->linkstat = status; ret = musb_mailbox(status); if (ret) twl->linkstat = MUSB_UNKNOWN; } else { twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR); twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); } twl6030_writeb(twl, TWL_MODULE_USB, status, USB_ID_INT_LATCH_CLR); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi12273.05%337.50%
fabio baltierifabio baltieri1810.78%112.50%
tony lindgrentony lindgren169.58%225.00%
moiz sonasathmoiz sonasath105.99%112.50%
kishon vijay abraham ikishon vijay abraham i10.60%112.50%
Total167100.00%8100.00%


static void twl6030_status_work(struct work_struct *work) { struct twl6030_usb *twl = container_of(work, struct twl6030_usb, get_status_work.work); twl6030_usb_irq(twl->irq2, twl); twl6030_usbotg_irq(twl->irq1, twl); }

Contributors

PersonTokensPropCommitsCommitProp
tony lindgrentony lindgren46100.00%1100.00%
Total46100.00%1100.00%


static int twl6030_enable_irq(struct twl6030_usb *twl) { twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C); twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C); twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, REG_INT_MSK_LINE_C); twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK, REG_INT_MSK_STS_C); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi4992.45%133.33%
moiz sonasathmoiz sonasath23.77%133.33%
kishon vijay abraham ikishon vijay abraham i23.77%133.33%
Total53100.00%3100.00%


static void otg_set_vbus_work(struct work_struct *data) { struct twl6030_usb *twl = container_of(data, struct twl6030_usb, set_vbus_work); /* * Start driving VBUS. Set OPA_MODE bit in CHARGERUSB_CTRL1 * register. This enables boost mode. */ if (twl->vbus_enable) twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE, 0x40, CHARGERUSB_CTRL1); else twl6030_writeb(twl, TWL_MODULE_MAIN_CHARGE, 0x00, CHARGERUSB_CTRL1); }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi4173.21%150.00%
moiz sonasathmoiz sonasath1526.79%150.00%
Total56100.00%2100.00%


static int twl6030_set_vbus(struct phy_companion *comparator, bool enabled) { struct twl6030_usb *twl = comparator_to_twl(comparator); twl->vbus_enable = enabled; schedule_work(&twl->set_vbus_work); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
moiz sonasathmoiz sonasath3380.49%133.33%
hema kalliguddihema kalliguddi49.76%133.33%
kishon vijay abraham ikishon vijay abraham i49.76%133.33%
Total41100.00%3100.00%


static int twl6030_usb_probe(struct platform_device *pdev) { u32 ret; struct twl6030_usb *twl; int status, err; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; struct twl4030_usb_data *pdata = dev_get_platdata(dev); twl = devm_kzalloc(dev, sizeof(*twl), GFP_KERNEL); if (!twl) return -ENOMEM; twl->dev = &pdev->dev; twl->irq1 = platform_get_irq(pdev, 0); twl->irq2 = platform_get_irq(pdev, 1); twl->linkstat = MUSB_UNKNOWN; twl->comparator.set_vbus = twl6030_set_vbus; twl->comparator.start_srp = twl6030_start_srp; ret = omap_usb2_set_comparator(&twl->comparator); if (ret == -ENODEV) { dev_info(&pdev->dev, "phy not ready, deferring probe"); return -EPROBE_DEFER; } if (np) { twl->regulator = "usb"; } else if (pdata) { if (pdata->features & TWL6032_SUBCLASS) twl->regulator = "ldousb"; else twl->regulator = "vusb"; } else { dev_err(&pdev->dev, "twl6030 initialized without pdata\n"); return -EINVAL; } /* init spinlock for workqueue */ spin_lock_init(&twl->lock); err = twl6030_usb_ldo_init(twl); if (err) { dev_err(&pdev->dev, "ldo init failed\n"); return err; } platform_set_drvdata(pdev, twl); if (device_create_file(&pdev->dev, &dev_attr_vbus)) dev_warn(&pdev->dev, "could not create sysfs file\n"); INIT_WORK(&twl->set_vbus_work, otg_set_vbus_work); INIT_DELAYED_WORK(&twl->get_status_work, twl6030_status_work); status = request_threaded_irq(twl->irq1, NULL, twl6030_usbotg_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl6030_usb", twl); if (status < 0) { dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", twl->irq1, status); device_remove_file(twl->dev, &dev_attr_vbus); return status; } status = request_threaded_irq(twl->irq2, NULL, twl6030_usb_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl6030_usb", twl); if (status < 0) { dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", twl->irq2, status); free_irq(twl->irq1, twl); device_remove_file(twl->dev, &dev_attr_vbus); return status; } twl->asleep = 0; twl6030_enable_irq(twl); schedule_delayed_work(&twl->get_status_work, HZ); dev_info(&pdev->dev, "Initialized TWL6030 USB module\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi31266.38%425.00%
kishon vijay abraham ikishon vijay abraham i11223.83%425.00%
tony lindgrentony lindgren214.47%212.50%
moiz sonasathmoiz sonasath102.13%16.25%
heikki krogerusheikki krogerus40.85%16.25%
ming leiming lei40.85%16.25%
sachin kamatsachin kamat30.64%16.25%
jingoo hanjingoo han30.64%16.25%
graeme gregorygraeme gregory10.21%16.25%
Total470100.00%16100.00%


static int twl6030_usb_remove(struct platform_device *pdev) { struct twl6030_usb *twl = platform_get_drvdata(pdev); cancel_delayed_work(&twl->get_status_work); twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, REG_INT_MSK_LINE_C); twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK, REG_INT_MSK_STS_C); free_irq(twl->irq1, twl); free_irq(twl->irq2, twl); regulator_put(twl->usb3v3); device_remove_file(twl->dev, &dev_attr_vbus); cancel_work_sync(&twl->set_vbus_work); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi7280.90%125.00%
tony lindgrentony lindgren88.99%125.00%
moiz sonasathmoiz sonasath88.99%125.00%
heikki krogerusheikki krogerus11.12%125.00%
Total89100.00%4100.00%

#ifdef CONFIG_OF static const struct of_device_id twl6030_usb_id_table[] = { { .compatible = "ti,twl6030-usb" }, {} }; MODULE_DEVICE_TABLE(of, twl6030_usb_id_table); #endif static struct platform_driver twl6030_usb_driver = { .probe = twl6030_usb_probe, .remove = twl6030_usb_remove, .driver = { .name = "twl6030_usb", .of_match_table = of_match_ptr(twl6030_usb_id_table), }, };
static int __init twl6030_usb_init(void) { return platform_driver_register(&twl6030_usb_driver); }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi16100.00%1100.00%
Total16100.00%1100.00%

subsys_initcall(twl6030_usb_init);
static void __exit twl6030_usb_exit(void) { platform_driver_unregister(&twl6030_usb_driver); }

Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi15100.00%1100.00%
Total15100.00%1100.00%

module_exit(twl6030_usb_exit); MODULE_ALIAS("platform:twl6030_usb"); MODULE_AUTHOR("Hema HK <hemahk@ti.com>"); MODULE_DESCRIPTION("TWL6030 USB transceiver driver"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
hema kalliguddihema kalliguddi148676.21%624.00%
kishon vijay abraham ikishon vijay abraham i1889.64%520.00%
tony lindgrentony lindgren1326.77%312.00%
moiz sonasathmoiz sonasath864.41%28.00%
fabio baltierifabio baltieri361.85%14.00%
sachin kamatsachin kamat60.31%28.00%
heikki krogerusheikki krogerus50.26%14.00%
ming leiming lei40.21%14.00%
jingoo hanjingoo han30.15%14.00%
dan carpenterdan carpenter20.10%14.00%
graeme gregorygraeme gregory20.10%28.00%
Total1950100.00%25100.00%
Directory: drivers/usb/phy
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}