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-rockchip-usb.c (14443B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Rockchip usb PHY driver
      4 *
      5 * Copyright (C) 2014 Yunzhi Li <lyz@rock-chips.com>
      6 * Copyright (C) 2014 ROCKCHIP, Inc.
      7 */
      8
      9#include <linux/clk.h>
     10#include <linux/clk-provider.h>
     11#include <linux/io.h>
     12#include <linux/kernel.h>
     13#include <linux/module.h>
     14#include <linux/mutex.h>
     15#include <linux/of.h>
     16#include <linux/of_address.h>
     17#include <linux/of_platform.h>
     18#include <linux/phy/phy.h>
     19#include <linux/platform_device.h>
     20#include <linux/regulator/consumer.h>
     21#include <linux/reset.h>
     22#include <linux/regmap.h>
     23#include <linux/mfd/syscon.h>
     24#include <linux/delay.h>
     25
     26static int enable_usb_uart;
     27
     28#define HIWORD_UPDATE(val, mask) \
     29		((val) | (mask) << 16)
     30
     31#define UOC_CON0					0x00
     32#define UOC_CON0_SIDDQ					BIT(13)
     33#define UOC_CON0_DISABLE				BIT(4)
     34#define UOC_CON0_COMMON_ON_N				BIT(0)
     35
     36#define UOC_CON2					0x08
     37#define UOC_CON2_SOFT_CON_SEL				BIT(2)
     38
     39#define UOC_CON3					0x0c
     40/* bits present on rk3188 and rk3288 phys */
     41#define UOC_CON3_UTMI_TERMSEL_FULLSPEED			BIT(5)
     42#define UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC		(1 << 3)
     43#define UOC_CON3_UTMI_XCVRSEELCT_MASK			(3 << 3)
     44#define UOC_CON3_UTMI_OPMODE_NODRIVING			(1 << 1)
     45#define UOC_CON3_UTMI_OPMODE_MASK			(3 << 1)
     46#define UOC_CON3_UTMI_SUSPENDN				BIT(0)
     47
     48struct rockchip_usb_phys {
     49	int reg;
     50	const char *pll_name;
     51};
     52
     53struct rockchip_usb_phy_base;
     54struct rockchip_usb_phy_pdata {
     55	struct rockchip_usb_phys *phys;
     56	int (*init_usb_uart)(struct regmap *grf,
     57			     const struct rockchip_usb_phy_pdata *pdata);
     58	int usb_uart_phy;
     59};
     60
     61struct rockchip_usb_phy_base {
     62	struct device *dev;
     63	struct regmap *reg_base;
     64	const struct rockchip_usb_phy_pdata *pdata;
     65};
     66
     67struct rockchip_usb_phy {
     68	struct rockchip_usb_phy_base *base;
     69	struct device_node *np;
     70	unsigned int	reg_offset;
     71	struct clk	*clk;
     72	struct clk      *clk480m;
     73	struct clk_hw	clk480m_hw;
     74	struct phy	*phy;
     75	bool		uart_enabled;
     76	struct reset_control *reset;
     77	struct regulator *vbus;
     78};
     79
     80static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
     81					   bool siddq)
     82{
     83	u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ);
     84
     85	return regmap_write(phy->base->reg_base, phy->reg_offset, val);
     86}
     87
     88static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
     89						unsigned long parent_rate)
     90{
     91	return 480000000;
     92}
     93
     94static void rockchip_usb_phy480m_disable(struct clk_hw *hw)
     95{
     96	struct rockchip_usb_phy *phy = container_of(hw,
     97						    struct rockchip_usb_phy,
     98						    clk480m_hw);
     99
    100	if (phy->vbus)
    101		regulator_disable(phy->vbus);
    102
    103	/* Power down usb phy analog blocks by set siddq 1 */
    104	rockchip_usb_phy_power(phy, 1);
    105}
    106
    107static int rockchip_usb_phy480m_enable(struct clk_hw *hw)
    108{
    109	struct rockchip_usb_phy *phy = container_of(hw,
    110						    struct rockchip_usb_phy,
    111						    clk480m_hw);
    112
    113	/* Power up usb phy analog blocks by set siddq 0 */
    114	return rockchip_usb_phy_power(phy, 0);
    115}
    116
    117static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw)
    118{
    119	struct rockchip_usb_phy *phy = container_of(hw,
    120						    struct rockchip_usb_phy,
    121						    clk480m_hw);
    122	int ret;
    123	u32 val;
    124
    125	ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val);
    126	if (ret < 0)
    127		return ret;
    128
    129	return (val & UOC_CON0_SIDDQ) ? 0 : 1;
    130}
    131
    132static const struct clk_ops rockchip_usb_phy480m_ops = {
    133	.enable = rockchip_usb_phy480m_enable,
    134	.disable = rockchip_usb_phy480m_disable,
    135	.is_enabled = rockchip_usb_phy480m_is_enabled,
    136	.recalc_rate = rockchip_usb_phy480m_recalc_rate,
    137};
    138
    139static int rockchip_usb_phy_power_off(struct phy *_phy)
    140{
    141	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
    142
    143	if (phy->uart_enabled)
    144		return -EBUSY;
    145
    146	clk_disable_unprepare(phy->clk480m);
    147
    148	return 0;
    149}
    150
    151static int rockchip_usb_phy_power_on(struct phy *_phy)
    152{
    153	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
    154
    155	if (phy->uart_enabled)
    156		return -EBUSY;
    157
    158	if (phy->vbus) {
    159		int ret;
    160
    161		ret = regulator_enable(phy->vbus);
    162		if (ret)
    163			return ret;
    164	}
    165
    166	return clk_prepare_enable(phy->clk480m);
    167}
    168
    169static int rockchip_usb_phy_reset(struct phy *_phy)
    170{
    171	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
    172
    173	if (phy->reset) {
    174		reset_control_assert(phy->reset);
    175		udelay(10);
    176		reset_control_deassert(phy->reset);
    177	}
    178
    179	return 0;
    180}
    181
    182static const struct phy_ops ops = {
    183	.power_on	= rockchip_usb_phy_power_on,
    184	.power_off	= rockchip_usb_phy_power_off,
    185	.reset		= rockchip_usb_phy_reset,
    186	.owner		= THIS_MODULE,
    187};
    188
    189static void rockchip_usb_phy_action(void *data)
    190{
    191	struct rockchip_usb_phy *rk_phy = data;
    192
    193	if (!rk_phy->uart_enabled) {
    194		of_clk_del_provider(rk_phy->np);
    195		clk_unregister(rk_phy->clk480m);
    196	}
    197
    198	if (rk_phy->clk)
    199		clk_put(rk_phy->clk);
    200}
    201
    202static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
    203				 struct device_node *child)
    204{
    205	struct rockchip_usb_phy *rk_phy;
    206	unsigned int reg_offset;
    207	const char *clk_name;
    208	struct clk_init_data init;
    209	int err, i;
    210
    211	rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL);
    212	if (!rk_phy)
    213		return -ENOMEM;
    214
    215	rk_phy->base = base;
    216	rk_phy->np = child;
    217
    218	if (of_property_read_u32(child, "reg", &reg_offset)) {
    219		dev_err(base->dev, "missing reg property in node %pOFn\n",
    220			child);
    221		return -EINVAL;
    222	}
    223
    224	rk_phy->reset = of_reset_control_get(child, "phy-reset");
    225	if (IS_ERR(rk_phy->reset))
    226		rk_phy->reset = NULL;
    227
    228	rk_phy->reg_offset = reg_offset;
    229
    230	rk_phy->clk = of_clk_get_by_name(child, "phyclk");
    231	if (IS_ERR(rk_phy->clk))
    232		rk_phy->clk = NULL;
    233
    234	i = 0;
    235	init.name = NULL;
    236	while (base->pdata->phys[i].reg) {
    237		if (base->pdata->phys[i].reg == reg_offset) {
    238			init.name = base->pdata->phys[i].pll_name;
    239			break;
    240		}
    241		i++;
    242	}
    243
    244	if (!init.name) {
    245		dev_err(base->dev, "phy data not found\n");
    246		return -EINVAL;
    247	}
    248
    249	if (enable_usb_uart && base->pdata->usb_uart_phy == i) {
    250		dev_dbg(base->dev, "phy%d used as uart output\n", i);
    251		rk_phy->uart_enabled = true;
    252	} else {
    253		if (rk_phy->clk) {
    254			clk_name = __clk_get_name(rk_phy->clk);
    255			init.flags = 0;
    256			init.parent_names = &clk_name;
    257			init.num_parents = 1;
    258		} else {
    259			init.flags = 0;
    260			init.parent_names = NULL;
    261			init.num_parents = 0;
    262		}
    263
    264		init.ops = &rockchip_usb_phy480m_ops;
    265		rk_phy->clk480m_hw.init = &init;
    266
    267		rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
    268		if (IS_ERR(rk_phy->clk480m)) {
    269			err = PTR_ERR(rk_phy->clk480m);
    270			goto err_clk;
    271		}
    272
    273		err = of_clk_add_provider(child, of_clk_src_simple_get,
    274					rk_phy->clk480m);
    275		if (err < 0)
    276			goto err_clk_prov;
    277	}
    278
    279	err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action,
    280				       rk_phy);
    281	if (err)
    282		return err;
    283
    284	rk_phy->phy = devm_phy_create(base->dev, child, &ops);
    285	if (IS_ERR(rk_phy->phy)) {
    286		dev_err(base->dev, "failed to create PHY\n");
    287		return PTR_ERR(rk_phy->phy);
    288	}
    289	phy_set_drvdata(rk_phy->phy, rk_phy);
    290
    291	rk_phy->vbus = devm_regulator_get_optional(&rk_phy->phy->dev, "vbus");
    292	if (IS_ERR(rk_phy->vbus)) {
    293		if (PTR_ERR(rk_phy->vbus) == -EPROBE_DEFER)
    294			return PTR_ERR(rk_phy->vbus);
    295		rk_phy->vbus = NULL;
    296	}
    297
    298	/*
    299	 * When acting as uart-pipe, just keep clock on otherwise
    300	 * only power up usb phy when it use, so disable it when init
    301	 */
    302	if (rk_phy->uart_enabled)
    303		return clk_prepare_enable(rk_phy->clk);
    304	else
    305		return rockchip_usb_phy_power(rk_phy, 1);
    306
    307err_clk_prov:
    308	if (!rk_phy->uart_enabled)
    309		clk_unregister(rk_phy->clk480m);
    310err_clk:
    311	if (rk_phy->clk)
    312		clk_put(rk_phy->clk);
    313	return err;
    314}
    315
    316static const struct rockchip_usb_phy_pdata rk3066a_pdata = {
    317	.phys = (struct rockchip_usb_phys[]){
    318		{ .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" },
    319		{ .reg = 0x188, .pll_name = "sclk_otgphy1_480m" },
    320		{ /* sentinel */ }
    321	},
    322};
    323
    324static int __init rockchip_init_usb_uart_common(struct regmap *grf,
    325				const struct rockchip_usb_phy_pdata *pdata)
    326{
    327	int regoffs = pdata->phys[pdata->usb_uart_phy].reg;
    328	int ret;
    329	u32 val;
    330
    331	/*
    332	 * COMMON_ON and DISABLE settings are described in the TRM,
    333	 * but were not present in the original code.
    334	 * Also disable the analog phy components to save power.
    335	 */
    336	val = HIWORD_UPDATE(UOC_CON0_COMMON_ON_N
    337				| UOC_CON0_DISABLE
    338				| UOC_CON0_SIDDQ,
    339			    UOC_CON0_COMMON_ON_N
    340				| UOC_CON0_DISABLE
    341				| UOC_CON0_SIDDQ);
    342	ret = regmap_write(grf, regoffs + UOC_CON0, val);
    343	if (ret)
    344		return ret;
    345
    346	val = HIWORD_UPDATE(UOC_CON2_SOFT_CON_SEL,
    347			    UOC_CON2_SOFT_CON_SEL);
    348	ret = regmap_write(grf, regoffs + UOC_CON2, val);
    349	if (ret)
    350		return ret;
    351
    352	val = HIWORD_UPDATE(UOC_CON3_UTMI_OPMODE_NODRIVING
    353				| UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC
    354				| UOC_CON3_UTMI_TERMSEL_FULLSPEED,
    355			    UOC_CON3_UTMI_SUSPENDN
    356				| UOC_CON3_UTMI_OPMODE_MASK
    357				| UOC_CON3_UTMI_XCVRSEELCT_MASK
    358				| UOC_CON3_UTMI_TERMSEL_FULLSPEED);
    359	ret = regmap_write(grf, UOC_CON3, val);
    360	if (ret)
    361		return ret;
    362
    363	return 0;
    364}
    365
    366#define RK3188_UOC0_CON0				0x10c
    367#define RK3188_UOC0_CON0_BYPASSSEL			BIT(9)
    368#define RK3188_UOC0_CON0_BYPASSDMEN			BIT(8)
    369
    370/*
    371 * Enable the bypass of uart2 data through the otg usb phy.
    372 * See description of rk3288-variant for details.
    373 */
    374static int __init rk3188_init_usb_uart(struct regmap *grf,
    375				const struct rockchip_usb_phy_pdata *pdata)
    376{
    377	u32 val;
    378	int ret;
    379
    380	ret = rockchip_init_usb_uart_common(grf, pdata);
    381	if (ret)
    382		return ret;
    383
    384	val = HIWORD_UPDATE(RK3188_UOC0_CON0_BYPASSSEL
    385				| RK3188_UOC0_CON0_BYPASSDMEN,
    386			    RK3188_UOC0_CON0_BYPASSSEL
    387				| RK3188_UOC0_CON0_BYPASSDMEN);
    388	ret = regmap_write(grf, RK3188_UOC0_CON0, val);
    389	if (ret)
    390		return ret;
    391
    392	return 0;
    393}
    394
    395static const struct rockchip_usb_phy_pdata rk3188_pdata = {
    396	.phys = (struct rockchip_usb_phys[]){
    397		{ .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" },
    398		{ .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" },
    399		{ /* sentinel */ }
    400	},
    401	.init_usb_uart = rk3188_init_usb_uart,
    402	.usb_uart_phy = 0,
    403};
    404
    405#define RK3288_UOC0_CON3				0x32c
    406#define RK3288_UOC0_CON3_BYPASSDMEN			BIT(6)
    407#define RK3288_UOC0_CON3_BYPASSSEL			BIT(7)
    408
    409/*
    410 * Enable the bypass of uart2 data through the otg usb phy.
    411 * Original description in the TRM.
    412 * 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1.
    413 * 2. Disable the pull-up resistance on the D+ line by setting
    414 *    OPMODE0[1:0] to 2’b01.
    415 * 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend
    416 *    mode, set COMMONONN to 1’b1.
    417 * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0.
    418 * 5. Set BYPASSSEL0 to 1’b1.
    419 * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0.
    420 * To receive data, monitor FSVPLUS0.
    421 *
    422 * The actual code in the vendor kernel does some things differently.
    423 */
    424static int __init rk3288_init_usb_uart(struct regmap *grf,
    425				const struct rockchip_usb_phy_pdata *pdata)
    426{
    427	u32 val;
    428	int ret;
    429
    430	ret = rockchip_init_usb_uart_common(grf, pdata);
    431	if (ret)
    432		return ret;
    433
    434	val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL
    435				| RK3288_UOC0_CON3_BYPASSDMEN,
    436			    RK3288_UOC0_CON3_BYPASSSEL
    437				| RK3288_UOC0_CON3_BYPASSDMEN);
    438	ret = regmap_write(grf, RK3288_UOC0_CON3, val);
    439	if (ret)
    440		return ret;
    441
    442	return 0;
    443}
    444
    445static const struct rockchip_usb_phy_pdata rk3288_pdata = {
    446	.phys = (struct rockchip_usb_phys[]){
    447		{ .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
    448		{ .reg = 0x334, .pll_name = "sclk_otgphy1_480m" },
    449		{ .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
    450		{ /* sentinel */ }
    451	},
    452	.init_usb_uart = rk3288_init_usb_uart,
    453	.usb_uart_phy = 0,
    454};
    455
    456static int rockchip_usb_phy_probe(struct platform_device *pdev)
    457{
    458	struct device *dev = &pdev->dev;
    459	struct rockchip_usb_phy_base *phy_base;
    460	struct phy_provider *phy_provider;
    461	const struct of_device_id *match;
    462	struct device_node *child;
    463	int err;
    464
    465	phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL);
    466	if (!phy_base)
    467		return -ENOMEM;
    468
    469	match = of_match_device(dev->driver->of_match_table, dev);
    470	if (!match || !match->data) {
    471		dev_err(dev, "missing phy data\n");
    472		return -EINVAL;
    473	}
    474
    475	phy_base->pdata = match->data;
    476
    477	phy_base->dev = dev;
    478	phy_base->reg_base = ERR_PTR(-ENODEV);
    479	if (dev->parent && dev->parent->of_node)
    480		phy_base->reg_base = syscon_node_to_regmap(
    481						dev->parent->of_node);
    482	if (IS_ERR(phy_base->reg_base))
    483		phy_base->reg_base = syscon_regmap_lookup_by_phandle(
    484						dev->of_node, "rockchip,grf");
    485	if (IS_ERR(phy_base->reg_base)) {
    486		dev_err(&pdev->dev, "Missing rockchip,grf property\n");
    487		return PTR_ERR(phy_base->reg_base);
    488	}
    489
    490	for_each_available_child_of_node(dev->of_node, child) {
    491		err = rockchip_usb_phy_init(phy_base, child);
    492		if (err) {
    493			of_node_put(child);
    494			return err;
    495		}
    496	}
    497
    498	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
    499	return PTR_ERR_OR_ZERO(phy_provider);
    500}
    501
    502static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
    503	{ .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata },
    504	{ .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata },
    505	{ .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata },
    506	{}
    507};
    508
    509MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids);
    510
    511static struct platform_driver rockchip_usb_driver = {
    512	.probe		= rockchip_usb_phy_probe,
    513	.driver		= {
    514		.name	= "rockchip-usb-phy",
    515		.of_match_table = rockchip_usb_phy_dt_ids,
    516	},
    517};
    518
    519module_platform_driver(rockchip_usb_driver);
    520
    521#ifndef MODULE
    522static int __init rockchip_init_usb_uart(void)
    523{
    524	const struct of_device_id *match;
    525	const struct rockchip_usb_phy_pdata *data;
    526	struct device_node *np;
    527	struct regmap *grf;
    528	int ret;
    529
    530	if (!enable_usb_uart)
    531		return 0;
    532
    533	np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids,
    534					     &match);
    535	if (!np) {
    536		pr_err("%s: failed to find usbphy node\n", __func__);
    537		return -ENOTSUPP;
    538	}
    539
    540	pr_debug("%s: using settings for %s\n", __func__, match->compatible);
    541	data = match->data;
    542
    543	if (!data->init_usb_uart) {
    544		pr_err("%s: usb-uart not available on %s\n",
    545		       __func__, match->compatible);
    546		return -ENOTSUPP;
    547	}
    548
    549	grf = ERR_PTR(-ENODEV);
    550	if (np->parent)
    551		grf = syscon_node_to_regmap(np->parent);
    552	if (IS_ERR(grf))
    553		grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
    554	if (IS_ERR(grf)) {
    555		pr_err("%s: Missing rockchip,grf property, %lu\n",
    556		       __func__, PTR_ERR(grf));
    557		return PTR_ERR(grf);
    558	}
    559
    560	ret = data->init_usb_uart(grf, data);
    561	if (ret) {
    562		pr_err("%s: could not init usb_uart, %d\n", __func__, ret);
    563		enable_usb_uart = 0;
    564		return ret;
    565	}
    566
    567	return 0;
    568}
    569early_initcall(rockchip_init_usb_uart);
    570
    571static int __init rockchip_usb_uart(char *buf)
    572{
    573	enable_usb_uart = true;
    574	return 0;
    575}
    576early_param("rockchip.usb_uart", rockchip_usb_uart);
    577#endif
    578
    579MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>");
    580MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
    581MODULE_LICENSE("GPL v2");