| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 
 | // SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2015 Broadcom Corporation
 */
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_mdio.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#define IPROC_GPHY_MDCDIV    0x1a
#define MII_CTRL_OFFSET      0x000
#define MII_CTRL_DIV_SHIFT   0
#define MII_CTRL_PRE_SHIFT   7
#define MII_CTRL_BUSY_SHIFT  8
#define MII_DATA_OFFSET      0x004
#define MII_DATA_MASK        0xffff
#define MII_DATA_TA_SHIFT    16
#define MII_DATA_TA_VAL      2
#define MII_DATA_RA_SHIFT    18
#define MII_DATA_PA_SHIFT    23
#define MII_DATA_OP_SHIFT    28
#define MII_DATA_OP_WRITE    1
#define MII_DATA_OP_READ     2
#define MII_DATA_SB_SHIFT    30
struct iproc_mdio_priv {
	struct mii_bus *mii_bus;
	void __iomem *base;
};
static inline int iproc_mdio_wait_for_idle(void __iomem *base)
{
	u32 val;
	unsigned int timeout = 1000; /* loop for 1s */
	do {
		val = readl(base + MII_CTRL_OFFSET);
		if ((val & BIT(MII_CTRL_BUSY_SHIFT)) == 0)
			return 0;
		usleep_range(1000, 2000);
	} while (timeout--);
	return -ETIMEDOUT;
}
static inline void iproc_mdio_config_clk(void __iomem *base)
{
	u32 val;
	val = (IPROC_GPHY_MDCDIV << MII_CTRL_DIV_SHIFT) |
		  BIT(MII_CTRL_PRE_SHIFT);
	writel(val, base + MII_CTRL_OFFSET);
}
static int iproc_mdio_read(struct mii_bus *bus, int phy_id, int reg)
{
	struct iproc_mdio_priv *priv = bus->priv;
	u32 cmd;
	int rc;
	rc = iproc_mdio_wait_for_idle(priv->base);
	if (rc)
		return rc;
	/* Prepare the read operation */
	cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) |
		(reg << MII_DATA_RA_SHIFT) |
		(phy_id << MII_DATA_PA_SHIFT) |
		BIT(MII_DATA_SB_SHIFT) |
		(MII_DATA_OP_READ << MII_DATA_OP_SHIFT);
	writel(cmd, priv->base + MII_DATA_OFFSET);
	rc = iproc_mdio_wait_for_idle(priv->base);
	if (rc)
		return rc;
	cmd = readl(priv->base + MII_DATA_OFFSET) & MII_DATA_MASK;
	return cmd;
}
static int iproc_mdio_write(struct mii_bus *bus, int phy_id,
			    int reg, u16 val)
{
	struct iproc_mdio_priv *priv = bus->priv;
	u32 cmd;
	int rc;
	rc = iproc_mdio_wait_for_idle(priv->base);
	if (rc)
		return rc;
	/* Prepare the write operation */
	cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) |
		(reg << MII_DATA_RA_SHIFT) |
		(phy_id << MII_DATA_PA_SHIFT) |
		BIT(MII_DATA_SB_SHIFT) |
		(MII_DATA_OP_WRITE << MII_DATA_OP_SHIFT) |
		((u32)(val) & MII_DATA_MASK);
	writel(cmd, priv->base + MII_DATA_OFFSET);
	rc = iproc_mdio_wait_for_idle(priv->base);
	if (rc)
		return rc;
	return 0;
}
static int iproc_mdio_probe(struct platform_device *pdev)
{
	struct iproc_mdio_priv *priv;
	struct mii_bus *bus;
	int rc;
	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;
	priv->base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(priv->base)) {
		dev_err(&pdev->dev, "failed to ioremap register\n");
		return PTR_ERR(priv->base);
	}
	priv->mii_bus = mdiobus_alloc();
	if (!priv->mii_bus) {
		dev_err(&pdev->dev, "MDIO bus alloc failed\n");
		return -ENOMEM;
	}
	bus = priv->mii_bus;
	bus->priv = priv;
	bus->name = "iProc MDIO bus";
	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);
	bus->parent = &pdev->dev;
	bus->read = iproc_mdio_read;
	bus->write = iproc_mdio_write;
	iproc_mdio_config_clk(priv->base);
	rc = of_mdiobus_register(bus, pdev->dev.of_node);
	if (rc) {
		dev_err(&pdev->dev, "MDIO bus registration failed\n");
		goto err_iproc_mdio;
	}
	platform_set_drvdata(pdev, priv);
	dev_info(&pdev->dev, "Broadcom iProc MDIO bus registered\n");
	return 0;
err_iproc_mdio:
	mdiobus_free(bus);
	return rc;
}
static void iproc_mdio_remove(struct platform_device *pdev)
{
	struct iproc_mdio_priv *priv = platform_get_drvdata(pdev);
	mdiobus_unregister(priv->mii_bus);
	mdiobus_free(priv->mii_bus);
}
#ifdef CONFIG_PM_SLEEP
static int iproc_mdio_resume(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct iproc_mdio_priv *priv = platform_get_drvdata(pdev);
	/* restore the mii clock configuration */
	iproc_mdio_config_clk(priv->base);
	return 0;
}
static const struct dev_pm_ops iproc_mdio_pm_ops = {
	.resume = iproc_mdio_resume
};
#endif /* CONFIG_PM_SLEEP */
static const struct of_device_id iproc_mdio_of_match[] = {
	{ .compatible = "brcm,iproc-mdio", },
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, iproc_mdio_of_match);
static struct platform_driver iproc_mdio_driver = {
	.driver = {
		.name = "iproc-mdio",
		.of_match_table = iproc_mdio_of_match,
#ifdef CONFIG_PM_SLEEP
		.pm = &iproc_mdio_pm_ops,
#endif
	},
	.probe = iproc_mdio_probe,
	.remove_new = iproc_mdio_remove,
};
module_platform_driver(iproc_mdio_driver);
MODULE_AUTHOR("Broadcom Corporation");
MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:iproc-mdio");
 |