cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");