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-tahvo.c (10810B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Tahvo USB transceiver driver
      4 *
      5 * Copyright (C) 2005-2006 Nokia Corporation
      6 *
      7 * Parts copied from isp1301_omap.c.
      8 * Copyright (C) 2004 Texas Instruments
      9 * Copyright (C) 2004 David Brownell
     10 *
     11 * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs.
     12 * Modified for Retu/Tahvo MFD by Aaro Koskinen.
     13 */
     14
     15#include <linux/io.h>
     16#include <linux/clk.h>
     17#include <linux/usb.h>
     18#include <linux/extcon-provider.h>
     19#include <linux/kernel.h>
     20#include <linux/module.h>
     21#include <linux/usb/otg.h>
     22#include <linux/mfd/retu.h>
     23#include <linux/usb/gadget.h>
     24#include <linux/platform_device.h>
     25
     26#define DRIVER_NAME     "tahvo-usb"
     27
     28#define TAHVO_REG_IDSR	0x02
     29#define TAHVO_REG_USBR	0x06
     30
     31#define USBR_SLAVE_CONTROL	(1 << 8)
     32#define USBR_VPPVIO_SW		(1 << 7)
     33#define USBR_SPEED		(1 << 6)
     34#define USBR_REGOUT		(1 << 5)
     35#define USBR_MASTER_SW2		(1 << 4)
     36#define USBR_MASTER_SW1		(1 << 3)
     37#define USBR_SLAVE_SW		(1 << 2)
     38#define USBR_NSUSPEND		(1 << 1)
     39#define USBR_SEMODE		(1 << 0)
     40
     41#define TAHVO_MODE_HOST		0
     42#define TAHVO_MODE_PERIPHERAL	1
     43
     44struct tahvo_usb {
     45	struct platform_device	*pt_dev;
     46	struct usb_phy		phy;
     47	int			vbus_state;
     48	struct mutex		serialize;
     49	struct clk		*ick;
     50	int			irq;
     51	int			tahvo_mode;
     52	struct extcon_dev	*extcon;
     53};
     54
     55static const unsigned int tahvo_cable[] = {
     56	EXTCON_USB,
     57	EXTCON_USB_HOST,
     58
     59	EXTCON_NONE,
     60};
     61
     62static ssize_t vbus_show(struct device *device,
     63			       struct device_attribute *attr, char *buf)
     64{
     65	struct tahvo_usb *tu = dev_get_drvdata(device);
     66	return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off");
     67}
     68static DEVICE_ATTR_RO(vbus);
     69
     70static void check_vbus_state(struct tahvo_usb *tu)
     71{
     72	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
     73	int reg, prev_state;
     74
     75	reg = retu_read(rdev, TAHVO_REG_IDSR);
     76	if (reg & TAHVO_STAT_VBUS) {
     77		switch (tu->phy.otg->state) {
     78		case OTG_STATE_B_IDLE:
     79			/* Enable the gadget driver */
     80			if (tu->phy.otg->gadget)
     81				usb_gadget_vbus_connect(tu->phy.otg->gadget);
     82			tu->phy.otg->state = OTG_STATE_B_PERIPHERAL;
     83			usb_phy_set_event(&tu->phy, USB_EVENT_ENUMERATED);
     84			break;
     85		case OTG_STATE_A_IDLE:
     86			/*
     87			 * Session is now valid assuming the USB hub is driving
     88			 * Vbus.
     89			 */
     90			tu->phy.otg->state = OTG_STATE_A_HOST;
     91			break;
     92		default:
     93			break;
     94		}
     95		dev_info(&tu->pt_dev->dev, "USB cable connected\n");
     96	} else {
     97		switch (tu->phy.otg->state) {
     98		case OTG_STATE_B_PERIPHERAL:
     99			if (tu->phy.otg->gadget)
    100				usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
    101			tu->phy.otg->state = OTG_STATE_B_IDLE;
    102			usb_phy_set_event(&tu->phy, USB_EVENT_NONE);
    103			break;
    104		case OTG_STATE_A_HOST:
    105			tu->phy.otg->state = OTG_STATE_A_IDLE;
    106			break;
    107		default:
    108			break;
    109		}
    110		dev_info(&tu->pt_dev->dev, "USB cable disconnected\n");
    111	}
    112
    113	prev_state = tu->vbus_state;
    114	tu->vbus_state = reg & TAHVO_STAT_VBUS;
    115	if (prev_state != tu->vbus_state) {
    116		extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
    117		sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state");
    118	}
    119}
    120
    121static void tahvo_usb_become_host(struct tahvo_usb *tu)
    122{
    123	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
    124
    125	extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, true);
    126
    127	/* Power up the transceiver in USB host mode */
    128	retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND |
    129		   USBR_MASTER_SW2 | USBR_MASTER_SW1);
    130	tu->phy.otg->state = OTG_STATE_A_IDLE;
    131
    132	check_vbus_state(tu);
    133}
    134
    135static void tahvo_usb_stop_host(struct tahvo_usb *tu)
    136{
    137	tu->phy.otg->state = OTG_STATE_A_IDLE;
    138}
    139
    140static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
    141{
    142	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
    143
    144	extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, false);
    145
    146	/* Power up transceiver and set it in USB peripheral mode */
    147	retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT |
    148		   USBR_NSUSPEND | USBR_SLAVE_SW);
    149	tu->phy.otg->state = OTG_STATE_B_IDLE;
    150
    151	check_vbus_state(tu);
    152}
    153
    154static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu)
    155{
    156	if (tu->phy.otg->gadget)
    157		usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
    158	tu->phy.otg->state = OTG_STATE_B_IDLE;
    159}
    160
    161static void tahvo_usb_power_off(struct tahvo_usb *tu)
    162{
    163	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
    164
    165	/* Disable gadget controller if any */
    166	if (tu->phy.otg->gadget)
    167		usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
    168
    169	/* Power off transceiver */
    170	retu_write(rdev, TAHVO_REG_USBR, 0);
    171	tu->phy.otg->state = OTG_STATE_UNDEFINED;
    172}
    173
    174static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend)
    175{
    176	struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy);
    177	struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
    178	u16 w;
    179
    180	dev_dbg(&tu->pt_dev->dev, "%s\n", __func__);
    181
    182	w = retu_read(rdev, TAHVO_REG_USBR);
    183	if (suspend)
    184		w &= ~USBR_NSUSPEND;
    185	else
    186		w |= USBR_NSUSPEND;
    187	retu_write(rdev, TAHVO_REG_USBR, w);
    188
    189	return 0;
    190}
    191
    192static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
    193{
    194	struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
    195					    phy);
    196
    197	mutex_lock(&tu->serialize);
    198
    199	if (host == NULL) {
    200		if (tu->tahvo_mode == TAHVO_MODE_HOST)
    201			tahvo_usb_power_off(tu);
    202		otg->host = NULL;
    203		mutex_unlock(&tu->serialize);
    204		return 0;
    205	}
    206
    207	if (tu->tahvo_mode == TAHVO_MODE_HOST) {
    208		otg->host = NULL;
    209		tahvo_usb_become_host(tu);
    210	}
    211
    212	otg->host = host;
    213
    214	mutex_unlock(&tu->serialize);
    215
    216	return 0;
    217}
    218
    219static int tahvo_usb_set_peripheral(struct usb_otg *otg,
    220				    struct usb_gadget *gadget)
    221{
    222	struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
    223					    phy);
    224
    225	mutex_lock(&tu->serialize);
    226
    227	if (!gadget) {
    228		if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
    229			tahvo_usb_power_off(tu);
    230		tu->phy.otg->gadget = NULL;
    231		mutex_unlock(&tu->serialize);
    232		return 0;
    233	}
    234
    235	tu->phy.otg->gadget = gadget;
    236	if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
    237		tahvo_usb_become_peripheral(tu);
    238
    239	mutex_unlock(&tu->serialize);
    240
    241	return 0;
    242}
    243
    244static irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu)
    245{
    246	struct tahvo_usb *tu = _tu;
    247
    248	mutex_lock(&tu->serialize);
    249	check_vbus_state(tu);
    250	mutex_unlock(&tu->serialize);
    251
    252	return IRQ_HANDLED;
    253}
    254
    255static ssize_t otg_mode_show(struct device *device,
    256			     struct device_attribute *attr, char *buf)
    257{
    258	struct tahvo_usb *tu = dev_get_drvdata(device);
    259
    260	switch (tu->tahvo_mode) {
    261	case TAHVO_MODE_HOST:
    262		return sprintf(buf, "host\n");
    263	case TAHVO_MODE_PERIPHERAL:
    264		return sprintf(buf, "peripheral\n");
    265	}
    266
    267	return -EINVAL;
    268}
    269
    270static ssize_t otg_mode_store(struct device *device,
    271			      struct device_attribute *attr,
    272			      const char *buf, size_t count)
    273{
    274	struct tahvo_usb *tu = dev_get_drvdata(device);
    275	int r;
    276
    277	mutex_lock(&tu->serialize);
    278	if (count >= 4 && strncmp(buf, "host", 4) == 0) {
    279		if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
    280			tahvo_usb_stop_peripheral(tu);
    281		tu->tahvo_mode = TAHVO_MODE_HOST;
    282		if (tu->phy.otg->host) {
    283			dev_info(device, "HOST mode: host controller present\n");
    284			tahvo_usb_become_host(tu);
    285		} else {
    286			dev_info(device, "HOST mode: no host controller, powering off\n");
    287			tahvo_usb_power_off(tu);
    288		}
    289		r = strlen(buf);
    290	} else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) {
    291		if (tu->tahvo_mode == TAHVO_MODE_HOST)
    292			tahvo_usb_stop_host(tu);
    293		tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
    294		if (tu->phy.otg->gadget) {
    295			dev_info(device, "PERIPHERAL mode: gadget driver present\n");
    296			tahvo_usb_become_peripheral(tu);
    297		} else {
    298			dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n");
    299			tahvo_usb_power_off(tu);
    300		}
    301		r = strlen(buf);
    302	} else {
    303		r = -EINVAL;
    304	}
    305	mutex_unlock(&tu->serialize);
    306
    307	return r;
    308}
    309static DEVICE_ATTR_RW(otg_mode);
    310
    311static struct attribute *tahvo_attrs[] = {
    312	&dev_attr_vbus.attr,
    313	&dev_attr_otg_mode.attr,
    314	NULL
    315};
    316ATTRIBUTE_GROUPS(tahvo);
    317
    318static int tahvo_usb_probe(struct platform_device *pdev)
    319{
    320	struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
    321	struct tahvo_usb *tu;
    322	int ret;
    323
    324	tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL);
    325	if (!tu)
    326		return -ENOMEM;
    327
    328	tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg),
    329				   GFP_KERNEL);
    330	if (!tu->phy.otg)
    331		return -ENOMEM;
    332
    333	tu->pt_dev = pdev;
    334
    335	/* Default mode */
    336#ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT
    337	tu->tahvo_mode = TAHVO_MODE_HOST;
    338#else
    339	tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
    340#endif
    341
    342	mutex_init(&tu->serialize);
    343
    344	tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick");
    345	if (!IS_ERR(tu->ick))
    346		clk_enable(tu->ick);
    347
    348	/*
    349	 * Set initial state, so that we generate kevents only on state changes.
    350	 */
    351	tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS;
    352
    353	tu->extcon = devm_extcon_dev_allocate(&pdev->dev, tahvo_cable);
    354	if (IS_ERR(tu->extcon)) {
    355		dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
    356		ret = PTR_ERR(tu->extcon);
    357		goto err_disable_clk;
    358	}
    359
    360	ret = devm_extcon_dev_register(&pdev->dev, tu->extcon);
    361	if (ret) {
    362		dev_err(&pdev->dev, "could not register extcon device: %d\n",
    363			ret);
    364		goto err_disable_clk;
    365	}
    366
    367	/* Set the initial cable state. */
    368	extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST,
    369			       tu->tahvo_mode == TAHVO_MODE_HOST);
    370	extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
    371
    372	/* Create OTG interface */
    373	tahvo_usb_power_off(tu);
    374	tu->phy.dev = &pdev->dev;
    375	tu->phy.otg->state = OTG_STATE_UNDEFINED;
    376	tu->phy.label = DRIVER_NAME;
    377	tu->phy.set_suspend = tahvo_usb_set_suspend;
    378
    379	tu->phy.otg->usb_phy = &tu->phy;
    380	tu->phy.otg->set_host = tahvo_usb_set_host;
    381	tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral;
    382
    383	ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2);
    384	if (ret < 0) {
    385		dev_err(&pdev->dev, "cannot register USB transceiver: %d\n",
    386			ret);
    387		goto err_disable_clk;
    388	}
    389
    390	dev_set_drvdata(&pdev->dev, tu);
    391
    392	tu->irq = ret = platform_get_irq(pdev, 0);
    393	if (ret < 0)
    394		return ret;
    395	ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt,
    396				   IRQF_ONESHOT,
    397				   "tahvo-vbus", tu);
    398	if (ret) {
    399		dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n",
    400			ret);
    401		goto err_remove_phy;
    402	}
    403
    404	return 0;
    405
    406err_remove_phy:
    407	usb_remove_phy(&tu->phy);
    408err_disable_clk:
    409	if (!IS_ERR(tu->ick))
    410		clk_disable(tu->ick);
    411
    412	return ret;
    413}
    414
    415static int tahvo_usb_remove(struct platform_device *pdev)
    416{
    417	struct tahvo_usb *tu = platform_get_drvdata(pdev);
    418
    419	free_irq(tu->irq, tu);
    420	usb_remove_phy(&tu->phy);
    421	if (!IS_ERR(tu->ick))
    422		clk_disable(tu->ick);
    423
    424	return 0;
    425}
    426
    427static struct platform_driver tahvo_usb_driver = {
    428	.probe		= tahvo_usb_probe,
    429	.remove		= tahvo_usb_remove,
    430	.driver		= {
    431		.name	= "tahvo-usb",
    432		.dev_groups = tahvo_groups,
    433	},
    434};
    435module_platform_driver(tahvo_usb_driver);
    436
    437MODULE_DESCRIPTION("Tahvo USB transceiver driver");
    438MODULE_LICENSE("GPL");
    439MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs");
    440MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");