linux/drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
Linus Torvalds 6b5e09a748 Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Pull networking fixes from David Miller:

 1) Netfilter list handling fix, from Linus.

 2) RXRPC/AFS bug fixes from David Howells (oops on call to serviceless
    endpoints, build warnings, missing notifications, etc.) From David
    Howells.

 3) Kernel log message missing newlines, from Colin Ian King.

 4) Don't enter direct reclaim in netlink dumps, the idea is to use a
    high order allocation first and fallback quickly to a 0-order
    allocation if such a high-order one cannot be done cheaply and
    without reclaim. From Eric Dumazet.

 5) Fix firmware download errors in btusb bluetooth driver, from Ethan
    Hsieh.

 6) Missing Kconfig deps for QCOM_EMAC, from Geert Uytterhoeven.

 7) Fix MDIO_XGENE dup Kconfig entry. From Laura Abbott.

 8) Constrain ipv6 rtr_solicits sysctl values properly, from Maciej
    Żenczykowski.

* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net: (65 commits)
  netfilter: Fix slab corruption.
  be2net: Enable VF link state setting for BE3
  be2net: Fix TX stats for TSO packets
  be2net: Update Copyright string in be_hw.h
  be2net: NCSI FW section should be properly updated with ethtool for BE3
  be2net: Provide an alternate way to read pf_num for BEx chips
  wan/fsl_ucc_hdlc: Fix size used in dma_free_coherent()
  net: macb: NULL out phydev after removing mdio bus
  xen-netback: make sure that hashes are not send to unaware frontends
  Fixing a bug in team driver due to incorrect 'unsigned int' to 'int' conversion
  MAINTAINERS: add myself as a maintainer of xen-netback
  ipv6 addrconf: disallow rtr_solicits < -1
  Bluetooth: btusb: Fix atheros firmware download error
  drivers: net: phy: Correct duplicate MDIO_XGENE entry
  ethernet: qualcomm: QCOM_EMAC should depend on HAS_DMA and HAS_IOMEM
  net: ethernet: mediatek: remove hwlro property in the device tree
  net: ethernet: mediatek: get hw lro capability by the chip id instead of by the dtsi
  net: ethernet: mediatek: get the chip id by ETHDMASYS registers
  net: bgmac: Fix errant feature flag check
  netlink: do not enter direct reclaim from netlink_dump()
  ...
2016-10-11 08:10:19 -07:00

1121 lines
27 KiB
C

/*
* Copyright (c) 2014-2015 Hisilicon Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_mdio.h>
#include <linux/phy.h>
#include <linux/platform_device.h>
#include "hns_dsaf_main.h"
#include "hns_dsaf_misc.h"
#include "hns_dsaf_rcb.h"
#define MAC_EN_FLAG_V 0xada0328
static const u16 mac_phy_to_speed[] = {
[PHY_INTERFACE_MODE_MII] = MAC_SPEED_100,
[PHY_INTERFACE_MODE_GMII] = MAC_SPEED_1000,
[PHY_INTERFACE_MODE_SGMII] = MAC_SPEED_1000,
[PHY_INTERFACE_MODE_TBI] = MAC_SPEED_1000,
[PHY_INTERFACE_MODE_RMII] = MAC_SPEED_100,
[PHY_INTERFACE_MODE_RGMII] = MAC_SPEED_1000,
[PHY_INTERFACE_MODE_RGMII_ID] = MAC_SPEED_1000,
[PHY_INTERFACE_MODE_RGMII_RXID] = MAC_SPEED_1000,
[PHY_INTERFACE_MODE_RGMII_TXID] = MAC_SPEED_1000,
[PHY_INTERFACE_MODE_RTBI] = MAC_SPEED_1000,
[PHY_INTERFACE_MODE_XGMII] = MAC_SPEED_10000
};
static const enum mac_mode g_mac_mode_100[] = {
[PHY_INTERFACE_MODE_MII] = MAC_MODE_MII_100,
[PHY_INTERFACE_MODE_RMII] = MAC_MODE_RMII_100
};
static const enum mac_mode g_mac_mode_1000[] = {
[PHY_INTERFACE_MODE_GMII] = MAC_MODE_GMII_1000,
[PHY_INTERFACE_MODE_SGMII] = MAC_MODE_SGMII_1000,
[PHY_INTERFACE_MODE_TBI] = MAC_MODE_TBI_1000,
[PHY_INTERFACE_MODE_RGMII] = MAC_MODE_RGMII_1000,
[PHY_INTERFACE_MODE_RGMII_ID] = MAC_MODE_RGMII_1000,
[PHY_INTERFACE_MODE_RGMII_RXID] = MAC_MODE_RGMII_1000,
[PHY_INTERFACE_MODE_RGMII_TXID] = MAC_MODE_RGMII_1000,
[PHY_INTERFACE_MODE_RTBI] = MAC_MODE_RTBI_1000
};
static enum mac_mode hns_get_enet_interface(const struct hns_mac_cb *mac_cb)
{
switch (mac_cb->max_speed) {
case MAC_SPEED_100:
return g_mac_mode_100[mac_cb->phy_if];
case MAC_SPEED_1000:
return g_mac_mode_1000[mac_cb->phy_if];
case MAC_SPEED_10000:
return MAC_MODE_XGMII_10000;
default:
return MAC_MODE_MII_100;
}
}
void hns_mac_get_link_status(struct hns_mac_cb *mac_cb, u32 *link_status)
{
struct mac_driver *mac_ctrl_drv;
int ret, sfp_prsnt;
mac_ctrl_drv = hns_mac_get_drv(mac_cb);
if (mac_ctrl_drv->get_link_status)
mac_ctrl_drv->get_link_status(mac_ctrl_drv, link_status);
else
*link_status = 0;
ret = mac_cb->dsaf_dev->misc_op->get_sfp_prsnt(mac_cb, &sfp_prsnt);
if (!ret)
*link_status = *link_status && sfp_prsnt;
mac_cb->link = *link_status;
}
int hns_mac_get_port_info(struct hns_mac_cb *mac_cb,
u8 *auto_neg, u16 *speed, u8 *duplex)
{
struct mac_driver *mac_ctrl_drv;
struct mac_info info;
mac_ctrl_drv = hns_mac_get_drv(mac_cb);
if (!mac_ctrl_drv->get_info)
return -ENODEV;
mac_ctrl_drv->get_info(mac_ctrl_drv, &info);
if (auto_neg)
*auto_neg = info.auto_neg;
if (speed)
*speed = info.speed;
if (duplex)
*duplex = info.duplex;
return 0;
}
void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex)
{
int ret;
struct mac_driver *mac_ctrl_drv;
mac_ctrl_drv = (struct mac_driver *)(mac_cb->priv.mac);
mac_cb->speed = speed;
mac_cb->half_duplex = !duplex;
if (mac_ctrl_drv->adjust_link) {
ret = mac_ctrl_drv->adjust_link(mac_ctrl_drv,
(enum mac_speed)speed, duplex);
if (ret) {
dev_err(mac_cb->dev,
"adjust_link failed, %s mac%d ret = %#x!\n",
mac_cb->dsaf_dev->ae_dev.name,
mac_cb->mac_id, ret);
return;
}
}
}
/**
*hns_mac_get_inner_port_num - get mac table inner port number
*@mac_cb: mac device
*@vmid: vm id
*@port_num:port number
*
*/
static int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb,
u8 vmid, u8 *port_num)
{
u8 tmp_port;
if (mac_cb->dsaf_dev->dsaf_mode <= DSAF_MODE_ENABLE) {
if (mac_cb->mac_id != DSAF_MAX_PORT_NUM) {
dev_err(mac_cb->dev,
"input invalid, %s mac%d vmid%d !\n",
mac_cb->dsaf_dev->ae_dev.name,
mac_cb->mac_id, vmid);
return -EINVAL;
}
} else if (mac_cb->dsaf_dev->dsaf_mode < DSAF_MODE_MAX) {
if (mac_cb->mac_id >= DSAF_MAX_PORT_NUM) {
dev_err(mac_cb->dev,
"input invalid, %s mac%d vmid%d!\n",
mac_cb->dsaf_dev->ae_dev.name,
mac_cb->mac_id, vmid);
return -EINVAL;
}
} else {
dev_err(mac_cb->dev, "dsaf mode invalid, %s mac%d!\n",
mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id);
return -EINVAL;
}
if (vmid >= mac_cb->dsaf_dev->rcb_common[0]->max_vfn) {
dev_err(mac_cb->dev, "input invalid, %s mac%d vmid%d !\n",
mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id, vmid);
return -EINVAL;
}
switch (mac_cb->dsaf_dev->dsaf_mode) {
case DSAF_MODE_ENABLE_FIX:
tmp_port = 0;
break;
case DSAF_MODE_DISABLE_FIX:
tmp_port = 0;
break;
case DSAF_MODE_ENABLE_0VM:
case DSAF_MODE_ENABLE_8VM:
case DSAF_MODE_ENABLE_16VM:
case DSAF_MODE_ENABLE_32VM:
case DSAF_MODE_ENABLE_128VM:
case DSAF_MODE_DISABLE_2PORT_8VM:
case DSAF_MODE_DISABLE_2PORT_16VM:
case DSAF_MODE_DISABLE_2PORT_64VM:
case DSAF_MODE_DISABLE_6PORT_0VM:
case DSAF_MODE_DISABLE_6PORT_2VM:
case DSAF_MODE_DISABLE_6PORT_4VM:
case DSAF_MODE_DISABLE_6PORT_16VM:
tmp_port = vmid;
break;
default:
dev_err(mac_cb->dev, "dsaf mode invalid, %s mac%d!\n",
mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id);
return -EINVAL;
}
tmp_port += DSAF_BASE_INNER_PORT_NUM;
*port_num = tmp_port;
return 0;
}
/**
*hns_mac_change_vf_addr - change vf mac address
*@mac_cb: mac device
*@vmid: vmid
*@addr:mac address
*/
int hns_mac_change_vf_addr(struct hns_mac_cb *mac_cb,
u32 vmid, char *addr)
{
int ret;
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
struct dsaf_drv_mac_single_dest_entry mac_entry;
struct mac_entry_idx *old_entry;
old_entry = &mac_cb->addr_entry_idx[vmid];
if (!HNS_DSAF_IS_DEBUG(dsaf_dev)) {
memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr));
mac_entry.in_vlan_id = old_entry->vlan_id;
mac_entry.in_port_num = mac_cb->mac_id;
ret = hns_mac_get_inner_port_num(mac_cb, (u8)vmid,
&mac_entry.port_num);
if (ret)
return ret;
if ((old_entry->valid != 0) &&
(memcmp(old_entry->addr,
addr, sizeof(mac_entry.addr)) != 0)) {
ret = hns_dsaf_del_mac_entry(dsaf_dev,
old_entry->vlan_id,
mac_cb->mac_id,
old_entry->addr);
if (ret)
return ret;
}
ret = hns_dsaf_set_mac_uc_entry(dsaf_dev, &mac_entry);
if (ret)
return ret;
}
if ((mac_ctrl_drv->set_mac_addr) && (vmid == 0))
mac_ctrl_drv->set_mac_addr(mac_cb->priv.mac, addr);
memcpy(old_entry->addr, addr, sizeof(old_entry->addr));
old_entry->valid = 1;
return 0;
}
int hns_mac_set_multi(struct hns_mac_cb *mac_cb,
u32 port_num, char *addr, bool enable)
{
int ret;
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
struct dsaf_drv_mac_single_dest_entry mac_entry;
if (!HNS_DSAF_IS_DEBUG(dsaf_dev) && addr) {
memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr));
mac_entry.in_vlan_id = 0;/*vlan_id;*/
mac_entry.in_port_num = mac_cb->mac_id;
mac_entry.port_num = port_num;
if (!enable)
ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
else
ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
if (ret) {
dev_err(dsaf_dev->dev,
"set mac mc port failed, %s mac%d ret = %#x!\n",
mac_cb->dsaf_dev->ae_dev.name,
mac_cb->mac_id, ret);
return ret;
}
}
return 0;
}
/**
*hns_mac_del_mac - delete mac address into dsaf table,can't delete the same
* address twice
*@net_dev: net device
*@vfn : vf lan
*@mac : mac address
*return status
*/
int hns_mac_del_mac(struct hns_mac_cb *mac_cb, u32 vfn, char *mac)
{
struct mac_entry_idx *old_mac;
struct dsaf_device *dsaf_dev;
u32 ret;
dsaf_dev = mac_cb->dsaf_dev;
if (vfn < DSAF_MAX_VM_NUM) {
old_mac = &mac_cb->addr_entry_idx[vfn];
} else {
dev_err(mac_cb->dev,
"vf queue is too large, %s mac%d queue = %#x!\n",
mac_cb->dsaf_dev->ae_dev.name, mac_cb->mac_id, vfn);
return -EINVAL;
}
if (dsaf_dev) {
ret = hns_dsaf_del_mac_entry(dsaf_dev, old_mac->vlan_id,
mac_cb->mac_id, old_mac->addr);
if (ret)
return ret;
if (memcmp(old_mac->addr, mac, sizeof(old_mac->addr)) == 0)
old_mac->valid = 0;
}
return 0;
}
static void hns_mac_param_get(struct mac_params *param,
struct hns_mac_cb *mac_cb)
{
param->vaddr = (void *)mac_cb->vaddr;
param->mac_mode = hns_get_enet_interface(mac_cb);
memcpy(param->addr, mac_cb->addr_entry_idx[0].addr,
MAC_NUM_OCTETS_PER_ADDR);
param->mac_id = mac_cb->mac_id;
param->dev = mac_cb->dev;
}
/**
*hns_mac_queue_config_bc_en - set broadcast rx&tx enable
*@mac_cb: mac device
*@queue: queue number
*@en:enable
*retuen 0 - success , negative --fail
*/
static int hns_mac_port_config_bc_en(struct hns_mac_cb *mac_cb,
u32 port_num, u16 vlan_id, bool enable)
{
int ret;
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
u8 addr[MAC_NUM_OCTETS_PER_ADDR]
= {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
struct dsaf_drv_mac_single_dest_entry mac_entry;
/* directy return ok in debug network mode */
if (mac_cb->mac_type == HNAE_PORT_DEBUG)
return 0;
if (!HNS_DSAF_IS_DEBUG(dsaf_dev)) {
memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr));
mac_entry.in_vlan_id = vlan_id;
mac_entry.in_port_num = mac_cb->mac_id;
mac_entry.port_num = port_num;
if (!enable)
ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
else
ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
return ret;
}
return 0;
}
/**
*hns_mac_vm_config_bc_en - set broadcast rx&tx enable
*@mac_cb: mac device
*@vmid: vm id
*@en:enable
*retuen 0 - success , negative --fail
*/
int hns_mac_vm_config_bc_en(struct hns_mac_cb *mac_cb, u32 vmid, bool enable)
{
int ret;
struct dsaf_device *dsaf_dev = mac_cb->dsaf_dev;
u8 port_num;
u8 addr[MAC_NUM_OCTETS_PER_ADDR]
= {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
struct mac_entry_idx *uc_mac_entry;
struct dsaf_drv_mac_single_dest_entry mac_entry;
if (mac_cb->mac_type == HNAE_PORT_DEBUG)
return 0;
uc_mac_entry = &mac_cb->addr_entry_idx[vmid];
if (!HNS_DSAF_IS_DEBUG(dsaf_dev)) {
memcpy(mac_entry.addr, addr, sizeof(mac_entry.addr));
mac_entry.in_vlan_id = uc_mac_entry->vlan_id;
mac_entry.in_port_num = mac_cb->mac_id;
ret = hns_mac_get_inner_port_num(mac_cb, vmid, &port_num);
if (ret)
return ret;
mac_entry.port_num = port_num;
if (!enable)
ret = hns_dsaf_del_mac_mc_port(dsaf_dev, &mac_entry);
else
ret = hns_dsaf_add_mac_mc_port(dsaf_dev, &mac_entry);
return ret;
}
return 0;
}
void hns_mac_reset(struct hns_mac_cb *mac_cb)
{
struct mac_driver *drv = hns_mac_get_drv(mac_cb);
bool is_ver1 = AE_IS_VER1(mac_cb->dsaf_dev->dsaf_ver);
drv->mac_init(drv);
if (drv->config_max_frame_length)
drv->config_max_frame_length(drv, mac_cb->max_frm);
if (drv->set_tx_auto_pause_frames)
drv->set_tx_auto_pause_frames(drv, mac_cb->tx_pause_frm_time);
if (drv->set_an_mode)
drv->set_an_mode(drv, 1);
if (drv->mac_pausefrm_cfg) {
if (mac_cb->mac_type == HNAE_PORT_DEBUG)
drv->mac_pausefrm_cfg(drv, !is_ver1, !is_ver1);
else /* mac rx must disable, dsaf pfc close instead of it*/
drv->mac_pausefrm_cfg(drv, 0, 1);
}
}
int hns_mac_set_mtu(struct hns_mac_cb *mac_cb, u32 new_mtu)
{
struct mac_driver *drv = hns_mac_get_drv(mac_cb);
u32 buf_size = mac_cb->dsaf_dev->buf_size;
u32 new_frm = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
u32 max_frm = AE_IS_VER1(mac_cb->dsaf_dev->dsaf_ver) ?
MAC_MAX_MTU : MAC_MAX_MTU_V2;
if (mac_cb->mac_type == HNAE_PORT_DEBUG)
max_frm = MAC_MAX_MTU_DBG;
if ((new_mtu < MAC_MIN_MTU) || (new_frm > max_frm) ||
(new_frm > HNS_RCB_RING_MAX_BD_PER_PKT * buf_size))
return -EINVAL;
if (!drv->config_max_frame_length)
return -ECHILD;
/* adjust max frame to be at least the size of a standard frame */
if (new_frm < (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN))
new_frm = (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN);
drv->config_max_frame_length(drv, new_frm);
mac_cb->max_frm = new_frm;
return 0;
}
void hns_mac_start(struct hns_mac_cb *mac_cb)
{
struct mac_driver *mac_drv = hns_mac_get_drv(mac_cb);
/* for virt */
if (mac_drv->mac_en_flg == MAC_EN_FLAG_V) {
/*plus 1 when the virtual mac has been enabled */
mac_drv->virt_dev_num += 1;
return;
}
if (mac_drv->mac_enable) {
mac_drv->mac_enable(mac_cb->priv.mac, MAC_COMM_MODE_RX_AND_TX);
mac_drv->mac_en_flg = MAC_EN_FLAG_V;
}
}
void hns_mac_stop(struct hns_mac_cb *mac_cb)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
/*modified for virtualization */
if (mac_ctrl_drv->virt_dev_num > 0) {
mac_ctrl_drv->virt_dev_num -= 1;
if (mac_ctrl_drv->virt_dev_num > 0)
return;
}
if (mac_ctrl_drv->mac_disable)
mac_ctrl_drv->mac_disable(mac_cb->priv.mac,
MAC_COMM_MODE_RX_AND_TX);
mac_ctrl_drv->mac_en_flg = 0;
mac_cb->link = 0;
mac_cb->dsaf_dev->misc_op->cpld_reset_led(mac_cb);
}
/**
* hns_mac_get_autoneg - get auto autonegotiation
* @mac_cb: mac control block
* @enable: enable or not
* retuen 0 - success , negative --fail
*/
void hns_mac_get_autoneg(struct hns_mac_cb *mac_cb, u32 *auto_neg)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
if (mac_ctrl_drv->autoneg_stat)
mac_ctrl_drv->autoneg_stat(mac_ctrl_drv, auto_neg);
else
*auto_neg = 0;
}
/**
* hns_mac_get_pauseparam - set rx & tx pause parameter
* @mac_cb: mac control block
* @rx_en: rx enable status
* @tx_en: tx enable status
* retuen 0 - success , negative --fail
*/
void hns_mac_get_pauseparam(struct hns_mac_cb *mac_cb, u32 *rx_en, u32 *tx_en)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
if (mac_ctrl_drv->get_pause_enable) {
mac_ctrl_drv->get_pause_enable(mac_ctrl_drv, rx_en, tx_en);
} else {
*rx_en = 0;
*tx_en = 0;
}
}
/**
* hns_mac_set_autoneg - set auto autonegotiation
* @mac_cb: mac control block
* @enable: enable or not
* retuen 0 - success , negative --fail
*/
int hns_mac_set_autoneg(struct hns_mac_cb *mac_cb, u8 enable)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII && enable) {
dev_err(mac_cb->dev, "enabling autoneg is not allowed!\n");
return -ENOTSUPP;
}
if (mac_ctrl_drv->set_an_mode)
mac_ctrl_drv->set_an_mode(mac_ctrl_drv, enable);
return 0;
}
/**
* hns_mac_set_autoneg - set rx & tx pause parameter
* @mac_cb: mac control block
* @rx_en: rx enable or not
* @tx_en: tx enable or not
* return 0 - success , negative --fail
*/
int hns_mac_set_pauseparam(struct hns_mac_cb *mac_cb, u32 rx_en, u32 tx_en)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
bool is_ver1 = AE_IS_VER1(mac_cb->dsaf_dev->dsaf_ver);
if (mac_cb->mac_type == HNAE_PORT_DEBUG) {
if (is_ver1 && (tx_en || rx_en)) {
dev_err(mac_cb->dev, "macv1 can't enable tx/rx_pause!\n");
return -EINVAL;
}
}
if (mac_ctrl_drv->mac_pausefrm_cfg)
mac_ctrl_drv->mac_pausefrm_cfg(mac_ctrl_drv, rx_en, tx_en);
return 0;
}
/**
* hns_mac_init_ex - mac init
* @mac_cb: mac control block
* retuen 0 - success , negative --fail
*/
static int hns_mac_init_ex(struct hns_mac_cb *mac_cb)
{
int ret;
struct mac_params param;
struct mac_driver *drv;
hns_dsaf_fix_mac_mode(mac_cb);
memset(&param, 0, sizeof(struct mac_params));
hns_mac_param_get(&param, mac_cb);
if (MAC_SPEED_FROM_MODE(param.mac_mode) < MAC_SPEED_10000)
drv = (struct mac_driver *)hns_gmac_config(mac_cb, &param);
else
drv = (struct mac_driver *)hns_xgmac_config(mac_cb, &param);
if (!drv)
return -ENOMEM;
mac_cb->priv.mac = (void *)drv;
hns_mac_reset(mac_cb);
hns_mac_adjust_link(mac_cb, mac_cb->speed, !mac_cb->half_duplex);
ret = hns_mac_port_config_bc_en(mac_cb, mac_cb->mac_id, 0, true);
if (ret)
goto free_mac_drv;
return 0;
free_mac_drv:
drv->mac_free(mac_cb->priv.mac);
mac_cb->priv.mac = NULL;
return ret;
}
static int
hns_mac_phy_parse_addr(struct device *dev, struct fwnode_handle *fwnode)
{
u32 addr;
int ret;
ret = fwnode_property_read_u32(fwnode, "phy-addr", &addr);
if (ret) {
dev_err(dev, "has invalid PHY address ret:%d\n", ret);
return ret;
}
if (addr >= PHY_MAX_ADDR) {
dev_err(dev, "PHY address %i is too large\n", addr);
return -EINVAL;
}
return addr;
}
static int
hns_mac_register_phydev(struct mii_bus *mdio, struct hns_mac_cb *mac_cb,
u32 addr)
{
struct phy_device *phy;
const char *phy_type;
bool is_c45;
int rc;
rc = fwnode_property_read_string(mac_cb->fw_port,
"phy-mode", &phy_type);
if (rc < 0)
return rc;
if (!strcmp(phy_type, phy_modes(PHY_INTERFACE_MODE_XGMII)))
is_c45 = 1;
else if (!strcmp(phy_type, phy_modes(PHY_INTERFACE_MODE_SGMII)))
is_c45 = 0;
else
return -ENODATA;
phy = get_phy_device(mdio, addr, is_c45);
if (!phy || IS_ERR(phy))
return -EIO;
phy->irq = mdio->irq[addr];
/* All data is now stored in the phy struct;
* register it
*/
rc = phy_device_register(phy);
if (rc) {
phy_device_free(phy);
return -ENODEV;
}
mac_cb->phy_dev = phy;
dev_dbg(&mdio->dev, "registered phy at address %i\n", addr);
return 0;
}
static void hns_mac_register_phy(struct hns_mac_cb *mac_cb)
{
struct acpi_reference_args args;
struct platform_device *pdev;
struct mii_bus *mii_bus;
int rc;
int addr;
/* Loop over the child nodes and register a phy_device for each one */
if (!to_acpi_device_node(mac_cb->fw_port))
return;
rc = acpi_node_get_property_reference(
mac_cb->fw_port, "mdio-node", 0, &args);
if (rc)
return;
addr = hns_mac_phy_parse_addr(mac_cb->dev, mac_cb->fw_port);
if (addr < 0)
return;
/* dev address in adev */
pdev = hns_dsaf_find_platform_device(acpi_fwnode_handle(args.adev));
mii_bus = platform_get_drvdata(pdev);
rc = hns_mac_register_phydev(mii_bus, mac_cb, addr);
if (!rc)
dev_dbg(mac_cb->dev, "mac%d register phy addr:%d\n",
mac_cb->mac_id, addr);
}
#define MAC_MEDIA_TYPE_MAX_LEN 16
static const struct {
enum hnae_media_type value;
const char *name;
} media_type_defs[] = {
{HNAE_MEDIA_TYPE_UNKNOWN, "unknown" },
{HNAE_MEDIA_TYPE_FIBER, "fiber" },
{HNAE_MEDIA_TYPE_COPPER, "copper" },
{HNAE_MEDIA_TYPE_BACKPLANE, "backplane" },
};
/**
*hns_mac_get_info - get mac information from device node
*@mac_cb: mac device
*@np:device node
* return: 0 --success, negative --fail
*/
static int hns_mac_get_info(struct hns_mac_cb *mac_cb)
{
struct device_node *np;
struct regmap *syscon;
struct of_phandle_args cpld_args;
const char *media_type;
u32 i;
u32 ret;
mac_cb->link = false;
mac_cb->half_duplex = false;
mac_cb->media_type = HNAE_MEDIA_TYPE_UNKNOWN;
mac_cb->speed = mac_phy_to_speed[mac_cb->phy_if];
mac_cb->max_speed = mac_cb->speed;
if (mac_cb->phy_if == PHY_INTERFACE_MODE_SGMII) {
mac_cb->if_support = MAC_GMAC_SUPPORTED;
mac_cb->if_support |= SUPPORTED_1000baseT_Full;
} else if (mac_cb->phy_if == PHY_INTERFACE_MODE_XGMII) {
mac_cb->if_support = SUPPORTED_10000baseR_FEC;
mac_cb->if_support |= SUPPORTED_10000baseKR_Full;
}
mac_cb->max_frm = MAC_DEFAULT_MTU;
mac_cb->tx_pause_frm_time = MAC_DEFAULT_PAUSE_TIME;
mac_cb->port_rst_off = mac_cb->mac_id;
mac_cb->port_mode_off = 0;
/* if the dsaf node doesn't contain a port subnode, get phy-handle
* from dsaf node
*/
if (!mac_cb->fw_port) {
np = of_parse_phandle(mac_cb->dev->of_node, "phy-handle",
mac_cb->mac_id);
mac_cb->phy_dev = of_phy_find_device(np);
if (mac_cb->phy_dev) {
/* refcount is held by of_phy_find_device()
* if the phy_dev is found
*/
put_device(&mac_cb->phy_dev->mdio.dev);
dev_dbg(mac_cb->dev, "mac%d phy_node: %s\n",
mac_cb->mac_id, np->name);
}
of_node_put(np);
return 0;
}
if (is_of_node(mac_cb->fw_port)) {
/* parse property from port subnode in dsaf */
np = of_parse_phandle(to_of_node(mac_cb->fw_port),
"phy-handle", 0);
mac_cb->phy_dev = of_phy_find_device(np);
if (mac_cb->phy_dev) {
/* refcount is held by of_phy_find_device()
* if the phy_dev is found
*/
put_device(&mac_cb->phy_dev->mdio.dev);
dev_dbg(mac_cb->dev, "mac%d phy_node: %s\n",
mac_cb->mac_id, np->name);
}
of_node_put(np);
np = of_parse_phandle(to_of_node(mac_cb->fw_port),
"serdes-syscon", 0);
syscon = syscon_node_to_regmap(np);
of_node_put(np);
if (IS_ERR_OR_NULL(syscon)) {
dev_err(mac_cb->dev, "serdes-syscon is needed!\n");
return -EINVAL;
}
mac_cb->serdes_ctrl = syscon;
ret = fwnode_property_read_u32(mac_cb->fw_port,
"port-rst-offset",
&mac_cb->port_rst_off);
if (ret) {
dev_dbg(mac_cb->dev,
"mac%d port-rst-offset not found, use default value.\n",
mac_cb->mac_id);
}
ret = fwnode_property_read_u32(mac_cb->fw_port,
"port-mode-offset",
&mac_cb->port_mode_off);
if (ret) {
dev_dbg(mac_cb->dev,
"mac%d port-mode-offset not found, use default value.\n",
mac_cb->mac_id);
}
ret = of_parse_phandle_with_fixed_args(
to_of_node(mac_cb->fw_port), "cpld-syscon", 1, 0,
&cpld_args);
if (ret) {
dev_dbg(mac_cb->dev, "mac%d no cpld-syscon found.\n",
mac_cb->mac_id);
mac_cb->cpld_ctrl = NULL;
} else {
syscon = syscon_node_to_regmap(cpld_args.np);
if (IS_ERR_OR_NULL(syscon)) {
dev_dbg(mac_cb->dev, "no cpld-syscon found!\n");
mac_cb->cpld_ctrl = NULL;
} else {
mac_cb->cpld_ctrl = syscon;
mac_cb->cpld_ctrl_reg = cpld_args.args[0];
}
}
} else if (is_acpi_node(mac_cb->fw_port)) {
hns_mac_register_phy(mac_cb);
} else {
dev_err(mac_cb->dev, "mac%d cannot find phy node\n",
mac_cb->mac_id);
}
if (!fwnode_property_read_string(mac_cb->fw_port, "media-type",
&media_type)) {
for (i = 0; i < ARRAY_SIZE(media_type_defs); i++) {
if (!strncmp(media_type_defs[i].name, media_type,
MAC_MEDIA_TYPE_MAX_LEN)) {
mac_cb->media_type = media_type_defs[i].value;
break;
}
}
}
return 0;
}
/**
* hns_mac_get_mode - get mac mode
* @phy_if: phy interface
* retuen 0 - gmac, 1 - xgmac , negative --fail
*/
static int hns_mac_get_mode(phy_interface_t phy_if)
{
switch (phy_if) {
case PHY_INTERFACE_MODE_SGMII:
return MAC_GMAC_IDX;
case PHY_INTERFACE_MODE_XGMII:
return MAC_XGMAC_IDX;
default:
return -EINVAL;
}
}
u8 __iomem *hns_mac_get_vaddr(struct dsaf_device *dsaf_dev,
struct hns_mac_cb *mac_cb, u32 mac_mode_idx)
{
u8 __iomem *base = dsaf_dev->io_base;
int mac_id = mac_cb->mac_id;
if (mac_cb->mac_type == HNAE_PORT_SERVICE)
return base + 0x40000 + mac_id * 0x4000 -
mac_mode_idx * 0x20000;
else
return dsaf_dev->ppe_base + 0x1000;
}
/**
* hns_mac_get_cfg - get mac cfg from dtb or acpi table
* @dsaf_dev: dsa fabric device struct pointer
* @mac_cb: mac control block
* return 0 - success , negative --fail
*/
int hns_mac_get_cfg(struct dsaf_device *dsaf_dev, struct hns_mac_cb *mac_cb)
{
int ret;
u32 mac_mode_idx;
mac_cb->dsaf_dev = dsaf_dev;
mac_cb->dev = dsaf_dev->dev;
mac_cb->sys_ctl_vaddr = dsaf_dev->sc_base;
mac_cb->serdes_vaddr = dsaf_dev->sds_base;
mac_cb->sfp_prsnt = 0;
mac_cb->txpkt_for_led = 0;
mac_cb->rxpkt_for_led = 0;
if (!HNS_DSAF_IS_DEBUG(dsaf_dev))
mac_cb->mac_type = HNAE_PORT_SERVICE;
else
mac_cb->mac_type = HNAE_PORT_DEBUG;
mac_cb->phy_if = dsaf_dev->misc_op->get_phy_if(mac_cb);
ret = hns_mac_get_mode(mac_cb->phy_if);
if (ret < 0) {
dev_err(dsaf_dev->dev,
"hns_mac_get_mode failed, mac%d ret = %#x!\n",
mac_cb->mac_id, ret);
return ret;
}
mac_mode_idx = (u32)ret;
ret = hns_mac_get_info(mac_cb);
if (ret)
return ret;
mac_cb->dsaf_dev->misc_op->cpld_reset_led(mac_cb);
mac_cb->vaddr = hns_mac_get_vaddr(dsaf_dev, mac_cb, mac_mode_idx);
return 0;
}
static int hns_mac_get_max_port_num(struct dsaf_device *dsaf_dev)
{
if (HNS_DSAF_IS_DEBUG(dsaf_dev))
return 1;
else
return DSAF_MAX_PORT_NUM;
}
/**
* hns_mac_init - init mac
* @dsaf_dev: dsa fabric device struct pointer
* return 0 - success , negative --fail
*/
int hns_mac_init(struct dsaf_device *dsaf_dev)
{
bool found = false;
int ret;
u32 port_id;
int max_port_num = hns_mac_get_max_port_num(dsaf_dev);
struct hns_mac_cb *mac_cb;
struct fwnode_handle *child;
device_for_each_child_node(dsaf_dev->dev, child) {
ret = fwnode_property_read_u32(child, "reg", &port_id);
if (ret) {
dev_err(dsaf_dev->dev,
"get reg fail, ret=%d!\n", ret);
return ret;
}
if (port_id >= max_port_num) {
dev_err(dsaf_dev->dev,
"reg(%u) out of range!\n", port_id);
return -EINVAL;
}
mac_cb = devm_kzalloc(dsaf_dev->dev, sizeof(*mac_cb),
GFP_KERNEL);
if (!mac_cb)
return -ENOMEM;
mac_cb->fw_port = child;
mac_cb->mac_id = (u8)port_id;
dsaf_dev->mac_cb[port_id] = mac_cb;
found = true;
}
/* if don't get any port subnode from dsaf node
* will init all port then, this is compatible with the old dts
*/
if (!found) {
for (port_id = 0; port_id < max_port_num; port_id++) {
mac_cb = devm_kzalloc(dsaf_dev->dev, sizeof(*mac_cb),
GFP_KERNEL);
if (!mac_cb)
return -ENOMEM;
mac_cb->mac_id = port_id;
dsaf_dev->mac_cb[port_id] = mac_cb;
}
}
/* init mac_cb for all port */
for (port_id = 0; port_id < max_port_num; port_id++) {
mac_cb = dsaf_dev->mac_cb[port_id];
if (!mac_cb)
continue;
ret = hns_mac_get_cfg(dsaf_dev, mac_cb);
if (ret)
return ret;
ret = hns_mac_init_ex(mac_cb);
if (ret)
return ret;
}
return 0;
}
void hns_mac_uninit(struct dsaf_device *dsaf_dev)
{
int i;
int max_port_num = hns_mac_get_max_port_num(dsaf_dev);
for (i = 0; i < max_port_num; i++) {
dsaf_dev->misc_op->cpld_reset_led(dsaf_dev->mac_cb[i]);
dsaf_dev->mac_cb[i] = NULL;
}
}
int hns_mac_config_mac_loopback(struct hns_mac_cb *mac_cb,
enum hnae_loop loop, int en)
{
int ret;
struct mac_driver *drv = hns_mac_get_drv(mac_cb);
if (drv->config_loopback)
ret = drv->config_loopback(drv, loop, en);
else
ret = -ENOTSUPP;
return ret;
}
void hns_mac_update_stats(struct hns_mac_cb *mac_cb)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
mac_ctrl_drv->update_stats(mac_ctrl_drv);
}
void hns_mac_get_stats(struct hns_mac_cb *mac_cb, u64 *data)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
mac_ctrl_drv->get_ethtool_stats(mac_ctrl_drv, data);
}
void hns_mac_get_strings(struct hns_mac_cb *mac_cb,
int stringset, u8 *data)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
mac_ctrl_drv->get_strings(stringset, data);
}
int hns_mac_get_sset_count(struct hns_mac_cb *mac_cb, int stringset)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
return mac_ctrl_drv->get_sset_count(stringset);
}
void hns_mac_set_promisc(struct hns_mac_cb *mac_cb, u8 en)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
if (mac_ctrl_drv->set_promiscuous)
mac_ctrl_drv->set_promiscuous(mac_ctrl_drv, en);
}
int hns_mac_get_regs_count(struct hns_mac_cb *mac_cb)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
return mac_ctrl_drv->get_regs_count();
}
void hns_mac_get_regs(struct hns_mac_cb *mac_cb, void *data)
{
struct mac_driver *mac_ctrl_drv = hns_mac_get_drv(mac_cb);
mac_ctrl_drv->get_regs(mac_ctrl_drv, data);
}
void hns_set_led_opt(struct hns_mac_cb *mac_cb)
{
int nic_data = 0;
int txpkts, rxpkts;
txpkts = mac_cb->txpkt_for_led - mac_cb->hw_stats.tx_good_pkts;
rxpkts = mac_cb->rxpkt_for_led - mac_cb->hw_stats.rx_good_pkts;
if (txpkts || rxpkts)
nic_data = 1;
else
nic_data = 0;
mac_cb->txpkt_for_led = mac_cb->hw_stats.tx_good_pkts;
mac_cb->rxpkt_for_led = mac_cb->hw_stats.rx_good_pkts;
mac_cb->dsaf_dev->misc_op->cpld_set_led(mac_cb, (int)mac_cb->link,
mac_cb->speed, nic_data);
}
int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb,
enum hnae_led_state status)
{
if (!mac_cb || !mac_cb->cpld_ctrl)
return 0;
return mac_cb->dsaf_dev->misc_op->cpld_set_led_id(mac_cb, status);
}