blob: a4667326f745350fc90a121cf7672a9467c9ca8d [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Grant Likely92744982009-04-25 12:53:39 +00002/*
3 * MDIO bus driver for the Xilinx TEMAC device
4 *
5 * Copyright (c) 2009 Secret Lab Technologies, Ltd.
6 */
7
8#include <linux/io.h>
9#include <linux/netdevice.h>
10#include <linux/mutex.h>
11#include <linux/phy.h>
12#include <linux/of.h>
13#include <linux/of_device.h>
Michal Simek9f1a1fc2010-09-01 08:55:23 -060014#include <linux/of_address.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090015#include <linux/slab.h>
Grant Likely92744982009-04-25 12:53:39 +000016#include <linux/of_mdio.h>
Esben Haabendal8425c412019-04-30 09:17:49 +020017#include <linux/platform_data/xilinx-ll-temac.h>
Grant Likely92744982009-04-25 12:53:39 +000018
19#include "ll_temac.h"
20
21/* ---------------------------------------------------------------------
22 * MDIO Bus functions
23 */
24static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
25{
26 struct temac_local *lp = bus->priv;
27 u32 rc;
28
29 /* Write the PHY address to the MIIM Access Initiator register.
30 * When the transfer completes, the PHY register value will appear
31 * in the LSW0 register */
Esben Haabendalf14f5c12019-04-30 09:17:54 +020032 mutex_lock(lp->indirect_mutex);
Grant Likely92744982009-04-25 12:53:39 +000033 temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg);
34 rc = temac_indirect_in32(lp, XTE_MIIMAI_OFFSET);
Esben Haabendalf14f5c12019-04-30 09:17:54 +020035 mutex_unlock(lp->indirect_mutex);
Grant Likely92744982009-04-25 12:53:39 +000036
37 dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n",
38 phy_id, reg, rc);
39
40 return rc;
41}
42
43static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
44{
45 struct temac_local *lp = bus->priv;
46
47 dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
48 phy_id, reg, val);
49
50 /* First write the desired value into the write data register
51 * and then write the address into the access initiator register
52 */
Esben Haabendalf14f5c12019-04-30 09:17:54 +020053 mutex_lock(lp->indirect_mutex);
Grant Likely92744982009-04-25 12:53:39 +000054 temac_indirect_out32(lp, XTE_MGTDR_OFFSET, val);
55 temac_indirect_out32(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg);
Esben Haabendalf14f5c12019-04-30 09:17:54 +020056 mutex_unlock(lp->indirect_mutex);
Grant Likely92744982009-04-25 12:53:39 +000057
58 return 0;
59}
60
Esben Haabendala63625d2019-04-30 09:17:48 +020061int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev)
Grant Likely92744982009-04-25 12:53:39 +000062{
Esben Haabendal8425c412019-04-30 09:17:49 +020063 struct ll_temac_platform_data *pdata = dev_get_platdata(&pdev->dev);
Esben Haabendala63625d2019-04-30 09:17:48 +020064 struct device_node *np = dev_of_node(&pdev->dev);
Grant Likely92744982009-04-25 12:53:39 +000065 struct mii_bus *bus;
Tobias Klausere734a422015-09-23 09:20:02 +020066 u32 bus_hz;
Grant Likely92744982009-04-25 12:53:39 +000067 int clk_div;
Tobias Klausere734a422015-09-23 09:20:02 +020068 int rc;
Grant Likely92744982009-04-25 12:53:39 +000069 struct resource res;
70
Esben Haabendal8425c412019-04-30 09:17:49 +020071 /* Get MDIO bus frequency (if specified) */
72 bus_hz = 0;
73 if (np)
74 of_property_read_u32(np, "clock-frequency", &bus_hz);
75 else if (pdata)
76 bus_hz = pdata->mdio_clk_freq;
77
Grant Likely92744982009-04-25 12:53:39 +000078 /* Calculate a reasonable divisor for the clock rate */
79 clk_div = 0x3f; /* worst-case default setting */
Esben Haabendal8425c412019-04-30 09:17:49 +020080 if (bus_hz != 0) {
Tobias Klausere734a422015-09-23 09:20:02 +020081 clk_div = bus_hz / (2500 * 1000 * 2) - 1;
Grant Likely92744982009-04-25 12:53:39 +000082 if (clk_div < 1)
83 clk_div = 1;
84 if (clk_div > 0x3f)
85 clk_div = 0x3f;
86 }
87
88 /* Enable the MDIO bus by asserting the enable bit and writing
89 * in the clock config */
Esben Haabendalf14f5c12019-04-30 09:17:54 +020090 mutex_lock(lp->indirect_mutex);
Grant Likely92744982009-04-25 12:53:39 +000091 temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div);
Esben Haabendalf14f5c12019-04-30 09:17:54 +020092 mutex_unlock(lp->indirect_mutex);
Grant Likely92744982009-04-25 12:53:39 +000093
Esben Haabendala63625d2019-04-30 09:17:48 +020094 bus = devm_mdiobus_alloc(&pdev->dev);
Grant Likely92744982009-04-25 12:53:39 +000095 if (!bus)
96 return -ENOMEM;
97
Esben Haabendal8425c412019-04-30 09:17:49 +020098 if (np) {
99 of_address_to_resource(np, 0, &res);
100 snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
101 (unsigned long long)res.start);
Dan Carpenterb52d0312019-05-03 15:50:51 +0300102 } else if (pdata) {
Esben Haabendal8425c412019-04-30 09:17:49 +0200103 snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
104 pdata->mdio_bus_id);
105 }
106
Grant Likely92744982009-04-25 12:53:39 +0000107 bus->priv = lp;
108 bus->name = "Xilinx TEMAC MDIO";
109 bus->read = temac_mdio_read;
110 bus->write = temac_mdio_write;
111 bus->parent = lp->dev;
Grant Likely92744982009-04-25 12:53:39 +0000112
113 lp->mii_bus = bus;
114
115 rc = of_mdiobus_register(bus, np);
116 if (rc)
Esben Haabendala63625d2019-04-30 09:17:48 +0200117 return rc;
Grant Likely92744982009-04-25 12:53:39 +0000118
Esben Haabendalf14f5c12019-04-30 09:17:54 +0200119 mutex_lock(lp->indirect_mutex);
Grant Likely92744982009-04-25 12:53:39 +0000120 dev_dbg(lp->dev, "MDIO bus registered; MC:%x\n",
121 temac_indirect_in32(lp, XTE_MC_OFFSET));
Esben Haabendalf14f5c12019-04-30 09:17:54 +0200122 mutex_unlock(lp->indirect_mutex);
Grant Likely92744982009-04-25 12:53:39 +0000123 return 0;
Grant Likely92744982009-04-25 12:53:39 +0000124}
125
126void temac_mdio_teardown(struct temac_local *lp)
127{
128 mdiobus_unregister(lp->mii_bus);
Grant Likely92744982009-04-25 12:53:39 +0000129}