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-bcm-ns2-usbdrd.c (11444B)


      1/*
      2 * Copyright (C) 2017 Broadcom
      3 *
      4 * This program is free software; you can redistribute it and/or
      5 * modify it under the terms of the GNU General Public License as
      6 * published by the Free Software Foundation version 2.
      7 *
      8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
      9 * kind, whether express or implied; without even the implied warranty
     10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11 * GNU General Public License for more details.
     12 */
     13
     14#include <linux/delay.h>
     15#include <linux/extcon-provider.h>
     16#include <linux/gpio.h>
     17#include <linux/gpio/consumer.h>
     18#include <linux/init.h>
     19#include <linux/interrupt.h>
     20#include <linux/io.h>
     21#include <linux/iopoll.h>
     22#include <linux/irq.h>
     23#include <linux/mfd/syscon.h>
     24#include <linux/module.h>
     25#include <linux/of.h>
     26#include <linux/of_address.h>
     27#include <linux/phy/phy.h>
     28#include <linux/platform_device.h>
     29#include <linux/regmap.h>
     30#include <linux/slab.h>
     31#include <linux/workqueue.h>
     32
     33#define ICFG_DRD_AFE		0x0
     34#define ICFG_MISC_STAT		0x18
     35#define ICFG_DRD_P0CTL		0x1C
     36#define ICFG_STRAP_CTRL		0x20
     37#define ICFG_FSM_CTRL		0x24
     38
     39#define ICFG_DEV_BIT		BIT(2)
     40#define IDM_RST_BIT		BIT(0)
     41#define AFE_CORERDY_VDDC	BIT(18)
     42#define PHY_PLL_RESETB		BIT(15)
     43#define PHY_RESETB		BIT(14)
     44#define PHY_PLL_LOCK		BIT(0)
     45
     46#define DRD_DEV_MODE		BIT(20)
     47#define OHCI_OVRCUR_POL		BIT(11)
     48#define ICFG_OFF_MODE		BIT(6)
     49#define PLL_LOCK_RETRY		1000
     50
     51#define EVT_DEVICE		0
     52#define EVT_HOST		1
     53
     54#define DRD_HOST_MODE		(BIT(2) | BIT(3))
     55#define DRD_DEVICE_MODE		(BIT(4) | BIT(5))
     56#define DRD_HOST_VAL		0x803
     57#define DRD_DEV_VAL		0x807
     58#define GPIO_DELAY		20
     59
     60struct ns2_phy_data;
     61struct ns2_phy_driver {
     62	void __iomem *icfgdrd_regs;
     63	void __iomem *idmdrd_rst_ctrl;
     64	void __iomem *crmu_usb2_ctrl;
     65	void __iomem *usb2h_strap_reg;
     66	struct ns2_phy_data *data;
     67	struct extcon_dev *edev;
     68	struct gpio_desc *vbus_gpiod;
     69	struct gpio_desc *id_gpiod;
     70	int id_irq;
     71	int vbus_irq;
     72	unsigned long debounce_jiffies;
     73	struct delayed_work wq_extcon;
     74};
     75
     76struct ns2_phy_data {
     77	struct ns2_phy_driver *driver;
     78	struct phy *phy;
     79	int new_state;
     80};
     81
     82static const unsigned int usb_extcon_cable[] = {
     83	EXTCON_USB,
     84	EXTCON_USB_HOST,
     85	EXTCON_NONE,
     86};
     87
     88static inline int pll_lock_stat(u32 usb_reg, int reg_mask,
     89				struct ns2_phy_driver *driver)
     90{
     91	u32 val;
     92
     93	return readl_poll_timeout_atomic(driver->icfgdrd_regs + usb_reg,
     94					 val, (val & reg_mask), 1,
     95					 PLL_LOCK_RETRY);
     96}
     97
     98static int ns2_drd_phy_init(struct phy *phy)
     99{
    100	struct ns2_phy_data *data = phy_get_drvdata(phy);
    101	struct ns2_phy_driver *driver = data->driver;
    102	u32 val;
    103
    104	val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
    105
    106	if (data->new_state == EVT_HOST) {
    107		val &= ~DRD_DEVICE_MODE;
    108		val |= DRD_HOST_MODE;
    109	} else {
    110		val &= ~DRD_HOST_MODE;
    111		val |= DRD_DEVICE_MODE;
    112	}
    113	writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
    114
    115	return 0;
    116}
    117
    118static int ns2_drd_phy_poweroff(struct phy *phy)
    119{
    120	struct ns2_phy_data *data = phy_get_drvdata(phy);
    121	struct ns2_phy_driver *driver = data->driver;
    122	u32 val;
    123
    124	val = readl(driver->crmu_usb2_ctrl);
    125	val &= ~AFE_CORERDY_VDDC;
    126	writel(val, driver->crmu_usb2_ctrl);
    127
    128	val = readl(driver->crmu_usb2_ctrl);
    129	val &= ~DRD_DEV_MODE;
    130	writel(val, driver->crmu_usb2_ctrl);
    131
    132	/* Disable Host and Device Mode */
    133	val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
    134	val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE | ICFG_OFF_MODE);
    135	writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
    136
    137	return 0;
    138}
    139
    140static int ns2_drd_phy_poweron(struct phy *phy)
    141{
    142	struct ns2_phy_data *data = phy_get_drvdata(phy);
    143	struct ns2_phy_driver *driver = data->driver;
    144	u32 extcon_event = data->new_state;
    145	int ret;
    146	u32 val;
    147
    148	if (extcon_event == EVT_DEVICE) {
    149		writel(DRD_DEV_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
    150
    151		val = readl(driver->idmdrd_rst_ctrl);
    152		val &= ~IDM_RST_BIT;
    153		writel(val, driver->idmdrd_rst_ctrl);
    154
    155		val = readl(driver->crmu_usb2_ctrl);
    156		val |= (AFE_CORERDY_VDDC | DRD_DEV_MODE);
    157		writel(val, driver->crmu_usb2_ctrl);
    158
    159		/* Bring PHY and PHY_PLL out of Reset */
    160		val = readl(driver->crmu_usb2_ctrl);
    161		val |= (PHY_PLL_RESETB | PHY_RESETB);
    162		writel(val, driver->crmu_usb2_ctrl);
    163
    164		ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver);
    165		if (ret < 0) {
    166			dev_err(&phy->dev, "Phy PLL lock failed\n");
    167			return ret;
    168		}
    169	} else {
    170		writel(DRD_HOST_VAL, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
    171
    172		val = readl(driver->crmu_usb2_ctrl);
    173		val |= AFE_CORERDY_VDDC;
    174		writel(val, driver->crmu_usb2_ctrl);
    175
    176		ret = pll_lock_stat(ICFG_MISC_STAT, PHY_PLL_LOCK, driver);
    177		if (ret < 0) {
    178			dev_err(&phy->dev, "Phy PLL lock failed\n");
    179			return ret;
    180		}
    181
    182		val = readl(driver->idmdrd_rst_ctrl);
    183		val &= ~IDM_RST_BIT;
    184		writel(val, driver->idmdrd_rst_ctrl);
    185
    186		/* port over current Polarity */
    187		val = readl(driver->usb2h_strap_reg);
    188		val |= OHCI_OVRCUR_POL;
    189		writel(val, driver->usb2h_strap_reg);
    190	}
    191
    192	return 0;
    193}
    194
    195static void connect_change(struct ns2_phy_driver *driver)
    196{
    197	u32 extcon_event;
    198	u32 val;
    199
    200	extcon_event = driver->data->new_state;
    201	val = readl(driver->icfgdrd_regs + ICFG_FSM_CTRL);
    202
    203	switch (extcon_event) {
    204	case EVT_DEVICE:
    205		val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE);
    206		writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
    207
    208		val = (val & ~DRD_HOST_MODE) | DRD_DEVICE_MODE;
    209		writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
    210
    211		val = readl(driver->icfgdrd_regs + ICFG_DRD_P0CTL);
    212		val |= ICFG_DEV_BIT;
    213		writel(val, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
    214		break;
    215
    216	case EVT_HOST:
    217		val &= ~(DRD_HOST_MODE | DRD_DEVICE_MODE);
    218		writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
    219
    220		val = (val & ~DRD_DEVICE_MODE) | DRD_HOST_MODE;
    221		writel(val, driver->icfgdrd_regs + ICFG_FSM_CTRL);
    222
    223		val = readl(driver->usb2h_strap_reg);
    224		val |= OHCI_OVRCUR_POL;
    225		writel(val, driver->usb2h_strap_reg);
    226
    227		val = readl(driver->icfgdrd_regs + ICFG_DRD_P0CTL);
    228		val &= ~ICFG_DEV_BIT;
    229		writel(val, driver->icfgdrd_regs + ICFG_DRD_P0CTL);
    230		break;
    231
    232	default:
    233		pr_err("Invalid extcon event\n");
    234		break;
    235	}
    236}
    237
    238static void extcon_work(struct work_struct *work)
    239{
    240	struct ns2_phy_driver *driver;
    241	int vbus;
    242	int id;
    243
    244	driver  = container_of(to_delayed_work(work),
    245			       struct ns2_phy_driver, wq_extcon);
    246
    247	id = gpiod_get_value_cansleep(driver->id_gpiod);
    248	vbus = gpiod_get_value_cansleep(driver->vbus_gpiod);
    249
    250	if (!id && vbus) { /* Host connected */
    251		extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, true);
    252		pr_debug("Host cable connected\n");
    253		driver->data->new_state = EVT_HOST;
    254		connect_change(driver);
    255	} else if (id && !vbus) { /* Disconnected */
    256		extcon_set_state_sync(driver->edev, EXTCON_USB_HOST, false);
    257		extcon_set_state_sync(driver->edev, EXTCON_USB, false);
    258		pr_debug("Cable disconnected\n");
    259	} else if (id && vbus) { /* Device connected */
    260		extcon_set_state_sync(driver->edev, EXTCON_USB, true);
    261		pr_debug("Device cable connected\n");
    262		driver->data->new_state = EVT_DEVICE;
    263		connect_change(driver);
    264	}
    265}
    266
    267static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
    268{
    269	struct ns2_phy_driver *driver = dev_id;
    270
    271	queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon,
    272			   driver->debounce_jiffies);
    273
    274	return IRQ_HANDLED;
    275}
    276
    277static const struct phy_ops ops = {
    278	.init		= ns2_drd_phy_init,
    279	.power_on	= ns2_drd_phy_poweron,
    280	.power_off	= ns2_drd_phy_poweroff,
    281	.owner		= THIS_MODULE,
    282};
    283
    284static const struct of_device_id ns2_drd_phy_dt_ids[] = {
    285	{ .compatible = "brcm,ns2-drd-phy", },
    286	{ }
    287};
    288MODULE_DEVICE_TABLE(of, ns2_drd_phy_dt_ids);
    289
    290static int ns2_drd_phy_probe(struct platform_device *pdev)
    291{
    292	struct phy_provider *phy_provider;
    293	struct device *dev = &pdev->dev;
    294	struct ns2_phy_driver *driver;
    295	struct ns2_phy_data *data;
    296	int ret;
    297	u32 val;
    298
    299	driver = devm_kzalloc(dev, sizeof(struct ns2_phy_driver),
    300			      GFP_KERNEL);
    301	if (!driver)
    302		return -ENOMEM;
    303
    304	driver->data = devm_kzalloc(dev, sizeof(struct ns2_phy_data),
    305				  GFP_KERNEL);
    306	if (!driver->data)
    307		return -ENOMEM;
    308
    309	driver->icfgdrd_regs = devm_platform_ioremap_resource_byname(pdev, "icfg");
    310	if (IS_ERR(driver->icfgdrd_regs))
    311		return PTR_ERR(driver->icfgdrd_regs);
    312
    313	driver->idmdrd_rst_ctrl = devm_platform_ioremap_resource_byname(pdev, "rst-ctrl");
    314	if (IS_ERR(driver->idmdrd_rst_ctrl))
    315		return PTR_ERR(driver->idmdrd_rst_ctrl);
    316
    317	driver->crmu_usb2_ctrl = devm_platform_ioremap_resource_byname(pdev, "crmu-ctrl");
    318	if (IS_ERR(driver->crmu_usb2_ctrl))
    319		return PTR_ERR(driver->crmu_usb2_ctrl);
    320
    321	driver->usb2h_strap_reg = devm_platform_ioremap_resource_byname(pdev, "usb2-strap");
    322	if (IS_ERR(driver->usb2h_strap_reg))
    323		return PTR_ERR(driver->usb2h_strap_reg);
    324
    325	 /* create extcon */
    326	driver->id_gpiod = devm_gpiod_get(&pdev->dev, "id", GPIOD_IN);
    327	if (IS_ERR(driver->id_gpiod)) {
    328		dev_err(dev, "failed to get ID GPIO\n");
    329		return PTR_ERR(driver->id_gpiod);
    330	}
    331	driver->vbus_gpiod = devm_gpiod_get(&pdev->dev, "vbus", GPIOD_IN);
    332	if (IS_ERR(driver->vbus_gpiod)) {
    333		dev_err(dev, "failed to get VBUS GPIO\n");
    334		return PTR_ERR(driver->vbus_gpiod);
    335	}
    336
    337	driver->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
    338	if (IS_ERR(driver->edev)) {
    339		dev_err(dev, "failed to allocate extcon device\n");
    340		return -ENOMEM;
    341	}
    342
    343	ret = devm_extcon_dev_register(dev, driver->edev);
    344	if (ret < 0) {
    345		dev_err(dev, "failed to register extcon device\n");
    346		return ret;
    347	}
    348
    349	ret = gpiod_set_debounce(driver->id_gpiod, GPIO_DELAY * 1000);
    350	if (ret < 0)
    351		driver->debounce_jiffies = msecs_to_jiffies(GPIO_DELAY);
    352
    353	INIT_DELAYED_WORK(&driver->wq_extcon, extcon_work);
    354
    355	driver->id_irq = gpiod_to_irq(driver->id_gpiod);
    356	if (driver->id_irq < 0) {
    357		dev_err(dev, "failed to get ID IRQ\n");
    358		return driver->id_irq;
    359	}
    360
    361	driver->vbus_irq = gpiod_to_irq(driver->vbus_gpiod);
    362	if (driver->vbus_irq < 0) {
    363		dev_err(dev, "failed to get ID IRQ\n");
    364		return driver->vbus_irq;
    365	}
    366
    367	ret = devm_request_irq(dev, driver->id_irq, gpio_irq_handler,
    368			       IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
    369			       "usb_id", driver);
    370	if (ret < 0) {
    371		dev_err(dev, "failed to request handler for ID IRQ\n");
    372		return ret;
    373	}
    374
    375	ret = devm_request_irq(dev, driver->vbus_irq, gpio_irq_handler,
    376			       IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
    377			       "usb_vbus", driver);
    378	if (ret < 0) {
    379		dev_err(dev, "failed to request handler for VBUS IRQ\n");
    380		return ret;
    381	}
    382
    383	dev_set_drvdata(dev, driver);
    384
    385	/* Shutdown all ports. They can be powered up as required */
    386	val = readl(driver->crmu_usb2_ctrl);
    387	val &= ~(AFE_CORERDY_VDDC | PHY_RESETB);
    388	writel(val, driver->crmu_usb2_ctrl);
    389
    390	data = driver->data;
    391	data->phy = devm_phy_create(dev, dev->of_node, &ops);
    392	if (IS_ERR(data->phy)) {
    393		dev_err(dev, "Failed to create usb drd phy\n");
    394		return PTR_ERR(data->phy);
    395	}
    396
    397	data->driver = driver;
    398	phy_set_drvdata(data->phy, data);
    399
    400	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
    401	if (IS_ERR(phy_provider)) {
    402		dev_err(dev, "Failed to register as phy provider\n");
    403		return PTR_ERR(phy_provider);
    404	}
    405
    406	platform_set_drvdata(pdev, driver);
    407
    408	dev_info(dev, "Registered NS2 DRD Phy device\n");
    409	queue_delayed_work(system_power_efficient_wq, &driver->wq_extcon,
    410			   driver->debounce_jiffies);
    411
    412	return 0;
    413}
    414
    415static struct platform_driver ns2_drd_phy_driver = {
    416	.probe = ns2_drd_phy_probe,
    417	.driver = {
    418		.name = "bcm-ns2-usbphy",
    419		.of_match_table = of_match_ptr(ns2_drd_phy_dt_ids),
    420	},
    421};
    422module_platform_driver(ns2_drd_phy_driver);
    423
    424MODULE_ALIAS("platform:bcm-ns2-drd-phy");
    425MODULE_AUTHOR("Broadcom");
    426MODULE_DESCRIPTION("Broadcom NS2 USB2 PHY driver");
    427MODULE_LICENSE("GPL v2");