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