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.

Linux/DP83TC811SEVM: Linux driver

Part Number: DP83TC811SEVM

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

    dp83tc811.c
    /*
     * 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", &reg, &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", &registerRW_address, &registerRW_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);
    

  • Hi,

    thanks for the prompt answer.
    Yes the EVM will be wired to a uC.
    Which kernel version is this driver for?

    Thanks,
    Viktor