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

mdio-mux-bcm6368.c (4205B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Broadcom BCM6368 mdiomux bus controller driver
      4 *
      5 * Copyright (C) 2021 Álvaro Fernández Rojas <noltari@gmail.com>
      6 */
      7
      8#include <linux/delay.h>
      9#include <linux/io.h>
     10#include <linux/kernel.h>
     11#include <linux/mdio-mux.h>
     12#include <linux/module.h>
     13#include <linux/of.h>
     14#include <linux/of_platform.h>
     15#include <linux/of_mdio.h>
     16#include <linux/phy.h>
     17#include <linux/platform_device.h>
     18#include <linux/sched.h>
     19
     20#define MDIOC_REG		0x0
     21#define MDIOC_EXT_MASK		BIT(16)
     22#define MDIOC_REG_SHIFT		20
     23#define MDIOC_PHYID_SHIFT	25
     24#define MDIOC_RD_MASK		BIT(30)
     25#define MDIOC_WR_MASK		BIT(31)
     26
     27#define MDIOD_REG		0x4
     28
     29struct bcm6368_mdiomux_desc {
     30	void *mux_handle;
     31	void __iomem *base;
     32	struct device *dev;
     33	struct mii_bus *mii_bus;
     34	int ext_phy;
     35};
     36
     37static int bcm6368_mdiomux_read(struct mii_bus *bus, int phy_id, int loc)
     38{
     39	struct bcm6368_mdiomux_desc *md = bus->priv;
     40	uint32_t reg;
     41	int ret;
     42
     43	__raw_writel(0, md->base + MDIOC_REG);
     44
     45	reg = MDIOC_RD_MASK |
     46	      (phy_id << MDIOC_PHYID_SHIFT) |
     47	      (loc << MDIOC_REG_SHIFT);
     48	if (md->ext_phy)
     49		reg |= MDIOC_EXT_MASK;
     50
     51	__raw_writel(reg, md->base + MDIOC_REG);
     52	udelay(50);
     53	ret = __raw_readw(md->base + MDIOD_REG);
     54
     55	return ret;
     56}
     57
     58static int bcm6368_mdiomux_write(struct mii_bus *bus, int phy_id, int loc,
     59				 uint16_t val)
     60{
     61	struct bcm6368_mdiomux_desc *md = bus->priv;
     62	uint32_t reg;
     63
     64	__raw_writel(0, md->base + MDIOC_REG);
     65
     66	reg = MDIOC_WR_MASK |
     67	      (phy_id << MDIOC_PHYID_SHIFT) |
     68	      (loc << MDIOC_REG_SHIFT);
     69	if (md->ext_phy)
     70		reg |= MDIOC_EXT_MASK;
     71	reg |= val;
     72
     73	__raw_writel(reg, md->base + MDIOC_REG);
     74	udelay(50);
     75
     76	return 0;
     77}
     78
     79static int bcm6368_mdiomux_switch_fn(int current_child, int desired_child,
     80				     void *data)
     81{
     82	struct bcm6368_mdiomux_desc *md = data;
     83
     84	md->ext_phy = desired_child;
     85
     86	return 0;
     87}
     88
     89static int bcm6368_mdiomux_probe(struct platform_device *pdev)
     90{
     91	struct bcm6368_mdiomux_desc *md;
     92	struct mii_bus *bus;
     93	struct resource *res;
     94	int rc;
     95
     96	md = devm_kzalloc(&pdev->dev, sizeof(*md), GFP_KERNEL);
     97	if (!md)
     98		return -ENOMEM;
     99	md->dev = &pdev->dev;
    100
    101	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    102	if (!res)
    103		return -EINVAL;
    104
    105	/*
    106	 * Just ioremap, as this MDIO block is usually integrated into an
    107	 * Ethernet MAC controller register range
    108	 */
    109	md->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
    110	if (!md->base) {
    111		dev_err(&pdev->dev, "failed to ioremap register\n");
    112		return -ENOMEM;
    113	}
    114
    115	md->mii_bus = devm_mdiobus_alloc(&pdev->dev);
    116	if (!md->mii_bus) {
    117		dev_err(&pdev->dev, "mdiomux bus alloc failed\n");
    118		return -ENOMEM;
    119	}
    120
    121	bus = md->mii_bus;
    122	bus->priv = md;
    123	bus->name = "BCM6368 MDIO mux bus";
    124	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);
    125	bus->parent = &pdev->dev;
    126	bus->read = bcm6368_mdiomux_read;
    127	bus->write = bcm6368_mdiomux_write;
    128	bus->phy_mask = 0x3f;
    129	bus->dev.of_node = pdev->dev.of_node;
    130
    131	rc = mdiobus_register(bus);
    132	if (rc) {
    133		dev_err(&pdev->dev, "mdiomux registration failed\n");
    134		return rc;
    135	}
    136
    137	platform_set_drvdata(pdev, md);
    138
    139	rc = mdio_mux_init(md->dev, md->dev->of_node,
    140			   bcm6368_mdiomux_switch_fn, &md->mux_handle, md,
    141			   md->mii_bus);
    142	if (rc) {
    143		dev_info(md->dev, "mdiomux initialization failed\n");
    144		goto out_register;
    145	}
    146
    147	dev_info(&pdev->dev, "Broadcom BCM6368 MDIO mux bus\n");
    148
    149	return 0;
    150
    151out_register:
    152	mdiobus_unregister(bus);
    153	return rc;
    154}
    155
    156static int bcm6368_mdiomux_remove(struct platform_device *pdev)
    157{
    158	struct bcm6368_mdiomux_desc *md = platform_get_drvdata(pdev);
    159
    160	mdio_mux_uninit(md->mux_handle);
    161	mdiobus_unregister(md->mii_bus);
    162
    163	return 0;
    164}
    165
    166static const struct of_device_id bcm6368_mdiomux_ids[] = {
    167	{ .compatible = "brcm,bcm6368-mdio-mux", },
    168	{ /* sentinel */ }
    169};
    170MODULE_DEVICE_TABLE(of, bcm6368_mdiomux_ids);
    171
    172static struct platform_driver bcm6368_mdiomux_driver = {
    173	.driver = {
    174		.name = "bcm6368-mdio-mux",
    175		.of_match_table = bcm6368_mdiomux_ids,
    176	},
    177	.probe	= bcm6368_mdiomux_probe,
    178	.remove	= bcm6368_mdiomux_remove,
    179};
    180module_platform_driver(bcm6368_mdiomux_driver);
    181
    182MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
    183MODULE_DESCRIPTION("BCM6368 mdiomux bus controller driver");
    184MODULE_LICENSE("GPL v2");