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-rcar-gen2.c (11016B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Renesas R-Car Gen2 PHY driver
      4 *
      5 * Copyright (C) 2014 Renesas Solutions Corp.
      6 * Copyright (C) 2014 Cogent Embedded, Inc.
      7 * Copyright (C) 2019 Renesas Electronics Corp.
      8 */
      9
     10#include <linux/clk.h>
     11#include <linux/delay.h>
     12#include <linux/io.h>
     13#include <linux/module.h>
     14#include <linux/of.h>
     15#include <linux/phy/phy.h>
     16#include <linux/platform_device.h>
     17#include <linux/spinlock.h>
     18#include <linux/atomic.h>
     19#include <linux/of_device.h>
     20
     21#define USBHS_LPSTS			0x02
     22#define USBHS_UGCTRL			0x80
     23#define USBHS_UGCTRL2			0x84
     24#define USBHS_UGSTS			0x88	/* From technical update */
     25
     26/* Low Power Status register (LPSTS) */
     27#define USBHS_LPSTS_SUSPM		0x4000
     28
     29/* USB General control register (UGCTRL) */
     30#define USBHS_UGCTRL_CONNECT		0x00000004
     31#define USBHS_UGCTRL_PLLRESET		0x00000001
     32
     33/* USB General control register 2 (UGCTRL2) */
     34#define USBHS_UGCTRL2_USB2SEL		0x80000000
     35#define USBHS_UGCTRL2_USB2SEL_PCI	0x00000000
     36#define USBHS_UGCTRL2_USB2SEL_USB30	0x80000000
     37#define USBHS_UGCTRL2_USB0SEL		0x00000030
     38#define USBHS_UGCTRL2_USB0SEL_PCI	0x00000010
     39#define USBHS_UGCTRL2_USB0SEL_HS_USB	0x00000030
     40#define USBHS_UGCTRL2_USB0SEL_USB20	0x00000010
     41#define USBHS_UGCTRL2_USB0SEL_HS_USB20	0x00000020
     42
     43/* USB General status register (UGSTS) */
     44#define USBHS_UGSTS_LOCK		0x00000100 /* From technical update */
     45
     46#define PHYS_PER_CHANNEL	2
     47
     48struct rcar_gen2_phy {
     49	struct phy *phy;
     50	struct rcar_gen2_channel *channel;
     51	int number;
     52	u32 select_value;
     53};
     54
     55struct rcar_gen2_channel {
     56	struct device_node *of_node;
     57	struct rcar_gen2_phy_driver *drv;
     58	struct rcar_gen2_phy phys[PHYS_PER_CHANNEL];
     59	int selected_phy;
     60	u32 select_mask;
     61};
     62
     63struct rcar_gen2_phy_driver {
     64	void __iomem *base;
     65	struct clk *clk;
     66	spinlock_t lock;
     67	int num_channels;
     68	struct rcar_gen2_channel *channels;
     69};
     70
     71struct rcar_gen2_phy_data {
     72	const struct phy_ops *gen2_phy_ops;
     73	const u32 (*select_value)[PHYS_PER_CHANNEL];
     74	const u32 num_channels;
     75};
     76
     77static int rcar_gen2_phy_init(struct phy *p)
     78{
     79	struct rcar_gen2_phy *phy = phy_get_drvdata(p);
     80	struct rcar_gen2_channel *channel = phy->channel;
     81	struct rcar_gen2_phy_driver *drv = channel->drv;
     82	unsigned long flags;
     83	u32 ugctrl2;
     84
     85	/*
     86	 * Try to acquire exclusive access to PHY.  The first driver calling
     87	 * phy_init()  on a given channel wins, and all attempts  to use another
     88	 * PHY on this channel will fail until phy_exit() is called by the first
     89	 * driver.   Achieving this with cmpxcgh() should be SMP-safe.
     90	 */
     91	if (cmpxchg(&channel->selected_phy, -1, phy->number) != -1)
     92		return -EBUSY;
     93
     94	clk_prepare_enable(drv->clk);
     95
     96	spin_lock_irqsave(&drv->lock, flags);
     97	ugctrl2 = readl(drv->base + USBHS_UGCTRL2);
     98	ugctrl2 &= ~channel->select_mask;
     99	ugctrl2 |= phy->select_value;
    100	writel(ugctrl2, drv->base + USBHS_UGCTRL2);
    101	spin_unlock_irqrestore(&drv->lock, flags);
    102	return 0;
    103}
    104
    105static int rcar_gen2_phy_exit(struct phy *p)
    106{
    107	struct rcar_gen2_phy *phy = phy_get_drvdata(p);
    108	struct rcar_gen2_channel *channel = phy->channel;
    109
    110	clk_disable_unprepare(channel->drv->clk);
    111
    112	channel->selected_phy = -1;
    113
    114	return 0;
    115}
    116
    117static int rcar_gen2_phy_power_on(struct phy *p)
    118{
    119	struct rcar_gen2_phy *phy = phy_get_drvdata(p);
    120	struct rcar_gen2_phy_driver *drv = phy->channel->drv;
    121	void __iomem *base = drv->base;
    122	unsigned long flags;
    123	u32 value;
    124	int err = 0, i;
    125
    126	/* Skip if it's not USBHS */
    127	if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB)
    128		return 0;
    129
    130	spin_lock_irqsave(&drv->lock, flags);
    131
    132	/* Power on USBHS PHY */
    133	value = readl(base + USBHS_UGCTRL);
    134	value &= ~USBHS_UGCTRL_PLLRESET;
    135	writel(value, base + USBHS_UGCTRL);
    136
    137	value = readw(base + USBHS_LPSTS);
    138	value |= USBHS_LPSTS_SUSPM;
    139	writew(value, base + USBHS_LPSTS);
    140
    141	for (i = 0; i < 20; i++) {
    142		value = readl(base + USBHS_UGSTS);
    143		if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) {
    144			value = readl(base + USBHS_UGCTRL);
    145			value |= USBHS_UGCTRL_CONNECT;
    146			writel(value, base + USBHS_UGCTRL);
    147			goto out;
    148		}
    149		udelay(1);
    150	}
    151
    152	/* Timed out waiting for the PLL lock */
    153	err = -ETIMEDOUT;
    154
    155out:
    156	spin_unlock_irqrestore(&drv->lock, flags);
    157
    158	return err;
    159}
    160
    161static int rcar_gen2_phy_power_off(struct phy *p)
    162{
    163	struct rcar_gen2_phy *phy = phy_get_drvdata(p);
    164	struct rcar_gen2_phy_driver *drv = phy->channel->drv;
    165	void __iomem *base = drv->base;
    166	unsigned long flags;
    167	u32 value;
    168
    169	/* Skip if it's not USBHS */
    170	if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB)
    171		return 0;
    172
    173	spin_lock_irqsave(&drv->lock, flags);
    174
    175	/* Power off USBHS PHY */
    176	value = readl(base + USBHS_UGCTRL);
    177	value &= ~USBHS_UGCTRL_CONNECT;
    178	writel(value, base + USBHS_UGCTRL);
    179
    180	value = readw(base + USBHS_LPSTS);
    181	value &= ~USBHS_LPSTS_SUSPM;
    182	writew(value, base + USBHS_LPSTS);
    183
    184	value = readl(base + USBHS_UGCTRL);
    185	value |= USBHS_UGCTRL_PLLRESET;
    186	writel(value, base + USBHS_UGCTRL);
    187
    188	spin_unlock_irqrestore(&drv->lock, flags);
    189
    190	return 0;
    191}
    192
    193static int rz_g1c_phy_power_on(struct phy *p)
    194{
    195	struct rcar_gen2_phy *phy = phy_get_drvdata(p);
    196	struct rcar_gen2_phy_driver *drv = phy->channel->drv;
    197	void __iomem *base = drv->base;
    198	unsigned long flags;
    199	u32 value;
    200
    201	spin_lock_irqsave(&drv->lock, flags);
    202
    203	/* Power on USBHS PHY */
    204	value = readl(base + USBHS_UGCTRL);
    205	value &= ~USBHS_UGCTRL_PLLRESET;
    206	writel(value, base + USBHS_UGCTRL);
    207
    208	/* As per the data sheet wait 340 micro sec for power stable */
    209	udelay(340);
    210
    211	if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) {
    212		value = readw(base + USBHS_LPSTS);
    213		value |= USBHS_LPSTS_SUSPM;
    214		writew(value, base + USBHS_LPSTS);
    215	}
    216
    217	spin_unlock_irqrestore(&drv->lock, flags);
    218
    219	return 0;
    220}
    221
    222static int rz_g1c_phy_power_off(struct phy *p)
    223{
    224	struct rcar_gen2_phy *phy = phy_get_drvdata(p);
    225	struct rcar_gen2_phy_driver *drv = phy->channel->drv;
    226	void __iomem *base = drv->base;
    227	unsigned long flags;
    228	u32 value;
    229
    230	spin_lock_irqsave(&drv->lock, flags);
    231	/* Power off USBHS PHY */
    232	if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) {
    233		value = readw(base + USBHS_LPSTS);
    234		value &= ~USBHS_LPSTS_SUSPM;
    235		writew(value, base + USBHS_LPSTS);
    236	}
    237
    238	value = readl(base + USBHS_UGCTRL);
    239	value |= USBHS_UGCTRL_PLLRESET;
    240	writel(value, base + USBHS_UGCTRL);
    241
    242	spin_unlock_irqrestore(&drv->lock, flags);
    243
    244	return 0;
    245}
    246
    247static const struct phy_ops rcar_gen2_phy_ops = {
    248	.init		= rcar_gen2_phy_init,
    249	.exit		= rcar_gen2_phy_exit,
    250	.power_on	= rcar_gen2_phy_power_on,
    251	.power_off	= rcar_gen2_phy_power_off,
    252	.owner		= THIS_MODULE,
    253};
    254
    255static const struct phy_ops rz_g1c_phy_ops = {
    256	.init		= rcar_gen2_phy_init,
    257	.exit		= rcar_gen2_phy_exit,
    258	.power_on	= rz_g1c_phy_power_on,
    259	.power_off	= rz_g1c_phy_power_off,
    260	.owner		= THIS_MODULE,
    261};
    262
    263static const u32 pci_select_value[][PHYS_PER_CHANNEL] = {
    264	[0]	= { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB },
    265	[2]	= { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 },
    266};
    267
    268static const u32 usb20_select_value[][PHYS_PER_CHANNEL] = {
    269	{ USBHS_UGCTRL2_USB0SEL_USB20, USBHS_UGCTRL2_USB0SEL_HS_USB20 },
    270};
    271
    272static const struct rcar_gen2_phy_data rcar_gen2_usb_phy_data = {
    273	.gen2_phy_ops = &rcar_gen2_phy_ops,
    274	.select_value = pci_select_value,
    275	.num_channels = ARRAY_SIZE(pci_select_value),
    276};
    277
    278static const struct rcar_gen2_phy_data rz_g1c_usb_phy_data = {
    279	.gen2_phy_ops = &rz_g1c_phy_ops,
    280	.select_value = usb20_select_value,
    281	.num_channels = ARRAY_SIZE(usb20_select_value),
    282};
    283
    284static const struct of_device_id rcar_gen2_phy_match_table[] = {
    285	{
    286		.compatible = "renesas,usb-phy-r8a77470",
    287		.data = &rz_g1c_usb_phy_data,
    288	},
    289	{
    290		.compatible = "renesas,usb-phy-r8a7790",
    291		.data = &rcar_gen2_usb_phy_data,
    292	},
    293	{
    294		.compatible = "renesas,usb-phy-r8a7791",
    295		.data = &rcar_gen2_usb_phy_data,
    296	},
    297	{
    298		.compatible = "renesas,usb-phy-r8a7794",
    299		.data = &rcar_gen2_usb_phy_data,
    300	},
    301	{
    302		.compatible = "renesas,rcar-gen2-usb-phy",
    303		.data = &rcar_gen2_usb_phy_data,
    304	},
    305	{ /* sentinel */ },
    306};
    307MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table);
    308
    309static struct phy *rcar_gen2_phy_xlate(struct device *dev,
    310				       struct of_phandle_args *args)
    311{
    312	struct rcar_gen2_phy_driver *drv;
    313	struct device_node *np = args->np;
    314	int i;
    315
    316	drv = dev_get_drvdata(dev);
    317	if (!drv)
    318		return ERR_PTR(-EINVAL);
    319
    320	for (i = 0; i < drv->num_channels; i++) {
    321		if (np == drv->channels[i].of_node)
    322			break;
    323	}
    324
    325	if (i >= drv->num_channels || args->args[0] >= 2)
    326		return ERR_PTR(-ENODEV);
    327
    328	return drv->channels[i].phys[args->args[0]].phy;
    329}
    330
    331static const u32 select_mask[] = {
    332	[0]	= USBHS_UGCTRL2_USB0SEL,
    333	[2]	= USBHS_UGCTRL2_USB2SEL,
    334};
    335
    336static int rcar_gen2_phy_probe(struct platform_device *pdev)
    337{
    338	struct device *dev = &pdev->dev;
    339	struct rcar_gen2_phy_driver *drv;
    340	struct phy_provider *provider;
    341	struct device_node *np;
    342	void __iomem *base;
    343	struct clk *clk;
    344	const struct rcar_gen2_phy_data *data;
    345	int i = 0;
    346
    347	if (!dev->of_node) {
    348		dev_err(dev,
    349			"This driver is required to be instantiated from device tree\n");
    350		return -EINVAL;
    351	}
    352
    353	clk = devm_clk_get(dev, "usbhs");
    354	if (IS_ERR(clk)) {
    355		dev_err(dev, "Can't get USBHS clock\n");
    356		return PTR_ERR(clk);
    357	}
    358
    359	base = devm_platform_ioremap_resource(pdev, 0);
    360	if (IS_ERR(base))
    361		return PTR_ERR(base);
    362
    363	drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
    364	if (!drv)
    365		return -ENOMEM;
    366
    367	spin_lock_init(&drv->lock);
    368
    369	drv->clk = clk;
    370	drv->base = base;
    371
    372	data = of_device_get_match_data(dev);
    373	if (!data)
    374		return -EINVAL;
    375
    376	drv->num_channels = of_get_child_count(dev->of_node);
    377	drv->channels = devm_kcalloc(dev, drv->num_channels,
    378				     sizeof(struct rcar_gen2_channel),
    379				     GFP_KERNEL);
    380	if (!drv->channels)
    381		return -ENOMEM;
    382
    383	for_each_child_of_node(dev->of_node, np) {
    384		struct rcar_gen2_channel *channel = drv->channels + i;
    385		u32 channel_num;
    386		int error, n;
    387
    388		channel->of_node = np;
    389		channel->drv = drv;
    390		channel->selected_phy = -1;
    391
    392		error = of_property_read_u32(np, "reg", &channel_num);
    393		if (error || channel_num >= data->num_channels) {
    394			dev_err(dev, "Invalid \"reg\" property\n");
    395			of_node_put(np);
    396			return error;
    397		}
    398		channel->select_mask = select_mask[channel_num];
    399
    400		for (n = 0; n < PHYS_PER_CHANNEL; n++) {
    401			struct rcar_gen2_phy *phy = &channel->phys[n];
    402
    403			phy->channel = channel;
    404			phy->number = n;
    405			phy->select_value = data->select_value[channel_num][n];
    406
    407			phy->phy = devm_phy_create(dev, NULL,
    408						   data->gen2_phy_ops);
    409			if (IS_ERR(phy->phy)) {
    410				dev_err(dev, "Failed to create PHY\n");
    411				of_node_put(np);
    412				return PTR_ERR(phy->phy);
    413			}
    414			phy_set_drvdata(phy->phy, phy);
    415		}
    416
    417		i++;
    418	}
    419
    420	provider = devm_of_phy_provider_register(dev, rcar_gen2_phy_xlate);
    421	if (IS_ERR(provider)) {
    422		dev_err(dev, "Failed to register PHY provider\n");
    423		return PTR_ERR(provider);
    424	}
    425
    426	dev_set_drvdata(dev, drv);
    427
    428	return 0;
    429}
    430
    431static struct platform_driver rcar_gen2_phy_driver = {
    432	.driver = {
    433		.name		= "phy_rcar_gen2",
    434		.of_match_table	= rcar_gen2_phy_match_table,
    435	},
    436	.probe	= rcar_gen2_phy_probe,
    437};
    438
    439module_platform_driver(rcar_gen2_phy_driver);
    440
    441MODULE_LICENSE("GPL v2");
    442MODULE_DESCRIPTION("Renesas R-Car Gen2 PHY");
    443MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>");