blob: 4b99f2c4c19824cacd84e6c33a0efbf2dbb5637b [file] [log] [blame]
/*
* Copyright (c) 2012, 2014, 2017, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "sw.h"
#include "ssdk_init.h"
#include "fal_init.h"
#include "fal_misc.h"
#include "fal_mib.h"
#include "fal_port_ctrl.h"
#include "fal_portvlan.h"
#include "fal_fdb.h"
#include "fal_stp.h"
#include "fal_igmp.h"
#include "fal_qos.h"
#include "fal_acl.h"
#include "hsl.h"
#include "hsl_dev.h"
#include "ssdk_init.h"
#include "ssdk_dts.h"
#include "hsl_phy.h"
#include <linux/kconfig.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include <linux/types.h>
//#include <asm/mach-types.h>
#include <generated/autoconf.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0))
#include <linux/ar8216_platform.h>
#endif
#include <linux/delay.h>
#include <linux/phy.h>
#include <linux/netdevice.h>
#include "ssdk_plat.h"
#include "ref_vlan.h"
#ifdef BOARD_AR71XX
#include "ssdk_uci.h"
#endif
extern ssdk_chip_type SSDK_CURRENT_CHIP_TYPE;
#if !defined(IN_VLAN_MINI)
sw_error_t
qca_lan_wan_cfg_set(a_uint32_t dev_id, qca_lan_wan_cfg_t *lan_wan_cfg)
{
a_uint32_t i = 0, lan_bmp = 0, wan_bmp = 0;
sw_error_t rv = SW_OK;
fal_vlan_t vlan_entry;
SW_RTN_ON_NULL(lan_wan_cfg);
switch (SSDK_CURRENT_CHIP_TYPE) {
case CHIP_ISIS:
case CHIP_ISISC:
case CHIP_DESS:
break;
default:
return SW_NOT_SUPPORTED;
}
fal_vlan_flush(dev_id);
aos_mem_set(&vlan_entry, 0, sizeof(vlan_entry));
if (lan_wan_cfg->lan_only_mode) {
#if defined(IN_PORTVLAN)
while (i < sizeof(lan_wan_cfg->v_port_info)/sizeof(lan_wan_cfg->v_port_info[0])) {
if (lan_wan_cfg->v_port_info[i].valid) {
/* use the portbased vlan table for forwarding */
rv = fal_port_1qmode_set(dev_id,
lan_wan_cfg->v_port_info[i].port_id,
FAL_1Q_DISABLE);
SW_RTN_ON_ERROR(rv);
rv = fal_port_egvlanmode_set(dev_id,
lan_wan_cfg->v_port_info[i].port_id,
FAL_EG_UNMODIFIED);
SW_RTN_ON_ERROR(rv);
rv = fal_port_default_cvid_set(dev_id,
lan_wan_cfg->v_port_info[i].port_id,
0);
SW_RTN_ON_ERROR(rv);
lan_bmp |= (0x1 << lan_wan_cfg->v_port_info[i].port_id);
}
i++;
}
/* CPU port 0 configurations */
rv = fal_port_1qmode_set(dev_id, SSDK_PORT_CPU, FAL_1Q_DISABLE);
SW_RTN_ON_ERROR(rv);
rv = fal_port_egvlanmode_set(dev_id, SSDK_PORT_CPU, FAL_EG_UNMODIFIED);
SW_RTN_ON_ERROR(rv);
rv = fal_port_default_cvid_set(dev_id, SSDK_PORT_CPU, 0);
SW_RTN_ON_ERROR(rv);
rv = fal_portvlan_member_update(dev_id, SSDK_PORT_CPU, lan_bmp);
SW_RTN_ON_ERROR(rv);
#endif
} else {
while (i < sizeof(lan_wan_cfg->v_port_info)/sizeof(lan_wan_cfg->v_port_info[0])) {
if (lan_wan_cfg->v_port_info[i].valid) {
rv = fal_vlan_find(dev_id,
lan_wan_cfg->v_port_info[i].vid, &vlan_entry);
/* create vlan entry if the vlan entry does not exist */
if (rv == SW_NOT_FOUND) {
rv = fal_vlan_create(dev_id,
lan_wan_cfg->v_port_info[i].vid);
SW_RTN_ON_ERROR(rv);
#if defined(IN_PORTVLAN)
rv = fal_port_1qmode_set(dev_id,
SSDK_PORT_CPU, FAL_1Q_SECURE);
SW_RTN_ON_ERROR(rv);
#endif
rv = fal_vlan_member_add(dev_id,
lan_wan_cfg->v_port_info[i].vid,
SSDK_PORT_CPU, FAL_EG_TAGGED);
SW_RTN_ON_ERROR(rv);
}
rv = fal_vlan_member_add(dev_id,
lan_wan_cfg->v_port_info[i].vid,
lan_wan_cfg->v_port_info[i].port_id,
FAL_EG_UNTAGGED);
SW_RTN_ON_ERROR(rv);
#if defined(IN_PORTVLAN)
rv = fal_port_1qmode_set(dev_id,
lan_wan_cfg->v_port_info[i].port_id,
FAL_1Q_SECURE);
SW_RTN_ON_ERROR(rv);
rv = fal_port_default_cvid_set(dev_id,
lan_wan_cfg->v_port_info[i].port_id,
lan_wan_cfg->v_port_info[i].vid);
SW_RTN_ON_ERROR(rv);
#endif
if (lan_wan_cfg->v_port_info[i].is_wan_port) {
wan_bmp |= (0x1 << lan_wan_cfg->v_port_info[i].port_id);
} else {
lan_bmp |= (0x1 << lan_wan_cfg->v_port_info[i].port_id);
}
}
i++;
}
}
ssdk_lan_bmp_set(dev_id, lan_bmp);
ssdk_wan_bmp_set(dev_id, wan_bmp);
qca_ssdk_port_bmp_set(dev_id, lan_bmp|wan_bmp);
#if defined(DESS) && defined(IN_TRUNK)
if(SSDK_CURRENT_CHIP_TYPE == CHIP_DESS) {
ssdk_dess_trunk_init(dev_id, wan_bmp);
}
#endif
#if defined(IN_PORTVLAN)
ssdk_portvlan_init(dev_id);
#endif
return rv;
}
sw_error_t
qca_lan_wan_cfg_get(a_uint32_t dev_id, qca_lan_wan_cfg_t *lan_wan_cfg)
{
sw_error_t rv = SW_OK;
fal_vlan_t vlan_entry;
fal_pbmp_t member_pmap, lan_bmp, wan_bmp;
a_uint32_t port_id, entry_id, vlan_id;
SW_RTN_ON_NULL(lan_wan_cfg);
switch (SSDK_CURRENT_CHIP_TYPE) {
case CHIP_ISIS:
case CHIP_ISISC:
case CHIP_DESS:
break;
default:
return SW_NOT_SUPPORTED;
}
lan_bmp = ssdk_lan_bmp_get(dev_id);
wan_bmp = ssdk_wan_bmp_get(dev_id);
member_pmap = lan_bmp | wan_bmp;
vlan_id = FAL_NEXT_ENTRY_FIRST_ID;
entry_id = 0;
while (1) {
aos_mem_set(&vlan_entry, 0, sizeof(vlan_entry));
rv = fal_vlan_next(dev_id, vlan_id, &vlan_entry);
if (rv != SW_OK) {
break;
}
/*
* the special port id should be existed only in one vlan entry
* starting from port 1.
*/
port_id = 1;
while (vlan_entry.mem_ports >> port_id) {
if (((vlan_entry.mem_ports >> port_id) & 1) &&
SW_IS_PBMP_MEMBER(member_pmap, port_id)) {
lan_wan_cfg->v_port_info[entry_id].port_id = port_id;
lan_wan_cfg->v_port_info[entry_id].vid = vlan_entry.vid;
lan_wan_cfg->v_port_info[entry_id].valid = A_TRUE;
lan_wan_cfg->v_port_info[entry_id].is_wan_port =
SW_IS_PBMP_MEMBER(wan_bmp, port_id) ? A_TRUE : A_FALSE;
entry_id++;
}
port_id++;
}
vlan_id = vlan_entry.vid;
}
/*
* no vlan entry exists, the portbased vlan used.
*/
#if defined(IN_PORTVLAN)
if (entry_id == 0) {
lan_wan_cfg->lan_only_mode = A_TRUE;
port_id = 1;
while (lan_bmp >> port_id) {
if ((lan_bmp >> port_id) & 1) {
lan_wan_cfg->v_port_info[entry_id].port_id = port_id;
lan_wan_cfg->v_port_info[entry_id].vid = 0;
lan_wan_cfg->v_port_info[entry_id].is_wan_port = A_FALSE;
member_pmap = 0;
#if !defined(IN_PORTVLAN_MINI)
fal_portvlan_member_get(dev_id, port_id, &member_pmap);
#endif
lan_wan_cfg->v_port_info[entry_id].valid =
member_pmap ? A_TRUE : A_FALSE;
entry_id++;
}
port_id++;
}
}
#endif
return SW_OK;
}
#endif
int
qca_ar8327_sw_enable_vlan0(a_uint32_t dev_id, a_bool_t enable, a_uint8_t portmap)
{
fal_vlan_t entry;
fal_acl_rule_t rule;
int i = 0;
memset(&entry, 0, sizeof(fal_vlan_t));
memset(&rule, 0, sizeof(fal_acl_rule_t));
for (i = 0; i < AR8327_NUM_PORTS; i ++) {
fal_port_tls_set(dev_id, i, A_FALSE);
fal_port_vlan_propagation_set(dev_id, i, FAL_VLAN_PROPAGATION_REPLACE);
}
if (enable) {
entry.fid = 0;
entry.mem_ports = portmap;
entry.unmodify_ports = portmap;
entry.vid = 0;
fal_vlan_entry_append(dev_id, &entry);
for (i = 0; i < AR8327_NUM_PORTS; i++) {
if (portmap & (0x1 << i)) {
fal_port_egvlanmode_set(dev_id, i, FAL_EG_UNTOUCHED);
fal_port_tls_set(dev_id, i, A_TRUE);
fal_port_vlan_propagation_set(dev_id, i, FAL_VLAN_PROPAGATION_DISABLE);
fal_acl_port_udf_profile_set(dev_id, i, FAL_ACL_UDF_TYPE_L2, 12, 4);
}
}
fal_acl_list_creat(dev_id, 0, 0);
rule.rule_type = FAL_ACL_RULE_UDF;
rule.udf_len = 4;
rule.udf_val[0] = 0x81;
rule.udf_val[1] = 0;
rule.udf_val[2] = 0;
rule.udf_val[3] = 0;
rule.udf_mask[0] = 0xff;
rule.udf_mask[1] = 0xff;
rule.udf_mask[2] = 0xf;
rule.udf_mask[3] = 0xff;
FAL_FIELD_FLG_SET(rule.field_flg, FAL_ACL_FIELD_UDF);
FAL_ACTION_FLG_SET(rule.action_flg, FAL_ACL_ACTION_REMARK_LOOKUP_VID);
fal_acl_rule_add(dev_id, 0, 0, 1, &rule);
for (i = 0; i < AR8327_NUM_PORTS; i ++) {
fal_acl_list_unbind(dev_id, 0, 0, 0, i);
if (portmap & (0x1 << i)) {
fal_acl_list_bind(dev_id, 0, 0, 0, i);
}
}
fal_acl_status_set(dev_id, A_TRUE);
}
else {
fal_acl_rule_delete(dev_id, 0, 0, 1);
}
return 0;
}
#if defined(IN_SWCONFIG)
int
qca_ar8327_sw_set_vlan(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct qca_phy_priv *priv = qca_phy_priv_get(dev);
priv->vlan = !!val->value.i;
#ifdef BOARD_AR71XX
if(SSDK_CURRENT_CHIP_TYPE == CHIP_SHIVA) {
ssdk_uci_sw_set_vlan(attr, val);
}
#endif
return 0;
}
int
qca_ar8327_sw_get_vlan(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct qca_phy_priv *priv = qca_phy_priv_get(dev);
val->value.i = priv->vlan;
return 0;
}
int
qca_ar8327_sw_set_vid(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct qca_phy_priv *priv = qca_phy_priv_get(dev);
priv->vlan_id[val->port_vlan] = val->value.i;
#ifdef BOARD_AR71XX
if(SSDK_CURRENT_CHIP_TYPE == CHIP_SHIVA) {
ssdk_uci_sw_set_vid(attr, val);
}
#endif
return 0;
}
int
qca_ar8327_sw_get_vid(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct qca_phy_priv *priv = qca_phy_priv_get(dev);
val->value.i = priv->vlan_id[val->port_vlan];
return 0;
}
int
qca_ar8327_sw_get_pvid(struct switch_dev *dev, int port, int *vlan)
{
struct qca_phy_priv *priv = qca_phy_priv_get(dev);
*vlan = priv->pvid[port];
return 0;
}
int
qca_ar8327_sw_set_pvid(struct switch_dev *dev, int port, int vlan)
{
struct qca_phy_priv *priv = qca_phy_priv_get(dev);
/* make sure no invalid PVIDs get set */
if (vlan >= dev->vlans)
return -1;
priv->pvid[port] = vlan;
#ifdef BOARD_AR71XX
if(SSDK_CURRENT_CHIP_TYPE == CHIP_SHIVA) {
ssdk_uci_sw_set_pvid(port, vlan);
}
#endif
return 0;
}
int
qca_ar8327_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
{
struct qca_phy_priv *priv = qca_phy_priv_get(dev);
a_uint8_t ports = priv->vlan_table[val->port_vlan];
int i;
val->len = 0;
for (i = 0; i < dev->ports; i++) {
struct switch_port *p;
if (!(ports & (1 << i)))
continue;
p = &val->value.ports[val->len++];
p->id = i;
if (priv->vlan_tagged[val->port_vlan] & (1 << i))
p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
else
p->flags = 0;
/*Handle for VLAN 0*/
if (val->port_vlan == 0)
p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
}
return 0;
}
int
qca_ar8327_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
{
struct qca_phy_priv *priv = qca_phy_priv_get(dev);
a_uint8_t *vt = &priv->vlan_table[val->port_vlan];
int i;
#ifdef BOARD_AR71XX
if(SSDK_CURRENT_CHIP_TYPE == CHIP_SHIVA) {
ssdk_uci_sw_set_ports(val);
}
#endif
/*Handle for VLAN 0*/
if (val->port_vlan == 0) {
priv->vlan_table[0] = 0;
for (i = 0; i < val->len; i++) {
struct switch_port *p = &val->value.ports[i];
priv->vlan_table[0] |= (1 << p->id);
}
return 0;
}
if (priv->vlan_id[val->port_vlan] == 0)
priv->vlan_id[val->port_vlan] = val->port_vlan;
*vt = 0;
for (i = 0; i < val->len; i++) {
struct switch_port *p = &val->value.ports[i];
if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
priv->vlan_tagged[val->port_vlan] |= (1 << p->id);
} else {
priv->vlan_tagged[val->port_vlan] &= ~(1 << p->id);
priv->pvid[p->id] = val->port_vlan;
}
*vt |= 1 << p->id;
}
return 0;
}
int
qca_ar8327_sw_hw_apply(struct switch_dev *dev)
{
struct qca_phy_priv *priv = qca_phy_priv_get(dev);
fal_pbmp_t *portmask = NULL;
int i, j;
if (priv->version == QCA_VER_HPPE) {
return 0;
}
portmask = aos_mem_alloc(sizeof(fal_pbmp_t) * dev->ports);
if (portmask == NULL) {
SSDK_ERROR("%s: portmask malloc failed. \n", __func__);
return -1;
}
memset(portmask, 0, sizeof(fal_pbmp_t) * dev->ports);
mutex_lock(&priv->reg_mutex);
if (!priv->init) {
/*Handle VLAN 0 entry*/
if (priv->vlan_id[0] == 0 && priv->vlan_table[0] == 0) {
qca_ar8327_sw_enable_vlan0(priv->device_id, A_FALSE, 0);
}
/* calculate the port destination masks and load vlans
* into the vlan translation unit */
for (j = 0; j < AR8327_MAX_VLANS; j++) {
u8 vp = priv->vlan_table[j];
if (!vp) {
fal_vlan_delete(priv->device_id, priv->vlan_id[j]);
continue;
}
fal_vlan_delete(priv->device_id, priv->vlan_id[j]);
fal_vlan_create(priv->device_id, priv->vlan_id[j]);
for (i = 0; i < dev->ports; i++) {
u8 mask = (1 << i);
if (vp & mask) {
fal_vlan_member_add(priv->device_id, priv->vlan_id[j], i,
(mask & priv->vlan_tagged[j])? FAL_EG_TAGGED : FAL_EG_UNTAGGED);
portmask[i] |= vp & ~mask;
}
}
if (SSDK_CURRENT_CHIP_TYPE == CHIP_SHIVA)
fal_vlan_member_update(priv->device_id,priv->vlan_id[j],vp,0);
}
/*Hanlde VLAN 0 entry*/
if (priv->vlan_id[0] == 0 && priv->vlan_table[0]) {
qca_ar8327_sw_enable_vlan0(priv->device_id,A_TRUE, priv->vlan_table[0]);
}
} else {
/* vlan disabled: port based vlan used */
ssdk_portvlan_init(priv->device_id);
}
/* update the port destination mask registers and tag settings */
for (i = 0; i < dev->ports; i++) {
int pvid;
fal_pt_1qmode_t ingressMode;
fal_pt_1q_egmode_t egressMode;
if (priv->vlan) {
pvid = priv->vlan_id[priv->pvid[i]];
ingressMode = FAL_1Q_SECURE;
} else {
pvid = 0;
ingressMode = FAL_1Q_DISABLE;
}
egressMode = FAL_EG_UNTOUCHED;
fal_port_1qmode_set(priv->device_id, i, ingressMode);
fal_port_egvlanmode_set(priv->device_id, i, egressMode);
fal_port_default_cvid_set(priv->device_id, i, pvid);
if (!priv->init && priv->vlan) {
fal_portvlan_member_update(priv->device_id, i, portmask[i]);
}
}
aos_mem_free(portmask);
portmask = NULL;
mutex_unlock(&priv->reg_mutex);
return 0;
}
#endif