Tool/software: Linux
Hi there,
where could I find Linux kernel driver for the part in subject? Is there any implementation already available?
Thanks,
VIktor
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Tool/software: Linux
Hi there,
where could I find Linux kernel driver for the part in subject? Is there any implementation already available?
Thanks,
VIktor
Hi Viktor,
We just submitted the driver to be incorporated into the next update.
However, I have attached it here for you. We are still verifying it though.
Also, please be aware that the DP83TC811SEVM does not have a processor on-board.
Are you wiring the board over to your processor?dp83TC811_def.h
/* * drivers/net/phy/dp83tc811.c * * * 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. * * Steffen Graf (s-graf@ti.com) * Alexander Lange (a-lange@ti.com) * Björn Görner (b-goerner@ti.com) */ #include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> #include <linux/unistd.h> #include <linux/interrupt.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/spinlock.h> #include <linux/mm.h> #include <linux/module.h> #include <linux/mii.h> #include <linux/ethtool.h> #include <linux/phy.h> #include <linux/of.h> #include <linux/uaccess.h> #include <linux/io.h> #include <asm/irq.h> #include "dp83TC811_def.h" MODULE_DESCRIPTION("TI 811 PHY driver"); MODULE_AUTHOR("Alexander Lange"); MODULE_LICENSE("GPL"); #define DEBUG_811 #ifdef DEBUG_811 #define debug(fmt, args...) printk(KERN_ERR "%s: %d %s: "fmt, __FILE__, __LINE__, __FUNCTION__, ##args); #else #define debug(fmt, args...) #endif //#define TC_ID 0x20002950 //#define A0_ID 0x2000a250 #define TC_ID 0x2000a250 #define A0_ID 0x2000a251 #define ID_MASK 0xfffffff0 enum registerRW_enum{IDLErw, READrw}; static int registerRW_address=0; static int registerRW_data=0; static enum registerRW_enum registerRW_status=IDLErw; #define WAKE_ANY (WAKE_PHY | WAKE_ARP | WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST) /* #define REGCR 0xd #define ADDAR 0xe #define PHYRCR 0x001F #define LEDCFG1 0x0460 #define IOCTRL1 0x0462 #define IOCTRL2 0x0463 #define PHYSCR 0x0011 #define MISR1 0x0012 #define MMD1_CTRL1 0x1834 #define GEN_CFG1 0x9 #define PHY_CONTROL 0x10 #define ISR 0x13 #define GEN_CTRL 0x1f #define LEDS_CFG2 0x19 #define TRIM_BANDGAP 0x87 #define ANA_DATA_SERDES_CTRL_2 0xa1 #define ANA_DATA_SERDES_CTRL_4 0xae #define ANA_LINK_MONITOR_CTRL 0xc0 #define HOR_EYE_MON_STATUS 0xcb #define PHY_CTRL_TIMERS4 0x105 #define SCR_GEN_CFG2 0x10e #define SEQ_AEQ_CFG 0x11b // 1407 cable_norm 1007 cable_short #define PHY_CTRL_TIMERS9 0x12d #define RXF_PATTERN_1 0x013c #define RXF_PATTERN_CTRL 0x160 #define DIE_ID_0 0x180 */ static int dbgLvl=0; //static int lastlinkstatus811 = 0; enum chip_type_enum{TC, A0}; struct dp83tc811_struct{ enum chip_type_enum chip_type; struct kobject *dp83tc811_kobj; struct phy_device *phydev; }; static struct dp83tc811_struct dp83tc811; static void write_register_extended(struct phy_device *phydev, uint16_t reg, uint16_t data){ phy_write(phydev, REGCR, 0x1f); phy_write(phydev, ADDAR, reg); phy_write(phydev, REGCR, 0x401f); phy_write(phydev, ADDAR, data); } static uint16_t read_register_extended(struct phy_device *phydev, uint16_t reg){ phy_write(phydev, REGCR, 0x1f); phy_write(phydev, ADDAR, reg); phy_write(phydev, REGCR, 0x401f); return phy_read(phydev, ADDAR); } static uint16_t read_register(struct phy_device *phydev, uint16_t reg){ if(reg <= 0x1f) return phy_read(phydev, reg); else return read_register_extended(phydev, reg); } static void write_register(struct phy_device *phydev, uint16_t reg, uint16_t data){ if(reg <= 0x1f) phy_write(phydev, reg, data); else write_register_extended(phydev, reg, data); } static int read_phyid(struct phy_device *phydev){ return phy_read(phydev, 2) << 16 | phy_read(phydev, 3); } static ssize_t dump_reg_show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { //sprintf(buf, "Device is %s\n", (dp83tc811.chip_type==TC) ? "TC" : (dp83tc811.chip_type==A0) ? "A0" : "unknown"); sprintf(buf, "%s\n", buf); if(read_phyid(dp83tc811.phydev) == TC_ID){ sprintf(buf, "%sDetected silicon revision TC\n", buf); }else if(read_phyid(dp83tc811.phydev) == A0_ID){ sprintf(buf, "%sDetected silicon revision A0\n", buf); }else sprintf(buf, "%sERROR: silicon revision unknown: 0x%08X - the following register map is from A0 silicon. Readings may be faulty and unreliable!\n", buf, read_phyid(dp83tc811.phydev)); sprintf(buf, "%s-------------------START----------------------\n", buf); sprintf(buf, "%sREGISTER NAME ADDRESS\t\tVALUE\n", buf); sprintf(buf, "%s\n", buf); sprintf(buf, "%sBMCR 0x%04X:\t\t0x%04X\n", buf, BMCR, read_register(dp83tc811.phydev, BMCR)); sprintf(buf, "%sBMSR 0x%04X:\t\t0x%04X\n", buf, BMSR, read_register(dp83tc811.phydev, BMSR)); sprintf(buf, "%sPHYIDR1 0x%04X:\t\t0x%04X\n", buf, PHYIDR1, read_register(dp83tc811.phydev, PHYIDR1)); sprintf(buf, "%sPHYIDR2 0x%04X:\t\t0x%04X\n", buf, PHYIDR2, read_register(dp83tc811.phydev, PHYIDR2)); sprintf(buf, "%sGENCFG1 0x%04X:\t\t0x%04X\n", buf, GENCFG1, read_register(dp83tc811.phydev, GENCFG1)); sprintf(buf, "%sGENCFG2 0x%04X:\t\t0x%04X\n", buf, GENCFG2, read_register(dp83tc811.phydev, GENCFG2)); sprintf(buf, "%sFLD 0x%04X:\t\t0x%04X\n", buf, FLD, read_register(dp83tc811.phydev, FLD)); sprintf(buf, "%sREGCR 0x%04X:\t\t0x%04X\n", buf, REGCR, read_register(dp83tc811.phydev, REGCR)); sprintf(buf, "%sADDAR 0x%04X:\t\t0x%04X\n", buf, ADDAR, read_register(dp83tc811.phydev, ADDAR)); sprintf(buf, "%sFLDS 0x%04X:\t\t0x%04X\n", buf, FLDS, read_register(dp83tc811.phydev, FLDS)); sprintf(buf, "%sPHYSCR 0x%04X:\t\t0x%04X\n", buf, PHYSCR, read_register(dp83tc811.phydev, PHYSCR)); sprintf(buf, "%sMISR1 0x%04X:\t\t0x%04X\n", buf, MISR1, read_register(dp83tc811.phydev, MISR1)); sprintf(buf, "%sMISR2 0x%04X:\t\t0x%04X\n", buf, MISR2, read_register(dp83tc811.phydev, MISR2)); sprintf(buf, "%sFCSCR 0x%04X:\t\t0x%04X\n", buf, FCSCR, read_register(dp83tc811.phydev, FCSCR)); sprintf(buf, "%sRECR 0x%04X:\t\t0x%04X\n", buf, RECR, read_register(dp83tc811.phydev, RECR)); sprintf(buf, "%sBISCR 0x%04X:\t\t0x%04X\n", buf, BISCR, read_register(dp83tc811.phydev, BISCR)); sprintf(buf, "%sMIICTRL 0x%04X:\t\t0x%04X\n", buf, MIICTRL, read_register(dp83tc811.phydev, MIICTRL)); sprintf(buf, "%sBICSR1 0x%04X:\t\t0x%04X\n", buf, BICSR1, read_register(dp83tc811.phydev, BICSR1)); sprintf(buf, "%sBICSR2 0x%04X:\t\t0x%04X\n", buf, BICSR2, read_register(dp83tc811.phydev, BICSR2)); sprintf(buf, "%sCDCR 0x%04X:\t\t0x%04X\n", buf, CDCR, read_register(dp83tc811.phydev, CDCR)); sprintf(buf, "%sPHYRCR 0x%04X:\t\t0x%04X\n", buf, PHYRCR, read_register(dp83tc811.phydev, PHYRCR)); sprintf(buf, "%sLPS_CTRL 0x%04X:\t\t0x%04X\n", buf, LPS_CTRL, read_register(dp83tc811.phydev, LPS_CTRL)); sprintf(buf, "%sPWRM 0x%04X:\t\t0x%04X\n", buf, PWRM, read_register(dp83tc811.phydev, PWRM)); sprintf(buf, "%sLDCTRL2 0x%04X:\t\t0x%04X\n", buf, LDCTRL2, read_register(dp83tc811.phydev, LDCTRL2)); sprintf(buf, "%sLDCTRL3 0x%04X:\t\t0x%04X\n", buf, LDCTRL3, read_register(dp83tc811.phydev, LDCTRL3)); sprintf(buf, "%sSGMIICTRL1 0x%04X:\t\t0x%04X\n", buf, SGMIICTRL1, read_register(dp83tc811.phydev, SGMIICTRL1)); sprintf(buf, "%sTEMP_MON_CTRL 0x%04X:\t\t0x%04X\n", buf, TEMP_MON_CTRL, read_register(dp83tc811.phydev, TEMP_MON_CTRL)); sprintf(buf, "%sSUPPLY_MON_CTRL 0x%04X:\t\t0x%04X\n", buf, SUPPLY_MON_CTRL, read_register(dp83tc811.phydev, SUPPLY_MON_CTRL)); sprintf(buf, "%sRGMII_DLL_CTRL 0x%04X:\t\t0x%04X\n", buf, RGMII_DLL_CTRL, read_register(dp83tc811.phydev, RGMII_DLL_CTRL)); sprintf(buf, "%sGENCFG 0x%04X:\t\t0x%04X\n", buf, GENCFG, read_register(dp83tc811.phydev, GENCFG)); sprintf(buf, "%sSGMII_AUTO_NEG_STATUS 0x%04X:\t\t0x%04X\n", buf, SGMII_AUTO_NEG_STATUS, read_register(dp83tc811.phydev, SGMII_AUTO_NEG_STATUS)); sprintf(buf, "%sLEDCFG1 0x%04X:\t\t0x%04X\n", buf, LEDCFG1, read_register(dp83tc811.phydev, LEDCFG1)); sprintf(buf, "%sMACIMPCTRL 0x%04X:\t\t0x%04X\n", buf, MACIMPCTRL, read_register(dp83tc811.phydev, MACIMPCTRL)); sprintf(buf, "%sIOCTRL1 0x%04X:\t\t0x%04X\n", buf, IOCTRL1, read_register(dp83tc811.phydev, IOCTRL1)); sprintf(buf, "%sIOCTRL2 0x%04X:\t\t0x%04X\n", buf, IOCTRL2, read_register(dp83tc811.phydev, IOCTRL2)); sprintf(buf, "%sSOR1 0x%04X:\t\t0x%04X\n", buf, SOR1, read_register(dp83tc811.phydev, SOR1)); sprintf(buf, "%sLEDCFG 0x%04X:\t\t0x%04X\n", buf, LEDCFG, read_register(dp83tc811.phydev, LEDCFG)); sprintf(buf, "%sMONCFG1 0x%04X:\t\t0x%04X\n", buf, MONCFG1, read_register(dp83tc811.phydev, MONCFG1)); sprintf(buf, "%sMONCFG2 0x%04X:\t\t0x%04X\n", buf, MONCFG2, read_register(dp83tc811.phydev, MONCFG2)); sprintf(buf, "%sMONCFG3 0x%04X:\t\t0x%04X\n", buf, MONCFG3, read_register(dp83tc811.phydev, MONCFG3)); sprintf(buf, "%sMONSTAT1 0x%04X:\t\t0x%04X\n", buf, MONSTAT1, read_register(dp83tc811.phydev, MONSTAT1)); sprintf(buf, "%sMONSTAT2 0x%04X:\t\t0x%04X\n", buf, MONSTAT2, read_register(dp83tc811.phydev, MONSTAT2)); sprintf(buf, "%sPCS_CTRL1 0x%04X:\t\t0x%04X\n", buf, PCS_CTRL1, read_register(dp83tc811.phydev, PCS_CTRL1)); sprintf(buf, "%sPCS_CTRL2 0x%04X:\t\t0x%04X\n", buf, PCS_CTRL2, read_register(dp83tc811.phydev, PCS_CTRL2)); sprintf(buf, "%sLPS_CTRL3 0x%04X:\t\t0x%04X\n", buf, LPS_CTRL3, read_register(dp83tc811.phydev, LPS_CTRL3)); sprintf(buf, "%sTX_INTER_CFG 0x%04X:\t\t0x%04X\n", buf, TX_INTER_CFG, read_register(dp83tc811.phydev, TX_INTER_CFG)); sprintf(buf, "%sLPS_CTRL1 0x%04X:\t\t0x%04X\n", buf, LPS_CTRL1, read_register(dp83tc811.phydev, LPS_CTRL1)); sprintf(buf, "%sJABBER_CFG 0x%04X:\t\t0x%04X\n", buf, JABBER_CFG, read_register(dp83tc811.phydev, JABBER_CFG)); sprintf(buf, "%sTEST_MODE_CTRL 0x%04X:\t\t0x%04X\n", buf, TEST_MODE_CTRL, read_register(dp83tc811.phydev, TEST_MODE_CTRL)); sprintf(buf, "%sRXFCFG 0x%04X:\t\t0x%04X\n", buf, RXFCFG, read_register(dp83tc811.phydev, RXFCFG)); sprintf(buf, "%sRXFS 0x%04X:\t\t0x%04X\n", buf, RXFS, read_register(dp83tc811.phydev, RXFS)); sprintf(buf, "%sRXFPMD1 0x%04X:\t\t0x%04X\n", buf, RXFPMD1, read_register(dp83tc811.phydev, RXFPMD1)); sprintf(buf, "%sRXFPMD2 0x%04X:\t\t0x%04X\n", buf, RXFPMD2, read_register(dp83tc811.phydev, RXFPMD2)); sprintf(buf, "%sRXFPMD3 0x%04X:\t\t0x%04X\n", buf, RXFPMD3, read_register(dp83tc811.phydev, RXFPMD3)); sprintf(buf, "%sRXFSOP1 0x%04X:\t\t0x%04X\n", buf, RXFSOP1, read_register(dp83tc811.phydev, RXFSOP1)); sprintf(buf, "%sRXFSOP2 0x%04X:\t\t0x%04X\n", buf, RXFSOP2, read_register(dp83tc811.phydev, RXFSOP2)); sprintf(buf, "%sRXFSOP3 0x%04X:\t\t0x%04X\n", buf, RXFSOP3, read_register(dp83tc811.phydev, RXFSOP3)); sprintf(buf, "%sRXFPAT1 0x%04X:\t\t0x%04X\n", buf, RXFPAT1, read_register(dp83tc811.phydev, RXFPAT1)); sprintf(buf, "%sRXFPAT2 0x%04X:\t\t0x%04X\n", buf, RXFPAT2, read_register(dp83tc811.phydev, RXFPAT2)); sprintf(buf, "%sRXFPAT3 0x%04X:\t\t0x%04X\n", buf, RXFPAT3, read_register(dp83tc811.phydev, RXFPAT3)); sprintf(buf, "%sRXFPAT4 0x%04X:\t\t0x%04X\n", buf, RXFPAT4, read_register(dp83tc811.phydev, RXFPAT4)); sprintf(buf, "%sRXFPAT5 0x%04X:\t\t0x%04X\n", buf, RXFPAT5, read_register(dp83tc811.phydev, RXFPAT5)); sprintf(buf, "%sRXFPAT6 0x%04X:\t\t0x%04X\n", buf, RXFPAT6, read_register(dp83tc811.phydev, RXFPAT6)); sprintf(buf, "%sRXFPAT7 0x%04X:\t\t0x%04X\n", buf, RXFPAT7, read_register(dp83tc811.phydev, RXFPAT7)); sprintf(buf, "%sRXFPAT8 0x%04X:\t\t0x%04X\n", buf, RXFPAT8, read_register(dp83tc811.phydev, RXFPAT8)); sprintf(buf, "%sRXFPAT9 0x%04X:\t\t0x%04X\n", buf, RXFPAT9, read_register(dp83tc811.phydev, RXFPAT9)); sprintf(buf, "%sRXFPAT10 0x%04X:\t\t0x%04X\n", buf, RXFPAT10, read_register(dp83tc811.phydev, RXFPAT10)); sprintf(buf, "%sRXFPAT11 0x%04X:\t\t0x%04X\n", buf, RXFPAT11, read_register(dp83tc811.phydev, RXFPAT11)); sprintf(buf, "%sRXFPAT12 0x%04X:\t\t0x%04X\n", buf, RXFPAT12, read_register(dp83tc811.phydev, RXFPAT12)); sprintf(buf, "%sRXFPAT13 0x%04X:\t\t0x%04X\n", buf, RXFPAT13, read_register(dp83tc811.phydev, RXFPAT13)); sprintf(buf, "%sRXFPAT14 0x%04X:\t\t0x%04X\n", buf, RXFPAT14, read_register(dp83tc811.phydev, RXFPAT14)); sprintf(buf, "%sRXFPAT15 0x%04X:\t\t0x%04X\n", buf, RXFPAT15, read_register(dp83tc811.phydev, RXFPAT15)); sprintf(buf, "%sRXFPAT16 0x%04X:\t\t0x%04X\n", buf, RXFPAT16, read_register(dp83tc811.phydev, RXFPAT16)); sprintf(buf, "%sRXFPAT17 0x%04X:\t\t0x%04X\n", buf, RXFPAT17, read_register(dp83tc811.phydev, RXFPAT17)); sprintf(buf, "%sRXFPAT18 0x%04X:\t\t0x%04X\n", buf, RXFPAT18, read_register(dp83tc811.phydev, RXFPAT18)); sprintf(buf, "%sRXFPAT19 0x%04X:\t\t0x%04X\n", buf, RXFPAT19, read_register(dp83tc811.phydev, RXFPAT19)); sprintf(buf, "%sRXFPAT20 0x%04X:\t\t0x%04X\n", buf, RXFPAT20, read_register(dp83tc811.phydev, RXFPAT20)); sprintf(buf, "%sRXFPAT21 0x%04X:\t\t0x%04X\n", buf, RXFPAT21, read_register(dp83tc811.phydev, RXFPAT21)); sprintf(buf, "%sRXFPAT22 0x%04X:\t\t0x%04X\n", buf, RXFPAT22, read_register(dp83tc811.phydev, RXFPAT22)); sprintf(buf, "%sRXFPAT23 0x%04X:\t\t0x%04X\n", buf, RXFPAT23, read_register(dp83tc811.phydev, RXFPAT23)); sprintf(buf, "%sRXFPAT24 0x%04X:\t\t0x%04X\n", buf, RXFPAT24, read_register(dp83tc811.phydev, RXFPAT24)); sprintf(buf, "%sRXFPAT25 0x%04X:\t\t0x%04X\n", buf, RXFPAT25, read_register(dp83tc811.phydev, RXFPAT25)); sprintf(buf, "%sRXFPAT26 0x%04X:\t\t0x%04X\n", buf, RXFPAT26, read_register(dp83tc811.phydev, RXFPAT26)); sprintf(buf, "%sRXFPAT27 0x%04X:\t\t0x%04X\n", buf, RXFPAT27, read_register(dp83tc811.phydev, RXFPAT27)); sprintf(buf, "%sRXFPAT28 0x%04X:\t\t0x%04X\n", buf, RXFPAT28, read_register(dp83tc811.phydev, RXFPAT28)); sprintf(buf, "%sRXFPAT29 0x%04X:\t\t0x%04X\n", buf, RXFPAT29, read_register(dp83tc811.phydev, RXFPAT29)); sprintf(buf, "%sRXFPAT30 0x%04X:\t\t0x%04X\n", buf, RXFPAT30, read_register(dp83tc811.phydev, RXFPAT30)); sprintf(buf, "%sRXFPAT31 0x%04X:\t\t0x%04X\n", buf, RXFPAT31, read_register(dp83tc811.phydev, RXFPAT31)); sprintf(buf, "%sRXFPAT32 0x%04X:\t\t0x%04X\n", buf, RXFPAT32, read_register(dp83tc811.phydev, RXFPAT32)); sprintf(buf, "%sRXFPBM1 0x%04X:\t\t0x%04X\n", buf, RXFPBM1, read_register(dp83tc811.phydev, RXFPBM1)); sprintf(buf, "%sRXFPBM2 0x%04X:\t\t0x%04X\n", buf, RXFPBM2, read_register(dp83tc811.phydev, RXFPBM2)); sprintf(buf, "%sRXFPBM3 0x%04X:\t\t0x%04X\n", buf, RXFPBM3, read_register(dp83tc811.phydev, RXFPBM3)); sprintf(buf, "%sRXFPBM4 0x%04X:\t\t0x%04X\n", buf, RXFPBM4, read_register(dp83tc811.phydev, RXFPBM4)); sprintf(buf, "%sRXFPATC 0x%04X:\t\t0x%04X\n", buf, RXFPATC, read_register(dp83tc811.phydev, RXFPATC)); sprintf(buf, "%sTX_LPS_CODES 0x%04X:\t\t0x%04X\n", buf, TX_LPS_CODES, read_register(dp83tc811.phydev, TX_LPS_CODES)); sprintf(buf, "%sTX_WUR_CODES 0x%04X:\t\t0x%04X\n", buf, TX_WUR_CODES, read_register(dp83tc811.phydev, TX_WUR_CODES)); sprintf(buf, "%sRX_LPS_CODES 0x%04X:\t\t0x%04X\n", buf, RX_LPS_CODES, read_register(dp83tc811.phydev, RX_LPS_CODES)); sprintf(buf, "%sRX_WUR_CODES 0x%04X:\t\t0x%04X\n", buf, RX_WUR_CODES, read_register(dp83tc811.phydev, RX_WUR_CODES)); sprintf(buf, "%sLPS_WUR_CFG 0x%04X:\t\t0x%04X\n", buf, LPS_WUR_CFG, read_register(dp83tc811.phydev, LPS_WUR_CFG)); sprintf(buf, "%sPMACTRL2 0x%04X:\t\t0x%04X\n", buf, PMACTRL2, read_register(dp83tc811.phydev, PMACTRL2)); sprintf(buf, "%sPMAEXT1 0x%04X:\t\t0x%04X\n", buf, PMAEXT1, read_register(dp83tc811.phydev, PMAEXT1)); sprintf(buf, "%sPMAEXT2 0x%04X:\t\t0x%04X\n", buf, PMAEXT2, read_register(dp83tc811.phydev, PMAEXT2)); sprintf(buf, "%sPMACTRL1 0x%04X:\t\t0x%04X\n", buf, PMACTRL1, read_register(dp83tc811.phydev, PMACTRL1)); sprintf(buf, "%sTESTCTRL 0x%04X:\t\t0x%04X\n", buf, TESTCTRL, read_register(dp83tc811.phydev, TESTCTRL)); sprintf(buf, "%sPCSCTRL 0x%04X:\t\t0x%04X\n", buf, PCSCTRL, read_register(dp83tc811.phydev, PCSCTRL)); sprintf(buf, "%sPCSSTAT 0x%04X:\t\t0x%04X\n", buf, PCSSTAT, read_register(dp83tc811.phydev, PCSSTAT)); return sprintf(buf, "%s--------------------END-----------------------\n", buf); //return sprintf(buf, "%sDIE_ID_7:\t\t0x%04X\n", buf, read_register(dp83tc811.phydev, 0x0187)); } static ssize_t dump_reg_store(struct kobject * kobj, struct kobj_attribute * attr, const char * buf, size_t count) { int reg, data=0xfffff; sscanf(buf, "%x %x", ®, &data); debug("Reg: 0x%04X, Data 0x%04X\n", reg, data); if(data < 0xfffff){ printk(KERN_ERR "Reg 0x%04X: 0x%04X ", reg, read_register(dp83tc811.phydev, reg)); write_register(dp83tc811.phydev, reg, data); printk(KERN_ERR "changed to 0x%04X\n", read_register(dp83tc811.phydev, reg)); }else printk(KERN_ERR "Reg 0x%04X: 0x%04X\n", reg, read_register(dp83tc811.phydev, reg)); return count; } /* static ssize_t cable_show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { return sprintf(buf, "SEQ_AEQ_CFG set to 0x%04X - %s\n", read_register(dp83tc811.phydev, SEQ_AEQ_CFG), (read_register(dp83tc811.phydev, SEQ_AEQ_CFG) == 0x1007) ? "short" : (read_register(dp83tc811.phydev, SEQ_AEQ_CFG) == 0x1407) ? "normal" : "unknown"); } static ssize_t cable_store(struct kobject * kobj, struct kobj_attribute * attr, const char * buf, size_t count) { if(!strncmp(buf, "short", 5)){ write_register(dp83tc811.phydev, SEQ_AEQ_CFG, 0x1007); debug("configured for short cable\n"); }else if(!strncmp(buf, "normal", 6)){ write_register(dp83tc811.phydev, SEQ_AEQ_CFG, 0x1407); debug("configured to standard\n"); } return count; } */ static ssize_t link_show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { int linkstatus_i; double linkstatus_f; linkstatus_i = (read_register(dp83tc811.phydev, 0x197) & 0xFF); //////////////////////////////////////////////////////// DOCUMENTATION MISSING!! return sprintf(buf, "Signal Quality Indication is %d/255\n", linkstatus_i); } static ssize_t link_store(struct kobject * kobj, struct kobj_attribute * attr, const char * buf, size_t count) { int linkstatus_i; double linkstatus_f; linkstatus_i = (read_register(dp83tc811.phydev, 0x197) & 0xFF); //////////////////////////////////////////////////////// DOCUMENTATION MISSING!! printk(KERN_ERR "Signal Quality Indication is %d/255\n", linkstatus_i); return count; } static ssize_t tvm_show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { int temp, voltio, volta; temp = read_register(dp83tc811.phydev, MONSTAT1) &0x07; voltio = (read_register(dp83tc811.phydev, MONSTAT2)>>12) &0x07; volta = (read_register(dp83tc811.phydev, MONSTAT2)>>8) &0x07; return sprintf(buf, "Temperature is %s, VDDIO is %s, VDDA is %s.\n", (temp == 0) ? "below -40 degrees C" : (temp == 1) ? "between -40 and 0 degrees C" : (temp == 2) ? "between 0 and 30 degrees C" : (temp == 3) ? "between 30 and 70 degrees C" : (temp == 4) ? "between 70 and 100 degrees C" : (temp == 5) ? "between 100 and 135 degrees C" : (temp == 6) ? "between 135 and 165 degrees C" : "above 165 degrees C", (voltio == 0) ? "higher than VDD + 9%%": (voltio == 1) ? "between 1.5%% and 9%% higher than VDD": (voltio == 2) ? "between 4.5%% below and 1.5%% higher than VDD": (voltio == 3) ? "between 7.5%% and 4.5%% lower than VDD" : (voltio == 4) ? "between 10.5%% and 7.5%% lower than VDD" : (voltio == 5) ? "between 13.5%% and 10.5%% lower than VDD" : (voltio == 6) ? "between 16.5.5%% and 13.5%% lower than VDD" : "at least 16.5%% lower than VDD", (volta == 0) ? "higher than VDD + 9%%": (volta == 1) ? "between 1.5%% and 9%% higher than VDD": (volta == 2) ? "between 4.5%% below and 1.5%% higher than VDD": (volta == 3) ? "between 7.5%% and 4.5%% lower than VDD" : (volta == 4) ? "between 10.5%% and 7.5%% lower than VDD" : (volta == 5) ? "between 13.5%% and 10.5%% lower than VDD" : (volta == 6) ? "between 16.5.5%% and 13.5%% lower than VDD" : "at least 16.5%% lower than VDD"); } static ssize_t tvm_store(struct kobject * kobj, struct kobj_attribute * attr, const char * buf, size_t count) { int temp, voltio, volta; temp = read_register(dp83tc811.phydev, MONSTAT1) &0x07; voltio = (read_register(dp83tc811.phydev, MONSTAT2)>>12) &0x07; volta = (read_register(dp83tc811.phydev, MONSTAT2)>>8) &0x07; printk(KERN_ERR "Temperature is %s, VDDIO is %s, VDDA is %s.\n", (temp == 0) ? "below -40 degrees C" : (temp == 1) ? "between -40 and 0 degrees C" : (temp == 2) ? "between 0 and 30 degrees C" : (temp == 3) ? "between 30 and 70 degrees C" : (temp == 4) ? "between 70 and 100 degrees C" : (temp == 5) ? "between 100 and 135 degrees C" : (temp == 6) ? "between 135 and 165 degrees C" : "above 165 degrees C", (voltio == 0) ? "higher than VDD + 9%%": (voltio == 1) ? "between 1.5%% and 9%% higher than VDD": (voltio == 2) ? "between 4.5%% below and 1.5%% higher than VDD": (voltio == 3) ? "between 7.5%% and 4.5%% lower than VDD" : (voltio == 4) ? "between 10.5%% and 7.5%% lower than VDD" : (voltio == 5) ? "between 13.5%% and 10.5%% lower than VDD" : (voltio == 6) ? "between 16.5.5%% and 13.5%% lower than VDD" : "at least 16.5%% lower than VDD", (volta == 0) ? "higher than VDD + 9%%": (volta == 1) ? "between 1.5%% and 9%% higher than VDD": (volta == 2) ? "between 4.5%% below and 1.5%% higher than VDD": (volta == 3) ? "between 7.5%% and 4.5%% lower than VDD" : (volta == 4) ? "between 10.5%% and 7.5%% lower than VDD" : (volta == 5) ? "between 13.5%% and 10.5%% lower than VDD" : (volta == 6) ? "between 16.5.5%% and 13.5%% lower than VDD" : "at least 16.5%% lower than VDD"); return count; } /* static ssize_t int_show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { int intstatus = read_register(dp83tc811.phydev, ISR); sprintf(buf, "%sInterrupt status is 0x%04X:\n", buf, intstatus); if(intstatus == 0) sprintf(buf, "%sno interrupt pending\n", buf); if(intstatus & 0x02) sprintf(buf, "%sPOLARITY_CHNG_INT\n", buf); if(intstatus & 0x04) sprintf(buf, "%sXGMII_ERR_INT\n", buf); if(intstatus & 0x08) sprintf(buf, "%sPTTRN_MATCH_INT\n", buf); if(intstatus & 0x400) sprintf(buf, "%sLINK_STATUS_CHNG_INT\n", buf); if(intstatus & 0x800) sprintf(buf, "%sLINK_QUALITY_CHNG_INT\n", buf); if(intstatus & 0x1000) sprintf(buf, "%sOVER_TEMP_INT\n", buf); if(intstatus & 0x2000) sprintf(buf, "%sOVER_VOLT_INT\n", buf); if(intstatus & 0x4000) sprintf(buf, "%sUNDER_VOLT_INT\n", buf); return sprintf(buf, "%s", buf); } */ static ssize_t int_show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { uint16_t int1 = read_register(dp83tc811.phydev, MISR1); uint16_t int2 = read_register(dp83tc811.phydev, MISR2); sprintf(buf, "%sMISR1: 0x%04X MISR2: 0x%04X\n", buf, int1, int2); sprintf(buf, "%s------INTERRUPTS--------\n", buf); if(!((int1 & 0xF700) | (int2 & 0xDF00))) // Masks for actual interrupts (not the enable bits) sprintf(buf, "%sNo interrupt pending\n", buf); else sprintf(buf, "%sInterrupts pending:\n", buf); if(int1 & 0x8000) sprintf(buf, "%sLINK_QUALITY_CHANGE_INT\n", buf); if(int1 & 0x4000) sprintf(buf, "%sENERGY_CHANGE_INT\n", buf); if(int1 & 0x2000) sprintf(buf, "%sLINK_STATUS_CHANGE_INT\n", buf); if(int1 & 0x1000) sprintf(buf, "%sWOL_INT\n", buf); //0x0800 is reserved bit if(int1 & 0x400) sprintf(buf, "%sM/S_TRAINING_COMPLETE_INT\n", buf); if(int1 & 0x0200) sprintf(buf, "%sFALSE_CARRIER_CNT_HALF_FULL_INT\n", buf); if(int1 & 0x0100) sprintf(buf, "%sRCV_ERROR_CNT_HALF_FULL_INT\n", buf); if(int2 & 0x8000) sprintf(buf, "%sUNDER_VOLT_INT\n", buf); if(int2 & 0x4000) sprintf(buf, "%sOVER_VOLT_INT\n", buf); //0x2000 bit is reserved if(int2 & 0x1000) sprintf(buf, "%sLOOP_FIFO_OF/UF_INT\n", buf); if(int2 & 0x0800) sprintf(buf, "%sOVER_TEMP_INT\n", buf); if(int2 & 0x0400) sprintf(buf, "%sSLEEP_MODE_INT\n", buf); if(int2 & 0x0200) sprintf(buf, "%sPOLARITY_CHANGE_INT\n", buf); if(int2 & 0x0100) sprintf(buf, "%sJABBER_DETECT_INT\n", buf); sprintf(buf, "%s---------ENABLES---------\n", buf); if(!((int1 & 0x00F7) | (int2 & 0x00DF))) // Masks for interrupt enables sprintf(buf, "%sNo interrupt enabled\n", buf); else sprintf(buf, "%sInterrupts enabled:\n", buf); if(int1 & 0x0080) sprintf(buf, "%sLINK_QUALITY_CHANGE_INT_EN\n", buf); if(int1 & 0x0040) sprintf(buf, "%sENERGY_CHANGE_INT\n", buf); if(int1 & 0x0020) sprintf(buf, "%sLINK_STATUS_CHANGE_INT_EN\n", buf); if(int1 & 0x0010) sprintf(buf, "%sWOL_INT_EN\n", buf); // 0x0008 is reserved bit if(int1 & 0x0004) sprintf(buf, "%sM/S_TRAINING_COMPLETE_INT_EN\n", buf); if(int1 & 0x0002) sprintf(buf, "%sFALSE_CARRIER_CNT_HALF_FULL_INT_EN\n", buf); if(int1 & 0x0001) sprintf(buf, "%sRCV_ERROR_CNT_HALF_FULL_INT_EN\n", buf); if(int2 & 0x0080) sprintf(buf, "%sUNDER_VOLT_INT_EN\n", buf); if(int2 & 0x0040) sprintf(buf, "%sOVER_VOLT_INT\n", buf); //0x0020 bit is reserved if(int2 & 0x0010) sprintf(buf, "%sLOOP_FIFO_OF/UF_INT_EN\n", buf); if(int2 & 0x0008) sprintf(buf, "%sOVER_TEMP_INT_EN\n", buf); if(int2 & 0x0004) sprintf(buf, "%sSLEEP_MODE_INT_EN\n", buf); if(int2 & 0x0002) sprintf(buf, "%sPOLARITY_CHANGE_INT_EN\n", buf); if(int2 & 0x0001) sprintf(buf, "%sJABBER_DETECT_INT_EN\n", buf); sprintf(buf, "%s----------DONE----------\n", buf); return sprintf(buf, "%s", buf); } static ssize_t int_store(struct kobject * kobj, struct kobj_attribute * attr, const char * buf, size_t count) { int int1=0xfffff; int int2=0xfffff; sscanf(buf, "%x %x", &int1, &int2); debug("MISR1: 0x%04X, MISR2: 0x%04X\n", int1, int2); if(int1 < 0x10000){ printk(KERN_ERR "MISR1 0x%04X: 0x%04X ", MISR1, read_register(dp83tc811.phydev, MISR1)); write_register(dp83tc811.phydev, MISR1, int1); printk(KERN_ERR "changed to 0x%04X\n", read_register(dp83tc811.phydev, MISR1)); }else{ printk(KERN_ERR "MISR1 0x%04X: 0x%04X ", MISR1, read_register(dp83tc811.phydev, MISR1)); printk(KERN_ERR "left unchanged because input out of range 0xFFFF: 0x%X ", int1); } if(int2 < 0x10000){ printk(KERN_ERR "MISR2 0x%04X: 0x%04X ", MISR2, read_register(dp83tc811.phydev, MISR2)); write_register(dp83tc811.phydev, MISR2, int2); printk(KERN_ERR "changed to 0x%04X\n", read_register(dp83tc811.phydev, MISR2)); }else{ printk(KERN_ERR "MISR2 0x%04X: 0x%04X ", MISR2, read_register(dp83tc811.phydev, MISR2)); printk(KERN_ERR "left unchanged because input out of range 0xFFFF: 0x%X ", int2); } return count; } static ssize_t manual_mode_show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { return sprintf(buf, "PMACTRL1 set to 0x%04X - %s\n", read_register(dp83tc811.phydev, PMACTRL1), (read_register(dp83tc811.phydev, PMACTRL1) & 0x4000) ? "master" : "slave"); } static ssize_t manual_mode_store(struct kobject * kobj, struct kobj_attribute * attr, const char * buf, size_t count) { int temp = read_register(dp83tc811.phydev, PMACTRL1); if(!strncmp(buf, "slave", 5)){ temp &= ~0x4000; write_register(dp83tc811.phydev, PMACTRL1, temp); debug("configured to manual slave\n"); }else if(!strncmp(buf, "master", 6)){ temp |= 0x4000; write_register(dp83tc811.phydev, PMACTRL1, temp); //mdelay(1000); //write_register(dp83tc811.phydev, PHY_CONTROL, 0x5041); debug("configured to manual master\n"); } return count; } static ssize_t pattern_dest_show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { return sprintf(buf, "MAC destination address RXFPMD1-3: 0x%04X %04X %04X\n", read_register(dp83tc811.phydev, RXFPMD1), read_register(dp83tc811.phydev, RXFPMD2), read_register(dp83tc811.phydev, RXFPMD3)); } static ssize_t pattern_dest_store(struct kobject * kobj, struct kobj_attribute * attr, const char * buf, size_t count) { int byte5, byte4, byte3, byte2, byte1, byte0; //int val; int scanfRes; scanfRes = sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", &byte5, &byte4, &byte3, &byte2, &byte1, &byte0); if(scanfRes !=6) { printk(KERN_ERR "Error, parsed %d bytes of the MAC address, expected 6!\nExpected format is xx:yy:zz:aa:bb:cc", scanfRes); return count; } //debug("sscanf parsed %d\n", val); //debug("Parsed MAC %02X:%02X:%02X:%02X:%02X:%02X\n", byte5, byte4, byte3, byte2, byte1, byte0); write_register(dp83tc811.phydev, RXFPMD1, ((byte4<<8) | byte5)); write_register(dp83tc811.phydev, RXFPMD2, ((byte2<<8) | byte3)); write_register(dp83tc811.phydev, RXFPMD3, ((byte0<<8) | byte1)); debug("wrote RXFPMD1: 0x%04X, RXFPMD2: 0x%04X, RXFPMD3: 0x%04X\n", ((byte4<<8) | byte5), ((byte2<<8) | byte3), ((byte0<<8) | byte1)); printk(KERN_ERR "MAC destination address RXFPMD1-3: 0x%04X %04X %04X\n", read_register(dp83tc811.phydev, RXFPMD1), read_register(dp83tc811.phydev, RXFPMD2), read_register(dp83tc811.phydev, RXFPMD3)); return count; } /* static ssize_t send_wake_show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { write_register(dp83tc811.phydev, PHY_CONTROL, read_register(dp83tc811.phydev, PHY_CONTROL) | 0x01); return sprintf(buf, "Sent wake command!\n"); } static ssize_t send_wake_store(struct kobject * kobj, struct kobj_attribute * attr, const char * buf, size_t count) { write_register(dp83tc811.phydev, PHY_CONTROL, read_register(dp83tc811.phydev, PHY_CONTROL) | 0x01); printk(KERN_ERR "Sent wake command!\n"); return count; } */ static ssize_t password_show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { return sprintf(buf, "Secure-ON password RXFSOP1-3: 0x%04X %04X %04X\n", read_register(dp83tc811.phydev, RXFSOP1), read_register(dp83tc811.phydev, RXFSOP2), read_register(dp83tc811.phydev, RXFSOP3)); } static ssize_t password_store(struct kobject * kobj, struct kobj_attribute * attr, const char * buf, size_t count) { int byte5, byte4, byte3, byte2, byte1, byte0; //int val; int scanfRes; scanfRes = sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", &byte0, &byte1, &byte2, &byte3, &byte4, &byte5); if(scanfRes !=6) { printk(KERN_ERR "Error, parsed %d bytes of the password, expected 6!\nExpected format is xx:yy:zz:aa:bb:cc\n", scanfRes); return count; } write_register(dp83tc811.phydev, RXFSOP1, ((byte1<<8) | byte0)); write_register(dp83tc811.phydev, RXFSOP2, ((byte3<<8) | byte2)); write_register(dp83tc811.phydev, RXFSOP3, ((byte5<<8) | byte4)); return count; } static ssize_t debug_show(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { sprintf(buf, "%sDebug level set to %d\n", buf, dbgLvl); return sprintf(buf, "%sDebug level can be 0 to 3:\n0: mute\n1:only important outputs\n2:normal outputs\n3: all outputs\n", buf); } static ssize_t debug_store(struct kobject * kobj, struct kobj_attribute * attr, const char * buf, size_t count) { sscanf(buf, "%d", &dbgLvl); if(dbgLvl >3) dbgLvl=3; if(dbgLvl <0) dbgLvl=0; printk(KERN_ERR "Debug level set to %d\n", dbgLvl); return count; } static ssize_t registerR(struct kobject * kobj, struct kobj_attribute * attr, char * buf) { int bufCnt=0; if(registerRW_status == READrw){ bufCnt=sprintf(buf, "%s%X %X\n", buf, registerRW_address, registerRW_data); registerRW_status = IDLErw; } else{ bufCnt=sprintf(buf, "%s%X %X\n", buf, -1, 0); // Register address -1 (0xFFFF) has to be considered as read error because write needed first printk(KERN_ERR "Write address to be read first\n"); } return bufCnt; } static ssize_t registerW(struct kobject * kobj, struct kobj_attribute * attr, const char * buf, size_t count) { int parseCnt=0; if(registerRW_status == IDLErw){ parseCnt = sscanf(buf, "%x %x", ®isterRW_address, ®isterRW_data); if(parseCnt == 0){ printk(KERN_ERR "Usage 0xABCD for read or 0xABCD 0x1234 for write\n"); } else if(parseCnt == 1){ registerRW_data = read_register(dp83tc811.phydev, registerRW_address); //printk(KERN_ERR "Received address %X, have read %X\n", registerRW_address, registerRW_data); registerRW_status = READrw; } else if(parseCnt == 2){ //printk(KERN_ERR "Received address %X and data %X\n", registerRW_address, registerRW_data); write_register(dp83tc811.phydev, registerRW_address, registerRW_data); } } return count; } //static struct kobj_attribute dp83tc811_wake_attribute = __ATTR(send_wake, 0666, send_wake_show, send_wake_store); static struct kobj_attribute dp83tc811_mgc_pttrn_dest_attribute = __ATTR(magic_pattern_own_MAC, 0666, pattern_dest_show, pattern_dest_store); static struct kobj_attribute dp83tc811_password_attribute = __ATTR(secureON_pswd, 0666, password_show, password_store); static struct kobj_attribute dp83tc811_reg_attribute = __ATTR(mdio_reg, 0666, dump_reg_show, dump_reg_store); //static struct kobj_attribute dp83tc811_cable_attribute = __ATTR(cable_config, 0666, cable_show, cable_store); static struct kobj_attribute dp83tc811_link_attribute = __ATTR(link_quality, 0666, link_show, link_store); static struct kobj_attribute dp83tc811_tempVolt_attribute = __ATTR(temp_volt_monitor, 0666, tvm_show, tvm_store); static struct kobj_attribute dp83tc811_int_attribute = __ATTR(interrupt_status, 0666, int_show, int_store); static struct kobj_attribute dp83tc811_mode_attribute = __ATTR(master_slave_config, 0666, manual_mode_show, manual_mode_store); static struct kobj_attribute dp83tc811_debug_attribute = __ATTR(dbgLvl, 0666, debug_show, debug_store); static struct kobj_attribute dp83tc822_registerRW_attribute = __ATTR(registerRW, 0666, registerR, registerW); static struct attribute * attrs [] = { //&dp83tc811_wake_attribute.attr, &dp83tc811_mgc_pttrn_dest_attribute.attr, &dp83tc811_password_attribute.attr, &dp83tc811_reg_attribute.attr, //&dp83tc811_cable_attribute.attr, &dp83tc811_link_attribute.attr, &dp83tc811_tempVolt_attribute.attr, &dp83tc811_int_attribute.attr, &dp83tc811_mode_attribute.attr, &dp83tc811_debug_attribute.attr, &dp83tc822_registerRW_attribute.attr, NULL, }; static struct attribute_group attr_group = { .attrs = attrs, }; /* void check_set_trim(struct phy_device *phydev){ int i; int bandgap; int term; int rec_off; int hor_link; uint16_t die[8]; for(i=7;i>=0;i--) die[i] = read_register(phydev, DIE_ID_0 + i); if((die[0] & 0x3ff) != 0x001) return; // check lower 10 bits in row 0 for 0x001 for(i=2;i<8;i++) if(die[i] != 0) return; // check row 1 - 3 to be 0 // parse remaining bits in row 0 bandgap = (die[1]>>10) & 0x3f; term = (die[1]>>4) & 0x3f; rec_off = ((die[1]) & 0xf)<<2 | ((die[0]>>12)&0x3); hor_link = (die[0]>>10) & 0xf; printk(KERN_ERR "A0 Silicon detected at ADDR 0x%04X\n", phydev->addr); printk(KERN_ERR "Trim parameters from DIE ID loaded\n"); printk(KERN_ERR "Bandgap 0x%04X\n", bandgap); printk(KERN_ERR "Termination 0x%04X\n", term); printk(KERN_ERR "Receiver Offset 0x%04X\n", rec_off); printk(KERN_ERR "Horizontal Link 0x%04X\n", hor_link); // write trim parameters to device write_register(phydev, TRIM_BANDGAP, (read_register(phydev, TRIM_BANDGAP) & 0xffc0) | bandgap); write_register(phydev, ANA_DATA_SERDES_CTRL_2, (read_register(phydev, ANA_DATA_SERDES_CTRL_2) & 0xffc0) | term); write_register(phydev, ANA_DATA_SERDES_CTRL_4, (read_register(phydev, ANA_DATA_SERDES_CTRL_4) & 0xc0ff) | rec_off<<8); write_register(phydev, ANA_LINK_MONITOR_CTRL, (read_register(phydev, ANA_LINK_MONITOR_CTRL) & 0xff0f) | hor_link<<4); } */ static int set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { struct net_device *ndev = phydev->attached_dev; const u8 *mac; u32 value; debug("811 set_wol called, opt 0x%04X\n", wol->wolopts); if (!ndev) return -ENODEV; if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { mac = (const u8 *) ndev->dev_addr; if (!is_valid_ether_addr(mac)) return -EFAULT; write_register(phydev, RXFPMD1, (mac[1] << 8) | mac[0]); // MAC addresses start with byte 5, but stored in mac[0]. 811 and 822 PHYs store bytes 4|5, 2|3, 0|1 write_register(phydev, RXFPMD2, (mac[3] << 8) | mac[2]); write_register(phydev, RXFPMD3, (mac[5] << 8) | mac[4]); value = read_register(phydev, RXFCFG); debug("811 set_wol value 0x%04X\n", value); if(wol->wolopts & WAKE_MAGIC) value |= 0x0001; else value &= ~0x0001; if(wol->wolopts & WAKE_MAGICSECURE) { value |= 0x0020; write_register(phydev, RXFSOP1, (wol->sopass[1] << 8) | wol->sopass[0]); write_register(phydev, RXFSOP2, (wol->sopass[3] << 8) | wol->sopass[2]); write_register(phydev, RXFSOP3, (wol->sopass[5] << 8) | wol->sopass[4]); debug("811 sopass 0x%04X\n", ((wol->sopass[1] << 8) | wol->sopass[0])); debug("811 read sopass 0x%04X\n", read_register(phydev, RXFSOP1)); } else value &= ~0x0020; value |= 0x0080; //enable WoL value |= 0x0100; // Level change value |= 0x0800; // clear indication debug("811 set_wol wrote 0x%04X\n", value); write_register(phydev, RXFCFG, value); } else { value = read_register(phydev, RXFCFG); value &= (~0x0080); // disable WoL write_register(phydev, RXFCFG, value); debug("811 set_wol wrote 0x%04X\n", value); debug("811 set_wol read back RXFCFG: 0x%04X\n", read_register(phydev, RXFCFG)); } return 0; } static void get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { int value; wol->supported = WAKE_ANY; wol->wolopts = 0; value = read_register(phydev, RXFCFG); if(value & 0x0001) wol->wolopts |= WAKE_MAGIC; if(value & 0x0020) wol->wolopts |= WAKE_MAGICSECURE; if((~value) & 0x0080) // WoL enable cleared? wol->wolopts = 0; wol->sopass[0] = (read_register(dp83tc811.phydev, RXFSOP1) &0xFF); wol->sopass[1] = (read_register(dp83tc811.phydev, RXFSOP1) >>8); wol->sopass[2] = (read_register(dp83tc811.phydev, RXFSOP2) &0xFF); wol->sopass[3] = (read_register(dp83tc811.phydev, RXFSOP2) >>8); wol->sopass[4] = (read_register(dp83tc811.phydev, RXFSOP3) &0xFF); wol->sopass[5] = (read_register(dp83tc811.phydev, RXFSOP3) >>8); printk(KERN_ERR "811 get_wol: RXFCFG 0x%04X, opts 0x%04X\n", value, wol->wolopts); } static int netbr_config_aneg(struct phy_device *phydev) { debug("phy_state: %x\n", phydev->state); return 0; } static int netbr_tc_read_status(struct phy_device *phydev) { int status; status = phy_read(phydev, MII_BMSR); if(dbgLvl==3) debug("TC read status: BMSR: 0x%04X\n", status); if (status < 0) return status; if(status & BMSR_LSTATUS){ phydev->state = PHY_RUNNING; phydev->link = 1; }else{ phydev->state = PHY_NOLINK; phydev->link = 0; } phydev->speed = SPEED_100; phydev->duplex = DUPLEX_FULL; /*if(dbgLvl==3) { int linkstatus = read_register(dp83tc811.phydev, HOR_EYE_MON_STATUS); printk(KERN_ERR "Link status is 0x%04X - %s\n", linkstatus, (linkstatus == 0x00) ? "bad" : (linkstatus == 0x0100) ? "medium" : (linkstatus == 0x0200) ? "reserved" : "good"); } if(dbgLvl==2) { int linkstatus = read_register(dp83tc811.phydev, HOR_EYE_MON_STATUS); if(linkstatus != lastlinkstatus811) { printk(KERN_ERR "Link status changed to 0x%04X - %s\n", linkstatus, (linkstatus == 0x00) ? "bad" : (linkstatus == 0x0100) ? "medium" : (linkstatus == 0x0200) ? "reserved" : "good"); lastlinkstatus811 = linkstatus; } }*/ return genphy_read_status(phydev); } static int netbr_a0_read_status(struct phy_device *phydev) { int status; status = phy_read(phydev, MII_BMSR); if(dbgLvl==3) debug("A0 read status: BMSR: 0x%04X\n", status); if (status < 0) return status; if(status & BMSR_LSTATUS){ phydev->state = PHY_RUNNING; phydev->link = 1; }else{ phydev->state = PHY_NOLINK; phydev->link = 0; } phydev->speed = SPEED_100; phydev->duplex = DUPLEX_FULL; return genphy_read_status(phydev); } static int netbr_config_probe(struct phy_device *phydev) { int retval; char sysfsdevname[64]; debug("called netbr_config_probe for DP83TC811\n"); debug("PHYIDR1: 0x%04X\n", phy_read(phydev, PHYIDR1)); debug("PHYIDR2: 0x%04X\n", phy_read(phydev, PHYIDR2)); dp83tc811.phydev = phydev; if(read_phyid(phydev) == TC_ID){ debug("Testchip detected\n"); dp83tc811.chip_type = TC; }else if(read_phyid(phydev) == A0_ID){ debug("A0 Silicon detected\n"); dp83tc811.chip_type = A0; }else return -ENODEV; sprintf(sysfsdevname, "dp83tc811_%d", phydev->addr); dp83tc811.dp83tc811_kobj = kobject_create_and_add(sysfsdevname, kernel_kobj); //maybe another path is better?! if(!dp83tc811.dp83tc811_kobj) return ENOMEM; retval = sysfs_create_group(dp83tc811.dp83tc811_kobj, &attr_group); if(retval) kobject_put(dp83tc811.dp83tc811_kobj); return 0; } static int netbr_config_init(struct phy_device *phydev) { debug("called netbr_config_init for DP83TC811\n"); debug("Compile date and time: %s %s\n", __DATE__, __TIME__) debug("PHYIDR1: 0x%04X\n", phy_read(phydev, 2)); debug("PHYIDR2: 0x%04X\n", phy_read(phydev, 3)); switch(dp83tc811.chip_type){ case TC: break; case A0: write_register(phydev, PHYRCR, 0x8000); // send reset mdelay(1000); write_register(phydev, LEDCFG1, 0x2010); // configure led1: link OK+ RX/TX activity, 5Hz blink rate write_register(phydev, IOCTRL1, 0x5); // GPIO MUX: configure 1588RX write_register(phydev, IOCTRL2, 0x4); // GPIO MUX: configure 1588TX write_register(phydev, PHYSCR, 0x103); // interrupt pin: configure for WoL, logic 1 during interrupt write_register(phydev, MISR1, 0x10); // enable WoL interrupt //write_register(phydev, ANA_DATA_SERDES_CTRL_2, 0xb3c); // term resistor value //write_register(phydev, ANA_DATA_SERDES_CTRL_4, 0xf04); // rx dc offset trim value //write_register(phydev, ANA_LINK_MONITOR_CTRL, 0x187); // atp_eye_mon trim value // write_register(phydev, PHY_CTRL_TIMERS4, 0xb07d); // slave timing trim // write_register(phydev, PHY_CTRL_TIMERS9, 0x2ff); // master timing trim // write_register(phydev, SCR_GEN_CFG2, 0x7); // bypass scrambler // check_set_trim(phydev); // write_register(phydev, 0xca, 0x3402); // enable link quality monitor break; } return 0; } static int suspend(struct phy_device *phydev) // genphy_suspend and resume functions don't cover WoL. phy_resume and suspend crashed on J6 setup. Resume also needs to clear interrupt pin { int value; mutex_lock(&phydev->lock); value = read_register(phydev, RXFCFG); debug("811 suspend RXCFG: 0x%04X\n", value); if((~value) & 0x0080) // WoL enable cleared? { value = read_register(phydev, MII_BMCR); write_register(phydev, MII_BMCR, value | BMCR_PDOWN); debug("811 WoL disabled, powered down PHY\n"); } else debug("811 WoL enabled, won't power down PHY\n"); mutex_unlock(&phydev->lock); return 0; } static int resume(struct phy_device *phydev) { int value; debug("811 resume called\n"); mutex_lock(&phydev->lock); value = read_register(phydev, MII_BMCR); write_register(phydev, MII_BMCR, value & ~BMCR_PDOWN); value = read_register(phydev, RXFCFG); write_register(phydev, RXFCFG, value | 0x0800); // clear WoL indication (J6 needs Wakeup pin return to low to resume Linux user space) mutex_unlock(&phydev->lock); return 0; } static struct phy_driver netbr_drivers[] = { { .phy_id = TC_ID, .phy_id_mask = ID_MASK, .probe = netbr_config_probe, .config_init = netbr_config_init, .name = "TI DP83TC811 - TC", .features = PHY_BASIC_FEATURES, .config_aneg = netbr_config_aneg, .read_status = netbr_tc_read_status, .resume = resume,//.resume = phy_resume, //.resume = genphy_resume, .suspend = suspend,//.suspend = phy_suspend, //.suspend = genphy_suspend, .set_wol = set_wol, .get_wol = get_wol, .driver = { .owner = THIS_MODULE }, }, { .phy_id = A0_ID, .phy_id_mask = ID_MASK, .probe = netbr_config_probe, .config_init = netbr_config_init, .name = "TI DP83811 - A0", .features = PHY_BASIC_FEATURES, .config_aneg = netbr_config_aneg, .read_status = netbr_a0_read_status, .resume = resume,//.resume = phy_resume, //.resume = genphy_resume, .suspend = suspend,//.suspend = phy_suspend, //.suspend = genphy_suspend, .set_wol = set_wol, .get_wol = get_wol, .driver = { .owner = THIS_MODULE }, } }; static int __init netbr_init(void) { debug("called netbr_init for DP83TC811\n"); debug("Compile date and time: %s %s\n", __DATE__, __TIME__) return phy_drivers_register(netbr_drivers, ARRAY_SIZE(netbr_drivers)); } static void __exit netbr_exit(void) { phy_drivers_unregister(netbr_drivers, ARRAY_SIZE(netbr_drivers)); } module_init(netbr_init); module_exit(netbr_exit); static struct mdio_device_id __maybe_unused netbr_tbl[] = { { TC_ID, ID_MASK }, // TC silicon { A0_ID, ID_MASK }, // A0 silicon { } }; MODULE_DEVICE_TABLE(mdio, netbr_tbl);