162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2009-2016 Cavium, Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci#include <linux/io.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/phy.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "mdio-cavium.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic void cavium_mdiobus_set_mode(struct cavium_mdiobus *p,
1462306a36Sopenharmony_ci				    enum cavium_mdiobus_mode m)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	union cvmx_smix_clk smi_clk;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	if (m == p->mode)
1962306a36Sopenharmony_ci		return;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	smi_clk.u64 = oct_mdio_readq(p->register_base + SMI_CLK);
2262306a36Sopenharmony_ci	smi_clk.s.mode = (m == C45) ? 1 : 0;
2362306a36Sopenharmony_ci	smi_clk.s.preamble = 1;
2462306a36Sopenharmony_ci	oct_mdio_writeq(smi_clk.u64, p->register_base + SMI_CLK);
2562306a36Sopenharmony_ci	p->mode = m;
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic int cavium_mdiobus_c45_addr(struct cavium_mdiobus *p,
2962306a36Sopenharmony_ci				   int phy_id, int devad, int regnum)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	union cvmx_smix_cmd smi_cmd;
3262306a36Sopenharmony_ci	union cvmx_smix_wr_dat smi_wr;
3362306a36Sopenharmony_ci	int timeout = 1000;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	cavium_mdiobus_set_mode(p, C45);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	smi_wr.u64 = 0;
3862306a36Sopenharmony_ci	smi_wr.s.dat = regnum & 0xffff;
3962306a36Sopenharmony_ci	oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	smi_cmd.u64 = 0;
4262306a36Sopenharmony_ci	smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_45_ADDRESS */
4362306a36Sopenharmony_ci	smi_cmd.s.phy_adr = phy_id;
4462306a36Sopenharmony_ci	smi_cmd.s.reg_adr = devad;
4562306a36Sopenharmony_ci	oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	do {
4862306a36Sopenharmony_ci		/* Wait 1000 clocks so we don't saturate the RSL bus
4962306a36Sopenharmony_ci		 * doing reads.
5062306a36Sopenharmony_ci		 */
5162306a36Sopenharmony_ci		__delay(1000);
5262306a36Sopenharmony_ci		smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
5362306a36Sopenharmony_ci	} while (smi_wr.s.pending && --timeout);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (timeout <= 0)
5662306a36Sopenharmony_ci		return -EIO;
5762306a36Sopenharmony_ci	return 0;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciint cavium_mdiobus_read_c22(struct mii_bus *bus, int phy_id, int regnum)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct cavium_mdiobus *p = bus->priv;
6362306a36Sopenharmony_ci	union cvmx_smix_cmd smi_cmd;
6462306a36Sopenharmony_ci	union cvmx_smix_rd_dat smi_rd;
6562306a36Sopenharmony_ci	int timeout = 1000;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	cavium_mdiobus_set_mode(p, C22);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	smi_cmd.u64 = 0;
7062306a36Sopenharmony_ci	smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_22_READ */
7162306a36Sopenharmony_ci	smi_cmd.s.phy_adr = phy_id;
7262306a36Sopenharmony_ci	smi_cmd.s.reg_adr = regnum;
7362306a36Sopenharmony_ci	oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	do {
7662306a36Sopenharmony_ci		/* Wait 1000 clocks so we don't saturate the RSL bus
7762306a36Sopenharmony_ci		 * doing reads.
7862306a36Sopenharmony_ci		 */
7962306a36Sopenharmony_ci		__delay(1000);
8062306a36Sopenharmony_ci		smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT);
8162306a36Sopenharmony_ci	} while (smi_rd.s.pending && --timeout);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (smi_rd.s.val)
8462306a36Sopenharmony_ci		return smi_rd.s.dat;
8562306a36Sopenharmony_ci	else
8662306a36Sopenharmony_ci		return -EIO;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ciEXPORT_SYMBOL(cavium_mdiobus_read_c22);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ciint cavium_mdiobus_read_c45(struct mii_bus *bus, int phy_id, int devad,
9162306a36Sopenharmony_ci			    int regnum)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct cavium_mdiobus *p = bus->priv;
9462306a36Sopenharmony_ci	union cvmx_smix_cmd smi_cmd;
9562306a36Sopenharmony_ci	union cvmx_smix_rd_dat smi_rd;
9662306a36Sopenharmony_ci	int timeout = 1000;
9762306a36Sopenharmony_ci	int r;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	r = cavium_mdiobus_c45_addr(p, phy_id, devad, regnum);
10062306a36Sopenharmony_ci	if (r < 0)
10162306a36Sopenharmony_ci		return r;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	smi_cmd.u64 = 0;
10462306a36Sopenharmony_ci	smi_cmd.s.phy_op = 3; /* MDIO_CLAUSE_45_READ */
10562306a36Sopenharmony_ci	smi_cmd.s.phy_adr = phy_id;
10662306a36Sopenharmony_ci	smi_cmd.s.reg_adr = regnum;
10762306a36Sopenharmony_ci	oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	do {
11062306a36Sopenharmony_ci		/* Wait 1000 clocks so we don't saturate the RSL bus
11162306a36Sopenharmony_ci		 * doing reads.
11262306a36Sopenharmony_ci		 */
11362306a36Sopenharmony_ci		__delay(1000);
11462306a36Sopenharmony_ci		smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT);
11562306a36Sopenharmony_ci	} while (smi_rd.s.pending && --timeout);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (smi_rd.s.val)
11862306a36Sopenharmony_ci		return smi_rd.s.dat;
11962306a36Sopenharmony_ci	else
12062306a36Sopenharmony_ci		return -EIO;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ciEXPORT_SYMBOL(cavium_mdiobus_read_c45);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ciint cavium_mdiobus_write_c22(struct mii_bus *bus, int phy_id, int regnum,
12562306a36Sopenharmony_ci			     u16 val)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct cavium_mdiobus *p = bus->priv;
12862306a36Sopenharmony_ci	union cvmx_smix_cmd smi_cmd;
12962306a36Sopenharmony_ci	union cvmx_smix_wr_dat smi_wr;
13062306a36Sopenharmony_ci	int timeout = 1000;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	cavium_mdiobus_set_mode(p, C22);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	smi_wr.u64 = 0;
13562306a36Sopenharmony_ci	smi_wr.s.dat = val;
13662306a36Sopenharmony_ci	oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	smi_cmd.u64 = 0;
13962306a36Sopenharmony_ci	smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_22_WRITE */
14062306a36Sopenharmony_ci	smi_cmd.s.phy_adr = phy_id;
14162306a36Sopenharmony_ci	smi_cmd.s.reg_adr = regnum;
14262306a36Sopenharmony_ci	oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	do {
14562306a36Sopenharmony_ci		/* Wait 1000 clocks so we don't saturate the RSL bus
14662306a36Sopenharmony_ci		 * doing reads.
14762306a36Sopenharmony_ci		 */
14862306a36Sopenharmony_ci		__delay(1000);
14962306a36Sopenharmony_ci		smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
15062306a36Sopenharmony_ci	} while (smi_wr.s.pending && --timeout);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (timeout <= 0)
15362306a36Sopenharmony_ci		return -EIO;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ciEXPORT_SYMBOL(cavium_mdiobus_write_c22);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ciint cavium_mdiobus_write_c45(struct mii_bus *bus, int phy_id, int devad,
16062306a36Sopenharmony_ci			     int regnum, u16 val)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct cavium_mdiobus *p = bus->priv;
16362306a36Sopenharmony_ci	union cvmx_smix_cmd smi_cmd;
16462306a36Sopenharmony_ci	union cvmx_smix_wr_dat smi_wr;
16562306a36Sopenharmony_ci	int timeout = 1000;
16662306a36Sopenharmony_ci	int r;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	r = cavium_mdiobus_c45_addr(p, phy_id, devad, regnum);
16962306a36Sopenharmony_ci	if (r < 0)
17062306a36Sopenharmony_ci		return r;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	smi_wr.u64 = 0;
17362306a36Sopenharmony_ci	smi_wr.s.dat = val;
17462306a36Sopenharmony_ci	oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	smi_cmd.u64 = 0;
17762306a36Sopenharmony_ci	smi_cmd.s.phy_op = 1; /* MDIO_CLAUSE_45_WRITE */
17862306a36Sopenharmony_ci	smi_cmd.s.phy_adr = phy_id;
17962306a36Sopenharmony_ci	smi_cmd.s.reg_adr = devad;
18062306a36Sopenharmony_ci	oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	do {
18362306a36Sopenharmony_ci		/* Wait 1000 clocks so we don't saturate the RSL bus
18462306a36Sopenharmony_ci		 * doing reads.
18562306a36Sopenharmony_ci		 */
18662306a36Sopenharmony_ci		__delay(1000);
18762306a36Sopenharmony_ci		smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
18862306a36Sopenharmony_ci	} while (smi_wr.s.pending && --timeout);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (timeout <= 0)
19162306a36Sopenharmony_ci		return -EIO;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return 0;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ciEXPORT_SYMBOL(cavium_mdiobus_write_c45);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ciMODULE_DESCRIPTION("Common code for OCTEON and Thunder MDIO bus drivers");
19862306a36Sopenharmony_ciMODULE_AUTHOR("David Daney");
19962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
200