0001
0002
0003
0004
0005
0006 #include <linux/delay.h>
0007 #include <linux/io.h>
0008 #include <linux/module.h>
0009 #include <linux/phy.h>
0010
0011 #include "mdio-cavium.h"
0012
0013 static void cavium_mdiobus_set_mode(struct cavium_mdiobus *p,
0014 enum cavium_mdiobus_mode m)
0015 {
0016 union cvmx_smix_clk smi_clk;
0017
0018 if (m == p->mode)
0019 return;
0020
0021 smi_clk.u64 = oct_mdio_readq(p->register_base + SMI_CLK);
0022 smi_clk.s.mode = (m == C45) ? 1 : 0;
0023 smi_clk.s.preamble = 1;
0024 oct_mdio_writeq(smi_clk.u64, p->register_base + SMI_CLK);
0025 p->mode = m;
0026 }
0027
0028 static int cavium_mdiobus_c45_addr(struct cavium_mdiobus *p,
0029 int phy_id, int regnum)
0030 {
0031 union cvmx_smix_cmd smi_cmd;
0032 union cvmx_smix_wr_dat smi_wr;
0033 int timeout = 1000;
0034
0035 cavium_mdiobus_set_mode(p, C45);
0036
0037 smi_wr.u64 = 0;
0038 smi_wr.s.dat = regnum & 0xffff;
0039 oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
0040
0041 regnum = (regnum >> 16) & 0x1f;
0042
0043 smi_cmd.u64 = 0;
0044 smi_cmd.s.phy_op = 0;
0045 smi_cmd.s.phy_adr = phy_id;
0046 smi_cmd.s.reg_adr = regnum;
0047 oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
0048
0049 do {
0050
0051
0052
0053 __delay(1000);
0054 smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
0055 } while (smi_wr.s.pending && --timeout);
0056
0057 if (timeout <= 0)
0058 return -EIO;
0059 return 0;
0060 }
0061
0062 int cavium_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
0063 {
0064 struct cavium_mdiobus *p = bus->priv;
0065 union cvmx_smix_cmd smi_cmd;
0066 union cvmx_smix_rd_dat smi_rd;
0067 unsigned int op = 1;
0068 int timeout = 1000;
0069
0070 if (regnum & MII_ADDR_C45) {
0071 int r = cavium_mdiobus_c45_addr(p, phy_id, regnum);
0072
0073 if (r < 0)
0074 return r;
0075
0076 regnum = (regnum >> 16) & 0x1f;
0077 op = 3;
0078 } else {
0079 cavium_mdiobus_set_mode(p, C22);
0080 }
0081
0082 smi_cmd.u64 = 0;
0083 smi_cmd.s.phy_op = op;
0084 smi_cmd.s.phy_adr = phy_id;
0085 smi_cmd.s.reg_adr = regnum;
0086 oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
0087
0088 do {
0089
0090
0091
0092 __delay(1000);
0093 smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT);
0094 } while (smi_rd.s.pending && --timeout);
0095
0096 if (smi_rd.s.val)
0097 return smi_rd.s.dat;
0098 else
0099 return -EIO;
0100 }
0101 EXPORT_SYMBOL(cavium_mdiobus_read);
0102
0103 int cavium_mdiobus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
0104 {
0105 struct cavium_mdiobus *p = bus->priv;
0106 union cvmx_smix_cmd smi_cmd;
0107 union cvmx_smix_wr_dat smi_wr;
0108 unsigned int op = 0;
0109 int timeout = 1000;
0110
0111 if (regnum & MII_ADDR_C45) {
0112 int r = cavium_mdiobus_c45_addr(p, phy_id, regnum);
0113
0114 if (r < 0)
0115 return r;
0116
0117 regnum = (regnum >> 16) & 0x1f;
0118 op = 1;
0119 } else {
0120 cavium_mdiobus_set_mode(p, C22);
0121 }
0122
0123 smi_wr.u64 = 0;
0124 smi_wr.s.dat = val;
0125 oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT);
0126
0127 smi_cmd.u64 = 0;
0128 smi_cmd.s.phy_op = op;
0129 smi_cmd.s.phy_adr = phy_id;
0130 smi_cmd.s.reg_adr = regnum;
0131 oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD);
0132
0133 do {
0134
0135
0136
0137 __delay(1000);
0138 smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT);
0139 } while (smi_wr.s.pending && --timeout);
0140
0141 if (timeout <= 0)
0142 return -EIO;
0143
0144 return 0;
0145 }
0146 EXPORT_SYMBOL(cavium_mdiobus_write);
0147
0148 MODULE_DESCRIPTION("Common code for OCTEON and Thunder MDIO bus drivers");
0149 MODULE_AUTHOR("David Daney");
0150 MODULE_LICENSE("GPL v2");