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-qcom-usb-hs.c (6737B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2016 Linaro Ltd
      4 */
      5#include <linux/module.h>
      6#include <linux/ulpi/driver.h>
      7#include <linux/ulpi/regs.h>
      8#include <linux/clk.h>
      9#include <linux/regulator/consumer.h>
     10#include <linux/of_device.h>
     11#include <linux/phy/phy.h>
     12#include <linux/reset.h>
     13#include <linux/extcon.h>
     14#include <linux/notifier.h>
     15
     16#define ULPI_PWR_CLK_MNG_REG		0x88
     17# define ULPI_PWR_OTG_COMP_DISABLE	BIT(0)
     18
     19#define ULPI_MISC_A			0x96
     20# define ULPI_MISC_A_VBUSVLDEXTSEL	BIT(1)
     21# define ULPI_MISC_A_VBUSVLDEXT		BIT(0)
     22
     23
     24struct ulpi_seq {
     25	u8 addr;
     26	u8 val;
     27};
     28
     29struct qcom_usb_hs_phy {
     30	struct ulpi *ulpi;
     31	struct phy *phy;
     32	struct clk *ref_clk;
     33	struct clk *sleep_clk;
     34	struct regulator *v1p8;
     35	struct regulator *v3p3;
     36	struct reset_control *reset;
     37	struct ulpi_seq *init_seq;
     38	struct extcon_dev *vbus_edev;
     39	struct notifier_block vbus_notify;
     40};
     41
     42static int qcom_usb_hs_phy_set_mode(struct phy *phy,
     43				    enum phy_mode mode, int submode)
     44{
     45	struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
     46	u8 addr;
     47	int ret;
     48
     49	if (!uphy->vbus_edev) {
     50		u8 val = 0;
     51
     52		switch (mode) {
     53		case PHY_MODE_USB_OTG:
     54		case PHY_MODE_USB_HOST:
     55			val |= ULPI_INT_IDGRD;
     56			fallthrough;
     57		case PHY_MODE_USB_DEVICE:
     58			val |= ULPI_INT_SESS_VALID;
     59			break;
     60		default:
     61			break;
     62		}
     63
     64		ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_RISE, val);
     65		if (ret)
     66			return ret;
     67		ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_FALL, val);
     68	} else {
     69		switch (mode) {
     70		case PHY_MODE_USB_OTG:
     71		case PHY_MODE_USB_DEVICE:
     72			addr = ULPI_SET(ULPI_MISC_A);
     73			break;
     74		case PHY_MODE_USB_HOST:
     75			addr = ULPI_CLR(ULPI_MISC_A);
     76			break;
     77		default:
     78			return -EINVAL;
     79		}
     80
     81		ret = ulpi_write(uphy->ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG),
     82				 ULPI_PWR_OTG_COMP_DISABLE);
     83		if (ret)
     84			return ret;
     85		ret = ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXTSEL);
     86	}
     87
     88	return ret;
     89}
     90
     91static int
     92qcom_usb_hs_phy_vbus_notifier(struct notifier_block *nb, unsigned long event,
     93			      void *ptr)
     94{
     95	struct qcom_usb_hs_phy *uphy;
     96	u8 addr;
     97
     98	uphy = container_of(nb, struct qcom_usb_hs_phy, vbus_notify);
     99
    100	if (event)
    101		addr = ULPI_SET(ULPI_MISC_A);
    102	else
    103		addr = ULPI_CLR(ULPI_MISC_A);
    104
    105	return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXT);
    106}
    107
    108static int qcom_usb_hs_phy_power_on(struct phy *phy)
    109{
    110	struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
    111	struct ulpi *ulpi = uphy->ulpi;
    112	const struct ulpi_seq *seq;
    113	int ret, state;
    114
    115	ret = clk_prepare_enable(uphy->ref_clk);
    116	if (ret)
    117		return ret;
    118
    119	ret = clk_prepare_enable(uphy->sleep_clk);
    120	if (ret)
    121		goto err_sleep;
    122
    123	ret = regulator_set_load(uphy->v1p8, 50000);
    124	if (ret < 0)
    125		goto err_1p8;
    126
    127	ret = regulator_enable(uphy->v1p8);
    128	if (ret)
    129		goto err_1p8;
    130
    131	ret = regulator_set_voltage_triplet(uphy->v3p3, 3050000, 3300000,
    132					    3300000);
    133	if (ret)
    134		goto err_3p3;
    135
    136	ret = regulator_set_load(uphy->v3p3, 50000);
    137	if (ret < 0)
    138		goto err_3p3;
    139
    140	ret = regulator_enable(uphy->v3p3);
    141	if (ret)
    142		goto err_3p3;
    143
    144	for (seq = uphy->init_seq; seq->addr; seq++) {
    145		ret = ulpi_write(ulpi, ULPI_EXT_VENDOR_SPECIFIC + seq->addr,
    146				 seq->val);
    147		if (ret)
    148			goto err_ulpi;
    149	}
    150
    151	if (uphy->reset) {
    152		ret = reset_control_reset(uphy->reset);
    153		if (ret)
    154			goto err_ulpi;
    155	}
    156
    157	if (uphy->vbus_edev) {
    158		state = extcon_get_state(uphy->vbus_edev, EXTCON_USB);
    159		/* setup initial state */
    160		qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state,
    161					      uphy->vbus_edev);
    162		ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB,
    163					       &uphy->vbus_notify);
    164		if (ret)
    165			goto err_ulpi;
    166	}
    167
    168	return 0;
    169err_ulpi:
    170	regulator_disable(uphy->v3p3);
    171err_3p3:
    172	regulator_disable(uphy->v1p8);
    173err_1p8:
    174	clk_disable_unprepare(uphy->sleep_clk);
    175err_sleep:
    176	clk_disable_unprepare(uphy->ref_clk);
    177	return ret;
    178}
    179
    180static int qcom_usb_hs_phy_power_off(struct phy *phy)
    181{
    182	struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy);
    183
    184	if (uphy->vbus_edev)
    185		extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB,
    186					   &uphy->vbus_notify);
    187	regulator_disable(uphy->v3p3);
    188	regulator_disable(uphy->v1p8);
    189	clk_disable_unprepare(uphy->sleep_clk);
    190	clk_disable_unprepare(uphy->ref_clk);
    191
    192	return 0;
    193}
    194
    195static const struct phy_ops qcom_usb_hs_phy_ops = {
    196	.power_on = qcom_usb_hs_phy_power_on,
    197	.power_off = qcom_usb_hs_phy_power_off,
    198	.set_mode = qcom_usb_hs_phy_set_mode,
    199	.owner = THIS_MODULE,
    200};
    201
    202static int qcom_usb_hs_phy_probe(struct ulpi *ulpi)
    203{
    204	struct qcom_usb_hs_phy *uphy;
    205	struct phy_provider *p;
    206	struct clk *clk;
    207	struct regulator *reg;
    208	struct reset_control *reset;
    209	int size;
    210	int ret;
    211
    212	uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL);
    213	if (!uphy)
    214		return -ENOMEM;
    215	ulpi_set_drvdata(ulpi, uphy);
    216	uphy->ulpi = ulpi;
    217
    218	size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq");
    219	if (size < 0)
    220		size = 0;
    221	uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1,
    222					   sizeof(*uphy->init_seq), GFP_KERNEL);
    223	if (!uphy->init_seq)
    224		return -ENOMEM;
    225	ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq",
    226					(u8 *)uphy->init_seq, size);
    227	if (ret && size)
    228		return ret;
    229	/* NUL terminate */
    230	uphy->init_seq[size / 2].addr = uphy->init_seq[size / 2].val = 0;
    231
    232	uphy->ref_clk = clk = devm_clk_get(&ulpi->dev, "ref");
    233	if (IS_ERR(clk))
    234		return PTR_ERR(clk);
    235
    236	uphy->sleep_clk = clk = devm_clk_get(&ulpi->dev, "sleep");
    237	if (IS_ERR(clk))
    238		return PTR_ERR(clk);
    239
    240	uphy->v1p8 = reg = devm_regulator_get(&ulpi->dev, "v1p8");
    241	if (IS_ERR(reg))
    242		return PTR_ERR(reg);
    243
    244	uphy->v3p3 = reg = devm_regulator_get(&ulpi->dev, "v3p3");
    245	if (IS_ERR(reg))
    246		return PTR_ERR(reg);
    247
    248	uphy->reset = reset = devm_reset_control_get(&ulpi->dev, "por");
    249	if (IS_ERR(reset)) {
    250		if (PTR_ERR(reset) == -EPROBE_DEFER)
    251			return PTR_ERR(reset);
    252		uphy->reset = NULL;
    253	}
    254
    255	uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node,
    256				    &qcom_usb_hs_phy_ops);
    257	if (IS_ERR(uphy->phy))
    258		return PTR_ERR(uphy->phy);
    259
    260	uphy->vbus_edev = extcon_get_edev_by_phandle(&ulpi->dev, 0);
    261	if (IS_ERR(uphy->vbus_edev)) {
    262		if (PTR_ERR(uphy->vbus_edev) != -ENODEV)
    263			return PTR_ERR(uphy->vbus_edev);
    264		uphy->vbus_edev = NULL;
    265	}
    266
    267	uphy->vbus_notify.notifier_call = qcom_usb_hs_phy_vbus_notifier;
    268	phy_set_drvdata(uphy->phy, uphy);
    269
    270	p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate);
    271	return PTR_ERR_OR_ZERO(p);
    272}
    273
    274static const struct of_device_id qcom_usb_hs_phy_match[] = {
    275	{ .compatible = "qcom,usb-hs-phy", },
    276	{ }
    277};
    278MODULE_DEVICE_TABLE(of, qcom_usb_hs_phy_match);
    279
    280static struct ulpi_driver qcom_usb_hs_phy_driver = {
    281	.probe = qcom_usb_hs_phy_probe,
    282	.driver = {
    283		.name = "qcom_usb_hs_phy",
    284		.of_match_table = qcom_usb_hs_phy_match,
    285	},
    286};
    287module_ulpi_driver(qcom_usb_hs_phy_driver);
    288
    289MODULE_DESCRIPTION("Qualcomm USB HS phy");
    290MODULE_LICENSE("GPL v2");