162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Bitbanged MDIO support.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Scott Wood <scottwood@freescale.com>
662306a36Sopenharmony_ci * Copyright (c) 2007 Freescale Semiconductor
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Based on CPM2 MDIO code which is:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Copyright (c) 2003 Intracom S.A.
1162306a36Sopenharmony_ci *  by Pantelis Antoniou <panto@intracom.gr>
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * 2005 (c) MontaVista Software, Inc.
1462306a36Sopenharmony_ci * Vitaly Bordug <vbordug@ru.mvista.com>
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci#include <linux/mdio-bitbang.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/types.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define MDIO_READ 2
2362306a36Sopenharmony_ci#define MDIO_WRITE 1
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define MDIO_C45 (1<<15)
2662306a36Sopenharmony_ci#define MDIO_C45_ADDR (MDIO_C45 | 0)
2762306a36Sopenharmony_ci#define MDIO_C45_READ (MDIO_C45 | 3)
2862306a36Sopenharmony_ci#define MDIO_C45_WRITE (MDIO_C45 | 1)
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define MDIO_SETUP_TIME 10
3162306a36Sopenharmony_ci#define MDIO_HOLD_TIME 10
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* Minimum MDC period is 400 ns, plus some margin for error.  MDIO_DELAY
3462306a36Sopenharmony_ci * is done twice per period.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci#define MDIO_DELAY 250
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* The PHY may take up to 300 ns to produce data, plus some margin
3962306a36Sopenharmony_ci * for error.
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_ci#define MDIO_READ_DELAY 350
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* MDIO must already be configured as output. */
4462306a36Sopenharmony_cistatic void mdiobb_send_bit(struct mdiobb_ctrl *ctrl, int val)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	const struct mdiobb_ops *ops = ctrl->ops;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	ops->set_mdio_data(ctrl, val);
4962306a36Sopenharmony_ci	ndelay(MDIO_DELAY);
5062306a36Sopenharmony_ci	ops->set_mdc(ctrl, 1);
5162306a36Sopenharmony_ci	ndelay(MDIO_DELAY);
5262306a36Sopenharmony_ci	ops->set_mdc(ctrl, 0);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/* MDIO must already be configured as input. */
5662306a36Sopenharmony_cistatic int mdiobb_get_bit(struct mdiobb_ctrl *ctrl)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	const struct mdiobb_ops *ops = ctrl->ops;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	ndelay(MDIO_DELAY);
6162306a36Sopenharmony_ci	ops->set_mdc(ctrl, 1);
6262306a36Sopenharmony_ci	ndelay(MDIO_READ_DELAY);
6362306a36Sopenharmony_ci	ops->set_mdc(ctrl, 0);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	return ops->get_mdio_data(ctrl);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* MDIO must already be configured as output. */
6962306a36Sopenharmony_cistatic void mdiobb_send_num(struct mdiobb_ctrl *ctrl, u16 val, int bits)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	int i;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	for (i = bits - 1; i >= 0; i--)
7462306a36Sopenharmony_ci		mdiobb_send_bit(ctrl, (val >> i) & 1);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/* MDIO must already be configured as input. */
7862306a36Sopenharmony_cistatic u16 mdiobb_get_num(struct mdiobb_ctrl *ctrl, int bits)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	int i;
8162306a36Sopenharmony_ci	u16 ret = 0;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	for (i = bits - 1; i >= 0; i--) {
8462306a36Sopenharmony_ci		ret <<= 1;
8562306a36Sopenharmony_ci		ret |= mdiobb_get_bit(ctrl);
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return ret;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* Utility to send the preamble, address, and
9262306a36Sopenharmony_ci * register (common to read and write).
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_cistatic void mdiobb_cmd(struct mdiobb_ctrl *ctrl, int op, u8 phy, u8 reg)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	const struct mdiobb_ops *ops = ctrl->ops;
9762306a36Sopenharmony_ci	int i;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	ops->set_mdio_dir(ctrl, 1);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/*
10262306a36Sopenharmony_ci	 * Send a 32 bit preamble ('1's) with an extra '1' bit for good
10362306a36Sopenharmony_ci	 * measure.  The IEEE spec says this is a PHY optional
10462306a36Sopenharmony_ci	 * requirement.  The AMD 79C874 requires one after power up and
10562306a36Sopenharmony_ci	 * one after a MII communications error.  This means that we are
10662306a36Sopenharmony_ci	 * doing more preambles than we need, but it is safer and will be
10762306a36Sopenharmony_ci	 * much more robust.
10862306a36Sopenharmony_ci	 */
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	for (i = 0; i < 32; i++)
11162306a36Sopenharmony_ci		mdiobb_send_bit(ctrl, 1);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* send the start bit (01) and the read opcode (10) or write (01).
11462306a36Sopenharmony_ci	   Clause 45 operation uses 00 for the start and 11, 10 for
11562306a36Sopenharmony_ci	   read/write */
11662306a36Sopenharmony_ci	mdiobb_send_bit(ctrl, 0);
11762306a36Sopenharmony_ci	if (op & MDIO_C45)
11862306a36Sopenharmony_ci		mdiobb_send_bit(ctrl, 0);
11962306a36Sopenharmony_ci	else
12062306a36Sopenharmony_ci		mdiobb_send_bit(ctrl, 1);
12162306a36Sopenharmony_ci	mdiobb_send_bit(ctrl, (op >> 1) & 1);
12262306a36Sopenharmony_ci	mdiobb_send_bit(ctrl, (op >> 0) & 1);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	mdiobb_send_num(ctrl, phy, 5);
12562306a36Sopenharmony_ci	mdiobb_send_num(ctrl, reg, 5);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/* In clause 45 mode all commands are prefixed by MDIO_ADDR to specify the
12962306a36Sopenharmony_ci   lower 16 bits of the 21 bit address. This transfer is done identically to a
13062306a36Sopenharmony_ci   MDIO_WRITE except for a different code. Theoretically clause 45 and normal
13162306a36Sopenharmony_ci   devices can exist on the same bus. Normal devices should ignore the MDIO_ADDR
13262306a36Sopenharmony_ci   phase. */
13362306a36Sopenharmony_cistatic void mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, int dev_addr,
13462306a36Sopenharmony_ci			    int reg)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	mdiobb_cmd(ctrl, MDIO_C45_ADDR, phy, dev_addr);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* send the turnaround (10) */
13962306a36Sopenharmony_ci	mdiobb_send_bit(ctrl, 1);
14062306a36Sopenharmony_ci	mdiobb_send_bit(ctrl, 0);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	mdiobb_send_num(ctrl, reg, 16);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	ctrl->ops->set_mdio_dir(ctrl, 0);
14562306a36Sopenharmony_ci	mdiobb_get_bit(ctrl);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int mdiobb_read_common(struct mii_bus *bus, int phy)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct mdiobb_ctrl *ctrl = bus->priv;
15162306a36Sopenharmony_ci	int ret, i;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	ctrl->ops->set_mdio_dir(ctrl, 0);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* check the turnaround bit: the PHY should be driving it to zero, if this
15662306a36Sopenharmony_ci	 * PHY is listed in phy_ignore_ta_mask as having broken TA, skip that
15762306a36Sopenharmony_ci	 */
15862306a36Sopenharmony_ci	if (mdiobb_get_bit(ctrl) != 0 &&
15962306a36Sopenharmony_ci	    !(bus->phy_ignore_ta_mask & (1 << phy))) {
16062306a36Sopenharmony_ci		/* PHY didn't drive TA low -- flush any bits it
16162306a36Sopenharmony_ci		 * may be trying to send.
16262306a36Sopenharmony_ci		 */
16362306a36Sopenharmony_ci		for (i = 0; i < 32; i++)
16462306a36Sopenharmony_ci			mdiobb_get_bit(ctrl);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		return 0xffff;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	ret = mdiobb_get_num(ctrl, 16);
17062306a36Sopenharmony_ci	mdiobb_get_bit(ctrl);
17162306a36Sopenharmony_ci	return ret;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciint mdiobb_read_c22(struct mii_bus *bus, int phy, int reg)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct mdiobb_ctrl *ctrl = bus->priv;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	mdiobb_cmd(ctrl, ctrl->op_c22_read, phy, reg);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return mdiobb_read_common(bus, phy);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ciEXPORT_SYMBOL(mdiobb_read_c22);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ciint mdiobb_read_c45(struct mii_bus *bus, int phy, int devad, int reg)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct mdiobb_ctrl *ctrl = bus->priv;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	mdiobb_cmd_addr(ctrl, phy, devad, reg);
18962306a36Sopenharmony_ci	mdiobb_cmd(ctrl, MDIO_C45_READ, phy, devad);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return mdiobb_read_common(bus, phy);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ciEXPORT_SYMBOL(mdiobb_read_c45);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int mdiobb_write_common(struct mii_bus *bus, u16 val)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct mdiobb_ctrl *ctrl = bus->priv;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* send the turnaround (10) */
20062306a36Sopenharmony_ci	mdiobb_send_bit(ctrl, 1);
20162306a36Sopenharmony_ci	mdiobb_send_bit(ctrl, 0);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	mdiobb_send_num(ctrl, val, 16);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ctrl->ops->set_mdio_dir(ctrl, 0);
20662306a36Sopenharmony_ci	mdiobb_get_bit(ctrl);
20762306a36Sopenharmony_ci	return 0;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ciint mdiobb_write_c22(struct mii_bus *bus, int phy, int reg, u16 val)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct mdiobb_ctrl *ctrl = bus->priv;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	mdiobb_cmd(ctrl, ctrl->op_c22_write, phy, reg);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return mdiobb_write_common(bus, val);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ciEXPORT_SYMBOL(mdiobb_write_c22);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ciint mdiobb_write_c45(struct mii_bus *bus, int phy, int devad, int reg, u16 val)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	struct mdiobb_ctrl *ctrl = bus->priv;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	mdiobb_cmd_addr(ctrl, phy, devad, reg);
22562306a36Sopenharmony_ci	mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, devad);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return mdiobb_write_common(bus, val);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ciEXPORT_SYMBOL(mdiobb_write_c45);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistruct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct mii_bus *bus;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	bus = mdiobus_alloc();
23662306a36Sopenharmony_ci	if (!bus)
23762306a36Sopenharmony_ci		return NULL;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	__module_get(ctrl->ops->owner);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	bus->read = mdiobb_read_c22;
24262306a36Sopenharmony_ci	bus->write = mdiobb_write_c22;
24362306a36Sopenharmony_ci	bus->read_c45 = mdiobb_read_c45;
24462306a36Sopenharmony_ci	bus->write_c45 = mdiobb_write_c45;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	bus->priv = ctrl;
24762306a36Sopenharmony_ci	if (!ctrl->override_op_c22) {
24862306a36Sopenharmony_ci		ctrl->op_c22_read = MDIO_READ;
24962306a36Sopenharmony_ci		ctrl->op_c22_write = MDIO_WRITE;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return bus;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ciEXPORT_SYMBOL(alloc_mdio_bitbang);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_civoid free_mdio_bitbang(struct mii_bus *bus)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct mdiobb_ctrl *ctrl = bus->priv;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	module_put(ctrl->ops->owner);
26162306a36Sopenharmony_ci	mdiobus_free(bus);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ciEXPORT_SYMBOL(free_mdio_bitbang);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
266