--- /dev/null
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright © 2007 Atheros Communications, Inc., All Rights Reserved.
+ */
+
+/*
+ * Manage the atheros ethernet PHY.
+ *
+ * All definitions in this file are operating system independent!
+ */
+
+#include <config.h>
+#include <linux/types.h>
+#include <common.h>
+#include <miiphy.h>
+#include "phy.h"
+#include <asm/addrspace.h>
+#include "ar7240_soc.h"
+#include "athrs27_phy.h"
+
+/* PHY selections and access functions */
+
+typedef enum {
+ PHY_SRCPORT_INFO,
+ PHY_PORTINFO_SIZE,
+} PHY_CAP_TYPE;
+
+typedef enum {
+ PHY_SRCPORT_NONE,
+ PHY_SRCPORT_VLANTAG,
+ PHY_SRCPORT_TRAILER,
+} PHY_SRCPORT_TYPE;
+
+#define DRV_LOG(DBG_SW, X0, X1, X2, X3, X4, X5, X6)
+#define DRV_MSG(x,a,b,c,d,e,f)
+#define DRV_PRINT(DBG_SW,X)
+
+#define ATHR_LAN_PORT_VLAN 1
+#define ATHR_WAN_PORT_VLAN 2
+#define ENET_UNIT_LAN 1
+#define ENET_UNIT_WAN 0
+
+#define TRUE 1
+#define FALSE 0
+
+#define ATHR_PHY0_ADDR 0x0
+#define ATHR_PHY1_ADDR 0x1
+#define ATHR_PHY2_ADDR 0x2
+#define ATHR_PHY3_ADDR 0x3
+#define ATHR_PHY4_ADDR 0x4
+
+#define MODULE_NAME "ATHRS27"
+
+/*
+ * Track per-PHY port information.
+ */
+
+
+typedef struct {
+ BOOL isEnetPort; /* normal enet port */
+ BOOL isPhyAlive; /* last known state of link */
+ int ethUnit; /* MAC associated with this phy port */
+ uint32_t phyBase;
+ uint32_t phyAddr; /* PHY registers associated with this phy port */
+ uint32_t VLANTableSetting; /* Value to be written to VLAN table */
+} athrPhyInfo_t;
+
+/*
+ * Per-PHY information, indexed by PHY unit number.
+ */
+static athrPhyInfo_t athrPhyInfo[] = {
+
+ {TRUE, /* port 1 -- LAN port 1 */
+ FALSE,
+ ENET_UNIT_LAN,
+ 0,
+ ATHR_PHY0_ADDR,
+ ATHR_LAN_PORT_VLAN
+ },
+
+ {TRUE, /* port 2 -- LAN port 2 */
+ FALSE,
+ ENET_UNIT_LAN,
+ 0,
+ ATHR_PHY1_ADDR,
+ ATHR_LAN_PORT_VLAN
+ },
+
+ {TRUE, /* port 3 -- LAN port 3 */
+ FALSE,
+ ENET_UNIT_LAN,
+ 0,
+ ATHR_PHY2_ADDR,
+ ATHR_LAN_PORT_VLAN
+ },
+
+
+ {TRUE, /* port 4 -- LAN port 4 */
+ FALSE,
+ ENET_UNIT_LAN,
+ 0,
+ ATHR_PHY3_ADDR,
+ ATHR_LAN_PORT_VLAN /* Send to all ports */
+ },
+
+ {TRUE, /* port 5 -- WAN Port 5 */
+ FALSE,
+ ENET_UNIT_WAN,
+ 0,
+ ATHR_PHY4_ADDR,
+ ATHR_LAN_PORT_VLAN /* Send to all ports */
+ },
+
+ {FALSE, /* port 0 -- cpu port 0 */
+ TRUE,
+ ENET_UNIT_LAN,
+ 0,
+ 0x00,
+ ATHR_LAN_PORT_VLAN
+ },
+
+};
+
+
+#define ATHR_GLOBALREGBASE 0
+
+#define ATHR_PHY_MAX 5
+
+/* Range of valid PHY IDs is [MIN..MAX] */
+#define ATHR_ID_MIN 0
+#define ATHR_ID_MAX (ATHR_PHY_MAX-1)
+
+
+/* Convenience macros to access myPhyInfo */
+#define ATHR_IS_ENET_PORT(phyUnit) (athrPhyInfo[phyUnit].isEnetPort)
+#define ATHR_IS_PHY_ALIVE(phyUnit) (athrPhyInfo[phyUnit].isPhyAlive)
+#define ATHR_ETHUNIT(phyUnit) (athrPhyInfo[phyUnit].ethUnit)
+#define ATHR_PHYBASE(phyUnit) (athrPhyInfo[phyUnit].phyBase)
+#define ATHR_PHYADDR(phyUnit) (athrPhyInfo[phyUnit].phyAddr)
+#define ATHR_VLAN_TABLE_SETTING(phyUnit) (athrPhyInfo[phyUnit].VLANTableSetting)
+
+
+#define ATHR_IS_ETHUNIT(phyUnit, ethUnit) \
+ (ATHR_IS_ENET_PORT(phyUnit) && \
+ ATHR_ETHUNIT(phyUnit) == (ethUnit))
+
+#define ATHR_IS_WAN_PORT(phyUnit) (!(ATHR_ETHUNIT(phyUnit)==ENET_UNIT_LAN))
+
+/* Forward references */
+BOOL athrs27_phy_is_link_alive(int phyUnit);
+uint32_t athrs27_reg_read(uint32_t reg_addr);
+void athrs27_reg_write(uint32_t reg_addr, uint32_t reg_val);
+unsigned int s27_rd_phy(unsigned int phy_addr, unsigned int reg_addr);
+void s27_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data);
+
+
+void athrs27_powersave_off(int phy_addr)
+{
+ s27_wr_phy(phy_addr,ATHR_DEBUG_PORT_ADDRESS,0x29);
+ s27_wr_phy(phy_addr,ATHR_DEBUG_PORT_DATA,0x36c0);
+
+}
+void athrs27_sleep_off(int phy_addr)
+{
+ s27_wr_phy(phy_addr,ATHR_DEBUG_PORT_ADDRESS,0xb);
+ s27_wr_phy(phy_addr,ATHR_DEBUG_PORT_DATA,0x3c00);
+}
+
+void athrs27_force_100M(int phyAddr,int duplex)
+{
+ /*
+ * Force MDI and MDX to alternate ports
+ * Phy 0,2 and 4 -- MDI
+ * Phy 1 and 3 -- MDX
+ */
+
+ if(phyAddr%2) {
+ s27_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x820);
+ }
+ else {
+ s27_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x800);
+ }
+
+ s27_wr_phy(phyAddr,0x1d,0x29);
+ s27_wr_phy(phyAddr,0x1e,0x0);
+ s27_wr_phy(phyAddr,0x10,0xc60);
+ s27_wr_phy(phyAddr,ATHR_PHY_CONTROL,(0xa000|(duplex << 8)));
+}
+
+void athrs27_force_10M(int phyAddr,int duplex)
+{
+
+ athrs27_powersave_off(phyAddr);
+ athrs27_sleep_off(phyAddr);
+
+ s27_wr_phy(phyAddr,ATHR_PHY_CONTROL,(0x8000 |(duplex << 8)));
+}
+
+int athrs27_reg_init(void)
+{
+#if S27_PHY_DEBUG
+ uint32_t rd_val;
+#endif
+
+ /* if using header for register configuration, we have to */
+ /* configure s27 register after frame transmission is enabled */
+ athrs27_reg_rmw(0x8,(1<<28)); /* Set WAN port is connected to GE0 */
+
+#if defined(S27_FORCE_100M)
+ athrs27_force_100M(ATHR_PHY4_ADDR,1);
+#elif defined(S27_FORCE_10M)
+ athrs27_force_10M(ATHR_PHY4_ADDR,1);
+#else
+ s27_wr_phy(ATHR_PHY4_ADDR,ATHR_PHY_CONTROL,0x9000);
+
+#endif
+#ifdef S27_PHY_DEBUG
+ printf(MODULE_NAME":OPERATIONAL_MODE_REG0:%x\n",athrs27_reg_read(OPERATIONAL_MODE_REG0));
+ printf(MODULE_NAME":REG 0x4-->:%x\n",athrs27_reg_read(0x4));
+ printf(MODULE_NAME":REG 0x2c-->:%x\n",athrs27_reg_read(0x2c));
+ printf(MODULE_NAME":REG 0x8-->:%x\n",athrs27_reg_read(0x8));
+#endif
+
+ return 0;
+}
+
+int athrs27_reg_init_lan(void)
+{
+ int i = 60;
+#if S26_PHY_DEBUG
+ uint32_t rd_val;
+#endif
+ int phyUnit;
+ uint32_t phyBase = 0;
+ BOOL foundPhy = FALSE;
+ uint32_t phyAddr = 0;
+
+
+ /* reset switch */
+ printf(MODULE_NAME ": resetting s27\n");
+ athrs27_reg_write(0x0, athrs27_reg_read(0x0)|0x80000000);
+
+ while(i--) {
+ sysMsDelay(100);
+ if(!(athrs27_reg_read(0x0)&0x80000000))
+ break;
+ }
+ printf(MODULE_NAME ": s27 reset done\n");
+ athrs27_reg_write(PORT_STATUS_REGISTER0,0x4e);
+
+ athrs27_reg_rmw(OPERATIONAL_MODE_REG0,(1<<6)); /* Set GMII mode */
+
+ if (is_emu() || is_wasp()) {
+ athrs27_reg_rmw(0x2c,((1<<26)| (1<<16) | 0x1)); /* FiX ME: EBU debug */
+ }
+
+ for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
+
+ foundPhy = TRUE;
+ phyBase = ATHR_PHYBASE(phyUnit);
+ phyAddr = ATHR_PHYADDR(phyUnit);
+
+#if defined(S27_FORCE_100M)
+ athrs27_force_100M(phyAddr,1);
+#elif defined(S27_FORCE_10M)
+ athrs27_force_10M(phyAddr,1);
+#else
+ s27_wr_phy(phyAddr,ATHR_PHY_CONTROL,0x9000);
+#endif
+
+#if S27_PHY_DEBUG
+ rd_val = s27_rd_phy(phyAddr,ATHR_PHY_FUNC_CONTROL);
+ printf("S27 ATHR_PHY_FUNC_CONTROL (%d):%x\n",phyAddr,rd_val);
+ rd_val = s27_rd_phy(phyAddr,ATHR_PHY_ID1);
+ printf("S27 PHY ID (%d) :%x\n",phyAddr, rd_val);
+ rd_val = s27_rd_phy(phyAddr,ATHR_PHY_SPEC_STATUS);
+ printf("S27 PHY CTRL (%d) :%x\n",phyAddr, rd_val);
+ rd_val = s27_rd_phy(phyAddr,ATHR_PHY_STATUS);
+ printf("S27 ATHR PHY STATUS (%d) :%x\n",phyAddr, rd_val);
+#endif
+ }
+
+ /*
+ * status[1:0]=2'h2; - (0x10 - 1000 Mbps , 0x01 - 100Mbps, 0x0 - 10 Mbps)
+ * status[2]=1'h1; - Tx Mac En
+ * status[3]=1'h1; - Rx Mac En
+ * status[4]=1'h1; - Tx Flow Ctrl En
+ * status[5]=1'h1; - Rx Flow Ctrl En
+ * status[6]=1'h1; - Duplex Mode
+ */
+ athrs27_reg_write(PORT_STATUS_REGISTER1, 0x200); /* LAN - 1 */
+ athrs27_reg_write(PORT_STATUS_REGISTER2, 0x200); /* LAN - 2 */
+ athrs27_reg_write(PORT_STATUS_REGISTER3, 0x200); /* LAN - 3 */
+ athrs27_reg_write(PORT_STATUS_REGISTER4, 0x200); /* LAN - 4 */
+
+ if (is_emu()) {
+ athrs27_reg_write(PORT_STATUS_REGISTER1, 0x4C); /* LAN - 1 */
+ athrs27_reg_write(PORT_STATUS_REGISTER2, 0x4c); /* LAN - 2 */
+ athrs27_reg_write(PORT_STATUS_REGISTER3, 0x4c); /* LAN - 3 */
+ athrs27_reg_write(PORT_STATUS_REGISTER4, 0x4c); /* LAN - 4 */
+ }
+
+ /* QM Control */
+ athrs27_reg_write(0x38, 0xc000050e);
+
+ /*
+ * status[11]=1'h0; - CPU Disable
+ * status[7] = 1'b1; - Learn One Lock
+ * status[14] = 1'b0; - Learn Enable
+ */
+#ifdef ATHEROS_HEADER_EN
+ athrs27_reg_write(PORT_CONTROL_REGISTER0, 0x4804);
+#else
+ /* Atheros Header Disable */
+ athrs27_reg_write(PORT_CONTROL_REGISTER0, 0x4004);
+#endif
+
+ /* Tag Priority Mapping */
+ athrs27_reg_write(0x70, 0xfa50);
+
+ /* Enable ARP packets to CPU port */
+ athrs27_reg_write(S27_ARL_TBL_CTRL_REG,(athrs27_reg_read(S27_ARL_TBL_CTRL_REG) | 0x100000));
+
+ /* Enable Broadcast packets to CPU port */
+ athrs27_reg_write(S27_FLD_MASK_REG,(athrs27_reg_read(S27_FLD_MASK_REG) |
+ S27_ENABLE_CPU_BROADCAST | S27_ENABLE_CPU_BCAST_FWD ));
+
+ return 0;
+}
+
+/******************************************************************************
+*
+* athrs27_phy_is_link_alive - test to see if the specified link is alive
+*
+* RETURNS:
+* TRUE --> link is alive
+* FALSE --> link is down
+*/
+BOOL
+athrs27_phy_is_link_alive(int phyUnit)
+{
+ uint16_t phyHwStatus;
+ uint32_t phyBase;
+ uint32_t phyAddr;
+
+ phyBase = ATHR_PHYBASE(phyUnit);
+ phyAddr = ATHR_PHYADDR(phyUnit);
+ phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_SPEC_STATUS);
+
+ if (phyHwStatus & ATHR_STATUS_LINK_PASS)
+ return TRUE;
+
+ return FALSE;
+}
+
+/******************************************************************************
+*
+* athrs27_phy_setup - reset and setup the PHY associated with
+* the specified MAC unit number.
+*
+* Resets the associated PHY port.
+*
+* RETURNS:
+* TRUE --> associated PHY is alive
+* FALSE --> no LINKs on this ethernet unit
+*/
+BOOL
+athrs27_phy_setup(int ethUnit)
+{
+ int phyUnit;
+ uint16_t phyHwStatus;
+ uint16_t timeout;
+ int liveLinks = 0;
+ uint32_t phyBase = 0;
+ BOOL foundPhy = FALSE;
+ uint32_t phyAddr = 0;
+//#if S27_PHY_DEBUG
+ uint32_t rd_val = 0;
+//#endif
+ uint32_t ar7240_revid;
+
+
+ /* See if there's any configuration data for this enet */
+ /* start auto negogiation on each phy */
+ for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
+
+ foundPhy = TRUE;
+ phyBase = ATHR_PHYBASE(phyUnit);
+ phyAddr = ATHR_PHYADDR(phyUnit);
+
+ if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
+ continue;
+ }
+ if (!is_emu()) {
+ s27_wr_phy(phyAddr, ATHR_AUTONEG_ADVERT,ATHR_ADVERTISE_ALL);
+
+ s27_wr_phy(phyAddr, ATHR_PHY_CONTROL,ATHR_CTRL_AUTONEGOTIATION_ENABLE
+ | ATHR_CTRL_SOFTWARE_RESET);
+ }
+ else {
+ printf("############ is emulation ############\n");
+
+ if(ATHR_ETHUNIT(phyUnit) == ENET_UNIT_WAN) {
+ s27_wr_phy(phyAddr, ATHR_AUTONEG_ADVERT,ATHR_ADVERTISE_ALL);
+ s27_wr_phy(phyAddr,0x9, 0x0); //donot advertise 1000Mbps mode
+ s27_wr_phy(phyAddr, ATHR_PHY_CONTROL,0x0);
+ s27_wr_phy(phyAddr, ATHR_PHY_CONTROL,ATHR_CTRL_AUTONEGOTIATION_ENABLE
+ | ATHR_CTRL_SOFTWARE_RESET);
+ }
+ else {
+
+ s27_wr_phy(phyAddr, ATHR_AUTONEG_ADVERT,(ATHR_ADVERTISE_ASYM_PAUSE | ATHR_ADVERTISE_PAUSE |
+ ATHR_ADVERTISE_10HALF | ATHR_ADVERTISE_10FULL));
+ s27_wr_phy(phyAddr,0x9, 0x0); //donot advertise 1000Mbps mode
+ s27_wr_phy(phyAddr, ATHR_PHY_CONTROL,0x0);
+ s27_wr_phy(phyAddr, ATHR_PHY_CONTROL,ATHR_CTRL_AUTONEGOTIATION_ENABLE
+ | ATHR_CTRL_SOFTWARE_RESET);
+ }
+ }
+ rd_val = s27_rd_phy(phyAddr,ATHR_PHY_CONTROL);
+ printf("%s ATHR_PHY_CONTROL %d: 0x%x\n",__func__,phyAddr,rd_val);
+ rd_val = s27_rd_phy(phyAddr,ATHR_PHY_SPEC_STATUS);
+ printf("%s ATHR_PHY_SPEC_STAUS %d: 0x%x\n",__func__,phyAddr,rd_val);
+ }
+ if (!foundPhy) {
+ return FALSE; /* No PHY's configured for this ethUnit */
+ }
+
+ /*
+ * After the phy is reset, it takes a little while before
+ * it can respond properly.
+ */
+ if (ethUnit == ENET_UNIT_LAN)
+ sysMsDelay(100); // changed by lsz, sysMsDelay(1000);
+ else
+ sysMsDelay(300); // changed by lsz, sysMsDelay(3000);
+
+ /*
+ * Wait up to 3 seconds for ALL associated PHYs to finish
+ * autonegotiation. The only way we get out of here sooner is
+ * if ALL PHYs are connected AND finish autonegotiation.
+ */
+ for (phyUnit=0; (phyUnit < ATHR_PHY_MAX) /*&& (timeout > 0) */; phyUnit++) {
+ if (ATHR_ETHUNIT(phyUnit) == ENET_UNIT_WAN)
+ continue;
+
+ timeout=20;
+ for (;;) {
+ phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_CONTROL);
+
+ if (ATHR_RESET_DONE(phyHwStatus)) {
+ DRV_PRINT(DRV_DEBUG_PHYSETUP,
+ ("Port %d, Neg Success\n", phyUnit));
+ break;
+ }
+ if (timeout == 0) {
+ DRV_PRINT(DRV_DEBUG_PHYSETUP,
+ ("Port %d, Negogiation timeout\n", phyUnit));
+ break;
+ }
+ if (--timeout == 0) {
+ DRV_PRINT(DRV_DEBUG_PHYSETUP,
+ ("Port %d, Negogiation timeout\n", phyUnit));
+ break;
+ }
+
+ sysMsDelay(150);
+ }
+ /* fix IOT */
+ s27_wr_phy(phyUnit, 29, 0x14);
+ s27_wr_phy(phyUnit, 30, 0x1352);
+
+#ifdef S27_VER_1_0
+ /* turn off power saving */
+ s27_wr_phy(phyUnit, 29, 41);
+ s27_wr_phy(phyUnit, 30, 0);
+ printf("def_ S27_VER_1_0\n");
+#endif
+ }
+
+ /*
+ * All PHYs have had adequate time to autonegotiate.
+ * Now initialize software status.
+ *
+ * It's possible that some ports may take a bit longer
+ * to autonegotiate; but we can't wait forever. They'll
+ * get noticed by mv_phyCheckStatusChange during regular
+ * polling activities.
+ */
+ for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
+ if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
+ continue;
+ }
+
+ if (athrs27_phy_is_link_alive(phyUnit)) {
+ liveLinks++;
+ ATHR_IS_PHY_ALIVE(phyUnit) = TRUE;
+ } else {
+ ATHR_IS_PHY_ALIVE(phyUnit) = FALSE;
+ }
+ DRV_PRINT(DRV_DEBUG_PHYSETUP,
+ ("eth%d: Phy Specific Status=%4.4x\n",
+ ethUnit,
+ s27_rd_phy(ATHR_PHYADDR(phyUnit),ATHR_PHY_SPEC_STATUS)));
+ }
+
+ return (liveLinks > 0);
+}
+
+/******************************************************************************
+*
+* athrs27_phy_is_fdx - Determines whether the phy ports associated with the
+* specified device are FULL or HALF duplex.
+*
+* RETURNS:
+* 1 --> FULL
+* 0 --> HALF
+*/
+int
+athrs27_phy_is_fdx(int ethUnit,int phyUnit)
+{
+ uint32_t phyBase;
+ uint32_t phyAddr;
+ uint16_t phyHwStatus;
+ int ii = 200;
+
+ if (ethUnit == ENET_UNIT_LAN)
+ return TRUE;
+
+ for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
+ if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
+ continue;
+ }
+
+ if (athrs27_phy_is_link_alive(phyUnit)) {
+
+ phyBase = ATHR_PHYBASE(phyUnit);
+ phyAddr = ATHR_PHYADDR(phyUnit);
+
+ do {
+ phyHwStatus = s27_rd_phy (phyAddr, ATHR_PHY_SPEC_STATUS);
+ if(phyHwStatus & ATHR_STATUS_RESOVLED)
+ break;
+ sysMsDelay(10);
+ } while(--ii);
+ if (phyHwStatus & ATHER_STATUS_FULL_DUPLEX) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+/******************************************************************************
+*
+* athrs27_phy_speed - Determines the speed of phy ports associated with the
+* specified device.
+*
+* RETURNS:
+* ATHR_PHY_SPEED_10T, AG7240_PHY_SPEED_100T;
+* ATHR_PHY_SPEED_1000T;
+*/
+
+int
+athrs27_phy_speed(int ethUnit,int phyUnit)
+{
+ uint16_t phyHwStatus;
+ uint32_t phyBase;
+ uint32_t phyAddr;
+ int ii = 200;
+ int phySpeed;
+ for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
+ if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
+ continue;
+ }
+
+
+ phyBase = ATHR_PHYBASE(phyUnit);
+ phyAddr = ATHR_PHYADDR(phyUnit);
+ phySpeed = _10BASET;
+
+ if (athrs27_phy_is_link_alive(phyUnit)) {
+
+ do {
+ phyHwStatus = s27_rd_phy(phyAddr,
+ ATHR_PHY_SPEC_STATUS);
+ if(phyHwStatus & ATHR_STATUS_RESOVLED)
+ break;
+ sysMsDelay(10);
+ }while(--ii);
+
+ phyHwStatus = ((phyHwStatus & ATHER_STATUS_LINK_MASK) >>
+ ATHER_STATUS_LINK_SHIFT);
+
+ switch(phyHwStatus) {
+ case 0:
+ phySpeed = _10BASET;
+ break;
+ case 1:
+ phySpeed = _100BASET;
+ break;
+ case 2:
+ phySpeed = _1000BASET;
+ break;
+ default:
+ printf("Unkown speed read!\n");
+ }
+ }
+
+ phy_reg_write(1,phyAddr, ATHR_DEBUG_PORT_ADDRESS, 0x18);
+
+ if(phySpeed == _100BASET) {
+ phy_reg_write(1,phyAddr, ATHR_DEBUG_PORT_DATA, 0xba8);
+ } else {
+ phy_reg_write(1,phyAddr, ATHR_DEBUG_PORT_DATA, 0x2ea);
+ }
+ }
+
+ if (ethUnit == ENET_UNIT_LAN)
+ phySpeed = _1000BASET;
+
+ return phySpeed;
+}
+
+/*****************************************************************************
+*
+* athr_phy_is_up -- checks for significant changes in PHY state.
+*
+* A "significant change" is:
+* dropped link (e.g. ethernet cable unplugged) OR
+* autonegotiation completed + link (e.g. ethernet cable plugged in)
+*
+* When a PHY is plugged in, phyLinkGained is called.
+* When a PHY is unplugged, phyLinkLost is called.
+*/
+
+int
+athrs27_phy_is_up(int ethUnit)
+{
+
+ uint16_t phyHwStatus, phyHwControl;
+ athrPhyInfo_t *lastStatus;
+ int linkCount = 0;
+ int lostLinks = 0;
+ int gainedLinks = 0;
+ uint32_t phyBase;
+ uint32_t phyAddr;
+ int phyUnit;
+
+ for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) {
+ if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) {
+ continue;
+ }
+
+ phyBase = ATHR_PHYBASE(phyUnit);
+ phyAddr = ATHR_PHYADDR(phyUnit);
+
+ lastStatus = &athrPhyInfo[phyUnit];
+ if (lastStatus->isPhyAlive) { /* last known link status was ALIVE */
+ phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_SPEC_STATUS);
+
+ /* See if we've lost link */
+ if (phyHwStatus & ATHR_STATUS_LINK_PASS) {
+ linkCount++;
+ } else {
+ lostLinks++;
+ DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d down\n",
+ ethUnit, phyUnit));
+ printf("enet%d port%d down\n",ethUnit, phyUnit);
+ lastStatus->isPhyAlive = FALSE;
+ }
+ } else { /* last known link status was DEAD */
+ /* Check for reset complete */
+ if(is_emu())
+ {
+ phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_STATUS);
+ if(phyAddr%2) {
+ s27_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x820);
+ }
+ else {
+ s27_wr_phy(phyAddr,ATHR_PHY_FUNC_CONTROL,0x800);
+ }
+
+ if((phyHwStatus & 0x4)==0)
+ {
+ s27_wr_phy(phyAddr,0x9,0x0);
+ if(phyAddr !=0x4)
+ s27_wr_phy(phyAddr,0x4,0x41);
+ s27_wr_phy(phyAddr,0x0,0x9000);
+ }
+ }
+
+ phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_CONTROL);
+ if (!ATHR_RESET_DONE(phyHwStatus))
+ continue;
+
+ phyHwControl = s27_rd_phy(phyAddr, ATHR_PHY_CONTROL);
+ phyHwStatus = s27_rd_phy(phyAddr, ATHR_PHY_STATUS);
+
+ /* Check for AutoNegotiation complete */
+ if ((!(phyHwControl & ATHR_CTRL_AUTONEGOTIATION_ENABLE))
+ || ATHR_AUTONEG_DONE(phyHwStatus)) {
+ phyHwStatus = s27_rd_phy(phyAddr,
+ ATHR_PHY_SPEC_STATUS);
+
+ if (phyHwStatus & ATHR_STATUS_LINK_PASS) {
+ gainedLinks++;
+ linkCount++;
+ printf("enet%d port%d up\n",ethUnit, phyUnit);
+ DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d up\n",
+ ethUnit, phyUnit));
+ lastStatus->isPhyAlive = TRUE;
+ }
+ }
+ }
+ }
+ return (linkCount);
+}
+
+unsigned int athrs27_reg_read(unsigned int s27_addr)
+{
+ unsigned int addr_temp;
+ unsigned int s27_rd_csr_low, s27_rd_csr_high, s27_rd_csr;
+ unsigned int data,unit = 0;
+ unsigned int phy_address, reg_address;
+
+ addr_temp = s27_addr >>2;
+ data = addr_temp >> 7;
+
+ phy_address = 0x1f;
+ reg_address = 0x10;
+
+ if (is_ar7240()) {
+ unit = 0;
+ }
+ else if(is_ar7241() || is_ar7242() || is_wasp()) {
+ unit = 1;
+ }
+
+ phy_reg_write(unit,phy_address, reg_address, data);
+
+ phy_address = (0x17 & ((addr_temp >> 4) | 0x10));
+ reg_address = ((addr_temp << 1) & 0x1e);
+ s27_rd_csr_low = (uint32_t) phy_reg_read(unit,phy_address, reg_address);
+
+ reg_address = reg_address | 0x1;
+ s27_rd_csr_high = (uint32_t) phy_reg_read(unit,phy_address, reg_address);
+ s27_rd_csr = (s27_rd_csr_high << 16) | s27_rd_csr_low ;
+
+ return(s27_rd_csr);
+}
+
+void athrs27_reg_write(unsigned int s27_addr, unsigned int s27_write_data)
+{
+ unsigned int addr_temp;
+ unsigned int data;
+ unsigned int phy_address, reg_address,unit = 0;
+
+ addr_temp = (s27_addr ) >>2;
+ data = addr_temp >> 7;
+
+ phy_address = 0x1f;
+ reg_address = 0x10;
+
+ if (is_ar7240()) {
+ unit = 0;
+ }
+ else if(is_ar7241() || is_ar7242() || is_wasp()) {
+ unit = 1;
+ }
+ phy_reg_write(unit,phy_address, reg_address, data);
+
+ phy_address = (0x17 & ((addr_temp >> 4) | 0x10));
+
+ reg_address = (((addr_temp << 1) & 0x1e) | 0x1);
+ data = (s27_write_data >> 16) & 0xffff;
+ phy_reg_write(unit,phy_address, reg_address, data);
+
+ reg_address = ((addr_temp << 1) & 0x1e);
+ data = s27_write_data & 0xffff;
+ phy_reg_write(unit,phy_address, reg_address, data);
+
+}
+
+void athrs27_reg_rmw(unsigned int s27_addr, unsigned int s27_write_data)
+{
+ int val = athrs27_reg_read(s27_addr);
+ athrs27_reg_write(s27_addr,(val | s27_write_data));
+}
+
+unsigned int s27_rd_phy(unsigned int phy_addr, unsigned int reg_addr)
+{
+
+ unsigned int rddata, i = 100;
+
+
+ /* MDIO_CMD is set for read */
+
+ rddata = athrs27_reg_read(0x98);
+ rddata = (rddata & 0x0) | (reg_addr<<16)
+ | (phy_addr<<21) | (1<<27)
+ | (1<<30) | (1<<31) ;
+
+ athrs27_reg_write(0x98, rddata);
+
+ rddata = athrs27_reg_read(0x98);
+ rddata = rddata & (1<<31);
+
+ /* Check MDIO_BUSY status */
+ while(rddata && --i){
+ rddata = athrs27_reg_read(0x98);
+ rddata = rddata & (1<<31);
+ }
+
+ if(i <= 0)
+ printf("ERROR:%s failed:phy:%d reg:%X rddata:%X\n",
+ __func__,phy_addr,reg_addr,rddata);
+ /* Read the data from phy */
+
+ rddata = athrs27_reg_read(0x98);
+ rddata = rddata & 0xffff;
+ return(rddata);
+}
+void s27_wr_phy(unsigned int phy_addr, unsigned int reg_addr, unsigned int write_data)
+{
+ unsigned int rddata,i = 100;
+
+ /* MDIO_CMD is set for read */
+
+ rddata = athrs27_reg_read(0x98);
+
+ rddata = (rddata & 0x0) | (write_data & 0xffff)
+ | (reg_addr<<16) | (phy_addr<<21)
+ | (0<<27) | (1<<30) | (1<<31) ;
+
+ athrs27_reg_write(0x98, rddata);
+
+ rddata = athrs27_reg_read(0x98);
+ rddata = rddata & (1<<31);
+
+ /* Check MDIO_BUSY status */
+ while(rddata && --i){
+ rddata = athrs27_reg_read(0x98);
+ rddata = rddata & (1<<31);
+ }
+ if(i <= 0)
+ printf("ERROR:%s failed:phy:%d reg%X\n",__func__,phy_addr,reg_addr);
+
+}
+
+int athrs27_mdc_check()
+{
+ int i;
+
+ for (i=0; i<4000; i++) {
+ if(athrs27_reg_read(0x10c) != 0x18007fff)
+ return -1;
+ }
+ return 0;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2008, Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _ATHRS27_PHY_H
+#define _ATHRS27_PHY_H
+
+
+/*****************/
+/* PHY Registers */
+/*****************/
+#define ATHR_PHY_CONTROL 0
+#define ATHR_PHY_STATUS 1
+#define ATHR_PHY_ID1 2
+#define ATHR_PHY_ID2 3
+#define ATHR_AUTONEG_ADVERT 4
+#define ATHR_LINK_PARTNER_ABILITY 5
+#define ATHR_AUTONEG_EXPANSION 6
+#define ATHR_NEXT_PAGE_TRANSMIT 7
+#define ATHR_LINK_PARTNER_NEXT_PAGE 8
+#define ATHR_1000BASET_CONTROL 9
+#define ATHR_1000BASET_STATUS 10
+#define ATHR_PHY_FUNC_CONTROL 16
+#define ATHR_PHY_SPEC_STATUS 17
+#define ATHR_DEBUG_PORT_ADDRESS 29
+#define ATHR_DEBUG_PORT_DATA 30
+#define ATHR_PHY_INTR_ENABLE 0x12
+#define ATHR_PHY_INTR_STATUS 0x13
+
+/* ATHR_PHY_CONTROL fields */
+#define ATHR_CTRL_SOFTWARE_RESET 0x8000
+#define ATHR_CTRL_SPEED_LSB 0x2000
+#define ATHR_CTRL_AUTONEGOTIATION_ENABLE 0x1000
+#define ATHR_CTRL_RESTART_AUTONEGOTIATION 0x0200
+#define ATHR_CTRL_SPEED_FULL_DUPLEX 0x0100
+#define ATHR_CTRL_SPEED_MSB 0x0040
+
+#define ATHR_RESET_DONE(phy_control) \
+ (((phy_control) & (ATHR_CTRL_SOFTWARE_RESET)) == 0)
+
+/* Phy status fields */
+#define ATHR_STATUS_AUTO_NEG_DONE 0x0020
+
+#define ATHR_AUTONEG_DONE(ip_phy_status) \
+ (((ip_phy_status) & \
+ (ATHR_STATUS_AUTO_NEG_DONE)) == \
+ (ATHR_STATUS_AUTO_NEG_DONE))
+
+/* Link Partner ability */
+#define ATHR_LINK_100BASETX_FULL_DUPLEX 0x0100
+#define ATHR_LINK_100BASETX 0x0080
+#define ATHR_LINK_10BASETX_FULL_DUPLEX 0x0040
+#define ATHR_LINK_10BASETX 0x0020
+
+/* Advertisement register. */
+#define ATHR_ADVERTISE_NEXT_PAGE 0x8000
+#define ATHR_ADVERTISE_ASYM_PAUSE 0x0800
+#define ATHR_ADVERTISE_PAUSE 0x0400
+#define ATHR_ADVERTISE_100FULL 0x0100
+#define ATHR_ADVERTISE_100HALF 0x0080
+#define ATHR_ADVERTISE_10FULL 0x0040
+#define ATHR_ADVERTISE_10HALF 0x0020
+
+#define ATHR_ADVERTISE_ALL (ATHR_ADVERTISE_ASYM_PAUSE | ATHR_ADVERTISE_PAUSE | \
+ ATHR_ADVERTISE_10HALF | ATHR_ADVERTISE_10FULL | \
+ ATHR_ADVERTISE_100HALF | ATHR_ADVERTISE_100FULL)
+
+/* 1000BASET_CONTROL */
+#define ATHR_ADVERTISE_1000FULL 0x0200
+#define ATHR_ADVERTISE_1000HALF 0x0100
+
+/* Phy Specific status fields */
+#define ATHER_STATUS_LINK_MASK 0xC000
+#define ATHER_STATUS_LINK_SHIFT 14
+#define ATHER_STATUS_FULL_DUPLEX 0x2000
+#define ATHR_STATUS_LINK_PASS 0x0400
+#define ATHR_LATCH_LINK_PASS 0x0004
+#define ATHR_STATUS_RESOVLED 0x0800
+
+/*phy debug port register */
+#define ATHER_DEBUG_SERDES_REG 5
+
+/* Serdes debug fields */
+#define ATHER_SERDES_BEACON 0x0100
+
+#define OPERATIONAL_MODE_REG0 0x4
+
+/* S27 CSR Registers */
+
+#define PORT_STATUS_REGISTER0 0x0100
+#define PORT_STATUS_REGISTER1 0x0200
+#define PORT_STATUS_REGISTER2 0x0300
+#define PORT_STATUS_REGISTER3 0x0400
+#define PORT_STATUS_REGISTER4 0x0500
+#define PORT_STATUS_REGISTER5 0x0600
+
+#define RATE_LIMIT_REGISTER0 0x010C
+#define RATE_LIMIT_REGISTER1 0x020C
+#define RATE_LIMIT_REGISTER2 0x030C
+#define RATE_LIMIT_REGISTER3 0x040C
+#define RATE_LIMIT_REGISTER4 0x050C
+#define RATE_LIMIT_REGISTER5 0x060C
+
+#define PORT_CONTROL_REGISTER0 0x0104
+#define PORT_CONTROL_REGISTER1 0x0204
+#define PORT_CONTROL_REGISTER2 0x0304
+#define PORT_CONTROL_REGISTER3 0x0404
+#define PORT_CONTROL_REGISTER4 0x0504
+#define PORT_CONTROL_REGISTER5 0x0604
+
+#define CPU_PORT_REGISTER 0x0078
+#define MDIO_CTRL_REGISTER 0x0098
+
+#define S27_ARL_TBL_FUNC_REG0 0x0050
+#define S27_ARL_TBL_FUNC_REG1 0x0054
+#define S27_ARL_TBL_FUNC_REG2 0x0058
+#define S27_FLD_MASK_REG 0x002c
+#define S27_ARL_TBL_CTRL_REG 0x005c
+#define S27_GLOBAL_INTR_REG 0x10
+#define S27_GLOBAL_INTR_MASK_REG 0x14
+
+
+#define S27_ENABLE_CPU_BROADCAST (1 << 26)
+#define S27_ENABLE_CPU_BCAST_FWD (1 << 25)
+
+#define PHY_LINK_CHANGE_REG 0x4
+#define PHY_LINK_UP 0x400
+#define PHY_LINK_DOWN 0x800
+#define PHY_LINK_DUPLEX_CHANGE 0x2000
+#define PHY_LINK_SPEED_CHANGE 0x4000
+#define PHY_LINK_INTRS (PHY_LINK_UP | PHY_LINK_DOWN | PHY_LINK_DUPLEX_CHANGE | PHY_LINK_SPEED_CHANGE)
+
+/* SWITCH QOS REGISTERS */
+
+#define ATHR_QOS_PORT_0 0x110 /* CPU PORT */
+#define ATHR_QOS_PORT_1 0x210
+#define ATHR_QOS_PORT_2 0x310
+#define ATHR_QOS_PORT_3 0x410
+#define ATHR_QOS_PORT_4 0x510
+
+#define ATHR_ENABLE_TOS (1 << 16)
+
+#define ATHR_QOS_MODE_REGISTER 0x030
+#define ATHR_QOS_FIXED_PRIORITY ((0 << 31) | (0 << 28))
+#define ATHR_QOS_WEIGHTED ((1 << 31) | (0 << 28)) /* Fixed weight 8,4,2,1 */
+#define ATHR_QOS_MIXED ((1 << 31) | (1 << 28)) /* Q3 for managment; Q2,Q1,Q0 - 4,2,1 */
+
+#ifndef BOOL
+#define BOOL int
+#endif
+
+#define sysMsDelay(_x) udelay((_x) * 1000)
+#define mdelay(_x) sysMsDelay(_x)
+
+#undef S27_VER_1_0
+
+/*
+ * Atheros header defines
+ */
+#ifndef _ATH_HEADER_CONF
+#define _ATH_HEADER_CONF
+
+typedef enum {
+ NORMAL_PACKET,
+ RESERVED0,
+ MIB_1ST,
+ RESERVED1,
+ RESERVED2,
+ READ_WRITE_REG,
+ READ_WRITE_REG_ACK,
+ RESERVED3
+} AT_HEADER_TYPE;
+
+typedef struct {
+ uint16_t reserved0 :2;
+ uint16_t priority :2;
+ uint16_t type :4;
+ uint16_t broadcast :1;
+ uint16_t from_cpu :1;
+ uint16_t reserved1 :2;
+ uint16_t port_num :4;
+}at_header_t;
+
+#define ATHR_HEADER_LEN 2
+
+#endif // _ATH_HEADER_CONF
+
+typedef enum {
+ PORT_EG_UNMODIFIED = 0, /**< egress transmit packets unmodified */
+ PORT_EG_UNTAGGED, /**< egress transmit packets without vlan tag */
+ PORT_EG_TAGGED, /**< egress transmit packets with vlan tag */
+} port_1q_egmode_t;
+
+extern void set_packet_inspection_flag(int flag);
+
+#endif