| /* |
| * Copyright (c) 2012, 2014-2021, 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. |
| */ |
| |
| /*qca808x_start*/ |
| #include "sw.h" |
| #include "ssdk_init.h" |
| #include "fal_init.h" |
| #include "fal.h" |
| #include "hsl.h" |
| #include "hsl_dev.h" |
| #include "hsl_phy.h" |
| /*qca808x_end*/ |
| #include "ssdk_dts.h" |
| #include "ssdk_interrupt.h" |
| #include <linux/kconfig.h> |
| #include <linux/version.h> |
| #include <linux/kernel.h> |
| /*qca808x_start*/ |
| #include <linux/module.h> |
| #include <linux/phy.h> |
| #include <linux/platform_device.h> |
| #include <linux/types.h> |
| /*qca808x_end*/ |
| //#include <asm/mach-types.h> |
| #include <generated/autoconf.h> |
| #include <linux/if_arp.h> |
| #include <linux/inetdevice.h> |
| #include <linux/netdevice.h> |
| #include <linux/etherdevice.h> |
| /*qca808x_start*/ |
| #include <linux/phy.h> |
| #include <linux/mdio.h> |
| /*qca808x_end*/ |
| #include <linux/clk.h> |
| #include <linux/delay.h> |
| #include <linux/string.h> |
| #include <linux/gpio.h> |
| #include <linux/of_gpio.h> |
| |
| #if defined(IN_SWCONFIG) |
| #if defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) |
| #include <linux/switch.h> |
| #else |
| #include <net/switch.h> |
| #endif |
| #endif |
| |
| #if defined(ISIS) ||defined(ISISC) ||defined(GARUDA) |
| #include <f1_phy.h> |
| #endif |
| #if defined(ATHENA) ||defined(SHIVA) ||defined(HORUS) |
| #include <f2_phy.h> |
| #endif |
| #ifdef IN_MALIBU_PHY |
| #include <malibu_phy.h> |
| #endif |
| #if defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)) |
| /*qca808x_start*/ |
| #include <linux/of.h> |
| #include <linux/of_platform.h> |
| #include <linux/of_net.h> |
| #include <linux/of_address.h> |
| #include <linux/reset.h> |
| /*qca808x_end*/ |
| #ifdef BOARD_AR71XX |
| #ifdef CONFIG_AR8216_PHY |
| #include "drivers/net/phy/ar8327.h" |
| #endif |
| #include "drivers/net/ethernet/atheros/ag71xx/ag71xx.h" |
| #endif |
| #elif defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) |
| #include <linux/of.h> |
| #include <linux/of_platform.h> |
| #include <linux/of_net.h> |
| #include <linux/of_address.h> |
| #include <linux/reset.h> |
| #else |
| #include <linux/ar8216_platform.h> |
| #include <drivers/net/phy/ar8216.h> |
| #include <drivers/net/ethernet/atheros/ag71xx/ag71xx.h> |
| #endif |
| /*qca808x_start*/ |
| #include "ssdk_plat.h" |
| /*qca808x_end*/ |
| #include "ssdk_clk.h" |
| #include "ssdk_led.h" |
| #include "ref_vlan.h" |
| #include "ref_fdb.h" |
| #include "ref_mib.h" |
| #include "ref_port_ctrl.h" |
| #include "ref_misc.h" |
| #include "ref_uci.h" |
| #include "ref_vsi.h" |
| #include "shell.h" |
| #ifdef BOARD_AR71XX |
| #include "ssdk_uci.h" |
| #endif |
| /*qca808x_start*/ |
| #if defined(IN_PHY_I2C_MODE) |
| #include "ssdk_phy_i2c.h" |
| #endif |
| /*qca808x_end*/ |
| #ifdef IN_IP |
| #if defined (CONFIG_NF_FLOW_COOKIE) |
| #include "fal_flowcookie.h" |
| #ifdef IN_SFE |
| #include <shortcut-fe/sfe.h> |
| #endif |
| #endif |
| #endif |
| |
| #ifdef IN_RFS |
| #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) |
| #include <linux/if_vlan.h> |
| #endif |
| #include <qca-rfs/rfs_dev.h> |
| #ifdef IN_IP |
| #include "fal_rfs.h" |
| #endif |
| #endif |
| #include "adpt.h" |
| #ifdef HPPE |
| #include "ssdk_hppe.h" |
| #endif |
| #ifdef SCOMPHY |
| #include "ssdk_scomphy.h" |
| #endif |
| |
| #ifdef IN_RFS |
| struct rfs_device rfs_dev; |
| struct notifier_block ssdk_inet_notifier; |
| ssdk_rfs_intf_t rfs_intf_tbl[SSDK_RFS_INTF_MAX] = {{0}}; |
| #endif |
| |
| //#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) |
| struct notifier_block ssdk_dev_notifier; |
| //#endif |
| |
| |
| extern ssdk_chip_type SSDK_CURRENT_CHIP_TYPE; |
| extern a_uint32_t hsl_dev_wan_port_get(a_uint32_t dev_id); |
| extern void dess_rgmii_sw_mac_polling_task(struct qca_phy_priv *priv); |
| extern void qca_ar8327_sw_mac_polling_task(struct qca_phy_priv *priv); |
| extern void qca_ar8327_sw_mib_task(struct qca_phy_priv *priv); |
| |
| //#define PSGMII_DEBUG |
| |
| #define QCA_QM_WORK_DELAY 100 |
| #define QCA_QM_ITEM_NUMBER 41 |
| #define QCA_RGMII_WORK_DELAY 1000 |
| #define QCA_MAC_SW_SYNC_WORK_DELAY 1000 |
| #ifdef DESS |
| static bool qca_dess_rfs_registered = false; |
| #endif |
| /*qca808x_start*/ |
| struct qca_phy_priv **qca_phy_priv_global; |
| |
| struct qca_phy_priv* ssdk_phy_priv_data_get(a_uint32_t dev_id) |
| { |
| if (dev_id >= SW_MAX_NR_DEV || !qca_phy_priv_global) |
| return NULL; |
| |
| return qca_phy_priv_global[dev_id]; |
| } |
| /*qca808x_end*/ |
| |
| a_uint32_t hppe_port_type[6] = {0,0,0,0,0,0}; // this variable should be init by ssdk_init |
| |
| a_uint32_t |
| qca_hppe_port_mac_type_get(a_uint32_t dev_id, a_uint32_t port_id) |
| { |
| if ((port_id < 1) || (port_id > 6)) |
| return 0; |
| return hppe_port_type[port_id - 1]; |
| } |
| |
| a_uint32_t |
| qca_hppe_port_mac_type_set(a_uint32_t dev_id, a_uint32_t port_id, a_uint32_t port_type) |
| { |
| if ((port_id < 1) || (port_id > 6)) |
| return 0; |
| hppe_port_type[port_id - 1] = port_type; |
| |
| return 0; |
| } |
| |
| #ifndef BOARD_AR71XX |
| #if defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) |
| static void |
| ssdk_phy_rgmii_set(struct qca_phy_priv *priv) |
| { |
| struct device_node *np = NULL; |
| u32 rgmii_en = 0, tx_delay = 0, rx_delay = 0; |
| |
| if (priv->ess_switch_flag == A_TRUE) |
| np = priv->of_node; |
| else |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| np = priv->phy->mdio.dev.of_node; |
| #else |
| np = priv->phy->dev.of_node; |
| #endif |
| |
| if (!np) |
| return; |
| |
| if (!of_property_read_u32(np, "phy_rgmii_en", &rgmii_en)) { |
| a_uint16_t val = 0; |
| /*enable RGMII mode */ |
| qca_ar8327_phy_dbg_read(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_MODE_SEL, &val); |
| val |= AR8327_PHY_RGMII_MODE; |
| qca_ar8327_phy_dbg_write(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_MODE_SEL, val); |
| if (!of_property_read_u32(np, "txclk_delay_en", &tx_delay) |
| && tx_delay == 1) { |
| qca_ar8327_phy_dbg_read(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_SYS_CTRL, &val); |
| val |= AR8327_PHY_RGMII_TX_DELAY; |
| qca_ar8327_phy_dbg_write(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_SYS_CTRL, val); |
| } |
| if (!of_property_read_u32(np, "rxclk_delay_en", &rx_delay) |
| && rx_delay == 1) { |
| qca_ar8327_phy_dbg_read(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_TEST_CTRL, &val); |
| val |= AR8327_PHY_RGMII_RX_DELAY; |
| qca_ar8327_phy_dbg_write(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_TEST_CTRL, val); |
| } |
| } |
| } |
| #else |
| static void |
| ssdk_phy_rgmii_set(struct qca_phy_priv *priv) |
| { |
| struct ar8327_platform_data *plat_data; |
| |
| plat_data = priv->phy->dev.platform_data; |
| if (plat_data == NULL) { |
| return; |
| } |
| |
| if(plat_data->pad5_cfg) { |
| if(plat_data->pad5_cfg->mode == AR8327_PAD_PHY_RGMII) { |
| a_uint16_t val = 0; |
| /*enable RGMII mode */ |
| priv->phy_dbg_read(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_MODE_SEL, &val); |
| val |= AR8327_PHY_RGMII_MODE; |
| priv->phy_dbg_write(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_MODE_SEL, val); |
| if(plat_data->pad5_cfg->txclk_delay_en) { |
| priv->phy_dbg_read(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_SYS_CTRL, &val); |
| val |= AR8327_PHY_RGMII_TX_DELAY; |
| priv->phy_dbg_write(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_SYS_CTRL, val); |
| } |
| if(plat_data->pad5_cfg->rxclk_delay_en) { |
| priv->phy_dbg_read(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_TEST_CTRL, &val); |
| val |= AR8327_PHY_RGMII_RX_DELAY; |
| priv->phy_dbg_write(0, AR8327_PORT5_PHY_ADDR, |
| AR8327_PHY_REG_TEST_CTRL, val); |
| } |
| } |
| } |
| } |
| #endif |
| #endif |
| |
| |
| static void |
| qca_ar8327_phy_fixup(struct qca_phy_priv *priv, int phy) |
| { |
| switch (priv->revision) { |
| case 1: |
| /* 100m waveform */ |
| priv->phy_dbg_write(priv->device_id, phy, 0, 0x02ea); |
| /* turn on giga clock */ |
| priv->phy_dbg_write(priv->device_id, phy, 0x3d, 0x68a0); |
| break; |
| |
| case 2: |
| priv->phy_mmd_write(priv->device_id, phy, 0x7, 0x3c); |
| priv->phy_mmd_write(priv->device_id, phy, 0x4007, 0x0); |
| /* fallthrough */ |
| case 4: |
| if(priv->version == QCA_VER_AR8327) |
| { |
| priv->phy_mmd_write(priv->device_id, phy, 0x3, 0x800d); |
| priv->phy_mmd_write(priv->device_id, phy, 0x4003, 0x803f); |
| |
| priv->phy_dbg_write(priv->device_id, phy, 0x3d, 0x6860); |
| priv->phy_dbg_write(priv->device_id, phy, 0x5, 0x2c46); |
| priv->phy_dbg_write(priv->device_id, phy, 0x3c, 0x6000); |
| } |
| break; |
| } |
| } |
| |
| #ifdef IN_PORTVLAN |
| static void qca_port_isolate(a_uint32_t dev_id) |
| { |
| a_uint32_t port_id, mem_port_id, mem_port_map[AR8327_NUM_PORTS]={0}; |
| |
| for(port_id = 0; port_id < AR8327_NUM_PORTS; port_id++) |
| { |
| if(port_id == 6) |
| for(mem_port_id = 1; mem_port_id<= 4; mem_port_id++) |
| mem_port_map[port_id] |= (1 << mem_port_id); |
| else if (port_id == 0) |
| mem_port_map[port_id] |= (1 << 5); |
| else if (port_id >= 1 && port_id <= 4) |
| mem_port_map[port_id] |= (1 << 6); |
| else |
| mem_port_map[port_id] |= 1; |
| } |
| |
| for(port_id = 0; port_id < AR8327_NUM_PORTS; port_id++) |
| |
| fal_portvlan_member_update(dev_id, port_id, mem_port_map[port_id]); |
| |
| } |
| |
| void ssdk_portvlan_init(a_uint32_t dev_id) |
| { |
| a_uint32_t port = 0; |
| a_uint32_t cpu_bmp, lan_bmp, wan_bmp; |
| |
| cpu_bmp = ssdk_cpu_bmp_get(dev_id); |
| lan_bmp = ssdk_lan_bmp_get(dev_id); |
| wan_bmp = ssdk_wan_bmp_get(dev_id); |
| |
| if (!(cpu_bmp | lan_bmp | wan_bmp)) { |
| qca_port_isolate(dev_id); |
| return; |
| } |
| |
| for(port = 0; port < SSDK_MAX_PORT_NUM; port++) |
| { |
| if(cpu_bmp & (1 << port)) |
| { |
| fal_portvlan_member_update(dev_id, port, lan_bmp|wan_bmp); |
| } |
| if(lan_bmp & (1 << port)) |
| { |
| fal_portvlan_member_update(dev_id, port, (lan_bmp|cpu_bmp)&(~(1<<port))); |
| } |
| if(wan_bmp & (1 << port)) |
| { |
| fal_portvlan_member_update(dev_id, port, (wan_bmp|cpu_bmp)&(~(1<<port))); |
| } |
| } |
| } |
| #endif |
| |
| sw_error_t |
| qca_switch_init(a_uint32_t dev_id) |
| { |
| #if (defined(DESS) || defined(ISISC) || defined(ISIS)) && defined(IN_QOS) |
| a_uint32_t nr = 0; |
| #endif |
| int i = 0; |
| |
| /*fal_reset(dev_id);*/ |
| /*enable cpu and disable mirror*/ |
| #ifdef IN_MISC |
| fal_cpu_port_status_set(dev_id, A_TRUE); |
| /* setup MTU */ |
| fal_frame_max_size_set(dev_id, 1518); |
| #endif |
| #ifdef IN_MIB |
| /* Enable MIB counters */ |
| fal_mib_status_set(dev_id, A_TRUE); |
| fal_mib_cpukeep_set(dev_id, A_FALSE); |
| #endif |
| #ifdef IN_IGMP |
| fal_igmp_mld_rp_set(dev_id, 0); |
| #endif |
| |
| /*enable pppoe for dakota to support RSS*/ |
| if (SSDK_CURRENT_CHIP_TYPE == CHIP_DESS) { |
| #ifdef DESS |
| #ifdef IN_PPPOE |
| fal_pppoe_status_set(dev_id, A_TRUE); |
| #endif |
| #endif |
| } |
| |
| for (i = 0; i < AR8327_NUM_PORTS; i++) { |
| /* forward multicast and broadcast frames to CPU */ |
| #ifdef IN_MISC |
| fal_port_unk_uc_filter_set(dev_id, i, A_FALSE); |
| fal_port_unk_mc_filter_set(dev_id, i, A_FALSE); |
| fal_port_bc_filter_set(dev_id, i, A_FALSE); |
| #endif |
| #ifdef IN_PORTVLAN |
| fal_port_default_svid_set(dev_id, i, 0); |
| fal_port_default_cvid_set(dev_id, i, 0); |
| fal_port_1qmode_set(dev_id, i, FAL_1Q_DISABLE); |
| fal_port_egvlanmode_set(dev_id, i, FAL_EG_UNMODIFIED); |
| #endif |
| |
| #ifdef IN_FDB |
| fal_fdb_port_learn_set(dev_id, i, A_TRUE); |
| #endif |
| #ifdef IN_STP |
| fal_stp_port_state_set(dev_id, 0, i, FAL_STP_FARWARDING); |
| #endif |
| #ifdef IN_PORTVLAN |
| fal_port_vlan_propagation_set(dev_id, i, FAL_VLAN_PROPAGATION_REPLACE); |
| #endif |
| #ifdef IN_IGMP |
| fal_port_igmps_status_set(dev_id, i, A_FALSE); |
| fal_port_igmp_mld_join_set(dev_id, i, A_FALSE); |
| fal_port_igmp_mld_leave_set(dev_id, i, A_FALSE); |
| fal_igmp_mld_entry_creat_set(dev_id, A_FALSE); |
| fal_igmp_mld_entry_v3_set(dev_id, A_FALSE); |
| #endif |
| if (SSDK_CURRENT_CHIP_TYPE == CHIP_SHIVA) { |
| return SW_OK; |
| } else if (SSDK_CURRENT_CHIP_TYPE == CHIP_DESS) { |
| #ifdef DESS |
| #ifdef IN_PORTCONTROL |
| fal_port_flowctrl_forcemode_set(dev_id, i, A_FALSE); |
| fal_port_link_forcemode_set(dev_id, i, A_TRUE); |
| #endif |
| #ifdef IN_QOS |
| nr = 240; /*30*8*/ |
| fal_qos_port_tx_buf_nr_set(dev_id, i, &nr); |
| nr = 48; /*6*8*/ |
| fal_qos_port_rx_buf_nr_set(dev_id, i, &nr); |
| fal_qos_port_red_en_set(dev_id, i, A_TRUE); |
| nr = 32; |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 5, &nr); |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 4, &nr); |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 3, &nr); |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 2, &nr); |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 1, &nr); |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 0, &nr); |
| if (i != SSDK_PHYSICAL_PORT0) |
| fal_qos_port_mode_set(dev_id, i, |
| FAL_QOS_DSCP_MODE, A_TRUE); |
| #endif |
| #endif |
| } else if (SSDK_CURRENT_CHIP_TYPE == CHIP_ISISC || |
| SSDK_CURRENT_CHIP_TYPE == CHIP_ISIS) { |
| #if defined(ISISC) || defined(ISIS) |
| #ifdef IN_INTERFACECONTROL |
| fal_port_3az_status_set(dev_id, i, A_FALSE); |
| #endif |
| #ifdef IN_PORTCONTROL |
| fal_port_flowctrl_forcemode_set(dev_id, i, A_TRUE); |
| fal_port_flowctrl_set(dev_id, i, A_FALSE); |
| |
| if (i != 0 && i != 6) { |
| fal_port_flowctrl_set(dev_id, i, A_TRUE); |
| fal_port_flowctrl_forcemode_set(dev_id, i, A_FALSE); |
| } |
| #endif |
| if (i == 0 || i == 5 || i == 6) { |
| #ifdef IN_QOS |
| nr = 240; /*30*8*/ |
| fal_qos_port_tx_buf_nr_set(dev_id, i, &nr); |
| nr = 48; /*6*8*/ |
| fal_qos_port_rx_buf_nr_set(dev_id, i, &nr); |
| fal_qos_port_red_en_set(dev_id, i, A_TRUE); |
| if (SSDK_CURRENT_CHIP_TYPE == CHIP_ISISC) { |
| nr = 64; /*8*8*/ |
| } else if (SSDK_CURRENT_CHIP_TYPE == CHIP_ISIS) { |
| nr = 60; |
| } |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 5, &nr); |
| nr = 48; /*6*8*/ |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 4, &nr); |
| nr = 32; /*4*8*/ |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 3, &nr); |
| nr = 32; /*4*8*/ |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 2, &nr); |
| nr = 32; /*4*8*/ |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 1, &nr); |
| nr = 24; /*3*8*/ |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 0, &nr); |
| #endif |
| } else { |
| #ifdef IN_QOS |
| nr = 200; /*25*8*/ |
| fal_qos_port_tx_buf_nr_set(dev_id, i, &nr); |
| nr = 48; /*6*8*/ |
| fal_qos_port_rx_buf_nr_set(dev_id, i, &nr); |
| fal_qos_port_red_en_set(dev_id, i, A_TRUE); |
| if (SSDK_CURRENT_CHIP_TYPE == CHIP_ISISC) { |
| nr = 64; /*8*8*/ |
| } else if (SSDK_CURRENT_CHIP_TYPE == CHIP_ISIS) { |
| nr = 60; |
| } |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 3, &nr); |
| nr = 48; /*6*8*/ |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 2, &nr); |
| nr = 32; /*4*8*/ |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 1, &nr); |
| nr = 24; /*3*8*/ |
| fal_qos_queue_tx_buf_nr_set(dev_id, i, 0, &nr); |
| #endif |
| } |
| #endif |
| } |
| } |
| |
| return SW_OK; |
| } |
| |
| void qca_ar8327_phy_linkdown(a_uint32_t dev_id) |
| { |
| int i; |
| a_uint16_t phy_val; |
| |
| for (i = 0; i < AR8327_NUM_PHYS; i++) { |
| qca_ar8327_phy_write(dev_id, i, 0x0, 0x0800); // phy powerdown |
| |
| qca_ar8327_phy_dbg_read(dev_id, i, 0x3d, &phy_val); |
| phy_val &= ~0x0040; |
| qca_ar8327_phy_dbg_write(dev_id, i, 0x3d, phy_val); |
| |
| /*PHY will stop the tx clock for a while when link is down |
| 1. en_anychange debug port 0xb bit13 = 0 //speed up link down tx_clk |
| 2. sel_rst_80us debug port 0xb bit10 = 0 //speed up speed mode change to 2'b10 tx_clk |
| */ |
| qca_ar8327_phy_dbg_read(dev_id, i, 0xb, &phy_val); |
| phy_val &= ~0x2400; |
| qca_ar8327_phy_dbg_write(dev_id, i, 0xb, phy_val); |
| } |
| } |
| |
| void |
| qca_mac_disable(a_uint32_t device_id) |
| { |
| hsl_api_t *p_api; |
| |
| p_api = hsl_api_ptr_get (device_id); |
| if(p_api |
| && p_api->interface_mac_pad_set |
| && p_api->interface_mac_sgmii_set) |
| { |
| p_api->interface_mac_pad_set(device_id,0,0); |
| p_api->interface_mac_pad_set(device_id,5,0); |
| p_api->interface_mac_pad_set(device_id,6,0); |
| p_api->interface_mac_sgmii_set(device_id,AR8327_REG_PAD_SGMII_CTRL_HW_INIT); |
| } |
| else |
| { |
| SSDK_ERROR("API not support \n"); |
| } |
| } |
| |
| static void qca_switch_set_mac_force(struct qca_phy_priv *priv) |
| { |
| a_uint32_t value, value0, i; |
| if (priv == NULL || (priv->mii_read == NULL) || (priv->mii_write == NULL)) { |
| SSDK_ERROR("In qca_switch_set_mac_force, private data is NULL!\r\n"); |
| return; |
| } |
| |
| for (i=0; i < AR8327_NUM_PORTS; ++i) { |
| /* b3:2=0,Tx/Rx Mac disable, |
| b9=0,LINK_EN disable */ |
| value0 = priv->mii_read(priv->device_id, AR8327_REG_PORT_STATUS(i)); |
| value = value0 & ~(AR8327_PORT_STATUS_LINK_AUTO | |
| AR8327_PORT_STATUS_TXMAC | |
| AR8327_PORT_STATUS_RXMAC); |
| priv->mii_write(priv->device_id, AR8327_REG_PORT_STATUS(i), value); |
| |
| /* Force speed to 1000M Full */ |
| value = priv->mii_read(priv->device_id, AR8327_REG_PORT_STATUS(i)); |
| value &= ~(AR8327_PORT_STATUS_DUPLEX | AR8327_PORT_STATUS_SPEED); |
| value |= AR8327_PORT_SPEED_1000M | AR8327_PORT_STATUS_DUPLEX; |
| priv->mii_write(priv->device_id, AR8327_REG_PORT_STATUS(i), value); |
| } |
| return; |
| } |
| |
| void |
| qca_ar8327_phy_enable(struct qca_phy_priv *priv) |
| { |
| int i = 0; |
| #ifndef BOARD_AR71XX |
| ssdk_phy_rgmii_set(priv); |
| #endif |
| for (i = 0; i < AR8327_NUM_PHYS; i++) { |
| a_uint16_t value = 0; |
| |
| if (priv->version == QCA_VER_AR8327 || priv->version == QCA_VER_AR8337) |
| qca_ar8327_phy_fixup(priv, i); |
| |
| /* start autoneg*/ |
| priv->phy_write(priv->device_id, i, MII_ADVERTISE, ADVERTISE_ALL | |
| ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); |
| //phy reg 0x9, b10,1 = Prefer multi-port device (master) |
| priv->phy_write(priv->device_id, i, MII_CTRL1000, (0x0400|ADVERTISE_1000FULL)); |
| |
| priv->phy_write(priv->device_id, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); |
| |
| priv->phy_dbg_read(priv->device_id, i, 0, &value); |
| value &= (~(1<<12)); |
| priv->phy_dbg_write(priv->device_id, i, 0, value); |
| |
| msleep(100); |
| } |
| } |
| void qca_ar8327_sw_soft_reset(struct qca_phy_priv *priv) |
| { |
| a_uint32_t value = 0; |
| |
| value = priv->mii_read(priv->device_id, AR8327_REG_CTRL); |
| value |= 0x80000000; |
| priv->mii_write(priv->device_id, AR8327_REG_CTRL, value); |
| /*Need wait reset done*/ |
| do { |
| udelay(10); |
| value = priv->mii_read(priv->device_id, AR8327_REG_CTRL); |
| } while(value & AR8327_CTRL_RESET); |
| do { |
| udelay(10); |
| value = priv->mii_read(priv->device_id, 0x20); |
| } while ((value & SSDK_GLOBAL_INITIALIZED_STATUS) != |
| SSDK_GLOBAL_INITIALIZED_STATUS); |
| |
| return; |
| } |
| |
| #if defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) |
| int qca_ar8327_hw_init(struct qca_phy_priv *priv) |
| { |
| struct device_node *np = NULL; |
| const __be32 *paddr; |
| a_uint32_t reg, value, i; |
| a_int32_t len; |
| |
| if (priv->ess_switch_flag == A_TRUE) |
| np = priv->of_node; |
| else |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| np = priv->phy->mdio.dev.of_node; |
| #else |
| np = priv->phy->dev.of_node; |
| #endif |
| |
| if(!np) |
| return -EINVAL; |
| |
| /*Before switch software reset, disable PHY and clear MAC PAD*/ |
| qca_ar8327_phy_linkdown(priv->device_id); |
| qca_mac_disable(priv->device_id); |
| msleep(1000); |
| |
| /*First software reset S17 chip*/ |
| qca_ar8327_sw_soft_reset(priv); |
| |
| /*After switch software reset, need disable all ports' MAC with 1000M FULL*/ |
| qca_switch_set_mac_force(priv); |
| |
| /* Configure switch register from DT information */ |
| paddr = of_get_property(np, "qca,ar8327-initvals", &len); |
| if (paddr) { |
| if (len < (2 * sizeof(*paddr))) { |
| SSDK_ERROR("len:%d < 2 * sizeof(*paddr):%zu\n", len, 2 * sizeof(*paddr)); |
| return -EINVAL; |
| } |
| |
| len /= sizeof(*paddr); |
| |
| for (i = 0; i < len - 1; i += 2) { |
| reg = be32_to_cpup(paddr + i); |
| value = be32_to_cpup(paddr + i + 1); |
| priv->mii_write(priv->device_id, reg, value); |
| } |
| } |
| |
| value = priv->mii_read(priv->device_id, AR8327_REG_MODULE_EN); |
| value &= ~AR8327_REG_MODULE_EN_QM_ERR; |
| value &= ~AR8327_REG_MODULE_EN_LOOKUP_ERR; |
| priv->mii_write(priv->device_id, AR8327_REG_MODULE_EN, value); |
| |
| qca_switch_init(priv->device_id); |
| #ifdef IN_PORTVLAN |
| ssdk_portvlan_init(priv->device_id); |
| #endif |
| qca_mac_enable_intr(priv); |
| qca_ar8327_phy_enable(priv); |
| |
| return 0; |
| } |
| #else |
| static a_uint32_t |
| qca_ar8327_get_pad_cfg(struct ar8327_pad_cfg *pad_cfg) |
| { |
| a_uint32_t value = 0; |
| |
| if (pad_cfg == 0) { |
| return 0; |
| } |
| |
| if(pad_cfg->mode == AR8327_PAD_MAC2MAC_MII) { |
| value = AR8327_PAD_CTRL_MAC_MII_EN; |
| if (pad_cfg->rxclk_sel) |
| value |= AR8327_PAD_CTRL_MAC_MII_RXCLK_SEL; |
| if (pad_cfg->txclk_sel) |
| value |= AR8327_PAD_CTRL_MAC_MII_TXCLK_SEL; |
| |
| } else if (pad_cfg->mode == AR8327_PAD_MAC2MAC_GMII) { |
| value = AR8327_PAD_CTRL_MAC_GMII_EN; |
| if (pad_cfg->rxclk_sel) |
| value |= AR8327_PAD_CTRL_MAC_GMII_RXCLK_SEL; |
| if (pad_cfg->txclk_sel) |
| value |= AR8327_PAD_CTRL_MAC_GMII_TXCLK_SEL; |
| |
| } else if (pad_cfg->mode == AR8327_PAD_MAC_SGMII) { |
| value = AR8327_PAD_CTRL_SGMII_EN; |
| |
| /* WAR for AP136 board. */ |
| value |= pad_cfg->txclk_delay_sel << |
| AR8327_PAD_CTRL_RGMII_TXCLK_DELAY_SEL_S; |
| value |= pad_cfg->rxclk_delay_sel << |
| AR8327_PAD_CTRL_RGMII_RXCLK_DELAY_SEL_S; |
| if (pad_cfg->rxclk_delay_en) |
| value |= AR8327_PAD_CTRL_RGMII_RXCLK_DELAY_EN; |
| if (pad_cfg->txclk_delay_en) |
| value |= AR8327_PAD_CTRL_RGMII_TXCLK_DELAY_EN; |
| |
| } else if (pad_cfg->mode == AR8327_PAD_MAC2PHY_MII) { |
| value = AR8327_PAD_CTRL_PHY_MII_EN; |
| if (pad_cfg->rxclk_sel) |
| value |= AR8327_PAD_CTRL_PHY_MII_RXCLK_SEL; |
| if (pad_cfg->txclk_sel) |
| value |= AR8327_PAD_CTRL_PHY_MII_TXCLK_SEL; |
| |
| } else if (pad_cfg->mode == AR8327_PAD_MAC2PHY_GMII) { |
| value = AR8327_PAD_CTRL_PHY_GMII_EN; |
| if (pad_cfg->pipe_rxclk_sel) |
| value |= AR8327_PAD_CTRL_PHY_GMII_PIPE_RXCLK_SEL; |
| if (pad_cfg->rxclk_sel) |
| value |= AR8327_PAD_CTRL_PHY_GMII_RXCLK_SEL; |
| if (pad_cfg->txclk_sel) |
| value |= AR8327_PAD_CTRL_PHY_GMII_TXCLK_SEL; |
| |
| } else if (pad_cfg->mode == AR8327_PAD_MAC_RGMII) { |
| value = AR8327_PAD_CTRL_RGMII_EN; |
| value |= pad_cfg->txclk_delay_sel << |
| AR8327_PAD_CTRL_RGMII_TXCLK_DELAY_SEL_S; |
| value |= pad_cfg->rxclk_delay_sel << |
| AR8327_PAD_CTRL_RGMII_RXCLK_DELAY_SEL_S; |
| if (pad_cfg->rxclk_delay_en) |
| value |= AR8327_PAD_CTRL_RGMII_RXCLK_DELAY_EN; |
| if (pad_cfg->txclk_delay_en) |
| value |= AR8327_PAD_CTRL_RGMII_TXCLK_DELAY_EN; |
| |
| } else if (pad_cfg->mode == AR8327_PAD_PHY_GMII) { |
| value = AR8327_PAD_CTRL_PHYX_GMII_EN; |
| |
| } else if (pad_cfg->mode == AR8327_PAD_PHY_RGMII) { |
| value = AR8327_PAD_CTRL_PHYX_RGMII_EN; |
| |
| } else if (pad_cfg->mode == AR8327_PAD_PHY_MII) { |
| value = AR8327_PAD_CTRL_PHYX_MII_EN; |
| |
| } else { |
| value = 0; |
| } |
| |
| return value; |
| } |
| |
| #ifndef BOARD_AR71XX |
| static a_uint32_t |
| qca_ar8327_get_pwr_sel(struct qca_phy_priv *priv, |
| struct ar8327_platform_data *plat_data) |
| { |
| struct ar8327_pad_cfg *cfg = NULL; |
| a_uint32_t value; |
| |
| if (!plat_data) { |
| return 0; |
| } |
| |
| value = priv->mii_read(priv->device_id, AR8327_REG_PAD_MAC_PWR_SEL); |
| |
| cfg = plat_data->pad0_cfg; |
| |
| if (cfg && (cfg->mode == AR8327_PAD_MAC_RGMII) && |
| cfg->rgmii_1_8v) { |
| value |= AR8327_PAD_MAC_PWR_RGMII0_1_8V; |
| } |
| |
| cfg = plat_data->pad5_cfg; |
| if (cfg && (cfg->mode == AR8327_PAD_MAC_RGMII) && |
| cfg->rgmii_1_8v) { |
| value |= AR8327_PAD_MAC_PWR_RGMII1_1_8V; |
| } |
| |
| cfg = plat_data->pad6_cfg; |
| if (cfg && (cfg->mode == AR8327_PAD_MAC_RGMII) && |
| cfg->rgmii_1_8v) { |
| value |= AR8327_PAD_MAC_PWR_RGMII1_1_8V; |
| } |
| |
| return value; |
| } |
| #endif |
| |
| static a_uint32_t |
| qca_ar8327_set_led_cfg(struct qca_phy_priv *priv, |
| struct ar8327_platform_data *plat_data, |
| a_uint32_t pos) |
| { |
| struct ar8327_led_cfg *led_cfg; |
| a_uint32_t new_pos = pos; |
| |
| led_cfg = plat_data->led_cfg; |
| if (led_cfg) { |
| if (led_cfg->open_drain) |
| new_pos |= AR8327_POS_LED_OPEN_EN; |
| else |
| new_pos &= ~AR8327_POS_LED_OPEN_EN; |
| |
| priv->mii_write(priv->device_id, AR8327_REG_LED_CTRL_0, led_cfg->led_ctrl0); |
| priv->mii_write(priv->device_id, AR8327_REG_LED_CTRL_1, led_cfg->led_ctrl1); |
| priv->mii_write(priv->device_id, AR8327_REG_LED_CTRL_2, led_cfg->led_ctrl2); |
| priv->mii_write(priv->device_id, AR8327_REG_LED_CTRL_3, led_cfg->led_ctrl3); |
| |
| if (new_pos != pos) { |
| new_pos |= AR8327_POS_POWER_ON_SEL; |
| } |
| } |
| return new_pos; |
| } |
| #ifndef BOARD_AR71XX |
| static int |
| qca_ar8327_set_sgmii_cfg(struct qca_phy_priv *priv, |
| struct ar8327_platform_data *plat_data, |
| a_uint32_t* new_pos) |
| { |
| a_uint32_t value = 0; |
| |
| /*configure the SGMII*/ |
| value = priv->mii_read(priv->device_id, AR8327_REG_PAD_SGMII_CTRL); |
| value &= ~(AR8327_PAD_SGMII_CTRL_MODE_CTRL); |
| value |= ((plat_data->sgmii_cfg->sgmii_mode) << |
| AR8327_PAD_SGMII_CTRL_MODE_CTRL_S); |
| |
| if (priv->version == QCA_VER_AR8337) { |
| value |= (AR8327_PAD_SGMII_CTRL_EN_PLL | |
| AR8327_PAD_SGMII_CTRL_EN_RX | |
| AR8327_PAD_SGMII_CTRL_EN_TX); |
| } else { |
| value &= ~(AR8327_PAD_SGMII_CTRL_EN_PLL | |
| AR8327_PAD_SGMII_CTRL_EN_RX | |
| AR8327_PAD_SGMII_CTRL_EN_TX); |
| } |
| value |= AR8327_PAD_SGMII_CTRL_EN_SD; |
| |
| priv->mii_write(priv->device_id, AR8327_REG_PAD_SGMII_CTRL, value); |
| |
| if (plat_data->sgmii_cfg->serdes_aen) { |
| *new_pos &= ~AR8327_POS_SERDES_AEN; |
| } else { |
| *new_pos |= AR8327_POS_SERDES_AEN; |
| } |
| return 0; |
| } |
| #endif |
| |
| static int |
| qca_ar8327_set_plat_data_cfg(struct qca_phy_priv *priv, |
| struct ar8327_platform_data *plat_data) |
| { |
| a_uint32_t pos, new_pos; |
| |
| pos = priv->mii_read(priv->device_id, AR8327_REG_POS); |
| |
| new_pos = qca_ar8327_set_led_cfg(priv, plat_data, pos); |
| |
| #ifndef BOARD_AR71XX |
| /*configure the SGMII*/ |
| if (plat_data->sgmii_cfg) { |
| qca_ar8327_set_sgmii_cfg(priv, plat_data, &new_pos); |
| } |
| #endif |
| |
| priv->mii_write(priv->device_id, AR8327_REG_POS, new_pos); |
| |
| return 0; |
| } |
| |
| static int |
| qca_ar8327_set_pad_cfg(struct qca_phy_priv *priv, |
| struct ar8327_platform_data *plat_data) |
| { |
| a_uint32_t pad0 = 0, pad5 = 0, pad6 = 0; |
| |
| pad0 = qca_ar8327_get_pad_cfg(plat_data->pad0_cfg); |
| priv->mii_write(priv->device_id, AR8327_REG_PAD0_CTRL, pad0); |
| |
| pad5 = qca_ar8327_get_pad_cfg(plat_data->pad5_cfg); |
| if(priv->version == QCA_VER_AR8337) { |
| pad5 |= AR8327_PAD_CTRL_RGMII_RXCLK_DELAY_EN; |
| } |
| priv->mii_write(priv->device_id, AR8327_REG_PAD5_CTRL, pad5); |
| |
| pad6 = qca_ar8327_get_pad_cfg(plat_data->pad6_cfg); |
| if(plat_data->pad5_cfg && |
| (plat_data->pad5_cfg->mode == AR8327_PAD_PHY_RGMII)) |
| pad6 |= AR8327_PAD_CTRL_PHYX_RGMII_EN; |
| priv->mii_write(priv->device_id, AR8327_REG_PAD6_CTRL, pad6); |
| |
| return 0; |
| } |
| |
| void |
| qca_ar8327_port_init(struct qca_phy_priv *priv, a_uint32_t port) |
| { |
| struct ar8327_platform_data *plat_data; |
| struct ar8327_port_cfg *port_cfg; |
| a_uint32_t value; |
| |
| plat_data = priv->phy->dev.platform_data; |
| if (plat_data == NULL) { |
| return; |
| } |
| |
| if (((port == 0) && plat_data->pad0_cfg) || |
| ((port == 5) && plat_data->pad5_cfg) || |
| ((port == 6) && plat_data->pad6_cfg)) { |
| switch (port) { |
| case 0: |
| port_cfg = &plat_data->cpuport_cfg; |
| break; |
| case 5: |
| port_cfg = &plat_data->port5_cfg; |
| break; |
| case 6: |
| port_cfg = &plat_data->port6_cfg; |
| break; |
| } |
| } else { |
| return; |
| } |
| |
| /*disable mac at first*/ |
| fal_port_rxmac_status_set(priv->device_id, port, A_FALSE); |
| fal_port_txmac_status_set(priv->device_id, port, A_FALSE); |
| value = port_cfg->duplex ? FAL_FULL_DUPLEX : FAL_HALF_DUPLEX; |
| fal_port_duplex_set(priv->device_id, port, value); |
| value = port_cfg->txpause ? A_TRUE : A_FALSE; |
| fal_port_txfc_status_set(priv->device_id, port, value); |
| value = port_cfg->rxpause ? A_TRUE : A_FALSE; |
| fal_port_rxfc_status_set(priv->device_id, port, value); |
| if(port_cfg->speed == AR8327_PORT_SPEED_10) { |
| value = FAL_SPEED_10; |
| } else if(port_cfg->speed == AR8327_PORT_SPEED_100) { |
| value = FAL_SPEED_100; |
| } else if(port_cfg->speed == AR8327_PORT_SPEED_1000) { |
| value = FAL_SPEED_1000; |
| } else { |
| value = FAL_SPEED_1000; |
| } |
| fal_port_speed_set(priv->device_id, port, value); |
| /*enable mac at last*/ |
| udelay(800); |
| fal_port_rxmac_status_set(priv->device_id, port, A_TRUE); |
| fal_port_txmac_status_set(priv->device_id, port, A_TRUE); |
| } |
| |
| int |
| qca_ar8327_hw_init(struct qca_phy_priv *priv) |
| { |
| struct ar8327_platform_data *plat_data; |
| a_uint32_t i = 0; |
| a_uint32_t value = 0; |
| |
| plat_data = priv->phy->dev.platform_data; |
| if (plat_data == NULL) { |
| return -EINVAL; |
| } |
| |
| /*Before switch software reset, disable PHY and clear MAC PAD*/ |
| qca_ar8327_phy_linkdown(priv->device_id); |
| qca_mac_disable(priv->device_id); |
| udelay(10); |
| |
| qca_ar8327_set_plat_data_cfg(priv, plat_data); |
| |
| /*mac reset*/ |
| priv->mii_write(priv->device_id, AR8327_REG_MAC_SFT_RST, 0x3fff); |
| |
| msleep(100); |
| |
| /*First software reset S17 chip*/ |
| qca_ar8327_sw_soft_reset(priv); |
| udelay(1000); |
| |
| /*After switch software reset, need disable all ports' MAC with 1000M FULL*/ |
| qca_switch_set_mac_force(priv); |
| |
| qca_ar8327_set_pad_cfg(priv, plat_data); |
| |
| value = priv->mii_read(priv->device_id, AR8327_REG_MODULE_EN); |
| value &= ~AR8327_REG_MODULE_EN_QM_ERR; |
| value &= ~AR8327_REG_MODULE_EN_LOOKUP_ERR; |
| priv->mii_write(priv->device_id, AR8327_REG_MODULE_EN, value); |
| |
| qca_switch_init(priv->device_id); |
| |
| #ifndef BOARD_AR71XX |
| value = qca_ar8327_get_pwr_sel(priv, plat_data); |
| priv->mii_write(priv->device_id, AR8327_REG_PAD_MAC_PWR_SEL, value); |
| #endif |
| |
| msleep(1000); |
| |
| for (i = 0; i < AR8327_NUM_PORTS; i++) { |
| qca_ar8327_port_init(priv, i); |
| } |
| |
| qca_ar8327_phy_enable(priv); |
| |
| return 0; |
| } |
| #endif |
| |
| #if defined(IN_SWCONFIG) |
| #ifndef BOARD_AR71XX |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)) |
| static int |
| qca_ar8327_sw_get_reg_val(struct switch_dev *dev, |
| int reg, int *val) |
| { |
| return 0; |
| } |
| |
| static int |
| qca_ar8327_sw_set_reg_val(struct switch_dev *dev, |
| int reg, int val) |
| { |
| return 0; |
| } |
| #endif |
| #endif |
| static struct switch_attr qca_ar8327_globals[] = { |
| #if defined(IN_VLAN) |
| { |
| .name = "enable_vlan", |
| .description = "Enable 8021q VLAN", |
| .type = SWITCH_TYPE_INT, |
| .set = qca_ar8327_sw_set_vlan, |
| .get = qca_ar8327_sw_get_vlan, |
| .max = 1 |
| }, |
| #endif |
| #if defined(IN_MISC) |
| { |
| .name = "max_frame_size", |
| .description = "Set Max frame Size Of Mac", |
| .type = SWITCH_TYPE_INT, |
| .set = qca_ar8327_sw_set_max_frame_size, |
| .get = qca_ar8327_sw_get_max_frame_size, |
| .max = 9018 |
| }, |
| #endif |
| #if defined(IN_MIB) |
| { |
| .name = "reset_mibs", |
| .description = "Reset All MIB Counters", |
| .type = SWITCH_TYPE_NOVAL, |
| .set = qca_ar8327_sw_set_reset_mibs, |
| }, |
| #endif |
| #ifdef IN_FDB |
| { |
| .name = "flush_arl", |
| .description = "Flush All ARL table", |
| .type = SWITCH_TYPE_NOVAL, |
| .set = qca_ar8327_sw_atu_flush, |
| }, |
| { |
| .name = "dump_arl", |
| .description = "Dump All ARL table", |
| .type = SWITCH_TYPE_STRING, |
| .get = qca_ar8327_sw_atu_dump, |
| }, |
| #endif |
| { |
| .name = "switch_ext", |
| .description = "Switch extended configuration", |
| .type = SWITCH_TYPE_EXT, |
| .set = qca_ar8327_sw_switch_ext, |
| }, |
| }; |
| |
| static struct switch_attr qca_ar8327_port[] = { |
| #if defined(IN_MIB) |
| { |
| .name = "reset_mib", |
| .description = "Reset Mib Counters", |
| .type = SWITCH_TYPE_NOVAL, |
| .set = qca_ar8327_sw_set_port_reset_mib, |
| }, |
| { |
| .name = "mib", |
| .description = "Get Mib Counters", |
| .type = SWITCH_TYPE_STRING, |
| .set = NULL, |
| .get = qca_ar8327_sw_get_port_mib, |
| }, |
| #endif |
| #if defined(IN_PORTCONTROL) |
| { |
| .type = SWITCH_TYPE_INT, |
| .name = "enable_eee", |
| .description = "Enable EEE", |
| .set = qca_ar8327_sw_set_eee, |
| .get = qca_ar8327_sw_get_eee, |
| .max = 1, |
| }, |
| #endif |
| }; |
| |
| #if defined(IN_VLAN) |
| static struct switch_attr qca_ar8327_vlan[] = { |
| { |
| .name = "vid", |
| .description = "Configure Vlan Id", |
| .type = SWITCH_TYPE_INT, |
| .set = qca_ar8327_sw_set_vid, |
| .get = qca_ar8327_sw_get_vid, |
| .max = 4094, |
| }, |
| }; |
| #endif |
| |
| const struct switch_dev_ops qca_ar8327_sw_ops = { |
| .attr_global = { |
| .attr = qca_ar8327_globals, |
| .n_attr = ARRAY_SIZE(qca_ar8327_globals), |
| }, |
| .attr_port = { |
| .attr = qca_ar8327_port, |
| .n_attr = ARRAY_SIZE(qca_ar8327_port), |
| }, |
| #if defined(IN_VLAN) |
| .attr_vlan = { |
| .attr = qca_ar8327_vlan, |
| .n_attr = ARRAY_SIZE(qca_ar8327_vlan), |
| }, |
| .get_port_pvid = qca_ar8327_sw_get_pvid, |
| .set_port_pvid = qca_ar8327_sw_set_pvid, |
| .get_vlan_ports = qca_ar8327_sw_get_ports, |
| .set_vlan_ports = qca_ar8327_sw_set_ports, |
| .apply_config = qca_ar8327_sw_hw_apply, |
| #endif |
| #if defined(IN_MISC) |
| .reset_switch = qca_ar8327_sw_reset_switch, |
| #endif |
| #if defined(IN_PORTCONTROL) |
| .get_port_link = qca_ar8327_sw_get_port_link, |
| #endif |
| #ifndef BOARD_AR71XX |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)) |
| .get_reg_val = qca_ar8327_sw_get_reg_val, |
| .set_reg_val = qca_ar8327_sw_set_reg_val, |
| #endif |
| #endif |
| }; |
| #endif |
| |
| #define SSDK_MIB_CHANGE_WQ |
| |
| static int |
| qca_phy_mib_task(struct qca_phy_priv *priv) |
| { |
| qca_ar8327_sw_mib_task(priv); |
| return 0; |
| } |
| |
| static void |
| qca_phy_mib_work_task(struct work_struct *work) |
| { |
| struct qca_phy_priv *priv = container_of(work, struct qca_phy_priv, |
| mib_dwork.work); |
| |
| mutex_lock(&priv->mib_lock); |
| |
| qca_phy_mib_task(priv); |
| |
| mutex_unlock(&priv->mib_lock); |
| #ifndef SSDK_MIB_CHANGE_WQ |
| schedule_delayed_work(&priv->mib_dwork, |
| msecs_to_jiffies(QCA_PHY_MIB_WORK_DELAY)); |
| #else |
| queue_delayed_work_on(0, system_long_wq, &priv->mib_dwork, |
| msecs_to_jiffies(QCA_PHY_MIB_WORK_DELAY)); |
| #endif |
| } |
| |
| int |
| qca_phy_mib_work_start(struct qca_phy_priv *priv) |
| { |
| mutex_init(&priv->mib_lock); |
| if(SW_OK != fal_mib_counter_alloc(priv->device_id, &priv->mib_counters)){ |
| SSDK_ERROR("Memory allocation fail\n"); |
| return -ENOMEM; |
| } |
| |
| INIT_DELAYED_WORK(&priv->mib_dwork, qca_phy_mib_work_task); |
| #ifndef SSDK_MIB_CHANGE_WQ |
| schedule_delayed_work(&priv->mib_dwork, |
| msecs_to_jiffies(QCA_PHY_MIB_WORK_DELAY)); |
| #else |
| queue_delayed_work_on(0, system_long_wq, &priv->mib_dwork, |
| msecs_to_jiffies(QCA_PHY_MIB_WORK_DELAY)); |
| #endif |
| |
| return 0; |
| } |
| |
| void |
| qca_phy_mib_work_stop(struct qca_phy_priv *priv) |
| { |
| if(!priv) |
| return; |
| if(priv->mib_counters) |
| kfree(priv->mib_counters); |
| cancel_delayed_work_sync(&priv->mib_dwork); |
| } |
| |
| #define SSDK_QM_CHANGE_WQ |
| |
| static void |
| qm_err_check_work_task_polling(struct work_struct *work) |
| { |
| struct qca_phy_priv *priv = container_of(work, struct qca_phy_priv, |
| qm_dwork_polling.work); |
| |
| mutex_lock(&priv->qm_lock); |
| |
| qca_ar8327_sw_mac_polling_task(priv); |
| |
| mutex_unlock(&priv->qm_lock); |
| |
| #ifndef SSDK_QM_CHANGE_WQ |
| schedule_delayed_work(&priv->qm_dwork, |
| msecs_to_jiffies(QCA_QM_WORK_DELAY)); |
| #else |
| queue_delayed_work_on(0, system_long_wq, &priv->qm_dwork_polling, |
| msecs_to_jiffies(QCA_QM_WORK_DELAY)); |
| #endif |
| } |
| |
| static int config_gpio(a_uint32_t gpio_num) |
| { |
| int error; |
| |
| if (gpio_is_valid(gpio_num)) |
| { |
| error = gpio_request_one(gpio_num, GPIOF_IN, "linkchange"); |
| if (error < 0) { |
| SSDK_ERROR("gpio request faild \n"); |
| return -1; |
| } |
| gpio_set_debounce(gpio_num, 60000); |
| } |
| else |
| { |
| SSDK_ERROR("gpio is invalid\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| static int qca_link_polling_select(struct qca_phy_priv *priv) |
| { |
| struct device_node *np = NULL; |
| const __be32 *link_polling_required, *link_intr_gpio; |
| a_int32_t len; |
| |
| if (priv->ess_switch_flag == A_TRUE) |
| np = priv->of_node; |
| else if(priv->version == QCA_VER_AR8337 || priv->version == QCA_VER_AR8327) |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| np = priv->phy->mdio.dev.of_node; |
| #else |
| np = priv->phy->dev.of_node; |
| #endif |
| else |
| SSDK_ERROR("cannot find np node!\n"); |
| |
| if(!np) |
| { |
| SSDK_ERROR("np is null !\n"); |
| return -1; |
| } |
| |
| link_polling_required = of_get_property(np, "link-polling-required", &len); |
| if (!link_polling_required ) |
| { |
| SSDK_INFO("link-polling-required node does not exist\n"); |
| return -1; |
| } |
| priv->link_polling_required = be32_to_cpup(link_polling_required); |
| if(!priv->link_polling_required) |
| { |
| link_intr_gpio = of_get_property(np, "link-intr-gpio", &len); |
| if (!link_intr_gpio ) |
| { |
| SSDK_ERROR("cannot find link-intr-gpio node\n"); |
| return -1; |
| } |
| if(config_gpio(be32_to_cpup(link_intr_gpio))) |
| return -1; |
| priv->link_interrupt_no = gpio_to_irq (be32_to_cpup(link_intr_gpio)); |
| SSDK_INFO("the interrupt number is:%x\n",priv->link_interrupt_no); |
| } |
| |
| return 0; |
| } |
| |
| int |
| qm_err_check_work_start(struct qca_phy_priv *priv) |
| { |
| /*Only valid for S17c chip*/ |
| if (priv->version != QCA_VER_AR8337 && |
| priv->version != QCA_VER_AR8327 && |
| priv->version != QCA_VER_DESS) |
| { |
| return 0; |
| } |
| |
| mutex_init(&priv->qm_lock); |
| INIT_DELAYED_WORK(&priv->qm_dwork_polling, qm_err_check_work_task_polling); |
| #ifndef SSDK_MIB_CHANGE_WQ |
| schedule_delayed_work(&priv->qm_dwork_polling, |
| msecs_to_jiffies(QCA_QM_WORK_DELAY)); |
| #else |
| queue_delayed_work_on(0, system_long_wq, &priv->qm_dwork_polling, |
| msecs_to_jiffies(QCA_QM_WORK_DELAY)); |
| #endif |
| |
| return 0; |
| } |
| |
| void |
| qm_err_check_work_stop(struct qca_phy_priv *priv) |
| { |
| /*Only valid for S17c chip*/ |
| if (priv->version != QCA_VER_AR8337 && |
| priv->version != QCA_VER_AR8327 && |
| priv->version != QCA_VER_DESS) return; |
| |
| cancel_delayed_work_sync(&priv->qm_dwork_polling); |
| |
| } |
| #ifdef DESS |
| static void |
| dess_rgmii_mac_work_task(struct work_struct *work) |
| { |
| struct qca_phy_priv *priv = container_of(work, struct qca_phy_priv, |
| rgmii_dwork.work); |
| |
| mutex_lock(&priv->rgmii_lock); |
| |
| dess_rgmii_sw_mac_polling_task(priv); |
| |
| mutex_unlock(&priv->rgmii_lock); |
| |
| schedule_delayed_work(&priv->rgmii_dwork, msecs_to_jiffies(QCA_RGMII_WORK_DELAY)); |
| } |
| |
| int |
| dess_rgmii_mac_work_start(struct qca_phy_priv *priv) |
| { |
| mutex_init(&priv->rgmii_lock); |
| |
| INIT_DELAYED_WORK(&priv->rgmii_dwork, dess_rgmii_mac_work_task); |
| |
| schedule_delayed_work(&priv->rgmii_dwork, msecs_to_jiffies(QCA_RGMII_WORK_DELAY)); |
| |
| return 0; |
| } |
| |
| void |
| dess_rgmii_mac_work_stop(struct qca_phy_priv *priv) |
| { |
| cancel_delayed_work_sync(&priv->rgmii_dwork); |
| } |
| #endif |
| |
| void |
| qca_mac_port_status_init(a_uint32_t dev_id, a_uint32_t port_id) |
| { |
| if(port_id < SSDK_PHYSICAL_PORT1 || port_id >= SW_MAX_NR_PORT) |
| { |
| SSDK_ERROR("port %d does not support status init\n", port_id); |
| return; |
| } |
| qca_phy_priv_global[dev_id]->port_old_link[port_id - 1] = 0; |
| qca_phy_priv_global[dev_id]->port_old_speed[port_id - 1] = FAL_SPEED_BUTT; |
| qca_phy_priv_global[dev_id]->port_old_duplex[port_id - 1] = FAL_DUPLEX_BUTT; |
| qca_phy_priv_global[dev_id]->port_old_tx_flowctrl[port_id - 1] = A_FALSE; |
| qca_phy_priv_global[dev_id]->port_old_rx_flowctrl[port_id - 1] = A_FALSE; |
| qca_phy_priv_global[dev_id]->port_tx_flowctrl_forcemode[port_id - 1] = A_FALSE; |
| qca_phy_priv_global[dev_id]->port_rx_flowctrl_forcemode[port_id - 1] = A_FALSE; |
| } |
| |
| void |
| qca_mac_sw_sync_port_status_init(a_uint32_t dev_id) |
| { |
| a_uint32_t port_id; |
| |
| for (port_id = SSDK_PHYSICAL_PORT1; port_id < SW_MAX_NR_PORT; port_id ++) { |
| qca_mac_port_status_init(dev_id, port_id); |
| } |
| } |
| void |
| qca_mac_sw_sync_work_task(struct work_struct *work) |
| { |
| adpt_api_t *p_adpt_api; |
| |
| struct qca_phy_priv *priv = container_of(work, struct qca_phy_priv, |
| mac_sw_sync_dwork.work); |
| |
| mutex_lock(&priv->mac_sw_sync_lock); |
| |
| if((p_adpt_api = adpt_api_ptr_get(priv->device_id)) != NULL) { |
| if (NULL == p_adpt_api->adpt_port_polling_sw_sync_set) |
| return; |
| p_adpt_api->adpt_port_polling_sw_sync_set(priv); |
| } |
| |
| mutex_unlock(&priv->mac_sw_sync_lock); |
| |
| schedule_delayed_work(&priv->mac_sw_sync_dwork, |
| msecs_to_jiffies(QCA_MAC_SW_SYNC_WORK_DELAY)); |
| } |
| |
| int |
| qca_mac_sw_sync_work_start(struct qca_phy_priv *priv) |
| { |
| if ((priv->version != QCA_VER_HPPE) && (priv->version != QCA_VER_SCOMPHY)) |
| return 0; |
| |
| if (priv->version == QCA_VER_HPPE) { |
| qca_mac_sw_sync_port_status_init(priv->device_id); |
| } |
| |
| mutex_init(&priv->mac_sw_sync_lock); |
| |
| INIT_DELAYED_WORK(&priv->mac_sw_sync_dwork, |
| qca_mac_sw_sync_work_task); |
| schedule_delayed_work(&priv->mac_sw_sync_dwork, |
| msecs_to_jiffies(QCA_MAC_SW_SYNC_WORK_DELAY)); |
| |
| return 0; |
| } |
| |
| void |
| qca_mac_sw_sync_work_stop(struct qca_phy_priv *priv) |
| { |
| if ((priv->version != QCA_VER_HPPE) && (priv->version != QCA_VER_SCOMPHY)) { |
| return; |
| } |
| cancel_delayed_work_sync(&priv->mac_sw_sync_dwork); |
| } |
| |
| void |
| qca_mac_sw_sync_work_resume(struct qca_phy_priv *priv) |
| { |
| if ((priv->version != QCA_VER_HPPE) && (priv->version != QCA_VER_SCOMPHY)) { |
| return; |
| } |
| |
| schedule_delayed_work(&priv->mac_sw_sync_dwork, |
| msecs_to_jiffies(QCA_MAC_SW_SYNC_WORK_DELAY)); |
| } |
| |
| int |
| qca_phy_id_chip(struct qca_phy_priv *priv) |
| { |
| a_uint32_t value, version; |
| |
| value = qca_ar8216_mii_read(priv->device_id, AR8327_REG_CTRL); |
| version = value & (AR8327_CTRL_REVISION | |
| AR8327_CTRL_VERSION); |
| priv->version = (version & AR8327_CTRL_VERSION) >> |
| AR8327_CTRL_VERSION_S; |
| priv->revision = (version & AR8327_CTRL_REVISION); |
| |
| if((priv->version == QCA_VER_AR8327) || |
| (priv->version == QCA_VER_AR8337) || |
| (priv->version == QCA_VER_AR8227)) { |
| return 0; |
| |
| } else { |
| SSDK_ERROR("unsupported QCA device\n"); |
| return -ENODEV; |
| } |
| } |
| |
| #if defined(IN_SWCONFIG) |
| static int qca_switchdev_register(struct qca_phy_priv *priv) |
| { |
| struct switch_dev *sw_dev; |
| int ret = SW_OK; |
| sw_dev = &priv->sw_dev; |
| |
| switch (priv->version) { |
| case QCA_VER_AR8227: |
| sw_dev->name = "QCA AR8227"; |
| sw_dev->alias = "QCA AR8227"; |
| break; |
| case QCA_VER_AR8327: |
| sw_dev->name = "QCA AR8327"; |
| sw_dev->alias = "QCA AR8327"; |
| break; |
| case QCA_VER_AR8337: |
| sw_dev->name = "QCA AR8337"; |
| sw_dev->alias = "QCA AR8337"; |
| break; |
| case QCA_VER_DESS: |
| sw_dev->name = "QCA DESS"; |
| sw_dev->alias = "QCA DESS"; |
| break; |
| case QCA_VER_HPPE: |
| sw_dev->name = "QCA HPPE"; |
| sw_dev->alias = "QCA HPPE"; |
| break; |
| case QCA_VER_SCOMPHY: |
| #ifdef MP |
| if(adapt_scomphy_revision_get(priv->device_id) |
| == MP_GEPHY) |
| { |
| sw_dev->name = "QCA MP"; |
| sw_dev->alias = "QCA MP"; |
| } |
| #endif |
| break; |
| default: |
| sw_dev->name = "unknown switch"; |
| sw_dev->alias = "unknown switch"; |
| break; |
| } |
| |
| sw_dev->ops = &qca_ar8327_sw_ops; |
| sw_dev->vlans = AR8327_MAX_VLANS; |
| sw_dev->ports = priv->ports; |
| |
| ret = register_switch(sw_dev, NULL); |
| if (ret != SW_OK) { |
| SSDK_ERROR("register_switch failed for %s\n", sw_dev->name); |
| return ret; |
| } |
| |
| return ret; |
| } |
| #endif |
| |
| static int |
| qca_phy_config_init(struct phy_device *pdev) |
| { |
| struct qca_phy_priv *priv = pdev->priv; |
| int ret = 0; |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| if (pdev->mdio.addr != 0) { |
| #else |
| if (pdev->addr != 0) { |
| #endif |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)) |
| pdev->supported |= SUPPORTED_1000baseT_Full; |
| pdev->advertising |= ADVERTISED_1000baseT_Full; |
| #else |
| linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
| pdev->supported); |
| linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
| pdev->advertising); |
| #endif |
| |
| #ifndef BOARD_AR71XX |
| #if defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) |
| ssdk_phy_rgmii_set(priv); |
| #endif |
| #endif |
| return 0; |
| } |
| |
| if (priv == NULL) |
| return -ENOMEM; |
| |
| priv->phy = pdev; |
| ret = qca_phy_id_chip(priv); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| priv->mii_read = qca_ar8216_mii_read; |
| priv->mii_write = qca_ar8216_mii_write; |
| priv->phy_write = qca_ar8327_phy_write; |
| priv->phy_read = qca_ar8327_phy_read; |
| priv->phy_dbg_write = qca_ar8327_phy_dbg_write; |
| priv->phy_dbg_read = qca_ar8327_phy_dbg_read; |
| priv->phy_mmd_write = qca_ar8327_mmd_write; |
| priv->ports = AR8327_NUM_PORTS; |
| |
| ret = qca_link_polling_select(priv); |
| if(ret) |
| priv->link_polling_required = 1; |
| pdev->priv = priv; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)) |
| pdev->supported |= SUPPORTED_1000baseT_Full; |
| pdev->advertising |= ADVERTISED_1000baseT_Full; |
| #else |
| linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
| pdev->supported); |
| linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
| pdev->advertising); |
| #endif |
| |
| #if defined(IN_SWCONFIG) |
| ret = qca_switchdev_register(priv); |
| if (ret != SW_OK) { |
| return ret; |
| } |
| #endif |
| priv->qca_ssdk_sw_dev_registered = A_TRUE; |
| |
| snprintf(priv->link_intr_name, IFNAMSIZ, "switch0"); |
| |
| ret = qca_ar8327_hw_init(priv); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| qca_phy_mib_work_start(priv); |
| |
| if(priv->link_polling_required) |
| { |
| SSDK_INFO("polling is selected\n"); |
| ret = qm_err_check_work_start(priv); |
| if (ret != 0) |
| { |
| SSDK_ERROR("qm_err_check_work_start failed for chip 0x%02x%02x\n", priv->version, priv->revision); |
| return ret; |
| } |
| } |
| else |
| { |
| SSDK_INFO("interrupt is selected\n"); |
| priv->interrupt_flag = IRQF_TRIGGER_LOW; |
| ret = qca_intr_init(priv); |
| if(ret) |
| SSDK_ERROR("the interrupt init faild !\n"); |
| } |
| |
| return ret; |
| } |
| |
| #if defined(DESS) || defined(HPPE) || defined (ISISC) || defined (ISIS) || defined(MP) |
| static int ssdk_switch_register(a_uint32_t dev_id, ssdk_chip_type chip_type) |
| { |
| struct qca_phy_priv *priv; |
| int ret = 0; |
| a_uint32_t chip_id = 0; |
| priv = qca_phy_priv_global[dev_id]; |
| |
| priv->mii_read = qca_ar8216_mii_read; |
| priv->mii_write = qca_ar8216_mii_write; |
| priv->phy_write = qca_ar8327_phy_write; |
| priv->phy_read = qca_ar8327_phy_read; |
| priv->phy_dbg_write = qca_ar8327_phy_dbg_write; |
| priv->phy_dbg_read = qca_ar8327_phy_dbg_read; |
| priv->phy_mmd_write = qca_ar8327_mmd_write; |
| |
| if (chip_type == CHIP_DESS) { |
| priv->ports = 6; |
| } else if ((chip_type == CHIP_ISIS) || (chip_type == CHIP_ISISC)) { |
| priv->ports = 7; |
| } else if (chip_type == CHIP_SCOMPHY) { |
| #ifdef MP |
| if(adapt_scomphy_revision_get(priv->device_id) == MP_GEPHY) { |
| /*for ipq50xx, port id is 1 and 2, port 0 is not available*/ |
| priv->ports = 3; |
| } |
| #endif |
| } else { |
| priv->ports = SSDK_MAX_PORT_NUM; |
| } |
| |
| #ifdef MP |
| if(chip_type == CHIP_SCOMPHY) |
| { |
| priv->version = QCA_VER_SCOMPHY; |
| SSDK_INFO("Chip version 0x%02x\n", priv->version); |
| } |
| else |
| #endif |
| { |
| if (fal_reg_get(dev_id, 0, (a_uint8_t *)&chip_id, 4) == SW_OK) { |
| priv->version = ((chip_id >> 8) & 0xff); |
| priv->revision = (chip_id & 0xff); |
| SSDK_INFO("Chip version 0x%02x%02x\n", priv->version, priv->revision); |
| } |
| } |
| |
| mutex_init(&priv->reg_mutex); |
| |
| #if defined(IN_SWCONFIG) |
| ret = qca_switchdev_register(priv); |
| if (ret != SW_OK) { |
| return ret; |
| } |
| #endif |
| |
| snprintf(priv->link_intr_name, IFNAMSIZ, "switch%d", dev_id); |
| |
| priv->qca_ssdk_sw_dev_registered = A_TRUE; |
| ret = qca_phy_mib_work_start(qca_phy_priv_global[dev_id]); |
| if (ret != 0) { |
| SSDK_ERROR("qca_phy_mib_work_start failed for chip 0x%02x%02x\n", priv->version, priv->revision); |
| return ret; |
| } |
| ret = qca_link_polling_select(priv); |
| if(ret) |
| priv->link_polling_required = 1; |
| if(priv->link_polling_required) |
| { |
| SSDK_INFO("polling is selected\n"); |
| ret = qm_err_check_work_start(priv); |
| if (ret != 0) |
| { |
| SSDK_ERROR("qm_err_check_work_start failed for chip 0x%02x%02x\n", priv->version, priv->revision); |
| return ret; |
| } |
| } |
| else |
| { |
| SSDK_INFO("interrupt is selected\n"); |
| priv->interrupt_flag = IRQF_TRIGGER_MASK; |
| ret = qca_intr_init(priv); |
| if(ret) |
| { |
| SSDK_ERROR("the interrupt init faild !\n"); |
| return ret; |
| } |
| } |
| |
| #if 0 |
| #ifdef DESS |
| if ((ssdk_dt_global.mac_mode == PORT_WRAPPER_SGMII0_RGMII5) |
| ||(ssdk_dt_global.mac_mode == PORT_WRAPPER_SGMII1_RGMII5) |
| ||(ssdk_dt_global.mac_mode == PORT_WRAPPER_SGMII0_RGMII4) |
| ||(ssdk_dt_global.mac_mode == PORT_WRAPPER_SGMII1_RGMII4) |
| ||(ssdk_dt_global.mac_mode == PORT_WRAPPER_SGMII4_RGMII4)) { |
| ret = dess_rgmii_mac_work_start(priv); |
| if (ret != 0) { |
| SSDK_ERROR("dess_rgmii_mac_work_start failed for chip 0x%02x%02x\n", priv->version, priv->revision); |
| return ret; |
| } |
| } |
| #endif |
| #endif |
| #ifdef HPPE |
| if (priv->version == QCA_VER_HPPE) { |
| ret = qca_mac_sw_sync_work_start(priv); |
| if (ret != 0) { |
| SSDK_ERROR("qca_mac_sw_sync_work_start failed for chip 0x%02x%02x\n", |
| priv->version, priv->revision); |
| return ret; |
| } |
| } |
| #endif |
| |
| return 0; |
| |
| } |
| |
| static int ssdk_switch_unregister(a_uint32_t dev_id) |
| { |
| qca_phy_mib_work_stop(qca_phy_priv_global[dev_id]); |
| qm_err_check_work_stop(qca_phy_priv_global[dev_id]); |
| #ifdef HPPE |
| qca_mac_sw_sync_work_stop(qca_phy_priv_global[dev_id]); |
| #endif |
| #if defined(IN_SWCONFIG) |
| unregister_switch(&qca_phy_priv_global[dev_id]->sw_dev); |
| #endif |
| return 0; |
| } |
| #endif |
| |
| static int |
| qca_phy_read_status(struct phy_device *pdev) |
| { |
| struct qca_phy_priv *priv = pdev->priv; |
| a_uint32_t port_status; |
| a_uint32_t port_speed; |
| int ret = 0, addr = 0; |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| addr = pdev->mdio.addr; |
| #else |
| addr = pdev->addr; |
| #endif |
| if (addr != 0) { |
| mutex_lock(&priv->reg_mutex); |
| ret = genphy_read_status(pdev); |
| mutex_unlock(&priv->reg_mutex); |
| return ret; |
| } |
| |
| mutex_lock(&priv->reg_mutex); |
| port_status = priv->mii_read(priv->device_id, AR8327_REG_PORT_STATUS(addr)); |
| mutex_unlock(&priv->reg_mutex); |
| |
| pdev->link = 1; |
| if (port_status & AR8327_PORT_STATUS_LINK_AUTO) { |
| pdev->link = !!(port_status & AR8327_PORT_STATUS_LINK_UP); |
| if (pdev->link == 0) { |
| return ret; |
| } |
| } |
| |
| port_speed = (port_status & AR8327_PORT_STATUS_SPEED) >> |
| AR8327_PORT_STATUS_SPEED_S; |
| |
| switch (port_speed) { |
| case AR8327_PORT_SPEED_10M: |
| pdev->speed = SPEED_10; |
| break; |
| case AR8327_PORT_SPEED_100M: |
| pdev->speed = SPEED_100; |
| break; |
| case AR8327_PORT_SPEED_1000M: |
| pdev->speed = SPEED_1000; |
| break; |
| default: |
| pdev->speed = 0; |
| break; |
| } |
| |
| if(port_status & AR8327_PORT_STATUS_DUPLEX) { |
| pdev->duplex = DUPLEX_FULL; |
| } else { |
| pdev->duplex = DUPLEX_HALF; |
| } |
| |
| pdev->state = PHY_RUNNING; |
| netif_carrier_on(pdev->attached_dev); |
| pdev->adjust_link(pdev->attached_dev); |
| |
| return ret; |
| } |
| |
| static int |
| qca_phy_config_aneg(struct phy_device *pdev) |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| if (pdev->mdio.addr != 0) { |
| #else |
| if (pdev->addr != 0) { |
| #endif |
| return genphy_config_aneg(pdev); |
| } |
| |
| return 0; |
| } |
| |
| int qca_phy_suspend(struct phy_device *phydev) |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| struct mii_bus *bus = phydev->mdio.bus; |
| #else |
| struct mii_bus *bus = phydev->bus; |
| #endif |
| int val = 0; |
| int addr; |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| addr = phydev->mdio.addr; |
| #else |
| addr = phydev->addr; |
| #endif |
| |
| val = mdiobus_read(bus, addr, MII_BMCR); |
| return mdiobus_write(bus, addr, MII_BMCR, (u16)(val | BMCR_PDOWN)); |
| } |
| |
| int qca_phy_resume(struct phy_device *phydev) |
| { |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| struct mii_bus *bus = phydev->mdio.bus; |
| #else |
| struct mii_bus *bus = phydev->bus; |
| #endif |
| int val = 0; |
| int addr; |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| addr = phydev->mdio.addr; |
| #else |
| addr = phydev->addr; |
| #endif |
| |
| val = mdiobus_read(bus, addr, MII_BMCR); |
| return mdiobus_write(bus, addr, MII_BMCR, (u16)(val & ~BMCR_PDOWN)); |
| } |
| |
| static int |
| qca_phy_probe(struct phy_device *pdev) |
| { |
| struct qca_phy_priv *priv; |
| int ret; |
| |
| priv = kzalloc(sizeof(struct qca_phy_priv), GFP_KERNEL); |
| if (priv == NULL) { |
| return -ENOMEM; |
| } |
| |
| pdev->priv = priv; |
| priv->phy = pdev; |
| mutex_init(&priv->reg_mutex); |
| |
| ret = qca_phy_id_chip(priv); |
| return ret; |
| } |
| |
| static void |
| qca_phy_remove(struct phy_device *pdev) |
| { |
| struct qca_phy_priv *priv = pdev->priv; |
| int addr; |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| addr = pdev->mdio.addr; |
| #else |
| addr = pdev->addr; |
| #endif |
| |
| if ((addr == 0) && priv && (priv->ports != 0)) { |
| qca_phy_mib_work_stop(priv); |
| qm_err_check_work_stop(priv); |
| #if defined(IN_SWCONFIG) |
| if (priv->sw_dev.name != NULL) |
| unregister_switch(&priv->sw_dev); |
| #endif |
| } |
| |
| if (priv) { |
| kfree(priv); |
| } |
| } |
| |
| static struct phy_driver qca_phy_driver = { |
| .name = "QCA AR8216 AR8236 AR8316 AR8327 AR8337", |
| .phy_id = 0x004d0000, |
| .phy_id_mask= 0xffff0000, |
| .probe = qca_phy_probe, |
| .remove = qca_phy_remove, |
| .config_init= &qca_phy_config_init, |
| .config_aneg= &qca_phy_config_aneg, |
| .read_status= &qca_phy_read_status, |
| .suspend = qca_phy_suspend, |
| .resume = qca_phy_resume, |
| .features = PHY_BASIC_FEATURES, |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| .mdiodrv.driver = { .owner = THIS_MODULE }, |
| #else |
| .driver = { .owner = THIS_MODULE }, |
| #endif |
| }; |
| |
| #ifndef BOARD_AR71XX |
| #if defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) |
| #ifdef DESS |
| struct reset_control *ess_rst = NULL; |
| struct reset_control *ess_mac_clock_disable[5] = {NULL,NULL,NULL,NULL,NULL}; |
| |
| void ssdk_ess_reset(void) |
| { |
| if (!ess_rst) |
| return; |
| reset_control_assert(ess_rst); |
| mdelay(10); |
| reset_control_deassert(ess_rst); |
| mdelay(100); |
| } |
| #endif |
| |
| char ssdk_driver_name[] = "ess_ssdk"; |
| |
| static int ssdk_probe(struct platform_device *pdev) |
| { |
| struct device_node *np; |
| |
| np = of_node_get(pdev->dev.of_node); |
| if (of_device_is_compatible(np, "qcom,ess-instance")) |
| return of_platform_populate(np, NULL, NULL, &pdev->dev); |
| |
| #ifdef DESS |
| ess_rst = devm_reset_control_get(&pdev->dev, "ess_rst"); |
| ess_mac_clock_disable[0] = devm_reset_control_get(&pdev->dev, "ess_mac1_clk_dis"); |
| ess_mac_clock_disable[1] = devm_reset_control_get(&pdev->dev, "ess_mac2_clk_dis"); |
| ess_mac_clock_disable[2] = devm_reset_control_get(&pdev->dev, "ess_mac3_clk_dis"); |
| ess_mac_clock_disable[3] = devm_reset_control_get(&pdev->dev, "ess_mac4_clk_dis"); |
| ess_mac_clock_disable[4] = devm_reset_control_get(&pdev->dev, "ess_mac5_clk_dis"); |
| |
| if (IS_ERR(ess_rst)) { |
| SSDK_INFO("ess_rst doesn't exist!\n"); |
| return 0; |
| } |
| if (!ess_mac_clock_disable[0]) { |
| SSDK_ERROR("ess_mac1_clock_disable fail!\n"); |
| return -1; |
| } |
| if (!ess_mac_clock_disable[1]) { |
| SSDK_ERROR("ess_mac2_clock_disable fail!\n"); |
| return -1; |
| } |
| if (!ess_mac_clock_disable[2]) { |
| SSDK_ERROR("ess_mac3_clock_disable fail!\n"); |
| return -1; |
| } |
| if (!ess_mac_clock_disable[3]) { |
| SSDK_ERROR("ess_mac4_clock_disable fail!\n"); |
| return -1; |
| } |
| if (!ess_mac_clock_disable[4]) { |
| SSDK_ERROR("ess_mac5_clock_disable fail!\n"); |
| return -1; |
| } |
| #endif |
| return 0; |
| } |
| |
| static const struct of_device_id ssdk_of_mtable[] = { |
| {.compatible = "qcom,ess-switch" }, |
| {.compatible = "qcom,ess-switch-ipq60xx" }, |
| {.compatible = "qcom,ess-switch-ipq807x" }, |
| {.compatible = "qcom,ess-instance" }, |
| {} |
| }; |
| |
| static struct platform_driver ssdk_driver = { |
| .driver = { |
| .name = ssdk_driver_name, |
| .owner = THIS_MODULE, |
| .of_match_table = ssdk_of_mtable, |
| }, |
| .probe = ssdk_probe, |
| }; |
| #endif |
| #endif |
| #ifdef DESS |
| static u32 phy_t_status = 0; |
| static a_uint16_t modectrl_data = 0; |
| void ssdk_malibu_psgmii_and_dakota_dess_reset(a_uint32_t dev_id, a_uint32_t first_phy_addr) |
| { |
| #ifndef BOARD_AR71XX |
| #if defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) |
| int m = 0, n = 0; |
| a_uint32_t psgmii_phy_addr; |
| |
| psgmii_phy_addr = first_phy_addr + 5; |
| |
| /*reset Malibu PSGMII and Dakota ESS start*/ |
| qca_ar8327_phy_write(dev_id, psgmii_phy_addr, 0x0, 0x005b);/*fix phy psgmii RX 20bit*/ |
| qca_ar8327_phy_write(dev_id, psgmii_phy_addr, 0x0, 0x001b);/*reset phy psgmii*/ |
| qca_ar8327_phy_write(dev_id, psgmii_phy_addr, 0x0, 0x005b);/*release reset phy psgmii*/ |
| /* mdelay(100); this 100ms be replaced with below malibu psgmii calibration process*/ |
| /*check malibu psgmii calibration done start*/ |
| n = 0; |
| while (n < 100) { |
| u16 status; |
| status = qca_phy_mmd_read(dev_id, psgmii_phy_addr, 1, 0x28); |
| if (status & BIT(0)) |
| break; |
| mdelay(10); |
| n++; |
| } |
| #ifdef PSGMII_DEBUG |
| if (n >= 100) |
| SSDK_INFO("MALIBU PSGMII PLL_VCO_CALIB NOT READY\n"); |
| #endif |
| mdelay(50); |
| /*check malibu psgmii calibration done end..*/ |
| qca_ar8327_phy_write(dev_id, psgmii_phy_addr, 0x1a, 0x2230);/*freeze phy psgmii RX CDR*/ |
| |
| ssdk_ess_reset(); |
| /*check dakota psgmii calibration done start*/ |
| m = 0; |
| while (m < 100) { |
| u32 status = 0; |
| qca_psgmii_reg_read(dev_id, 0xa0, (a_uint8_t *)&status, 4); |
| if (status & BIT(0)) |
| break; |
| mdelay(10); |
| m++; |
| } |
| #ifdef PSGMII_DEBUG |
| if (m >= 100) |
| SSDK_INFO("DAKOTA PSGMII PLL_VCO_CALIB NOT READY\n"); |
| #endif |
| mdelay(50); |
| /*check dakota psgmii calibration done end..*/ |
| qca_ar8327_phy_write(dev_id, psgmii_phy_addr, 0x1a, 0x3230);/*relesae phy psgmii RX CDR*/ |
| qca_ar8327_phy_write(dev_id, psgmii_phy_addr, 0x0, 0x005f);/*release phy psgmii RX 20bit*/ |
| mdelay(200); |
| #endif |
| #endif |
| /*reset Malibu PSGMII and Dakota ESS end*/ |
| return; |
| } |
| |
| static void ssdk_psgmii_phy_testing_printf(a_uint32_t phy, u32 tx_ok, u32 rx_ok, |
| u32 tx_counter_error, u32 rx_counter_error) |
| { |
| SSDK_INFO("tx_ok = 0x%x, rx_ok = 0x%x, tx_counter_error = 0x%x, rx_counter_error = 0x%x\n", |
| tx_ok, rx_ok, tx_counter_error, rx_counter_error); |
| if (tx_ok== 0x3000 && tx_counter_error == 0) |
| SSDK_INFO("PHY %d single PSGMII test pass\n", phy); |
| else |
| SSDK_ERROR("PHY %d single PSGMII test fail\n", phy); |
| return; |
| |
| } |
| static void ssdk_psgmii_all_phy_testing_printf(a_uint32_t phy, u32 tx_ok, u32 rx_ok, |
| u32 tx_counter_error, u32 rx_counter_error) |
| { |
| SSDK_INFO("tx_ok = 0x%x, rx_ok = 0x%x, tx_counter_error = 0x%x, rx_counter_error = 0x%x\n", |
| tx_ok, rx_ok, tx_counter_error, rx_counter_error); |
| if (tx_ok== 0x3000 && tx_counter_error == 0) |
| SSDK_INFO("PHY %d all PSGMII test pass\n", phy); |
| else |
| SSDK_ERROR("PHY %d all PSGMII test fail\n", phy); |
| return; |
| |
| } |
| void ssdk_psgmii_single_phy_testing(a_uint32_t dev_id, a_uint32_t phy, a_bool_t enable) |
| { |
| int j = 0; |
| |
| u32 tx_counter_ok, tx_counter_error; |
| u32 rx_counter_ok, rx_counter_error; |
| u32 tx_counter_ok_high16; |
| u32 rx_counter_ok_high16; |
| u32 tx_ok, rx_ok; |
| qca_ar8327_phy_write(dev_id, phy, 0x0, 0x9000); |
| qca_ar8327_phy_write(dev_id, phy, 0x0, 0x4140); |
| j = 0; |
| while (j < 100) { |
| u16 status = 0; |
| qca_ar8327_phy_read(dev_id, phy, 0x11, &status); |
| if (status & (1 << 10)) |
| break; |
| mdelay(10); |
| j++; |
| } |
| /*add a 300ms delay as qm polling task existing*/ |
| if (enable == A_TRUE) |
| mdelay(300); |
| |
| /*enable check*/ |
| qca_phy_mmd_write(dev_id, phy, 7, 0x8029, 0x0000); |
| qca_phy_mmd_write(dev_id, phy, 7, 0x8029, 0x0003); |
| |
| /*start traffic*/ |
| qca_phy_mmd_write(dev_id, phy, 7, 0x8020, 0xa000); |
| mdelay(200); |
| |
| /*check counter*/ |
| tx_counter_ok = qca_phy_mmd_read(dev_id, phy, 7, 0x802e); |
| tx_counter_ok_high16 = qca_phy_mmd_read(dev_id, phy, 7, 0x802d); |
| tx_counter_error = qca_phy_mmd_read(dev_id, phy, 7, 0x802f); |
| rx_counter_ok = qca_phy_mmd_read(dev_id, phy, 7, 0x802b); |
| rx_counter_ok_high16 = qca_phy_mmd_read(dev_id, phy, 7, 0x802a); |
| rx_counter_error = qca_phy_mmd_read(dev_id, phy, 7, 0x802c); |
| tx_ok = tx_counter_ok + (tx_counter_ok_high16<<16); |
| rx_ok = rx_counter_ok + (rx_counter_ok_high16<<16); |
| if (tx_ok== 0x3000 && tx_counter_error == 0) { |
| /*success*/ |
| phy_t_status &= (~(1<<phy)); |
| } else { |
| phy_t_status |= (1<<phy); |
| } |
| |
| if (enable == A_TRUE) |
| ssdk_psgmii_phy_testing_printf(phy, tx_ok, rx_ok, |
| tx_counter_error, rx_counter_error); |
| |
| qca_ar8327_phy_write(dev_id, phy, 0x0, 0x1840); |
| } |
| |
| void ssdk_psgmii_all_phy_testing(a_uint32_t dev_id, a_uint32_t first_phy_addr, a_bool_t enable) |
| { |
| int j = 0; |
| a_uint32_t phy = 0; |
| qca_ar8327_phy_write(dev_id, 0x1f, 0x0, 0x9000); |
| qca_ar8327_phy_write(dev_id, 0x1f, 0x0, 0x4140); |
| j = 0; |
| while (j < 100) { |
| for (phy = first_phy_addr; phy < first_phy_addr + 5; phy++) { |
| u16 status = 0; |
| qca_ar8327_phy_read(dev_id, phy, 0x11, &status); |
| if (!(status & (1 << 10))) |
| break; |
| } |
| |
| if (phy >= (first_phy_addr + 5)) |
| break; |
| mdelay(10); |
| j++; |
| } |
| /*add a 300ms delay as qm polling task existing*/ |
| if (enable == A_TRUE) |
| mdelay(300); |
| |
| /*enable check*/ |
| qca_phy_mmd_write(dev_id, 0x1f, 7, 0x8029, 0x0000); |
| qca_phy_mmd_write(dev_id, 0x1f, 7, 0x8029, 0x0003); |
| |
| /*start traffic*/ |
| qca_phy_mmd_write(dev_id, 0x1f, 7, 0x8020, 0xa000); |
| mdelay(200); |
| for (phy = first_phy_addr; phy < first_phy_addr + 5; phy++) { |
| u32 tx_counter_ok, tx_counter_error; |
| u32 rx_counter_ok, rx_counter_error; |
| u32 tx_counter_ok_high16; |
| u32 rx_counter_ok_high16; |
| u32 tx_ok, rx_ok; |
| /*check counter*/ |
| tx_counter_ok = qca_phy_mmd_read(dev_id, phy, 7, 0x802e); |
| tx_counter_ok_high16 = qca_phy_mmd_read(dev_id, phy, 7, 0x802d); |
| tx_counter_error = qca_phy_mmd_read(dev_id, phy, 7, 0x802f); |
| rx_counter_ok = qca_phy_mmd_read(dev_id, phy, 7, 0x802b); |
| rx_counter_ok_high16 = qca_phy_mmd_read(dev_id, phy, 7, 0x802a); |
| rx_counter_error = qca_phy_mmd_read(dev_id, phy, 7, 0x802c); |
| tx_ok = tx_counter_ok + (tx_counter_ok_high16<<16); |
| rx_ok = rx_counter_ok + (rx_counter_ok_high16<<16); |
| if (tx_ok== 0x3000 && tx_counter_error == 0) { |
| /*success*/ |
| phy_t_status &= (~(1<<(phy+8))); |
| } else { |
| phy_t_status |= (1<<(phy+8)); |
| } |
| |
| if (enable == A_TRUE) |
| ssdk_psgmii_all_phy_testing_printf(phy, tx_ok, |
| rx_ok, |
| tx_counter_error, rx_counter_error); |
| } |
| if (enable == A_TRUE) |
| SSDK_INFO("PHY final test result: 0x%x \r\n",phy_t_status); |
| |
| } |
| |
| void ssdk_psgmii_get_first_phy_address(a_uint32_t dev_id, |
| a_uint32_t *first_phy_addr) |
| { |
| a_uint32_t port_id = 0, phy_addr = 0, phy_cnt = 0; |
| a_uint32_t port_bmp[SW_MAX_NR_DEV] = {0}; |
| |
| port_bmp[dev_id] = qca_ssdk_phy_type_port_bmp_get(dev_id, MALIBU_PHY_CHIP); |
| |
| for (port_id = 0; port_id < SW_MAX_NR_PORT; port_id ++) |
| { |
| if (port_bmp[dev_id] & (0x1 << port_id)) |
| { |
| phy_cnt ++; |
| phy_addr = qca_ssdk_port_to_phy_addr(dev_id, port_id); |
| if (phy_addr < *first_phy_addr) { |
| *first_phy_addr = phy_addr; |
| } |
| } |
| } |
| if ((phy_cnt == QCA8072_PHY_NUM) && (*first_phy_addr >= 0x3)) { |
| *first_phy_addr = *first_phy_addr - 3; |
| } |
| } |
| |
| void ssdk_psgmii_self_test(a_uint32_t dev_id, a_bool_t enable, a_uint32_t times, |
| a_uint32_t *result) |
| { |
| int i = 0; |
| u32 value = 0; |
| a_uint32_t first_phy_addr = MAX_PHY_ADDR + 1, phy = 0; |
| |
| ssdk_psgmii_get_first_phy_address(dev_id, &first_phy_addr); |
| if ((first_phy_addr < 0) || (first_phy_addr > MAX_PHY_ADDR)) { |
| return; |
| } |
| |
| if (enable == A_FALSE) { |
| ssdk_malibu_psgmii_and_dakota_dess_reset(dev_id, first_phy_addr); |
| } |
| |
| qca_ar8327_phy_read(dev_id, first_phy_addr + 4, 0x1f, &modectrl_data); |
| qca_ar8327_phy_write(dev_id, first_phy_addr + 4, 0x1f, 0x8500);/*switch to access MII reg for copper*/ |
| for(phy = first_phy_addr; phy < first_phy_addr + 5; phy++) { |
| /*enable phy mdio broadcast write*/ |
| qca_phy_mmd_write(dev_id, phy, 7, 0x8028, 0x801f); |
| } |
| |
| /* force no link by power down */ |
| qca_ar8327_phy_write(dev_id, 0x1f, 0x0, 0x1840); |
| |
| /*packet number*/ |
| qca_phy_mmd_write(dev_id, 0x1f, 7, 0x8021, 0x3000); |
| qca_phy_mmd_write(dev_id, 0x1f, 7, 0x8062, 0x05e0); |
| |
| /*fix mdi status */ |
| qca_ar8327_phy_write(dev_id, 0x1f, 0x10, 0x6800); |
| |
| for(i = 0; i < times; i++) { |
| phy_t_status = 0; |
| |
| for(phy = 0; phy < 5; phy++) { |
| value = readl(qca_phy_priv_global[dev_id]->hw_addr + 0x66c + phy * 0xc); |
| writel((value|(1<<21)), (qca_phy_priv_global[dev_id]->hw_addr + 0x66c + phy * 0xc)); |
| } |
| |
| for (phy = first_phy_addr; phy < first_phy_addr + 5; phy++) { |
| ssdk_psgmii_single_phy_testing(dev_id, phy, enable); |
| } |
| ssdk_psgmii_all_phy_testing(dev_id, first_phy_addr, enable); |
| if (enable == A_FALSE) { |
| if (phy_t_status) { |
| ssdk_malibu_psgmii_and_dakota_dess_reset(dev_id, first_phy_addr); |
| } |
| else |
| { |
| break; |
| } |
| } |
| } |
| |
| *result = phy_t_status; |
| #ifdef PSGMII_DEBUG |
| if (i>=100) |
| SSDK_ERROR("PSGMII cannot recover\n"); |
| else |
| SSDK_INFO("PSGMII recovered after %d times reset\n",i); |
| #endif |
| /*configuration recover*/ |
| /*packet number*/ |
| qca_phy_mmd_write(dev_id, 0x1f, 7, 0x8021, 0x0); |
| /*disable check*/ |
| qca_phy_mmd_write(dev_id, 0x1f, 7, 0x8029, 0x0); |
| /*disable traffic*/ |
| qca_phy_mmd_write(dev_id, 0x1f, 7, 0x8020, 0x0); |
| } |
| |
| |
| void clear_self_test_config(a_uint32_t dev_id) |
| { |
| u32 value = 0; |
| a_uint32_t first_phy_addr = MAX_PHY_ADDR + 1, phy = 0; |
| |
| ssdk_psgmii_get_first_phy_address(dev_id, &first_phy_addr); |
| if ((first_phy_addr < 0) || (first_phy_addr > MAX_PHY_ADDR)) { |
| return; |
| } |
| |
| /* disable EEE */ |
| /* qca_phy_mmd_write(0, 0x1f, 0x7, 0x3c, 0x0); */ |
| |
| /*disable phy internal loopback*/ |
| qca_ar8327_phy_write(dev_id, 0x1f, 0x10, 0x6860); |
| qca_ar8327_phy_write(dev_id, 0x1f, 0x0, 0x9040); |
| |
| for(phy = 0; phy < 5; phy++) |
| { |
| /*disable mac loop back*/ |
| value = readl(qca_phy_priv_global[dev_id]->hw_addr+0x66c+phy*0xc); |
| writel((value&(~(1<<21))), (qca_phy_priv_global[dev_id]->hw_addr+0x66c+phy*0xc)); |
| } |
| |
| for(phy = first_phy_addr; phy < first_phy_addr + 5; phy++) |
| { |
| /*diable phy mdio broadcast write*/ |
| qca_phy_mmd_write(dev_id, phy, 7, 0x8028, 0x001f); |
| |
| } |
| |
| qca_ar8327_phy_write(dev_id, first_phy_addr + 4, 0x1f, modectrl_data); |
| |
| /* clear fdb entry */ |
| fal_fdb_entry_flush(dev_id,1); |
| } |
| #endif |
| /*qca808x_start*/ |
| sw_error_t |
| ssdk_init(a_uint32_t dev_id, ssdk_init_cfg * cfg) |
| { |
| sw_error_t rv; |
| |
| rv = fal_init(dev_id, cfg); |
| if (rv != SW_OK) |
| SSDK_ERROR("ssdk fal init failed: %d. \r\n", rv); |
| |
| rv = ssdk_phy_driver_init(dev_id, cfg); |
| if (rv != SW_OK) |
| SSDK_ERROR("ssdk phy init failed: %d. \r\n", rv); |
| |
| return rv; |
| } |
| |
| sw_error_t |
| ssdk_cleanup(void) |
| { |
| sw_error_t rv; |
| |
| rv = fal_cleanup(); |
| rv = ssdk_phy_driver_cleanup(); |
| |
| return rv; |
| } |
| /*qca808x_end*/ |
| |
| sw_error_t |
| ssdk_hsl_access_mode_set(a_uint32_t dev_id, hsl_access_mode reg_mode) |
| { |
| sw_error_t rv; |
| |
| rv = hsl_access_mode_set(dev_id, reg_mode); |
| return rv; |
| } |
| |
| void switch_cpuport_setup(a_uint32_t dev_id) |
| { |
| #ifdef IN_PORTCONTROL |
| //According to HW suggestion, enable CPU port flow control for Dakota |
| fal_port_flowctrl_forcemode_set(dev_id, 0, A_TRUE); |
| fal_port_flowctrl_set(dev_id, 0, A_TRUE); |
| fal_port_duplex_set(dev_id, 0, FAL_FULL_DUPLEX); |
| fal_port_speed_set(dev_id, 0, FAL_SPEED_1000); |
| udelay(10); |
| fal_port_txmac_status_set(dev_id, 0, A_TRUE); |
| fal_port_rxmac_status_set(dev_id, 0, A_TRUE); |
| #endif |
| } |
| #ifdef IN_AQUANTIA_PHY |
| #ifdef CONFIG_MDIO |
| static struct mdio_if_info ssdk_mdio_ctl; |
| #endif |
| static struct net_device *ssdk_miireg_netdev = NULL; |
| |
| static int ssdk_miireg_open(struct net_device *netdev) |
| { |
| return 0; |
| } |
| static int ssdk_miireg_close(struct net_device *netdev) |
| { |
| return 0; |
| } |
| |
| static int ssdk_miireg_do_ioctl(struct net_device *netdev, |
| struct ifreq *ifr, int32_t cmd) |
| { |
| int ret = -EINVAL; |
| #ifdef CONFIG_MDIO |
| struct mii_ioctl_data *mii_data = if_mii(ifr); |
| ret = mdio_mii_ioctl(&ssdk_mdio_ctl, mii_data, cmd); |
| #endif |
| return ret; |
| } |
| |
| static const struct net_device_ops ssdk_netdev_ops = { |
| .ndo_open = &ssdk_miireg_open, |
| .ndo_stop = &ssdk_miireg_close, |
| .ndo_do_ioctl = &ssdk_miireg_do_ioctl, |
| }; |
| |
| #ifdef CONFIG_MDIO |
| extern struct mutex switch_mdio_lock; |
| static int ssdk_miireg_ioctl_read(struct net_device *netdev, int phy_addr, int mmd, uint16_t addr) |
| { |
| a_uint32_t reg = 0; |
| a_uint16_t val = 0; |
| |
| if (MDIO_DEVAD_NONE == mmd) { |
| qca_ar8327_phy_read(0, phy_addr, addr, &val); |
| return (int)val; |
| } |
| |
| reg = MII_ADDR_C45 | mmd << 16 | addr; |
| mutex_lock(&switch_mdio_lock); |
| qca_ar8327_phy_read(0, phy_addr, reg, &val); |
| mutex_unlock(&switch_mdio_lock); |
| |
| return (int)val; |
| } |
| |
| static int ssdk_miireg_ioctl_write(struct net_device *netdev, int phy_addr, int mmd, |
| uint16_t addr, uint16_t value) |
| { |
| a_uint32_t reg = 0; |
| |
| if (MDIO_DEVAD_NONE == mmd) { |
| qca_ar8327_phy_write(0, phy_addr, addr, value); |
| return 0; |
| } |
| |
| reg = MII_ADDR_C45 | mmd << 16 | addr; |
| mutex_lock(&switch_mdio_lock); |
| qca_ar8327_phy_write(0, phy_addr, reg, value); |
| mutex_unlock(&switch_mdio_lock); |
| |
| return 0; |
| } |
| #endif |
| |
| static void ssdk_netdev_setup(struct net_device *dev) |
| { |
| dev->netdev_ops = &ssdk_netdev_ops; |
| } |
| static void ssdk_miireg_ioctrl_register(void) |
| { |
| if (ssdk_miireg_netdev) |
| return; |
| #ifdef CONFIG_MDIO |
| ssdk_mdio_ctl.mdio_read = ssdk_miireg_ioctl_read; |
| ssdk_mdio_ctl.mdio_write = ssdk_miireg_ioctl_write; |
| ssdk_mdio_ctl.mode_support = MDIO_SUPPORTS_C45; |
| #endif |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0)) |
| ssdk_miireg_netdev = alloc_netdev(100, "miireg", 0, ssdk_netdev_setup); |
| #else |
| ssdk_miireg_netdev = alloc_netdev(100, "miireg", ssdk_netdev_setup); |
| #endif |
| if (ssdk_miireg_netdev) |
| register_netdev(ssdk_miireg_netdev); |
| } |
| |
| static void ssdk_miireg_ioctrl_unregister(void) |
| { |
| if (ssdk_miireg_netdev) { |
| unregister_netdev(ssdk_miireg_netdev); |
| kfree(ssdk_miireg_netdev); |
| ssdk_miireg_netdev = NULL; |
| } |
| } |
| #endif |
| static void ssdk_driver_register(a_uint32_t dev_id) |
| { |
| hsl_reg_mode reg_mode; |
| a_bool_t flag; |
| |
| reg_mode = ssdk_switch_reg_access_mode_get(dev_id); |
| |
| if(reg_mode == HSL_REG_LOCAL_BUS) { |
| #ifndef BOARD_AR71XX |
| #if defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) |
| platform_driver_register(&ssdk_driver); |
| #endif |
| #endif |
| } |
| |
| flag = ssdk_ess_switch_flag_get(dev_id); |
| if(reg_mode == HSL_REG_MDIO && flag == A_FALSE) { |
| if(driver_find(qca_phy_driver.name, &mdio_bus_type)){ |
| SSDK_ERROR("QCA PHY driver had been Registered\n"); |
| return; |
| } |
| |
| SSDK_INFO("Register QCA PHY driver\n"); |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0)) |
| phy_driver_register(&qca_phy_driver, THIS_MODULE); |
| #else |
| phy_driver_register(&qca_phy_driver); |
| #endif |
| |
| #ifdef BOARD_AR71XX |
| #if defined(IN_SWCONFIG) |
| ssdk_uci_takeover_init(); |
| #endif |
| |
| #ifdef CONFIG_AR8216_PHY |
| ar8327_port_link_notify_register(ssdk_port_link_notify); |
| #endif |
| ar7240_port_link_notify_register(ssdk_port_link_notify); |
| #endif |
| } |
| } |
| |
| static void ssdk_driver_unregister(a_uint32_t dev_id) |
| { |
| hsl_reg_mode reg_mode; |
| a_bool_t flag; |
| |
| reg_mode= ssdk_switch_reg_access_mode_get(dev_id); |
| flag = ssdk_ess_switch_flag_get(dev_id); |
| if(reg_mode == HSL_REG_MDIO && flag == A_FALSE) { |
| phy_driver_unregister(&qca_phy_driver); |
| |
| #if defined(BOARD_AR71XX) && defined(IN_SWCONFIG) |
| ssdk_uci_takeover_exit(); |
| #endif |
| } |
| |
| if (reg_mode == HSL_REG_LOCAL_BUS) { |
| #ifndef BOARD_AR71XX |
| #if defined(CONFIG_OF) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)) |
| platform_driver_unregister(&ssdk_driver); |
| #endif |
| #endif |
| } |
| } |
| /*qca808x_start*/ |
| static int chip_is_scomphy(a_uint32_t dev_id, ssdk_init_cfg* cfg) |
| { |
| int rv = -ENODEV; |
| a_uint32_t phy_id = 0, port_id = 0; |
| a_uint32_t port_bmp = qca_ssdk_port_bmp_get(dev_id); |
| while (port_bmp) { |
| if (port_bmp & 0x1) { |
| phy_id = hsl_phyid_get(dev_id, port_id, cfg); |
| switch (phy_id) { |
| /*qca808x_end*/ |
| case QCA8030_PHY: |
| case QCA8033_PHY: |
| case QCA8035_PHY: |
| case MP_GEPHY: |
| /*qca808x_start*/ |
| case QCA8081_PHY_V1_1: |
| cfg->chip_type = CHIP_SCOMPHY; |
| /*MP GEPHY is always the first port*/ |
| if(cfg->phy_id == 0) |
| { |
| cfg->phy_id = phy_id; |
| } |
| rv = SW_OK; |
| break; |
| default: |
| break; |
| } |
| } |
| port_bmp >>= 1; |
| port_id++; |
| } |
| |
| return rv; |
| } |
| |
| static int chip_ver_get(a_uint32_t dev_id, ssdk_init_cfg* cfg) |
| { |
| int rv = SW_OK; |
| a_uint8_t chip_ver = 0; |
| a_uint8_t chip_revision = 0; |
| /*qca808x_end*/ |
| hsl_reg_mode reg_mode; |
| |
| reg_mode= ssdk_switch_reg_access_mode_get(dev_id); |
| if(reg_mode == HSL_REG_MDIO) |
| { |
| chip_ver = (qca_ar8216_mii_read(dev_id, 0)&0xff00)>>8; |
| } |
| else { |
| a_uint32_t reg_val = 0; |
| qca_switch_reg_read(dev_id,0,(a_uint8_t *)®_val, 4); |
| chip_ver = (reg_val&0xff00)>>8; |
| chip_revision = reg_val&0xff; |
| } |
| /*qca808x_start*/ |
| if(chip_ver == QCA_VER_AR8227) |
| cfg->chip_type = CHIP_SHIVA; |
| else if(chip_ver == QCA_VER_AR8337) |
| cfg->chip_type = CHIP_ISISC; |
| else if(chip_ver == QCA_VER_AR8327) |
| cfg->chip_type = CHIP_ISIS; |
| else if(chip_ver == QCA_VER_DESS) |
| cfg->chip_type = CHIP_DESS; |
| else if(chip_ver == QCA_VER_HPPE) { |
| cfg->chip_type = CHIP_HPPE; |
| cfg->chip_revision = chip_revision; |
| } |
| else { |
| /* try single phy without switch connected */ |
| rv = chip_is_scomphy(dev_id, cfg); |
| } |
| |
| return rv; |
| } |
| /*qca808x_end*/ |
| |
| #ifdef DESS |
| static int ssdk_flow_default_act_init(a_uint32_t dev_id) |
| { |
| a_uint32_t vrf_id = 0; |
| fal_flow_type_t type = 0; |
| for(vrf_id = FAL_MIN_VRF_ID; vrf_id <= FAL_MAX_VRF_ID; vrf_id++) |
| { |
| for(type = FAL_FLOW_LAN_TO_LAN; type <= FAL_FLOW_WAN_TO_WAN; type++) |
| { |
| #ifdef IN_IP |
| #ifndef IN_IP_MINI |
| fal_default_flow_cmd_set(dev_id, vrf_id, type, FAL_DEFAULT_FLOW_ADMIT_ALL); |
| #endif |
| #endif |
| } |
| } |
| |
| return 0; |
| } |
| static int ssdk_dess_mac_mode_init(a_uint32_t dev_id, a_uint32_t mac_mode) |
| { |
| a_uint32_t reg_value; |
| u8 __iomem *gcc_addr = NULL; |
| |
| switch(mac_mode) { |
| case PORT_WRAPPER_PSGMII: |
| case PORT_WRAPPER_PSGMII_FIBER: |
| reg_value = 0x2200; |
| qca_psgmii_reg_write(dev_id, DESS_PSGMII_MODE_CONTROL, |
| (a_uint8_t *)®_value, 4); |
| reg_value = 0x8380; |
| qca_psgmii_reg_write(dev_id, DESS_PSGMIIPHY_TX_CONTROL, |
| (a_uint8_t *)®_value, 4); |
| break; |
| case PORT_WRAPPER_SGMII0_RGMII5: |
| case PORT_WRAPPER_SGMII1_RGMII5: |
| case PORT_WRAPPER_SGMII0_RGMII4: |
| case PORT_WRAPPER_SGMII1_RGMII4: |
| case PORT_WRAPPER_SGMII4_RGMII4: |
| |
| /*config sgmii */ |
| if ((mac_mode == PORT_WRAPPER_SGMII0_RGMII5) |
| ||(mac_mode == PORT_WRAPPER_SGMII0_RGMII4)) { |
| /*PSGMII channnel 0 as SGMII*/ |
| reg_value = 0x2001; |
| fal_psgmii_reg_set(dev_id, 0x1b4, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| } |
| if ((mac_mode == PORT_WRAPPER_SGMII1_RGMII5) |
| ||(mac_mode == PORT_WRAPPER_SGMII1_RGMII4)) { |
| /*PSGMII channnel 1 as SGMII*/ |
| reg_value = 0x2003; |
| fal_psgmii_reg_set(dev_id, 0x1b4, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| } |
| if ((mac_mode == PORT_WRAPPER_SGMII4_RGMII4)) { |
| /*PSGMII channnel 4 as SGMII*/ |
| reg_value = 0x2005; |
| fal_psgmii_reg_set(dev_id, 0x1b4, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| } |
| |
| /*clock gen 1*/ |
| reg_value = 0xea6; |
| fal_psgmii_reg_set(dev_id, 0x13c, |
| (a_uint8_t *)®_value, 4); |
| mdelay(10); |
| /*softreset psgmii, fixme*/ |
| gcc_addr = ioremap_nocache(0x1812000, 0x200); |
| if (!gcc_addr) { |
| SSDK_ERROR("gcc map fail!\n"); |
| return 0; |
| } else { |
| SSDK_INFO("gcc map success!\n"); |
| writel(0x20, gcc_addr+0xc); |
| mdelay(10); |
| writel(0x0, gcc_addr+0xc); |
| mdelay(10); |
| iounmap(gcc_addr); |
| } |
| /*relock pll*/ |
| reg_value = 0x2803; |
| fal_psgmii_reg_set(dev_id, DESS_PSGMII_PLL_VCO_RELATED_CONTROL_1, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| reg_value = 0x4ADA; |
| fal_psgmii_reg_set(dev_id, DESS_PSGMII_VCO_CALIBRATION_CONTROL_1, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| reg_value = 0xADA; |
| fal_psgmii_reg_set(dev_id, DESS_PSGMII_VCO_CALIBRATION_CONTROL_1, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| |
| /* Reconfig channel 0 as SGMII and re autoneg*/ |
| if ((mac_mode == PORT_WRAPPER_SGMII0_RGMII5) |
| ||(mac_mode == PORT_WRAPPER_SGMII0_RGMII4)) { |
| /*PSGMII channnel 0 as SGMII*/ |
| reg_value = 0x2001; |
| fal_psgmii_reg_set(dev_id, 0x1b4, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| /* restart channel 0 autoneg*/ |
| reg_value = 0xc4; |
| fal_psgmii_reg_set(dev_id, 0x1c8, |
| (a_uint8_t *)®_value, 4); |
| mdelay(10); |
| reg_value = 0x44; |
| fal_psgmii_reg_set(dev_id, 0x1c8, |
| (a_uint8_t *)®_value, 4); |
| mdelay(10); |
| } |
| /* Reconfig channel 1 as SGMII and re autoneg*/ |
| if ((mac_mode == PORT_WRAPPER_SGMII1_RGMII5) |
| ||(mac_mode == PORT_WRAPPER_SGMII1_RGMII4)) { |
| |
| /*PSGMII channnel 1 as SGMII*/ |
| reg_value = 0x2003; |
| fal_psgmii_reg_set(dev_id, 0x1b4, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| /* restart channel 1 autoneg*/ |
| reg_value = 0xc4; |
| fal_psgmii_reg_set(dev_id, 0x1e0, |
| (a_uint8_t *)®_value, 4); |
| mdelay(10); |
| reg_value = 0x44; |
| fal_psgmii_reg_set(dev_id, 0x1e0, |
| (a_uint8_t *)®_value, 4); |
| mdelay(10); |
| |
| } |
| /* Reconfig channel 4 as SGMII and re autoneg*/ |
| if ((mac_mode == PORT_WRAPPER_SGMII4_RGMII4)) { |
| /*PSGMII channnel 4 as SGMII*/ |
| reg_value = 0x2005; |
| fal_psgmii_reg_set(dev_id, 0x1b4, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| /* restart channel 4 autoneg*/ |
| reg_value = 0xc4; |
| fal_psgmii_reg_set(dev_id, 0x228, |
| (a_uint8_t *)®_value, 4); |
| mdelay(10); |
| reg_value = 0x44; |
| fal_psgmii_reg_set(dev_id, 0x228, |
| (a_uint8_t *)®_value, 4); |
| mdelay(10); |
| } |
| |
| /* config RGMII*/ |
| reg_value = 0x400; |
| fal_reg_set(dev_id, 0x4, (a_uint8_t *)®_value, 4); |
| /* config mac5 RGMII*/ |
| if ((mac_mode == PORT_WRAPPER_SGMII0_RGMII5) |
| ||(mac_mode == PORT_WRAPPER_SGMII1_RGMII5)) { |
| qca_ar8327_phy_dbg_write(0, 4, 0x5, 0x2d47); |
| qca_ar8327_phy_dbg_write(0, 4, 0xb, 0xbc40); |
| qca_ar8327_phy_dbg_write(0, 4, 0x0, 0x82ee); |
| reg_value = 0x72; |
| qca_switch_reg_write(dev_id, 0x90, (a_uint8_t *)®_value, 4); |
| } |
| /* config mac4 RGMII*/ |
| if ((mac_mode == PORT_WRAPPER_SGMII0_RGMII4) |
| ||(mac_mode == PORT_WRAPPER_SGMII1_RGMII4) |
| ||(mac_mode == PORT_WRAPPER_SGMII4_RGMII4)) { |
| qca_ar8327_phy_dbg_write(dev_id, 4, 0x5, 0x2d47); |
| qca_ar8327_phy_dbg_write(dev_id, 4, 0xb, 0xbc40); |
| qca_ar8327_phy_dbg_write(dev_id, 4, 0x0, 0x82ee); |
| reg_value = 0x72; |
| qca_switch_reg_write(dev_id, 0x8c, (a_uint8_t *)®_value, 4); |
| } |
| break; |
| case PORT_WRAPPER_PSGMII_RMII0_RMII1: |
| case PORT_WRAPPER_PSGMII_RMII0: |
| case PORT_WRAPPER_PSGMII_RMII1: |
| reg_value = 0x2200; |
| qca_psgmii_reg_write(dev_id, DESS_PSGMII_MODE_CONTROL, |
| (a_uint8_t *)®_value, 4); |
| reg_value = 0x8380; |
| qca_psgmii_reg_write(dev_id, DESS_PSGMIIPHY_TX_CONTROL, |
| (a_uint8_t *)®_value, 4); |
| /*switch RMII clock source to gcc_ess_clk,ESS_RGMII_CTRL:0x0C000004,dakota rmii1/rmii0 master mode*/ |
| if(mac_mode== PORT_WRAPPER_PSGMII_RMII0_RMII1) |
| reg_value = 0x3000000; |
| if(mac_mode== PORT_WRAPPER_PSGMII_RMII0) |
| reg_value = 0x1000000; |
| if(mac_mode== PORT_WRAPPER_PSGMII_RMII1) |
| reg_value = 0x2000000; |
| qca_switch_reg_write(dev_id, 0x4, (a_uint8_t *)®_value, 4); |
| /*enable RMII MAC5 100M/full*/ |
| if(mac_mode == PORT_WRAPPER_PSGMII_RMII0_RMII1 || mac_mode == PORT_WRAPPER_PSGMII_RMII0) |
| { |
| reg_value = 0x7d; |
| qca_switch_reg_write(dev_id, 0x90, (a_uint8_t *)®_value, 4); |
| } |
| |
| /*enable RMII MAC4 100M/full*/ |
| if(mac_mode == PORT_WRAPPER_PSGMII_RMII0_RMII1 || mac_mode == PORT_WRAPPER_PSGMII_RMII1) |
| { |
| reg_value = 0x7d; |
| qca_switch_reg_write(dev_id, 0x8C, (a_uint8_t *)®_value, 4); |
| } |
| /*set QM CONTROL REGISTER FLOW_DROP_CNT as max*/ |
| reg_value = 0x7f007f; |
| qca_switch_reg_write(dev_id, 0x808, (a_uint8_t *)®_value, 4); |
| |
| /*relock PSGMII PLL*/ |
| reg_value = 0x2803; |
| fal_psgmii_reg_set(dev_id, DESS_PSGMII_PLL_VCO_RELATED_CONTROL_1, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| reg_value = 0x4ADA; |
| fal_psgmii_reg_set(dev_id, DESS_PSGMII_VCO_CALIBRATION_CONTROL_1, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| reg_value = 0xADA; |
| fal_psgmii_reg_set(dev_id, DESS_PSGMII_VCO_CALIBRATION_CONTROL_1, |
| (a_uint8_t *)®_value, 4); |
| udelay(1000); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| #ifdef IN_TRUNK |
| #define MULTIPLE_WAN_PORT_CNT 2 |
| #define TRUNK_ID_OF_MULTIPLE_WAN_PORTS 0 |
| |
| static a_bool_t |
| ssdk_dess_multiple_wan_port_check(a_uint32_t dev_id, |
| a_uint32_t wan_bitmap) |
| { |
| a_uint32_t port_id = SSDK_PHYSICAL_PORT0, wan_ports_cnt = 0; |
| |
| for(port_id = SSDK_PHYSICAL_PORT0; port_id < SSDK_MAX_PORT_NUM; |
| port_id++) |
| { |
| if(BIT(port_id) & wan_bitmap) |
| { |
| wan_ports_cnt++; |
| } |
| } |
| if(wan_ports_cnt >= MULTIPLE_WAN_PORT_CNT) |
| { |
| return A_TRUE; |
| } |
| else |
| { |
| return A_FALSE; |
| } |
| } |
| |
| sw_error_t |
| ssdk_dess_trunk_init(a_uint32_t dev_id, a_uint32_t wan_bitmap) |
| { |
| sw_error_t rv = SW_OK; |
| |
| if(ssdk_dess_multiple_wan_port_check(dev_id, wan_bitmap)) |
| { |
| rv = fal_trunk_group_set(dev_id, TRUNK_ID_OF_MULTIPLE_WAN_PORTS, |
| A_TRUE, wan_bitmap); |
| SW_RTN_ON_ERROR(rv); |
| } |
| |
| return rv; |
| } |
| #endif |
| |
| static sw_error_t |
| qca_dess_hw_init(ssdk_init_cfg *cfg, a_uint32_t dev_id) |
| { |
| a_uint32_t reg_value = 0; |
| hsl_api_t *p_api; |
| a_uint32_t psgmii_result = 0; |
| a_uint32_t mac_mode; |
| |
| mac_mode = ssdk_dt_global_get_mac_mode(dev_id, 0); |
| /*Do Malibu self test to fix packet drop issue firstly*/ |
| if ((mac_mode == PORT_WRAPPER_PSGMII) || |
| (mac_mode == PORT_WRAPPER_PSGMII_FIBER)) { |
| ssdk_psgmii_self_test(dev_id, A_FALSE, 100, &psgmii_result); |
| clear_self_test_config(dev_id); |
| } else { |
| #ifndef BOARD_AR71XX |
| ssdk_ess_reset(); |
| #endif |
| } |
| |
| qca_switch_init(dev_id); |
| #ifdef IN_PORTVLAN |
| ssdk_portvlan_init(dev_id); |
| #endif |
| |
| #ifdef IN_PORTVLAN |
| fal_port_rxhdr_mode_set(dev_id, 0, FAL_ALL_TYPE_FRAME_EN); |
| #endif |
| #ifdef IN_IP |
| #ifndef IN_IP_MINI |
| fal_ip_route_status_set(dev_id, A_TRUE); |
| #endif |
| #endif |
| |
| ssdk_flow_default_act_init(dev_id); |
| |
| /*set normal hash and disable nat/napt*/ |
| qca_switch_reg_read(dev_id, 0x0e38, (a_uint8_t *)®_value, 4); |
| reg_value = (reg_value|0x1000000|0x8); |
| reg_value &= ~2; |
| qca_switch_reg_write(dev_id, 0x0e38, (a_uint8_t *)®_value, 4); |
| #ifdef IN_IP |
| #ifndef IN_IP_MINI |
| fal_ip_vrf_base_addr_set(dev_id, 0, 0); |
| #endif |
| #endif |
| |
| p_api = hsl_api_ptr_get (dev_id); |
| if (p_api && p_api->port_flowctrl_thresh_set) |
| p_api->port_flowctrl_thresh_set(dev_id, 0, SSDK_PORT0_FC_THRESH_ON_DFLT, |
| SSDK_PORT0_FC_THRESH_OFF_DFLT); |
| |
| if (p_api && p_api->ip_glb_lock_time_set) |
| p_api->ip_glb_lock_time_set(dev_id, FAL_GLB_LOCK_TIME_100US); |
| |
| |
| /*config psgmii,sgmii or rgmii mode for Dakota*/ |
| ssdk_dess_mac_mode_init(dev_id, cfg->mac_mode); |
| |
| #ifdef IN_LED |
| /*add BGA Board led contorl*/ |
| ssdk_dess_led_init(cfg); |
| #endif |
| #ifdef IN_TRUNK |
| SW_RTN_ON_ERROR(ssdk_dess_trunk_init(dev_id, cfg->port_cfg.wan_bmp)); |
| #endif |
| |
| return SW_OK; |
| } |
| #endif |
| /*qca808x_start*/ |
| static void ssdk_cfg_default_init(ssdk_init_cfg *cfg) |
| { |
| memset(cfg, 0, sizeof(ssdk_init_cfg)); |
| cfg->cpu_mode = HSL_CPU_1; |
| cfg->nl_prot = 30; |
| cfg->reg_func.mdio_set = qca_ar8327_phy_write; |
| cfg->reg_func.mdio_get = qca_ar8327_phy_read; |
| #if defined(IN_PHY_I2C_MODE) |
| cfg->reg_func.i2c_set = qca_phy_i2c_write; |
| cfg->reg_func.i2c_get = qca_phy_i2c_read; |
| #endif |
| /*qca808x_end*/ |
| |
| cfg->reg_func.header_reg_set = qca_switch_reg_write; |
| cfg->reg_func.header_reg_get = qca_switch_reg_read; |
| cfg->reg_func.mii_reg_set = qca_ar8216_mii_write; |
| cfg->reg_func.mii_reg_get = qca_ar8216_mii_read; |
| /*qca808x_start*/ |
| } |
| /*qca808x_end*/ |
| |
| #ifdef IN_RFS |
| #if defined(CONFIG_RFS_ACCEL) |
| int ssdk_netdev_rfs_cb( |
| struct net_device *dev, |
| __be32 src, __be32 dst, |
| __be16 sport, __be16 dport, |
| u8 proto, u16 rxq_index, u32 action) |
| { |
| return ssdk_rfs_ipct_rule_set(src, dst, sport, dport, |
| proto, rxq_index, action); |
| } |
| #endif |
| |
| #ifdef DESS |
| a_bool_t ssdk_intf_search( |
| a_uint8_t *mac, a_uint16_t vid, |
| a_uint8_t *ret_index, a_uint8_t *free_index) |
| { |
| a_uint8_t i = 0; |
| |
| for (i = 0; i < SSDK_RFS_INTF_MAX; i++) { |
| if (rfs_intf_tbl[i].vid == 0) |
| *free_index = i; |
| if (!memcmp(rfs_intf_tbl[i].macaddr.uc, mac, 6) && |
| rfs_intf_tbl[i].vid == vid) { |
| /* find it */ |
| *ret_index = i; |
| return A_TRUE; |
| } |
| } |
| |
| /* Not find the same entry */ |
| return A_FALSE; |
| } |
| |
| static a_bool_t ssdk_is_raw_dev(struct |