📄 athrs26_phy.c
字号:
/* * 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 <linux/config.h>#include <linux/types.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/delay.h>#include "ag7100_phy.h"#include "ag7100.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 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 "ATHRS26"/* * 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, /* phy port 0 -- LAN port 0 */ FALSE, ENET_UNIT_LAN, 0, ATHR_PHY0_ADDR, ATHR_LAN_PORT_VLAN }, {TRUE, /* phy port 1 -- LAN port 1 */ FALSE, ENET_UNIT_LAN, 0, ATHR_PHY1_ADDR, ATHR_LAN_PORT_VLAN }, {TRUE, /* phy port 2 -- LAN port 2 */ FALSE, ENET_UNIT_LAN, 0, ATHR_PHY2_ADDR, ATHR_LAN_PORT_VLAN }, {TRUE, /* phy port 3 -- LAN port 3 */ FALSE, ENET_UNIT_LAN, 0, ATHR_PHY3_ADDR, ATHR_LAN_PORT_VLAN }, {TRUE, /* phy port 4 -- WAN port or LAN port 4 */ FALSE, 1, 0, ATHR_PHY4_ADDR, ATHR_LAN_PORT_VLAN /* Send to all ports */ }, {FALSE, /* phy port 5 -- CPU port (no RJ45 connector) */ TRUE, ENET_UNIT_LAN, 0, 0x00, ATHR_LAN_PORT_VLAN /* Send to all ports */ },};static uint8_t athr26_init_flag = 0;static cmd_resp_t cmd_resp;static DECLARE_WAIT_QUEUE_HEAD (hd_conf_wait);static int wait_flag = 0;static ag7100_mac_t *ag7100_macs[2];static atomic_t seqcnt = ATOMIC_INIT(0);#define ATHR_GLOBALREGBASE 0//#define ATHR_PHY_MAX (sizeof(ipPhyInfo) / sizeof(ipPhyInfo[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 athrs26_phy_is_link_alive(int phyUnit);static uint32_t athrs26_reg_read(uint32_t reg_addr);static void athrs26_reg_write(uint32_t reg_addr, uint32_t reg_val);#if !defined(HEADER_REG_CONF) && !defined(CONFIG_AR9100) #define get_field_val(_reg, _mask, _shift, _res_reg) \ do { \ unsigned int temp; \ temp = ar7100_reg_rd(_reg); \ temp &= (unsigned int)_mask;\ _res_reg = temp >> _shift; \ } while (0)#define set_field_val(_reg, _mask, _shift, _val) \ do { \ unsigned int temp; \ temp = ar7100_reg_rd(_reg); \ temp &= ~_mask; \ temp |= _val << _shift; \ ar7100_reg_wr(_reg, temp);\ } while (0)static unsigned int old_ahb_div = 0;void ag7100_ahb_feq_adjust(void){ unsigned int pll_fb = 0, ahb_div = 0, cpu_div = 0, mask = 0; mask = PLL_DIV_MASK << PLL_DIV_SHIFT; get_field_val(AR7100_PLL_CONFIG, mask, PLL_DIV_SHIFT, pll_fb); mask = AHB_DIV_MASK << AHB_DIV_SHIFT; get_field_val(AR7100_PLL_CONFIG, mask, AHB_DIV_SHIFT, old_ahb_div); mask = CPU_DIV_MASK << CPU_DIV_SHIFT; get_field_val(AR7100_PLL_CONFIG, mask, CPU_DIV_SHIFT, cpu_div); //ahb_div = ((((pll_fb + 1) * 40)*2/(200*(cpu_div + 1))) + 1)/2 - 1; ahb_div = ( (2*pll_fb + 2)/(5*cpu_div + 5) + 1)/2 - 1; mask = AHB_DIV_MASK << AHB_DIV_SHIFT; set_field_val(AR7100_PLL_CONFIG, mask, AHB_DIV_SHIFT, ahb_div);}void ag7100_ahb_feq_restore(void){ unsigned int mask = 0; mask = AHB_DIV_MASK << AHB_DIV_SHIFT; set_field_val(AR7100_PLL_CONFIG, mask, AHB_DIV_SHIFT, old_ahb_div); }#endif#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)static uint16_t port_def_vid[5] = {1, 1, 1, 1, 1};static uint8_t cpu_egress_tagged_flag = 0;inline uint8_t is_cpu_egress_tagged(void){ return cpu_egress_tagged_flag;}void set_cpu_egress_tagged(uint8_t is_tagged){ cpu_egress_tagged_flag = is_tagged; }inline uint16_t athrs26_defvid_get(uint32_t port_id){ if ((port_id == 0) || (port_id > 5)) return 0; return port_def_vid[port_id - 1];}BOOL athrs26_defvid_set(uint32_t port_id, uint16_t def_vid){ if ((def_vid == 0) || (def_vid > 4094)) return FALSE; if ((port_id == 0) || (port_id > 5)) return FALSE; port_def_vid[port_id - 1] = def_vid; return TRUE;}#endifvoid athrs26_reg_init(){ int i = 20; /* if using header for register configuration, we have to */ /* configure s26 register after frame transmission is enabled */ if (athr26_init_flag) return; #if (!defined(CONFIG_AR9100)) && (!defined(HEADER_REG_CONF)) ag7100_ahb_feq_adjust(); #endif /* reset switch */ printk(MODULE_NAME ": resetting s26\n"); athrs26_reg_write(0x0, athrs26_reg_read(0x0)|0x80000000); while(i--) { mdelay(100); if(!(athrs26_reg_read(0x0)&0x80000000)) break; } mdelay(3000); printk(MODULE_NAME ": s26 reset done\n"); phy_reg_write(0, ATHR_PHY4_ADDR, 0, 0x0800); athrs26_reg_write(0x200, 0x200); athrs26_reg_write(0x300, 0x200); athrs26_reg_write(0x400, 0x200); athrs26_reg_write(0x500, 0x200); athrs26_reg_write(0x600, 0x7d); athrs26_reg_write(0x38, 0xc000050e);#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) #ifdef HEADER_EN athrs26_reg_write(0x104, 0x6804);#else athrs26_reg_write(0x104, 0x6004);#endif athrs26_reg_write(0x204, 0x6004); athrs26_reg_write(0x304, 0x6004); athrs26_reg_write(0x404, 0x6004); athrs26_reg_write(0x504, 0x6004); athrs26_reg_write(0x604, 0x6004); #else#ifdef HEADER_EN athrs26_reg_write(0x104, 0x4804);#else athrs26_reg_write(0x104, 0x4004);#endif#endif athrs26_reg_write(0x60, 0xffffffff); athrs26_reg_write(0x64, 0xaaaaaaaa); athrs26_reg_write(0x68, 0x55555555); athrs26_reg_write(0x6c, 0x0); athrs26_reg_write(0x70, 0x41af);#if (!defined(CONFIG_AR9100)) && (!defined(HEADER_REG_CONF)) ag7100_ahb_feq_restore(); #endif#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) set_cpu_egress_tagged(0); /* use set_cpu_egress_tagged(1) to let s26 forward frame to cpu always tagged */ /* some applications need to know the vlan information, but please make sure */ /* define HEADER_EN in this case */ #endif#ifdef FULL_FEATURE athena_init(0, 2);#endif athr26_init_flag = 1;}static unsigned int phy_val_saved = 0;/******************************************************************************** athrs26_phy_off - power off the phy to change its speed** Power off the phy*/void athrs26_phy_off(ag7100_mac_t *mac){ struct net_device *dev = mac->mac_dev; if (mac->mac_unit == ENET_UNIT_LAN) return; netif_carrier_off(dev); netif_stop_queue(dev); phy_val_saved = phy_reg_read(0, ATHR_PHY4_ADDR, ATHR_PHY_CONTROL); phy_reg_write(0, ATHR_PHY4_ADDR, ATHR_PHY_CONTROL, phy_val_saved | 0x800);}/******************************************************************************** athrs26_phy_on - power on the phy after speed changed** Power on the phy*/void athrs26_phy_on(ag7100_mac_t *mac){ if ((mac->mac_unit == ENET_UNIT_LAN) || (phy_val_saved == 0)) return; phy_reg_write(0, ATHR_PHY4_ADDR, ATHR_PHY_CONTROL, phy_val_saved & 0xf7ff); mdelay(2000);}/******************************************************************************** athrs26_mac_speed_set - set mac in s26 speed mode (actually RMII mode)** Set mac speed mode*/void athrs26_mac_speed_set(ag7100_mac_t *mac, ag7100_phy_speed_t speed){ if ((mac->mac_unit == ENET_UNIT_LAN)) return; switch (speed) { case AG7100_PHY_SPEED_100TX: athrs26_reg_write (0x600, 0x7d); break; case AG7100_PHY_SPEED_10T: athrs26_reg_write (0x600, 0x7c); break; default: break; }}/******************************************************************************** athrs26_phy_is_link_alive - test to see if the specified link is alive** RETURNS:* TRUE --> link is alive* FALSE --> link is down*/BOOLathrs26_phy_is_link_alive(int phyUnit){ uint16_t phyHwStatus; uint32_t phyBase; uint32_t phyAddr; phyBase = ATHR_PHYBASE(phyUnit); phyAddr = ATHR_PHYADDR(phyUnit); phyHwStatus = phy_reg_read(phyBase, phyAddr, ATHR_PHY_SPEC_STATUS); if (phyHwStatus & ATHR_STATUS_LINK_PASS) return TRUE; return FALSE;}/******************************************************************************** athrs26_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*/BOOLathrs26_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; /* See if there's any configuration data for this enet */ /* start auto negogiation on each phy */ for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { continue; } foundPhy = TRUE; phyBase = ATHR_PHYBASE(phyUnit); phyAddr = ATHR_PHYADDR(phyUnit); phy_reg_write(phyBase, phyAddr, ATHR_AUTONEG_ADVERT, ATHR_ADVERTISE_ALL); /* Reset PHYs*/ phy_reg_write(phyBase, phyAddr, ATHR_PHY_CONTROL, ATHR_CTRL_AUTONEGOTIATION_ENABLE | ATHR_CTRL_SOFTWARE_RESET); } 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) mdelay(1000); else mdelay(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_IS_ETHUNIT(phyUnit, ethUnit)) { continue; } timeout=20; for (;;) { phyHwStatus = phy_reg_read(phyBase, 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; } mdelay(150); } #ifdef S26_VER_1_0 //turn off power saving phy_reg_write(0, phyUnit, 29, 41); phy_reg_write(0, phyUnit, 30, 0); printk("def_ S26_VER_1_0\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -