18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2009-2016 Cavium, Inc.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/delay.h>
78c2ecf20Sopenharmony_ci#include <linux/io.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/phy.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "mdio-cavium.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic void cavium_mdiobus_set_mode(struct cavium_mdiobus *p,
148c2ecf20Sopenharmony_ci				    enum cavium_mdiobus_mode m)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	union cvmx_smix_clk smi_clk;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci	if (m == p->mode)
198c2ecf20Sopenharmony_ci		return;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	smi_clk.u64 = oct_mdio_readq(p->register_base + SMI_CLK);
228c2ecf20Sopenharmony_ci	smi_clk.s.mode = (m == C45) ? 1 : 0;
238c2ecf20Sopenharmony_ci	smi_clk.s.preamble = 1;
248c2ecf20Sopenharmony_ci	oct_mdio_writeq(smi_clk.u64, p->register_base + SMI_CLK);
258c2ecf20Sopenharmony_ci	p->mode = m;
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic int cavium_mdiobus_c45_addr(struct cavium_mdiobus *p,
298c2ecf20Sopenharmony_ci				   int phy_id, int regnum)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	union cvmx_smix_cmd smi_cmd;
328c2ecf20Sopenharmony_ci	union cvmx_smix_wr_dat smi_wr;
338c2ecf20Sopenharmony_ci	int timeout = 1000;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	cavium_mdiobus_set_mode(p, C45);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	smi_wr.u64 = 0;
388c2ecf20Sopenharmony_ci	smi_wr.s.dat = regnum & 0xffff;
398c2ecf20Sopenharmony_ci	oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	regnum = (regnum >> 16) & 0x1f;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	smi_cmd.u64 = 0;
448c2ecf20Sopenharmony_ci	smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_45_ADDRESS */
458c2ecf20Sopenharmony_ci	smi_cmd.s.phy_adr = phy_id;
468c2ecf20Sopenharmony_ci	smi_cmd.s.reg_adr = regnum;
478c2ecf20Sopenharmony_ci	oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	do {
508c2ecf20Sopenharmony_ci		/* Wait 1000 clocks so we don't saturate the RSL bus
518c2ecf20Sopenharmony_ci		 * doing reads.
528c2ecf20Sopenharmony_ci		 */
538c2ecf20Sopenharmony_ci		__delay(1000);
548c2ecf20Sopenharmony_ci		smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
558c2ecf20Sopenharmony_ci	} while (smi_wr.s.pending && --timeout);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (timeout <= 0)
588c2ecf20Sopenharmony_ci		return -EIO;
598c2ecf20Sopenharmony_ci	return 0;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciint cavium_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct cavium_mdiobus *p = bus->priv;
658c2ecf20Sopenharmony_ci	union cvmx_smix_cmd smi_cmd;
668c2ecf20Sopenharmony_ci	union cvmx_smix_rd_dat smi_rd;
678c2ecf20Sopenharmony_ci	unsigned int op = 1; /* MDIO_CLAUSE_22_READ */
688c2ecf20Sopenharmony_ci	int timeout = 1000;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (regnum & MII_ADDR_C45) {
718c2ecf20Sopenharmony_ci		int r = cavium_mdiobus_c45_addr(p, phy_id, regnum);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		if (r < 0)
748c2ecf20Sopenharmony_ci			return r;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci		regnum = (regnum >> 16) & 0x1f;
778c2ecf20Sopenharmony_ci		op = 3; /* MDIO_CLAUSE_45_READ */
788c2ecf20Sopenharmony_ci	} else {
798c2ecf20Sopenharmony_ci		cavium_mdiobus_set_mode(p, C22);
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	smi_cmd.u64 = 0;
838c2ecf20Sopenharmony_ci	smi_cmd.s.phy_op = op;
848c2ecf20Sopenharmony_ci	smi_cmd.s.phy_adr = phy_id;
858c2ecf20Sopenharmony_ci	smi_cmd.s.reg_adr = regnum;
868c2ecf20Sopenharmony_ci	oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	do {
898c2ecf20Sopenharmony_ci		/* Wait 1000 clocks so we don't saturate the RSL bus
908c2ecf20Sopenharmony_ci		 * doing reads.
918c2ecf20Sopenharmony_ci		 */
928c2ecf20Sopenharmony_ci		__delay(1000);
938c2ecf20Sopenharmony_ci		smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT);
948c2ecf20Sopenharmony_ci	} while (smi_rd.s.pending && --timeout);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (smi_rd.s.val)
978c2ecf20Sopenharmony_ci		return smi_rd.s.dat;
988c2ecf20Sopenharmony_ci	else
998c2ecf20Sopenharmony_ci		return -EIO;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cavium_mdiobus_read);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ciint cavium_mdiobus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct cavium_mdiobus *p = bus->priv;
1068c2ecf20Sopenharmony_ci	union cvmx_smix_cmd smi_cmd;
1078c2ecf20Sopenharmony_ci	union cvmx_smix_wr_dat smi_wr;
1088c2ecf20Sopenharmony_ci	unsigned int op = 0; /* MDIO_CLAUSE_22_WRITE */
1098c2ecf20Sopenharmony_ci	int timeout = 1000;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (regnum & MII_ADDR_C45) {
1128c2ecf20Sopenharmony_ci		int r = cavium_mdiobus_c45_addr(p, phy_id, regnum);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		if (r < 0)
1158c2ecf20Sopenharmony_ci			return r;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		regnum = (regnum >> 16) & 0x1f;
1188c2ecf20Sopenharmony_ci		op = 1; /* MDIO_CLAUSE_45_WRITE */
1198c2ecf20Sopenharmony_ci	} else {
1208c2ecf20Sopenharmony_ci		cavium_mdiobus_set_mode(p, C22);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	smi_wr.u64 = 0;
1248c2ecf20Sopenharmony_ci	smi_wr.s.dat = val;
1258c2ecf20Sopenharmony_ci	oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	smi_cmd.u64 = 0;
1288c2ecf20Sopenharmony_ci	smi_cmd.s.phy_op = op;
1298c2ecf20Sopenharmony_ci	smi_cmd.s.phy_adr = phy_id;
1308c2ecf20Sopenharmony_ci	smi_cmd.s.reg_adr = regnum;
1318c2ecf20Sopenharmony_ci	oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	do {
1348c2ecf20Sopenharmony_ci		/* Wait 1000 clocks so we don't saturate the RSL bus
1358c2ecf20Sopenharmony_ci		 * doing reads.
1368c2ecf20Sopenharmony_ci		 */
1378c2ecf20Sopenharmony_ci		__delay(1000);
1388c2ecf20Sopenharmony_ci		smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
1398c2ecf20Sopenharmony_ci	} while (smi_wr.s.pending && --timeout);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (timeout <= 0)
1428c2ecf20Sopenharmony_ci		return -EIO;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cavium_mdiobus_write);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Common code for OCTEON and Thunder MDIO bus drivers");
1498c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Daney");
1508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
151