aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/mediatek/files-5.10/drivers/net/phy/rtk/rtl8367s.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/mediatek/files-5.10/drivers/net/phy/rtk/rtl8367s.c')
-rw-r--r--target/linux/mediatek/files-5.10/drivers/net/phy/rtk/rtl8367s.c580
1 files changed, 580 insertions, 0 deletions
diff --git a/target/linux/mediatek/files-5.10/drivers/net/phy/rtk/rtl8367s.c b/target/linux/mediatek/files-5.10/drivers/net/phy/rtk/rtl8367s.c
new file mode 100644
index 0000000000..6a55631d52
--- /dev/null
+++ b/target/linux/mediatek/files-5.10/drivers/net/phy/rtk/rtl8367s.c
@@ -0,0 +1,580 @@
+/*
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/switch.h>
+
+//include from rtl8367c dir
+#include "./rtl8367c/include/rtk_switch.h"
+#include "./rtl8367c/include/vlan.h"
+#include "./rtl8367c/include/stat.h"
+#include "./rtl8367c/include/port.h"
+
+#define RTL8367C_SW_CPU_PORT 6
+
+ //RTL8367C_PHY_PORT_NUM + ext0 + ext1
+#define RTL8367C_NUM_PORTS 7
+#define RTL8367C_NUM_VIDS 4096
+
+struct rtl8367_priv {
+ struct switch_dev swdev;
+ bool global_vlan_enable;
+};
+
+struct rtl8367_mib_counter {
+ const char *name;
+};
+
+struct rtl8367_vlan_info {
+ unsigned short vid;
+ unsigned int untag;
+ unsigned int member;
+ unsigned char fid;
+};
+
+struct rtl8367_priv rtl8367_priv_data;
+
+unsigned int rtl8367c_port_id[RTL8367C_NUM_PORTS]={0,1,2,3,4,EXT_PORT1,EXT_PORT0};
+
+void (*rtl8367_switch_reset_func)(void)=NULL;
+
+static struct rtl8367_mib_counter rtl8367c_mib_counters[] = {
+ {"ifInOctets"},
+ {"dot3StatsFCSErrors"},
+ {"dot3StatsSymbolErrors"},
+ {"dot3InPauseFrames"},
+ {"dot3ControlInUnknownOpcodes"},
+ {"etherStatsFragments"},
+ {"etherStatsJabbers"},
+ {"ifInUcastPkts"},
+ {"etherStatsDropEvents"},
+ {"etherStatsOctets"},
+ {"etherStatsUndersizePkts"},
+ {"etherStatsOversizePkts"},
+ {"etherStatsPkts64Octets"},
+ {"etherStatsPkts65to127Octets"},
+ {"etherStatsPkts128to255Octets"},
+ {"etherStatsPkts256to511Octets"},
+ {"etherStatsPkts512to1023Octets"},
+ {"etherStatsPkts1024toMaxOctets"},
+ {"etherStatsMcastPkts"},
+ {"etherStatsBcastPkts"},
+ {"ifOutOctets"},
+ {"dot3StatsSingleCollisionFrames"},
+ {"dot3StatsMultipleCollisionFrames"},
+ {"dot3StatsDeferredTransmissions"},
+ {"dot3StatsLateCollisions"},
+ {"etherStatsCollisions"},
+ {"dot3StatsExcessiveCollisions"},
+ {"dot3OutPauseFrames"},
+ {"dot1dBasePortDelayExceededDiscards"},
+ {"dot1dTpPortInDiscards"},
+ {"ifOutUcastPkts"},
+ {"ifOutMulticastPkts"},
+ {"ifOutBrocastPkts"},
+ {"outOampduPkts"},
+ {"inOampduPkts"},
+ {"pktgenPkts"},
+ {"inMldChecksumError"},
+ {"inIgmpChecksumError"},
+ {"inMldSpecificQuery"},
+ {"inMldGeneralQuery"},
+ {"inIgmpSpecificQuery"},
+ {"inIgmpGeneralQuery"},
+ {"inMldLeaves"},
+ {"inIgmpLeaves"},
+ {"inIgmpJoinsSuccess"},
+ {"inIgmpJoinsFail"},
+ {"inMldJoinsSuccess"},
+ {"inMldJoinsFail"},
+ {"inReportSuppressionDrop"},
+ {"inLeaveSuppressionDrop"},
+ {"outIgmpReports"},
+ {"outIgmpLeaves"},
+ {"outIgmpGeneralQuery"},
+ {"outIgmpSpecificQuery"},
+ {"outMldReports"},
+ {"outMldLeaves"},
+ {"outMldGeneralQuery"},
+ {"outMldSpecificQuery"},
+ {"inKnownMulticastPkts"},
+ {"ifInMulticastPkts"},
+ {"ifInBroadcastPkts"},
+ {"ifOutDiscards"}
+};
+
+/*rtl8367c proprietary switch API wrapper */
+static inline unsigned int rtl8367c_sw_to_phy_port(int port)
+{
+ return rtl8367c_port_id[port];
+}
+
+static inline unsigned int rtl8367c_portmask_phy_to_sw(rtk_portmask_t phy_portmask)
+{
+ int i;
+ for (i = 0; i < RTL8367C_NUM_PORTS; i++) {
+ if(RTK_PORTMASK_IS_PORT_SET(phy_portmask,rtl8367c_sw_to_phy_port(i))) {
+ RTK_PORTMASK_PORT_CLEAR(phy_portmask,rtl8367c_sw_to_phy_port(i));
+ RTK_PORTMASK_PORT_SET(phy_portmask,i);
+ }
+
+ }
+ return (unsigned int)phy_portmask.bits[0];
+}
+
+static int rtl8367c_reset_mibs(void)
+{
+ return rtk_stat_global_reset();
+}
+
+static int rtl8367c_reset_port_mibs(int port)
+{
+
+ return rtk_stat_port_reset(rtl8367c_sw_to_phy_port(port));
+}
+
+static int rtl8367c_get_mibs_num(void)
+{
+ return ARRAY_SIZE(rtl8367c_mib_counters);
+}
+
+static const char *rtl8367c_get_mib_name(int idx)
+{
+
+ return rtl8367c_mib_counters[idx].name;
+}
+
+static int rtl8367c_get_port_mib_counter(int idx, int port, unsigned long long *counter)
+{
+ return rtk_stat_port_get(rtl8367c_sw_to_phy_port(port), idx, counter);
+}
+
+static int rtl8367c_is_vlan_valid(unsigned int vlan)
+{
+ unsigned max = RTL8367C_NUM_VIDS;
+
+ if (vlan == 0 || vlan >= max)
+ return 0;
+
+ return 1;
+}
+
+static int rtl8367c_get_vlan( unsigned short vid, struct rtl8367_vlan_info *vlan)
+{
+ rtk_vlan_cfg_t vlan_cfg;
+
+ memset(vlan, '\0', sizeof(struct rtl8367_vlan_info));
+
+ if (vid >= RTL8367C_NUM_VIDS)
+ return -EINVAL;
+
+ if(rtk_vlan_get(vid,&vlan_cfg))
+ return -EINVAL;
+
+ vlan->vid = vid;
+ vlan->member = rtl8367c_portmask_phy_to_sw(vlan_cfg.mbr);
+ vlan->untag = rtl8367c_portmask_phy_to_sw(vlan_cfg.untag);
+ vlan->fid = vlan_cfg.fid_msti;
+
+ return 0;
+}
+
+static int rtl8367c_set_vlan( unsigned short vid, u32 mbr, u32 untag, u8 fid)
+{
+ rtk_vlan_cfg_t vlan_cfg;
+ int i;
+
+ memset(&vlan_cfg, 0x00, sizeof(rtk_vlan_cfg_t));
+
+ for (i = 0; i < RTL8367C_NUM_PORTS; i++) {
+ if (mbr & (1 << i)) {
+ RTK_PORTMASK_PORT_SET(vlan_cfg.mbr, rtl8367c_sw_to_phy_port(i));
+ if(untag & (1 << i))
+ RTK_PORTMASK_PORT_SET(vlan_cfg.untag, rtl8367c_sw_to_phy_port(i));
+ }
+ }
+ vlan_cfg.fid_msti=fid;
+ vlan_cfg.ivl_en = 1;
+ return rtk_vlan_set(vid, &vlan_cfg);
+}
+
+
+static int rtl8367c_get_pvid( int port, int *pvid)
+{
+ u32 prio=0;
+
+ if (port >= RTL8367C_NUM_PORTS)
+ return -EINVAL;
+
+ return rtk_vlan_portPvid_get(rtl8367c_sw_to_phy_port(port),pvid,&prio);
+}
+
+
+static int rtl8367c_set_pvid( int port, int pvid)
+{
+ u32 prio=0;
+
+ if (port >= RTL8367C_NUM_PORTS)
+ return -EINVAL;
+
+ return rtk_vlan_portPvid_set(rtl8367c_sw_to_phy_port(port),pvid,prio);
+}
+
+static int rtl8367c_get_port_link(int port, int *link, int *speed, int *duplex)
+{
+
+ if(rtk_port_phyStatus_get(rtl8367c_sw_to_phy_port(port),(rtk_port_linkStatus_t *)link,
+ (rtk_port_speed_t *)speed,(rtk_port_duplex_t *)duplex))
+ return -EINVAL;
+
+ return 0;
+}
+
+/*common rtl8367 swconfig entry API*/
+
+static int
+rtl8367_sw_set_vlan_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rtl8367_priv *priv = container_of(dev, struct rtl8367_priv, swdev);
+
+ priv->global_vlan_enable = val->value.i ;
+
+ return 0;
+}
+
+static int
+rtl8367_sw_get_vlan_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rtl8367_priv *priv = container_of(dev, struct rtl8367_priv, swdev);
+
+ val->value.i = priv->global_vlan_enable;
+
+ return 0;
+}
+
+static int rtl8367_sw_reset_mibs(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ return rtl8367c_reset_mibs();
+}
+
+
+static int rtl8367_sw_reset_port_mibs(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ int port;
+
+ port = val->port_vlan;
+ if (port >= RTL8367C_NUM_PORTS)
+ return -EINVAL;
+
+ return rtl8367c_reset_port_mibs(port);
+}
+
+static int rtl8367_sw_get_port_mib(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ int i, len = 0;
+ unsigned long long counter = 0;
+ static char mib_buf[4096];
+
+ if (val->port_vlan >= RTL8367C_NUM_PORTS)
+ return -EINVAL;
+
+ len += snprintf(mib_buf + len, sizeof(mib_buf) - len,
+ "Port %d MIB counters\n",
+ val->port_vlan);
+
+ for (i = 0; i <rtl8367c_get_mibs_num(); ++i) {
+ len += snprintf(mib_buf + len, sizeof(mib_buf) - len,
+ "%-36s: ",rtl8367c_get_mib_name(i));
+ if (!rtl8367c_get_port_mib_counter(i, val->port_vlan,
+ &counter))
+ len += snprintf(mib_buf + len, sizeof(mib_buf) - len,
+ "%llu\n", counter);
+ else
+ len += snprintf(mib_buf + len, sizeof(mib_buf) - len,
+ "%s\n", "N/A");
+ }
+
+ val->value.s = mib_buf;
+ val->len = len;
+ return 0;
+}
+
+
+static int rtl8367_sw_get_vlan_info(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ int i;
+ u32 len = 0;
+ struct rtl8367_vlan_info vlan;
+ static char vlan_buf[256];
+ int err;
+
+ if (!rtl8367c_is_vlan_valid(val->port_vlan))
+ return -EINVAL;
+
+ memset(vlan_buf, '\0', sizeof(vlan_buf));
+
+ err = rtl8367c_get_vlan(val->port_vlan, &vlan);
+ if (err)
+ return err;
+
+ len += snprintf(vlan_buf + len, sizeof(vlan_buf) - len,
+ "VLAN %d: Ports: '", vlan.vid);
+
+ for (i = 0; i <RTL8367C_NUM_PORTS; i++) {
+ if (!(vlan.member & (1 << i)))
+ continue;
+
+ len += snprintf(vlan_buf + len, sizeof(vlan_buf) - len, "%d%s", i,
+ (vlan.untag & (1 << i)) ? "" : "t");
+ }
+
+ len += snprintf(vlan_buf + len, sizeof(vlan_buf) - len,
+ "', members=%04x, untag=%04x, fid=%u",
+ vlan.member, vlan.untag, vlan.fid);
+
+ val->value.s = vlan_buf;
+ val->len = len;
+
+ return 0;
+}
+
+
+static int rtl8367_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct switch_port *port;
+ struct rtl8367_vlan_info vlan;
+ int i;
+
+ if (!rtl8367c_is_vlan_valid(val->port_vlan))
+ return -EINVAL;
+
+ if(rtl8367c_get_vlan(val->port_vlan, &vlan))
+ return -EINVAL;
+
+ port = &val->value.ports[0];
+ val->len = 0;
+ for (i = 0; i <RTL8367C_NUM_PORTS ; i++) {
+ if (!(vlan.member & BIT(i)))
+ continue;
+
+ port->id = i;
+ port->flags = (vlan.untag & BIT(i)) ?
+ 0 : BIT(SWITCH_PORT_FLAG_TAGGED);
+ val->len++;
+ port++;
+ }
+ return 0;
+}
+
+
+static int rtl8367_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct switch_port *port;
+ u32 member = 0;
+ u32 untag = 0;
+ u8 fid=0;
+ int err;
+ int i;
+
+ if (!rtl8367c_is_vlan_valid(val->port_vlan))
+ return -EINVAL;
+
+ port = &val->value.ports[0];
+ for (i = 0; i < val->len; i++, port++) {
+ int pvid = 0;
+ member |= BIT(port->id);
+
+ if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED)))
+ untag |= BIT(port->id);
+
+ /*
+ * To ensure that we have a valid MC entry for this VLAN,
+ * initialize the port VLAN ID here.
+ */
+ err = rtl8367c_get_pvid(port->id, &pvid);
+ if (err < 0)
+ return err;
+ if (pvid == 0) {
+ err = rtl8367c_set_pvid(port->id, val->port_vlan);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ //pr_info("[%s] vid=%d , mem=%x,untag=%x,fid=%d \n",__func__,val->port_vlan,member,untag,fid);
+
+ return rtl8367c_set_vlan(val->port_vlan, member, untag, fid);
+
+}
+
+
+static int rtl8367_sw_get_port_pvid(struct switch_dev *dev, int port, int *val)
+{
+ return rtl8367c_get_pvid(port, val);
+}
+
+
+static int rtl8367_sw_set_port_pvid(struct switch_dev *dev, int port, int val)
+{
+ return rtl8367c_set_pvid(port, val);
+}
+
+
+static int rtl8367_sw_reset_switch(struct switch_dev *dev)
+{
+ if(rtl8367_switch_reset_func)
+ (*rtl8367_switch_reset_func)();
+ else
+ printk("rest switch is not supported\n");
+
+ return 0;
+}
+
+static int rtl8367_sw_get_port_link(struct switch_dev *dev, int port,
+ struct switch_port_link *link)
+{
+ int speed;
+
+ if (port >= RTL8367C_NUM_PORTS)
+ return -EINVAL;
+
+ if(rtl8367c_get_port_link(port,(int *)&link->link,(int *)&speed,(int *)&link->duplex))
+ return -EINVAL;
+
+ if (!link->link)
+ return 0;
+
+ switch (speed) {
+ case 0:
+ link->speed = SWITCH_PORT_SPEED_10;
+ break;
+ case 1:
+ link->speed = SWITCH_PORT_SPEED_100;
+ break;
+ case 2:
+ link->speed = SWITCH_PORT_SPEED_1000;
+ break;
+ default:
+ link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+
+static struct switch_attr rtl8367_globals[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_vlan",
+ .description = "Enable VLAN mode",
+ .set = rtl8367_sw_set_vlan_enable,
+ .get = rtl8367_sw_get_vlan_enable,
+ .max = 1,
+ }, {
+ .type = SWITCH_TYPE_NOVAL,
+ .name = "reset_mibs",
+ .description = "Reset all MIB counters",
+ .set = rtl8367_sw_reset_mibs,
+ }
+};
+
+static struct switch_attr rtl8367_port[] = {
+ {
+ .type = SWITCH_TYPE_NOVAL,
+ .name = "reset_mib",
+ .description = "Reset single port MIB counters",
+ .set = rtl8367_sw_reset_port_mibs,
+ }, {
+ .type = SWITCH_TYPE_STRING,
+ .name = "mib",
+ .description = "Get MIB counters for port",
+ //.max = 33,
+ .set = NULL,
+ .get = rtl8367_sw_get_port_mib,
+ },
+};
+
+static struct switch_attr rtl8367_vlan[] = {
+ {
+ .type = SWITCH_TYPE_STRING,
+ .name = "info",
+ .description = "Get vlan information",
+ .max = 1,
+ .set = NULL,
+ .get = rtl8367_sw_get_vlan_info,
+ },
+};
+
+static const struct switch_dev_ops rtl8367_sw_ops = {
+ .attr_global = {
+ .attr = rtl8367_globals,
+ .n_attr = ARRAY_SIZE(rtl8367_globals),
+ },
+ .attr_port = {
+ .attr = rtl8367_port,
+ .n_attr = ARRAY_SIZE(rtl8367_port),
+ },
+ .attr_vlan = {
+ .attr = rtl8367_vlan,
+ .n_attr = ARRAY_SIZE(rtl8367_vlan),
+ },
+
+ .get_vlan_ports = rtl8367_sw_get_vlan_ports,
+ .set_vlan_ports = rtl8367_sw_set_vlan_ports,
+ .get_port_pvid = rtl8367_sw_get_port_pvid,
+ .set_port_pvid = rtl8367_sw_set_port_pvid,
+ .reset_switch = rtl8367_sw_reset_switch,
+ .get_port_link = rtl8367_sw_get_port_link,
+};
+
+int rtl8367s_swconfig_init(void (*reset_func)(void))
+{
+ struct rtl8367_priv *priv = &rtl8367_priv_data;
+ struct switch_dev *dev=&priv->swdev;
+ int err=0;
+
+ rtl8367_switch_reset_func = reset_func ;
+
+ memset(priv, 0, sizeof(struct rtl8367_priv));
+ priv->global_vlan_enable =0;
+
+ dev->name = "RTL8367C";
+ dev->cpu_port = RTL8367C_SW_CPU_PORT;
+ dev->ports = RTL8367C_NUM_PORTS;
+ dev->vlans = RTL8367C_NUM_VIDS;
+ dev->ops = &rtl8367_sw_ops;
+ dev->alias = "RTL8367C";
+ err = register_switch(dev, NULL);
+
+ pr_info("[%s]\n",__func__);
+
+ return err;
+}