--- /dev/null
+// 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, ®))
+ 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, ®))
+ 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, ®))
+ 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, ®))
+ 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, ®))
+ 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, ®))
+ 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