Add Broadcom / Netgear changes from RAXE 1.0.0.48
[project/bcm63xx/u-boot.git] / drivers / net / bcmbca / phy / phy_drv.c
diff --git a/drivers/net/bcmbca/phy/phy_drv.c b/drivers/net/bcmbca/phy/phy_drv.c
new file mode 100644 (file)
index 0000000..12e7b52
--- /dev/null
@@ -0,0 +1,1258 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+   Copyright (c) 2015 Broadcom Corporation
+   All Rights Reserved
+
+    
+*/
+
+/*
+ *  Created on: Dec 2015
+ *      Author: yuval.raviv@broadcom.com
+ */
+
+#include "phy_drv.h"
+#include "phy_drv_mii.h"
+#ifdef PHY_LINK_CHANGE_NOTIFY
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#endif
+#ifdef PHY_PROC_FS
+#include "proc_cmd.h"
+#endif
+#ifdef MACSEC_SUPPORT
+#include "phy_macsec_api.h"
+#include "phy_macsec_common.h"
+#endif
+
+#define MAX_PHY_DEVS 31     /* value should be < 32 */ 
+
+extern phy_drv_t phy_drv_6858_egphy;
+extern phy_drv_t phy_drv_6846_egphy;
+extern phy_drv_t phy_drv_6856_sgmii;
+extern phy_drv_t phy_drv_ext1;
+extern phy_drv_t phy_drv_ext2;
+extern phy_drv_t phy_drv_ext3;
+extern phy_drv_t phy_drv_lport_serdes;
+extern phy_drv_t phy_drv_53125_gphy;
+extern phy_drv_t phy_drv_sf2_gphy;      //TODO_DSL? create 4 different phy types for 138,148,4908, don't know if above types can be reused ...
+extern phy_drv_t phy_drv_sf2_cl45_phy;
+extern phy_drv_t phy_drv_sf2_serdes;
+extern phy_drv_t phy_drv_sf2_xgae;
+extern phy_drv_t phy_drv_crossbar;
+extern phy_drv_t phy_drv_mac2mac;
+extern phy_drv_t phy_drv_63146_egphy;
+
+phy_drv_t *phy_drivers[PHY_TYPE_MAX] = {};
+
+#if defined(CONFIG_BCM_ETH_PWRSAVE)
+int apd_enabled = 1;
+#else
+int apd_enabled = 0;
+#endif
+EXPORT_SYMBOL(apd_enabled);
+
+#if defined(CONFIG_BCM_ENERGY_EFFICIENT_ETHERNET)
+int eee_enabled = 1;
+#else
+int eee_enabled = 0;
+#endif
+EXPORT_SYMBOL(eee_enabled);
+
+int phy_driver_set(phy_drv_t *phy_drv)
+{
+    if (phy_drivers[phy_drv->phy_type])
+    {
+        printk("Failed adding phy driver %s: already set\n", phy_drv->name);
+        return -1;
+    }
+    else
+    {
+        phy_drivers[phy_drv->phy_type] = phy_drv;
+        return 0;
+    }
+}
+EXPORT_SYMBOL(phy_driver_set);
+
+int phy_driver_init(phy_type_t phy_type)
+{
+    phy_drv_t *phy_drv;
+
+    if (!(phy_drv = phy_drivers[phy_type]))
+        return 0;
+
+    return phy_drv_init(phy_drv);
+}
+EXPORT_SYMBOL(phy_driver_init);
+
+int phy_drivers_set(void)
+{
+    int ret = 0;
+
+#ifdef PHY_6858_EGPHY
+    ret |= phy_driver_set(&phy_drv_6858_egphy);
+#endif
+#ifdef PHY_6846_EGPHY
+    ret |= phy_driver_set(&phy_drv_6846_egphy);
+#endif
+#ifdef PHY_6856_SGMII
+    ret |= phy_driver_set(&phy_drv_6856_sgmii);
+#endif
+#ifdef PHY_EXT1
+    ret |= phy_driver_set(&phy_drv_ext1);
+#endif
+#ifdef PHY_EXT2
+    ret |= phy_driver_set(&phy_drv_ext2);
+#endif
+#ifdef PHY_EXT3
+    ret |= phy_driver_set(&phy_drv_ext3);
+#endif
+#ifdef PHY_LPORT_SERDES
+    ret |= phy_driver_set(&phy_drv_lport_serdes);
+#endif
+#ifdef PHY_53125
+    ret |= phy_driver_set(&phy_drv_53125_gphy);
+#endif
+#ifdef PHY_SF2
+    ret |= phy_driver_set(&phy_drv_sf2_gphy);
+#endif
+#ifdef PHY_SF2_SERDES
+    ret |= phy_driver_set(&phy_drv_sf2_serdes);
+#if defined(PHY_SERDES_10G_CAPABLE)
+    ret |= phy_driver_set(&phy_drv_sf2_xgae);
+#endif
+#endif
+#ifdef PHY_63146_EGPHY
+    ret |= phy_driver_set(&phy_drv_63146_egphy);
+#endif
+#ifdef PHY_CROSSBAR
+    ret |= phy_driver_set(&phy_drv_crossbar);
+#endif
+#ifdef PHY_M2M
+    ret |= phy_driver_set(&phy_drv_mac2mac);
+#endif
+    return ret;
+}
+EXPORT_SYMBOL(phy_drivers_set);
+
+int phy_drivers_init(void)
+{
+    int ret = 0;
+
+#ifndef CONFIG_BRCM_QEMU
+    ret |= phy_driver_init(PHY_TYPE_6858_EGPHY);
+    ret |= phy_driver_init(PHY_TYPE_6846_EGPHY);
+    ret |= phy_driver_init(PHY_TYPE_6856_SGMII);
+    ret |= phy_driver_init(PHY_TYPE_EXT1);
+    ret |= phy_driver_init(PHY_TYPE_EXT2);
+    ret |= phy_driver_init(PHY_TYPE_EXT3);
+    ret |= phy_driver_init(PHY_TYPE_LPORT_SERDES);
+    ret |= phy_driver_init(PHY_TYPE_53125);
+    ret |= phy_driver_init(PHY_TYPE_CROSSBAR);
+    ret |= phy_driver_init(PHY_TYPE_SF2_GPHY);
+    ret |= phy_driver_init(PHY_TYPE_MAC2MAC);
+    ret |= phy_driver_init(PHY_TYPE_63146_EGPHY);
+#endif    
+    return ret;
+}
+EXPORT_SYMBOL(phy_drivers_init);
+
+void phy_dev_prog(phy_dev_t *phy_dev, prog_entry_t *prog_entry)
+{
+    while (prog_entry->desc)
+    {
+#ifdef DEBUG
+        printk("Configuring addr %d: ", phy_dev->addr);
+        printk("%s - Writing 0x%04x to register 0x%02x\n", prog_entry->desc, prog_entry->val, prog_entry->reg);
+#endif
+
+        if (phy_dev_write(phy_dev, prog_entry->reg, prog_entry->val) != 0)
+            break;
+
+        prog_entry++;
+    }
+}
+
+char *phy_dev_mii_type_to_str(phy_mii_type_t mii_type)
+{
+       switch (mii_type)
+    {
+       case PHY_MII_TYPE_MII:
+        return "MII";
+       case PHY_MII_TYPE_TMII:
+        return "TMII";
+       case PHY_MII_TYPE_GMII:
+        return "GMII";
+       case PHY_MII_TYPE_RGMII:
+        return "RGMII";
+       case PHY_MII_TYPE_SGMII:
+        return "SGMII";
+       case PHY_MII_TYPE_HSGMII:
+        return "HSGMII";
+       case PHY_MII_TYPE_XFI:
+        return "XFI";
+       case PHY_MII_TYPE_SERDES:
+        return "SERDES";
+       default:
+               return "Unknown";
+       }
+}
+
+char *phy_dev_speed_to_str(phy_speed_t speed)
+{
+       switch (speed)
+    {
+       case PHY_SPEED_10:
+               return "10 Mbps";
+       case PHY_SPEED_100:
+               return "100 Mbps";
+       case PHY_SPEED_1000:
+               return "1000 Mbps";
+       case PHY_SPEED_2500:
+               return "2.5 Gbps";
+       case PHY_SPEED_5000:
+               return "5 Gbps";
+       case PHY_SPEED_10000:
+               return "10 Gbps";
+       default:
+               return "Unknown";
+       }
+}
+
+char *phy_dev_duplex_to_str(phy_duplex_t duplex)
+{
+       switch (duplex)
+    {
+       case PHY_DUPLEX_HALF:
+               return "Half";
+       case PHY_DUPLEX_FULL:
+               return "Full";
+       default:
+               return "Unknown";
+    }
+}
+
+char *phy_dev_flowctrl_to_str(int pause_rx, int pause_tx)
+{
+    if (pause_rx && pause_tx)
+        return "TXRX";
+    else if (pause_rx)
+        return "RX";
+    else if (pause_tx)
+        return "TX";
+    else
+        return "";
+}
+
+void phy_dev_print_status(phy_dev_t *phy_dev)
+{
+    if (phy_dev->link)
+    {
+        printk("%s:%s:0x%x - Link Up %s %s duplex\n", 
+            phy_dev->phy_drv->name, phy_dev_mii_type_to_str(phy_dev->mii_type), phy_dev->addr,
+            phy_dev_speed_to_str(phy_dev->speed),
+            phy_dev_duplex_to_str(phy_dev->duplex));
+    }
+    else
+    {
+        printk("%s:%s:0x%x - Link Down\n", phy_dev->phy_drv->name, phy_dev_mii_type_to_str(phy_dev->mii_type), phy_dev->addr);
+       }
+}
+EXPORT_SYMBOL(phy_dev_print_status);
+
+static phy_dev_t phy_devices[MAX_PHY_DEVS] = {};
+
+phy_dev_t *phy_dev_get(phy_type_t phy_type, uint32_t addr)
+{
+    uint32_t i;
+    phy_dev_t *phy_dev = NULL;
+
+    for (i = 0; i < MAX_PHY_DEVS; i++)
+    {
+        phy_dev_t *phy = &phy_devices[i];
+        
+        if (phy->phy_drv == NULL)
+            continue;
+
+        if ((phy_type != PHY_TYPE_UNKNOWN) &&       // for ioctl use
+            (phy->phy_drv->phy_type != phy_type))
+            continue;
+
+        if (phy->addr != addr)
+            continue;
+
+        phy_dev = phy;
+        break;
+    }
+
+    return phy_dev;
+}
+EXPORT_SYMBOL(phy_dev_get);
+
+phy_dev_t *phy_dev_get_by_i2c(int bus_num)
+{
+    uint32_t i;
+    phy_dev_t *phy_dev = NULL;
+
+    for (i = 0; i < MAX_PHY_DEVS; i++)
+    {
+        phy_dev_t *phy = &phy_devices[i];
+        
+        if (phy->phy_drv == NULL)
+            continue;
+
+        if (phy->phy_drv->phy_type != PHY_TYPE_I2C_PHY)
+            continue;
+
+        if ((phy->addr != bus_num) || (phy->flag & PHY_FLAG_NOT_PRESENTED))
+            continue;
+
+        phy_dev = phy;
+        break;
+    }
+
+    return phy_dev;
+}
+EXPORT_SYMBOL(phy_dev_get_by_i2c);
+
+phy_dev_t *phy_dev_add(phy_type_t phy_type, uint32_t addr, void *priv)
+{
+    uint32_t i;
+    phy_drv_t *phy_drv;
+    phy_dev_t *phy_dev;
+
+    if (!(phy_drv = phy_drivers[phy_type]))
+    {
+        printk("Failed to find phy driver: phy_type=%d\n", phy_type);
+        return NULL;
+    }
+
+    if ((phy_dev = phy_dev_get(phy_type, addr)))
+    {
+        printk("Phy device already exists: %s:%d\n", phy_drv->name, addr);
+        return NULL;
+    }
+
+    for (i = 0; i < MAX_PHY_DEVS && phy_devices[i].phy_drv != NULL; i++);
+
+    if (i ==  MAX_PHY_DEVS)
+    {
+        printk("Failed adding phy device: %s:%d\n", phy_drv->name, addr);
+        return NULL;
+    }
+
+    phy_dev = &phy_devices[i];
+
+    phy_dev->phy_drv = phy_drv;
+    phy_dev->addr = addr;
+    phy_dev->priv = priv;
+
+    return phy_dev;
+}
+EXPORT_SYMBOL(phy_dev_add);
+
+int phy_dev_del(phy_dev_t *phy_dev)
+{
+    phy_drv_dev_del(phy_dev);
+#ifdef PHY_LINK_CHANGE_NOTIFY
+    phy_dev_link_change_unregister(phy_dev);
+#endif
+    memset(phy_dev, 0, sizeof(phy_dev_t));
+
+    return 0;
+}
+EXPORT_SYMBOL(phy_dev_del);
+
+phy_dev_t *phy_drv_find_device(dt_handle_t dt_handle)
+{
+    int i;
+
+    for (i = 0; i < MAX_PHY_DEVS; i++)
+    {
+        if (phy_devices[i].phy_drv == NULL)
+            continue;
+
+        if (dt_is_equal(dt_handle, phy_devices[i].dt_handle))
+            return &phy_devices[i];
+    }
+
+    return NULL;
+}
+EXPORT_SYMBOL(phy_drv_find_device);
+
+/* For internal use only by proc interface */
+int phy_devices_internal_index(phy_dev_t *phy_dev)
+{
+    uint32_t i;
+
+    for (i = 0; i < MAX_PHY_DEVS; i++)
+    {
+        if (&phy_devices[i] == phy_dev && phy_dev->phy_drv)
+            return i;
+    }
+
+    return -1;
+}
+EXPORT_SYMBOL(phy_devices_internal_index);
+
+#ifdef PHY_LINK_CHANGE_NOTIFY
+void phy_dev_link_change_notify(phy_dev_t *phy_dev)
+{
+    int old_link = phy_dev->link;
+
+    phy_dev_read_status(phy_dev);
+
+    if (phy_dev->link != old_link && phy_dev->link_change_cb)
+        phy_dev->link_change_cb(phy_dev->link_change_ctx);
+}
+EXPORT_SYMBOL(phy_dev_link_change_notify);
+
+static void phy_devices_link_change_notify(void)
+{
+    uint32_t i;
+
+    for (i = 0; i < MAX_PHY_DEVS; i++)
+    {
+        if (phy_devices[i].phy_drv == NULL)
+            continue;
+
+        if (phy_devices[i].phy_drv->link_change_register)
+            continue;
+
+        phy_dev_link_change_notify(&phy_devices[i]);
+    }
+}
+
+static struct timer_list phy_link_timer;
+static int phy_link_timer_refs = 0;
+
+static void phy_link_work_cb(struct work_struct *work)
+{
+    phy_devices_link_change_notify();
+    mod_timer(&phy_link_timer, jiffies + msecs_to_jiffies(1000));
+}
+
+DECLARE_WORK(_work, phy_link_work_cb);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+static void phy_link_timer_cb(unsigned long data)
+#else
+static void phy_link_timer_cb(struct timer_list *tl)
+#endif
+{
+    schedule_work(&_work);
+}
+
+static void phy_link_timer_start(void)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+    init_timer(&phy_link_timer);
+    phy_link_timer.function = phy_link_timer_cb;
+#else
+    timer_setup(&phy_link_timer, phy_link_timer_cb, 0);
+#endif
+    phy_link_timer.expires = jiffies + msecs_to_jiffies(1000);
+    add_timer(&phy_link_timer);
+}
+
+static void phy_link_timer_stop(void)
+{
+    del_timer(&phy_link_timer);
+}
+
+void phy_dev_link_change_register(phy_dev_t *phy_dev, link_change_cb_t cb, void *ctx)
+{
+    if (phy_dev->link_change_cb != NULL)
+        return;
+
+    phy_dev->link_change_cb = cb;
+    phy_dev->link_change_ctx = ctx;
+
+    if (phy_dev->phy_drv->link_change_register)
+    {
+        phy_dev->phy_drv->link_change_register(phy_dev);
+        return;
+    }
+
+    if (phy_link_timer_refs == 0)
+        phy_link_timer_start();
+
+    phy_link_timer_refs++;
+}
+EXPORT_SYMBOL(phy_dev_link_change_register);
+
+void phy_dev_link_change_unregister(phy_dev_t *phy_dev)
+{
+    if (phy_dev->link_change_cb == NULL)
+        return;
+
+    phy_dev->link_change_cb = NULL;
+    phy_dev->link_change_ctx = NULL;
+
+    if (phy_dev->phy_drv->link_change_unregister)
+    {
+        phy_dev->phy_drv->link_change_unregister(phy_dev);
+        return;
+    }
+
+    phy_link_timer_refs--;
+
+    if (phy_link_timer_refs == 0)
+        phy_link_timer_stop();
+}
+EXPORT_SYMBOL(phy_dev_link_change_unregister);
+
+typedef struct
+{
+    struct work_struct base_work;
+    phy_dev_t *phy_dev;
+    phy_dev_work_func_t func;
+} phy_dev_work_t;
+
+static void phy_dev_work_cb(struct work_struct *work)
+{
+    phy_dev_work_t *phy_dev_work = container_of(work, phy_dev_work_t, base_work);
+    phy_dev_t *phy_dev = phy_dev_work->phy_dev;
+    phy_dev_work_func_t func = phy_dev_work->func;
+
+    func(phy_dev); 
+    kfree(phy_dev_work);
+}
+
+int phy_dev_queue_work(phy_dev_t *phy_dev, phy_dev_work_func_t func)
+{
+    phy_dev_work_t *phy_dev_work = kmalloc(sizeof(phy_dev_work_t), GFP_ATOMIC);
+    if (!phy_dev_work)
+    {
+        printk("phy_dev_queue_work: kmalloc failed to allocate work struct\n");
+        return -1;
+    }
+
+    INIT_WORK(&phy_dev_work->base_work, phy_dev_work_cb);
+    phy_dev_work->phy_dev = phy_dev;
+    phy_dev_work->func = func;
+
+    queue_work(system_unbound_wq, &phy_dev_work->base_work);
+
+    return 0;
+}
+#endif
+
+#ifdef PHY_PROC_FS
+
+#define PROC_DIR           "driver/phy"
+#define CMD_PROC_FILE      "cmd"
+
+static struct proc_dir_entry *proc_dir;
+static struct proc_dir_entry *cmd_proc_file;
+
+static int phy_proc_cmd_status(int argc, char *argv[])
+{
+    int id;
+    phy_dev_t *phy_dev;
+
+    if (argc < 2)
+        goto Error;
+
+    if (kstrtos32(argv[1], 10, &id) || id < 0 || id >= MAX_PHY_DEVS)
+        goto Error;
+
+    phy_dev = &phy_devices[id];
+    if (phy_dev->phy_drv == NULL)
+        goto Error;
+
+    printk("Phy %d status:\n", id);
+    printk("\tDriver: %s\n", phy_dev->phy_drv->name);
+    printk("\tMII: %s\n", phy_dev_mii_type_to_str(phy_dev->mii_type));
+    printk("\tAddress: 0x%02x\n", phy_dev->addr);
+    printk("\tLink: %s\n", phy_dev->link ? "Up" : "Down");
+    printk("\tSpeed: %s\n", phy_dev_speed_to_str(phy_dev->speed));
+    printk("\tDuplex: %s\n", phy_dev_duplex_to_str(phy_dev->duplex));
+    printk("\tFlowctrl: %s\n", phy_dev_flowctrl_to_str(phy_dev->pause_rx, phy_dev->pause_tx));
+
+    return 0;
+
+Error:
+    printk("Usage: status <id>\n");
+    return 0;
+}
+
+static int phy_proc_cmd_init(int argc, char *argv[])
+{
+    int id;
+    phy_dev_t *phy_dev;
+
+    if (argc < 2)
+        goto Error;
+
+    if (kstrtos32(argv[1], 10, &id) || id < 0 || id >= MAX_PHY_DEVS)
+        goto Error;
+
+    phy_dev = &phy_devices[id];
+    if (phy_dev->phy_drv == NULL)
+        goto Error;
+
+    if (phy_dev_init(phy_dev))
+        goto Error;
+
+    return 0;
+
+Error:
+    printk("Usage: init <id>\n");
+    return 0;
+}
+
+static int phy_proc_cmd_list(int argc, char *argv[])
+{
+    int id;
+    uint32_t phyid;
+    phy_dev_t *phy_dev;
+
+    printk("|====================================================================================|\n");
+    printk("|  Id |  State  |   Phy   |   Bus  | Addr |   Speed   | Duplex | Flowctl |   PHYID   |\n");
+    printk("|====================================================================================|\n"); 
+
+    for (id = 0; id < MAX_PHY_DEVS; id++)
+    {
+        phy_dev = &phy_devices[id];
+        if (phy_dev->phy_drv == NULL)
+            continue;
+
+        printk("| %2d  ", id);
+
+        phy_dev_phyid_get(phy_dev, &phyid);
+
+        pr_cont("| %s  ", phy_dev->link ? " Up   " : " Down ");
+        pr_cont("| %7s ", phy_dev->phy_drv->name);
+        pr_cont("| %6s ", phy_dev->mii_type == PHY_MII_TYPE_UNKNOWN ? "" : phy_dev_mii_type_to_str(phy_dev->mii_type));
+        pr_cont("| 0x%02x ", phy_dev->addr);
+        pr_cont("| %9s ", phy_dev->speed == PHY_SPEED_UNKNOWN ? "" : phy_dev_speed_to_str(phy_dev->speed));
+        pr_cont("| %5s  ", phy_dev->duplex == PHY_DUPLEX_UNKNOWN ? "" : phy_dev_duplex_to_str(phy_dev->duplex));
+        pr_cont("|  %4s   ", phy_dev_flowctrl_to_str(phy_dev->pause_rx, phy_dev->pause_tx));
+        pr_cont("| %04x:%04x ", phyid >> 16, phyid & 0xffff); 
+        pr_cont("|\n");
+    }
+    printk("|====================================================================================|\n"); 
+
+    return 0;
+}
+
+static int phy_proc_cmd_read(int argc, char *argv[])
+{
+    int id;
+    uint16_t reg, val;
+    phy_dev_t *phy_dev;
+
+    if (argc < 3)
+        goto Error;
+
+    if (kstrtos32(argv[1], 10, &id) || id < 0 || id >= MAX_PHY_DEVS)
+        goto Error;
+
+    if (kstrtou16(argv[2], 16, &reg))
+        goto Error;
+
+    phy_dev = &phy_devices[id];
+    if (phy_dev->phy_drv == NULL)
+        goto Error;
+
+    if (phy_dev_read(phy_dev, reg, &val))
+        goto Error;
+
+    printk("Read register 0x%04x=0x%04x\n", reg, val);
+
+    return 0;
+
+Error:
+    printk("Usage: read <id> <reg>\n");
+    return 0;
+}
+
+static int phy_proc_cmd_write(int argc, char *argv[])
+{
+    int id;
+    uint16_t reg, val;
+    phy_dev_t *phy_dev;
+
+    if (argc < 4)
+        goto Error;
+
+    if (kstrtos32(argv[1], 10, &id) || id < 0 || id >= MAX_PHY_DEVS)
+        goto Error;
+
+    if (kstrtou16(argv[2], 16, &reg))
+        goto Error;
+
+    if (kstrtou16(argv[3], 16, &val))
+        goto Error;
+
+    phy_dev = &phy_devices[id];
+    if (phy_dev->phy_drv == NULL)
+        goto Error;
+
+    if (phy_dev_write(phy_dev, reg, val))
+        goto Error;
+
+    printk("Write register 0x%04x=0x%04x\n", reg, val);
+
+    return 0;
+
+Error:
+    printk("Usage: write <id> <reg> <val>\n");
+    return 0;
+}
+
+static int phy_proc_cmd_read45(int argc, char *argv[])
+{
+    int id;
+    uint16_t dev, reg, val;
+    phy_dev_t *phy_dev;
+
+    if (argc < 4)
+        goto Error;
+
+    if (kstrtos32(argv[1], 10, &id) || id < 0 || id >= MAX_PHY_DEVS)
+        goto Error;
+
+    if (kstrtou16(argv[2], 16, &dev))
+        goto Error;
+
+    if (kstrtou16(argv[3], 16, &reg))
+        goto Error;
+
+    phy_dev = &phy_devices[id];
+    if (phy_dev->phy_drv == NULL)
+        goto Error;
+
+    if (phy_bus_c45_read(phy_dev, dev, reg, &val))
+        goto Error;
+
+    printk("Read45 dev=%d register 0x%04x=0x%04x\n", dev, reg, val);
+
+    return 0;
+
+Error:
+    printk("Usage: read45 <id> <dev> <reg>\n");
+    return 0;
+}
+
+static int phy_proc_cmd_write45(int argc, char *argv[])
+{
+    int id;
+    uint16_t dev, reg, val;
+    phy_dev_t *phy_dev;
+
+    if (argc < 5)
+        goto Error;
+
+    if (kstrtos32(argv[1], 10, &id) || id < 0 || id >= MAX_PHY_DEVS)
+        goto Error;
+
+    if (kstrtou16(argv[2], 16, &dev))
+        goto Error;
+
+    if (kstrtou16(argv[3], 16, &reg))
+        goto Error;
+
+    if (kstrtou16(argv[4], 16, &val))
+        goto Error;
+
+    phy_dev = &phy_devices[id];
+    if (phy_dev->phy_drv == NULL)
+        goto Error;
+
+    if (phy_bus_c45_write(phy_dev, dev, reg, val))
+        goto Error;
+
+    printk("Write45 dev=%d register 0x%04x=0x%04x\n", dev, reg, val);
+
+    return 0;
+
+Error:
+    printk("Usage: write45 <id> <dev> <reg> <val>\n");
+    return 0;
+}
+
+static int phy_proc_cmd_read_indirect(int argc, char *argv[])
+{
+    int id;
+    uint16_t temp=0;
+    uint32_t reg, val;
+    phy_dev_t *phy_dev;
+
+    if (argc < 3)
+        goto Error;
+
+    if (kstrtos32(argv[1], 10, &id) || id < 0 || id >= MAX_PHY_DEVS)
+        goto Error;
+
+    if (kstrtou32(argv[2], 16, &reg))
+        goto Error;
+
+    phy_dev = &phy_devices[id];
+    if (phy_dev->phy_drv == NULL)
+        goto Error;
+
+
+    temp = (uint16_t)(reg & 0xffff);
+    if (phy_bus_c45_write(phy_dev, 0x01, 0xa819, temp))
+        goto Error;
+
+    temp = (uint16_t)(reg >> 16);
+    if (phy_bus_c45_write(phy_dev, 0x01, 0xa81a, temp))
+        goto Error;
+
+    if (phy_bus_c45_write(phy_dev, 0x01, 0xa817, 0x000a))
+        goto Error;
+
+    if (phy_bus_c45_read(phy_dev, 0x01, 0xa81b, (uint16_t*)(&val)))
+        goto Error;
+
+    if (phy_bus_c45_read(phy_dev, 0x01, 0xa81c, &temp))
+        goto Error;
+
+    val |= (uint32_t)(temp<<16);
+
+    printk("Read32 register 0x%08x=0x%08x\n", reg, val);
+
+    return 0;
+
+Error:
+    printk("Usage: read32 <id> <reg>\n");
+    return 0;
+}
+
+static int phy_proc_cmd_write_indirect(int argc, char *argv[])
+{
+    int id;
+    uint32_t reg, val;
+    uint16_t temp=0;
+    phy_dev_t *phy_dev;
+
+    if (argc < 4)
+        goto Error;
+
+    if (kstrtos32(argv[1], 10, &id) || id < 0 || id >= MAX_PHY_DEVS)
+        goto Error;
+
+    if (kstrtou32(argv[2], 16, &reg))
+        goto Error;
+
+    if (kstrtou32(argv[3], 16, &val))
+        goto Error;
+
+    phy_dev = &phy_devices[id];
+    if (phy_dev->phy_drv == NULL)
+        goto Error;
+
+
+    temp = (uint16_t)(reg & 0xffff);
+    if (phy_bus_c45_write(phy_dev, 0x01, 0xa819, temp))
+        goto Error;
+
+    temp = (uint16_t)(reg >> 16);
+    if (phy_bus_c45_write(phy_dev, 0x01, 0xa81a, temp))
+        goto Error;
+
+    temp = (uint16_t)(val & 0xffff);
+    if (phy_bus_c45_write(phy_dev, 0x01, 0xa81b, temp))
+        goto Error;
+
+    temp = (uint16_t)(val >> 16);
+    if (phy_bus_c45_write(phy_dev, 0x01, 0xa81c, temp))
+        goto Error;
+
+    if (phy_bus_c45_write(phy_dev, 0x01, 0xa817, 0x0009))
+        goto Error;
+
+    return 0;
+
+Error:
+    printk("Usage: write32 <id> <reg> <val>\n");
+    return 0;
+}
+
+#ifdef MACSEC_SUPPORT
+static int phy_proc_macsec_enable64(int argc, char *argv[])
+{
+    int id, ret, enable;
+    phy_dev_t *phy_dev;
+    macsec_api_data macsec_data = {};
+    macsec_api_settings_t settings = {0,0,0,0,0,0,0,0,1,0,0,0,1};
+    macsec_api_sa_t sa_egress = {{0xde, 0xad, 0xbe, 0xef, 0xa5, 0x5a, 0x00, 0x01}, {0x7A, 0x30, 0xC1, 0x18}, {0xE6, 0x30, 0xE8, 0x1A, 0x48, 0xDE, 0x86, 0xA2, 0x1C, 0x66, 0xFA, 0x6D},
+                                 {0xad, 0x7a, 0x2b, 0xd0, 0x3e, 0xac, 0x83, 0x5a, 0x6f, 0x62, 0x0f, 0xdc, 0xb5, 0x06, 0xb3, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
+                                 {0x73, 0xa2, 0x3d, 0x80, 0x12, 0x1d, 0xe2, 0xd5, 0xa8, 0x50, 0x25, 0x3f, 0xcf, 0x43, 0x12, 0x0e}, 
+                                 16, 1, {{0,1,1,0,0,0,1,1,1}}, MACSEC_SA_ACTION_EGRESS};
+    macsec_api_sa_t sa_ingress = {{0xde, 0xad, 0xbe, 0xef, 0xa5, 0x5a, 0x00, 0x01}, {0x7A, 0x30, 0xC1, 0x18}, {0xE6, 0x30, 0xE8, 0x1A, 0x48, 0xDE, 0x86, 0xA2, 0x1C, 0x66, 0xFA, 0x6D},
+                                 {0xad, 0x7a, 0x2b, 0xd0, 0x3e, 0xac, 0x83, 0x5a, 0x6f, 0x62, 0x0f, 0xdc, 0xb5, 0x06, 0xb3, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
+                                 {0x73, 0xa2, 0x3d, 0x80, 0x12, 0x1d, 0xe2, 0xd5, 0xa8, 0x50, 0x25, 0x3f, 0xcf, 0x43, 0x12, 0x0e}, 
+                                 16, 1, {{0,0,MACSEC_FRAME_VALIDATE_STRICT,0,1,0,0,0}}, MACSEC_SA_ACTION_INGRESS};
+
+    if (argc < 3)
+        goto Error;
+
+    if (kstrtos32(argv[1], 10, &id) || id < 0 || id >= MAX_PHY_DEVS)
+        goto Error;
+
+    if (kstrtou32(argv[2], 10, &enable))
+        goto Error;
+
+    phy_dev = &phy_devices[id];
+    if (phy_dev->phy_drv == NULL)
+        goto Error;
+
+    if (phy_dev->phy_drv->macsec_oper == NULL)
+        goto Error;
+
+    macsec_data.op = MACSEC_OPER_INIT;
+    memcpy(&macsec_data.ext_data.secy_conf, &settings, sizeof(macsec_api_settings_t));
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    /* Egress */
+    macsec_data.direction = 0;
+
+    macsec_data.op = MACSEC_OPER_VPORT_ADD;
+    macsec_data.index1 = 0;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    macsec_data.op = MACSEC_OPER_SA_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    memcpy(&macsec_data.ext_data.sa_conf, &sa_egress, sizeof(macsec_api_sa_t));
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    macsec_data.ext_data.rule_conf.num_tags = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ENABLE;
+    macsec_data.index1 = 0;
+    macsec_data.data1 = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    /* Ingress */
+    macsec_data.direction = 1;
+
+    macsec_data.op = MACSEC_OPER_VPORT_ADD;
+    macsec_data.index1 = 0;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    macsec_data.op = MACSEC_OPER_SA_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    memcpy(&macsec_data.ext_data.sa_conf, &sa_ingress, sizeof(macsec_api_sa_t));
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    macsec_data.ext_data.rule_conf.num_tags = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ENABLE;
+    macsec_data.index1 = 0;
+    macsec_data.data1 = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+Error:
+    printk("Usage: macsecenable64 <id> <1/0>\n");
+    return 0;
+}
+
+static int phy_proc_macsec_enable(int argc, char *argv[])
+{
+    int id, ret, enable;
+    phy_dev_t *phy_dev;
+    macsec_api_data macsec_data = {};
+    macsec_api_settings_t settings = {0,0,0,0,0,0,0,0,1,0,0,0,1};
+    macsec_api_sa_t sa_egress = {{0xde, 0xad, 0xbe, 0xef, 0xa5, 0x5a, 0x00, 0x01}, {}, {},
+                                 {0xad, 0x7a, 0x2b, 0xd0, 0x3e, 0xac, 0x83, 0x5a, 0x6f, 0x62, 0x0f, 0xdc, 0xb5, 0x06, 0xb3, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
+                                 {0x73, 0xa2, 0x3d, 0x80, 0x12, 0x1d, 0xe2, 0xd5, 0xa8, 0x50, 0x25, 0x3f, 0xcf, 0x43, 0x12, 0x0e}, 
+                                 16, 0, {{0,1,1,0,0,0,1,1,0}}, MACSEC_SA_ACTION_EGRESS};
+    macsec_api_sa_t sa_ingress = {{0xde, 0xad, 0xbe, 0xef, 0xa5, 0x5a, 0x00, 0x01}, {}, {},
+                                 {0xad, 0x7a, 0x2b, 0xd0, 0x3e, 0xac, 0x83, 0x5a, 0x6f, 0x62, 0x0f, 0xdc, 0xb5, 0x06, 0xb3, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
+                                 {0x73, 0xa2, 0x3d, 0x80, 0x12, 0x1d, 0xe2, 0xd5, 0xa8, 0x50, 0x25, 0x3f, 0xcf, 0x43, 0x12, 0x0e}, 
+                                 16, 0, {{0,0,MACSEC_FRAME_VALIDATE_STRICT,0,1,0,0,1}}, MACSEC_SA_ACTION_INGRESS};
+
+    if (argc < 3)
+        goto Error;
+
+    if (kstrtos32(argv[1], 10, &id) || id < 0 || id >= MAX_PHY_DEVS)
+        goto Error;
+
+    if (kstrtou32(argv[2], 10, &enable))
+        goto Error;
+
+    phy_dev = &phy_devices[id];
+    if (phy_dev->phy_drv == NULL)
+        goto Error;
+
+    if (phy_dev->phy_drv->macsec_oper == NULL)
+        goto Error;
+
+    macsec_data.op = MACSEC_OPER_INIT;
+    memcpy(&macsec_data.ext_data.secy_conf, &settings, sizeof(macsec_api_settings_t));
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    /* Egress */
+    macsec_data.direction = 0;
+
+    macsec_data.op = MACSEC_OPER_VPORT_ADD;
+    macsec_data.index1 = 0;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    macsec_data.op = MACSEC_OPER_SA_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    memcpy(&macsec_data.ext_data.sa_conf, &sa_egress, sizeof(macsec_api_sa_t));
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    macsec_data.ext_data.rule_conf.num_tags = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ENABLE;
+    macsec_data.index1 = 0;
+    macsec_data.data1 = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    /* Ingress */
+    macsec_data.direction = 1;
+
+    macsec_data.op = MACSEC_OPER_VPORT_ADD;
+    macsec_data.index1 = 0;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    macsec_data.op = MACSEC_OPER_SA_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    memcpy(&macsec_data.ext_data.sa_conf, &sa_ingress, sizeof(macsec_api_sa_t));
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    macsec_data.ext_data.rule_conf.num_tags = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ENABLE;
+    macsec_data.index1 = 0;
+    macsec_data.data1 = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+Error:
+    printk("Usage: macsecenable <id> <1/0>\n");
+    return 0;
+}
+
+static int phy_proc_macsec_enablebp(int argc, char *argv[])
+{
+    int id, ret, enable;
+    phy_dev_t *phy_dev;
+    macsec_api_data macsec_data = {};
+    macsec_api_settings_t settings = {0,0,0,0,0,0,0,0,1,0,0,0,1};
+    macsec_api_sa_bd_t sa_bp = {};
+
+    if (argc < 3)
+        goto Error;
+
+    if (kstrtos32(argv[1], 10, &id) || id < 0 || id >= MAX_PHY_DEVS)
+        goto Error;
+
+    if (kstrtou32(argv[2], 10, &enable))
+        goto Error;
+
+    phy_dev = &phy_devices[id];
+    if (phy_dev->phy_drv == NULL)
+        goto Error;
+
+    if (phy_dev->phy_drv->macsec_oper == NULL)
+        goto Error;
+
+    macsec_data.op = MACSEC_OPER_INIT;
+    memcpy(&macsec_data.ext_data.secy_conf, &settings, sizeof(macsec_api_settings_t));
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    /* Egress */
+    macsec_data.direction = 0;
+
+    macsec_data.op = MACSEC_OPER_VPORT_ADD;
+    macsec_data.index1 = 0;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    macsec_data.op = MACSEC_OPER_SA_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    memcpy(&macsec_data.ext_data.sa_conf, &sa_bp, sizeof(macsec_api_sa_t));
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    macsec_data.ext_data.rule_conf.num_tags = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ENABLE;
+    macsec_data.index1 = 0;
+    macsec_data.data1 = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    /* Ingress */
+    macsec_data.direction = 1;
+
+    macsec_data.op = MACSEC_OPER_VPORT_ADD;
+    macsec_data.index1 = 0;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    macsec_data.op = MACSEC_OPER_SA_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    memcpy(&macsec_data.ext_data.sa_conf, &sa_bp, sizeof(macsec_api_sa_t));
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ADD;
+    macsec_data.index1 = 0;
+    macsec_data.index2 = 0;
+    macsec_data.ext_data.rule_conf.num_tags = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+    memset(&macsec_data.ext_data, 0, sizeof(macsec_api_ext_data));
+
+    macsec_data.op = MACSEC_OPER_RULE_ENABLE;
+    macsec_data.index1 = 0;
+    macsec_data.data1 = 1;
+    ret = phy_dev->phy_drv->macsec_oper(phy_dev, &macsec_data);
+
+Error:
+    printk("Usage: macsecenable <id> <1/0>\n");
+    return 0;
+}
+#endif
+
+#if defined(PHY_SF2_SERDES)
+extern void phy_drv_sfp_group_list(void);
+static int phy_proc_cmd_sfp_list(int argc, char *argv[])
+{
+    phy_drv_sfp_group_list();
+    return 0;
+}
+#endif
+
+#ifdef PHY_CROSSBAR
+extern void phy_drv_crossbar_group_list(void);
+
+static int phy_proc_cmd_crossbar_list(int argc, char *argv[])
+{
+    phy_drv_crossbar_group_list();
+
+    return 0;
+}
+#endif
+  
+static struct proc_cmd_ops command_entries[] = {
+    { .name = "status", .do_command    = phy_proc_cmd_status},
+    { .name = "init", .do_command      = phy_proc_cmd_init},
+    { .name = "list", .do_command      = phy_proc_cmd_list},
+    { .name = "read", .do_command      = phy_proc_cmd_read},
+    { .name = "write", .do_command     = phy_proc_cmd_write},
+    { .name = "read45", .do_command    = phy_proc_cmd_read45},
+    { .name = "write45", .do_command = phy_proc_cmd_write45},
+    { .name = "read32", .do_command    = phy_proc_cmd_read_indirect},
+    { .name = "write32", .do_command = phy_proc_cmd_write_indirect},
+#ifdef MACSEC_SUPPORT
+    { .name = "macsecenable", .do_command = phy_proc_macsec_enable},
+    { .name = "macsecenable64", .do_command = phy_proc_macsec_enable64},
+    { .name = "macsecenablebp", .do_command = phy_proc_macsec_enablebp},
+#endif
+#ifdef PHY_CROSSBAR
+    { .name = "crossbars", .do_command = phy_proc_cmd_crossbar_list},
+#endif
+#if defined(PHY_SF2_SERDES)
+    { .name = "sfp", .do_command       = phy_proc_cmd_sfp_list},
+#endif
+};
+
+static struct proc_cmd_table phy_command_table = {
+    .module_name = "PHY",
+    .size = ARRAY_SIZE(command_entries),
+    .ops = command_entries
+};
+
+static void phy_proc_exit(void)
+{
+    if (cmd_proc_file) 
+    {
+        remove_proc_entry(CMD_PROC_FILE, proc_dir);
+        cmd_proc_file = NULL;
+    }  
+    if (proc_dir)
+    {
+        remove_proc_entry(PROC_DIR, NULL);
+        proc_dir = NULL;
+    }  
+}
+
+int __init phy_proc_init(void)
+{
+    int status = 0;
+
+    proc_dir = proc_mkdir(PROC_DIR, NULL);
+    if (!proc_dir) 
+    {
+        pr_err("Failed to create PROC directory %s.\n",
+            PROC_DIR);
+        goto error;
+    }
+    cmd_proc_file = proc_create_cmd(CMD_PROC_FILE, proc_dir,
+        &phy_command_table);
+    if (!cmd_proc_file) 
+    {
+        pr_err("Failed to create %s\n", CMD_PROC_FILE);
+        goto error;
+    }
+
+    return status;
+
+error:
+    if (proc_dir)
+        phy_proc_exit();
+
+    status = -EIO;
+    return status;
+}
+postcore_initcall(phy_proc_init);
+#endif