0
0
mirror of https://git.openwrt.org/openwrt/openwrt.git synced 2024-11-25 14:36:14 +00:00
openwrt/target/linux/mediatek/files/drivers/net/phy/rtk/rtl8367s.c
Felix Fietkau ade563ba84 mediatek: add patches for 5.15 and kernel config for mt7622
Signed-off-by: Felix Fietkau <nbd@nbd.name>
2022-03-27 12:55:53 +02:00

581 lines
13 KiB
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.
*
* 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;
}