Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.19 153 lines 3.6 kB view raw
1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2009-2016 Cavium, Inc. 7 */ 8 9#include <linux/delay.h> 10#include <linux/module.h> 11#include <linux/phy.h> 12#include <linux/io.h> 13 14#include "mdio-cavium.h" 15 16static void cavium_mdiobus_set_mode(struct cavium_mdiobus *p, 17 enum cavium_mdiobus_mode m) 18{ 19 union cvmx_smix_clk smi_clk; 20 21 if (m == p->mode) 22 return; 23 24 smi_clk.u64 = oct_mdio_readq(p->register_base + SMI_CLK); 25 smi_clk.s.mode = (m == C45) ? 1 : 0; 26 smi_clk.s.preamble = 1; 27 oct_mdio_writeq(smi_clk.u64, p->register_base + SMI_CLK); 28 p->mode = m; 29} 30 31static int cavium_mdiobus_c45_addr(struct cavium_mdiobus *p, 32 int phy_id, int regnum) 33{ 34 union cvmx_smix_cmd smi_cmd; 35 union cvmx_smix_wr_dat smi_wr; 36 int timeout = 1000; 37 38 cavium_mdiobus_set_mode(p, C45); 39 40 smi_wr.u64 = 0; 41 smi_wr.s.dat = regnum & 0xffff; 42 oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT); 43 44 regnum = (regnum >> 16) & 0x1f; 45 46 smi_cmd.u64 = 0; 47 smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_45_ADDRESS */ 48 smi_cmd.s.phy_adr = phy_id; 49 smi_cmd.s.reg_adr = regnum; 50 oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD); 51 52 do { 53 /* Wait 1000 clocks so we don't saturate the RSL bus 54 * doing reads. 55 */ 56 __delay(1000); 57 smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT); 58 } while (smi_wr.s.pending && --timeout); 59 60 if (timeout <= 0) 61 return -EIO; 62 return 0; 63} 64 65int cavium_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum) 66{ 67 struct cavium_mdiobus *p = bus->priv; 68 union cvmx_smix_cmd smi_cmd; 69 union cvmx_smix_rd_dat smi_rd; 70 unsigned int op = 1; /* MDIO_CLAUSE_22_READ */ 71 int timeout = 1000; 72 73 if (regnum & MII_ADDR_C45) { 74 int r = cavium_mdiobus_c45_addr(p, phy_id, regnum); 75 76 if (r < 0) 77 return r; 78 79 regnum = (regnum >> 16) & 0x1f; 80 op = 3; /* MDIO_CLAUSE_45_READ */ 81 } else { 82 cavium_mdiobus_set_mode(p, C22); 83 } 84 85 smi_cmd.u64 = 0; 86 smi_cmd.s.phy_op = op; 87 smi_cmd.s.phy_adr = phy_id; 88 smi_cmd.s.reg_adr = regnum; 89 oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD); 90 91 do { 92 /* Wait 1000 clocks so we don't saturate the RSL bus 93 * doing reads. 94 */ 95 __delay(1000); 96 smi_rd.u64 = oct_mdio_readq(p->register_base + SMI_RD_DAT); 97 } while (smi_rd.s.pending && --timeout); 98 99 if (smi_rd.s.val) 100 return smi_rd.s.dat; 101 else 102 return -EIO; 103} 104EXPORT_SYMBOL(cavium_mdiobus_read); 105 106int cavium_mdiobus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val) 107{ 108 struct cavium_mdiobus *p = bus->priv; 109 union cvmx_smix_cmd smi_cmd; 110 union cvmx_smix_wr_dat smi_wr; 111 unsigned int op = 0; /* MDIO_CLAUSE_22_WRITE */ 112 int timeout = 1000; 113 114 if (regnum & MII_ADDR_C45) { 115 int r = cavium_mdiobus_c45_addr(p, phy_id, regnum); 116 117 if (r < 0) 118 return r; 119 120 regnum = (regnum >> 16) & 0x1f; 121 op = 1; /* MDIO_CLAUSE_45_WRITE */ 122 } else { 123 cavium_mdiobus_set_mode(p, C22); 124 } 125 126 smi_wr.u64 = 0; 127 smi_wr.s.dat = val; 128 oct_mdio_writeq(smi_wr.u64, p->register_base + SMI_WR_DAT); 129 130 smi_cmd.u64 = 0; 131 smi_cmd.s.phy_op = op; 132 smi_cmd.s.phy_adr = phy_id; 133 smi_cmd.s.reg_adr = regnum; 134 oct_mdio_writeq(smi_cmd.u64, p->register_base + SMI_CMD); 135 136 do { 137 /* Wait 1000 clocks so we don't saturate the RSL bus 138 * doing reads. 139 */ 140 __delay(1000); 141 smi_wr.u64 = oct_mdio_readq(p->register_base + SMI_WR_DAT); 142 } while (smi_wr.s.pending && --timeout); 143 144 if (timeout <= 0) 145 return -EIO; 146 147 return 0; 148} 149EXPORT_SYMBOL(cavium_mdiobus_write); 150 151MODULE_DESCRIPTION("Common code for OCTEON and Thunder MDIO bus drivers"); 152MODULE_AUTHOR("David Daney"); 153MODULE_LICENSE("GPL");