/* * drivers/net/titan_mdio.c - Driver for Titan ethernet ports * * Copyright (C) 2003 PMC-Sierra Inc. * Author : Manish Lachwani (lachwani@pmc-sierra.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Management Data IO (MDIO) driver for the Titan GMII. Interacts with the Marvel PHY * on the Titan. No support for the TBI as yet. * */ #include "titan_mdio.h" #define MDIO_DEBUG /* * Local constants */ #define MAX_CLKA 1023 #define MAX_PHY_DEV 31 #define MAX_PHY_REG 31 #define WRITEADDRS_OPCODE 0x0 #define READ_OPCODE 0x2 #define WRITE_OPCODE 0x1 #define MAX_MDIO_POLL 100 /* * Titan MDIO and SCMB registers */ #define TITAN_GE_SCMB_CONTROL 0x01c0 /* SCMB Control */ #define TITAN_GE_SCMB_CLKA 0x01c4 /* SCMB Clock A */ #define TITAN_GE_MDIO_COMMAND 0x01d0 /* MDIO Command */ #define TITAN_GE_MDIO_DEVICE_PORT_ADDRESS 0x01d4 /* MDIO Device and Port addrs */ #define TITAN_GE_MDIO_DATA 0x01d8 /* MDIO Data */ #define TITAN_GE_MDIO_INTERRUPTS 0x01dC /* MDIO Interrupts */ /* * Function to poll the MDIO */ static int titan_ge_mdio_poll(void) { int i, val; for (i = 0; i < MAX_MDIO_POLL; i++) { val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND); if (!(val & 0x8000)) return TITAN_GE_MDIO_GOOD; } return TITAN_GE_MDIO_ERROR; } /* * Initialize and configure the MDIO */ int titan_ge_mdio_setup(titan_ge_mdio_config *titan_mdio) { unsigned long val; /* Reset the SCMB and program into MDIO mode*/ TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CONTROL, 0x9000); TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CONTROL, 0x1000); /* CLK A */ val = TITAN_GE_MDIO_READ(TITAN_GE_SCMB_CLKA); val = ( (val & ~(0x03ff)) | (titan_mdio->clka & 0x03ff)); TITAN_GE_MDIO_WRITE(TITAN_GE_SCMB_CLKA, val); /* Preamble Suppresion */ val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND); val = ( (val & ~(0x0001)) | (titan_mdio->mdio_spre & 0x0001)); TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val); /* MDIO mode */ val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS); val = ( (val & ~(0x4000)) | (titan_mdio->mdio_mode & 0x4000)); TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val); return TITAN_GE_MDIO_GOOD; } /* * Set the PHY address in indirect mode */ int titan_ge_mdio_inaddrs(int dev_addr, int reg_addr) { volatile unsigned long val; /* Setup the PHY device */ val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS); val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00)); val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f)); TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val); /* Write the new address */ val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND); val = ( (val & ~(0x0300)) | ( (WRITEADDRS_OPCODE << 8) & 0x0300)); TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val); return TITAN_GE_MDIO_GOOD; } /* * Read the MDIO register. This is what the individual parametes mean: * * dev_addr : PHY ID * reg_addr : register offset * * See the spec for the Titan MAC. We operate in the Direct Mode. */ #define MAX_RETRIES 2 int titan_ge_mdio_read(int dev_addr, int reg_addr, unsigned int *pdata) { volatile unsigned long val; int retries = 0; /* Setup the PHY device */ again: val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS); val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00)); val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f)); val |= 0x4000; TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val); udelay(30); /* Issue the read command */ val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND); val = ( (val & ~(0x0300)) | ( (READ_OPCODE << 8) & 0x0300)); TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val); udelay(30); if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD) return TITAN_GE_MDIO_ERROR; *pdata = (unsigned int)TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DATA); val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_INTERRUPTS); udelay(30); if (val & 0x2) { if (retries == MAX_RETRIES) return TITAN_GE_MDIO_ERROR; else { retries++; goto again; } } return TITAN_GE_MDIO_GOOD; } /* * Write to the MDIO register * * dev_addr : PHY ID * reg_addr : register that needs to be written to * */ int titan_ge_mdio_write(int dev_addr, int reg_addr, unsigned int data) { volatile unsigned long val; if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD) return TITAN_GE_MDIO_ERROR; /* Setup the PHY device */ val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS); val = ( (val & ~(0x1f00)) | ( (dev_addr << 8) & 0x1f00)); val = ( (val & ~(0x001f)) | ( reg_addr & 0x001f)); val |= 0x4000; TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DEVICE_PORT_ADDRESS, val); udelay(30); /* Setup the data to write */ TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_DATA, data); udelay(30); /* Issue the write command */ val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_COMMAND); val = ( (val & ~(0x0300)) | ( (WRITE_OPCODE << 8) & 0x0300)); TITAN_GE_MDIO_WRITE(TITAN_GE_MDIO_COMMAND, val); udelay(30); if (titan_ge_mdio_poll() != TITAN_GE_MDIO_GOOD) return TITAN_GE_MDIO_ERROR; val = TITAN_GE_MDIO_READ(TITAN_GE_MDIO_INTERRUPTS); if (val & 0x2) return TITAN_GE_MDIO_ERROR; return TITAN_GE_MDIO_GOOD; }