atheros.c 3.69 KB
/* 
 * drivers/net/phy/atheros.c 
 * 
 * Driver for Atheros PHYs 
 * 
 * Author: Michael Johnston <[hidden email]> 
 *	  Chunhe Lan <[hidden email]> 
 * 
 * Copyright (c) 2013 Freescale Semiconductor, Inc. 
 * 
 * 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. 
 * 
 */ 

#include <linux/phy.h> 
#include <linux/module.h> 
#include <linux/string.h> 
#include <linux/netdevice.h> 
#include <linux/etherdevice.h> 
 
#define AT803X_INTR_ENABLE                      0x12
#define AT803X_INTR_STATUS                      0x13
#define AT803X_DEBUG_ADDR                       0x1D
#define AT803X_DEBUG_DATA                       0x1E
#define AT803X_DEBUG_SYSTEM_MODE_CTRL           0x05
#define AT803X_DEBUG_RGMII_TX_CLK_DLY           BIT(8)
 
MODULE_DESCRIPTION("Atheros PHY driver"); 
MODULE_AUTHOR("Michael Johnston"); 
MODULE_LICENSE("GPL"); 
 
static int ar8035_ack_interrupt(struct phy_device *phydev) 
{ 
	int err; 
 
	err = phy_read(phydev, AT803X_INTR_STATUS); 
 
	return (err < 0) ? err : 0; 
} 
 
static int ar8035_config_intr(struct phy_device *phydev) 
{ 
        int val;
        int ret;
        u32 features;

        features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
                   SUPPORTED_FIBRE | SUPPORTED_BNC;

        val = phy_read(phydev, MII_BMSR);
        if (val < 0)
                return val;

        if (val & BMSR_ANEGCAPABLE)
                features |= SUPPORTED_Autoneg;
        if (val & BMSR_100FULL)
                features |= SUPPORTED_100baseT_Full;
        if (val & BMSR_100HALF)
                features |= SUPPORTED_100baseT_Half;
        if (val & BMSR_10FULL)
                features |= SUPPORTED_10baseT_Full;
        if (val & BMSR_10HALF)
                features |= SUPPORTED_10baseT_Half;

        if (val & BMSR_ESTATEN) {
                val = phy_read(phydev, MII_ESTATUS);
                if (val < 0)
                        return val;

                if (val & ESTATUS_1000_TFULL)
                        features |= SUPPORTED_1000baseT_Full;
                if (val & ESTATUS_1000_THALF)
                        features |= SUPPORTED_1000baseT_Half;
        }

        phydev->supported = features;
        phydev->advertising = features;

        if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
                ret = phy_write(phydev, AT803X_DEBUG_ADDR,
                                AT803X_DEBUG_SYSTEM_MODE_CTRL);
                if (ret)
                        return ret;
                ret = phy_write(phydev, AT803X_DEBUG_DATA,
                                AT803X_DEBUG_RGMII_TX_CLK_DLY);
                if (ret)
                        return ret;
        }

        return 0;
} 
 
/* AR8035 */ 
static struct phy_driver ar8035_driver = { 

	.phy_id	= 0x004dd072, 

	.name	= "AR8035 Gigabit Ethernet", 
	.phy_id_mask	= 0xffffffef,
	.features	= PHY_GBIT_FEATURES, 
	.flags	= PHY_HAS_INTERRUPT, 
	.config_aneg	= genphy_config_aneg, 
	.read_status	= genphy_read_status, 
	.ack_interrupt	= ar8035_ack_interrupt, 
	.config_intr	= ar8035_config_intr, 
	.driver	= { 
	.owner = THIS_MODULE, 
	}, 
}; 
 
static int __init atheros_init(void) 
{ 
	int ret; 
 
	ret = phy_driver_register(&ar8035_driver); 
	if (ret < 0) 
	pr_warn("AR8035: phy_driver_register is error\n"); 
 
	return ret; 
} 
 
static void __exit atheros_exit(void) 
{ 
	phy_driver_unregister(&ar8035_driver); 
} 
 
module_init(atheros_init); 
module_exit(atheros_exit); 
 
static struct mdio_device_id __maybe_unused atheros_tbl[] = { 
	{ 0x004dd072, 0x007fffff }, 
	{ } 
}; 
 
MODULE_DEVICE_TABLE(mdio, atheros_tbl);