|
@@ -24,6 +24,7 @@
|
|
* This file may also be available under a different license from Cavium.
|
|
* This file may also be available under a different license from Cavium.
|
|
* Contact Cavium Networks for more information
|
|
* Contact Cavium Networks for more information
|
|
**********************************************************************/
|
|
**********************************************************************/
|
|
|
|
+#include <linux/platform_device.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
@@ -32,6 +33,7 @@
|
|
#include <linux/phy.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
+#include <linux/of_net.h>
|
|
|
|
|
|
#include <net/dst.h>
|
|
#include <net/dst.h>
|
|
|
|
|
|
@@ -113,15 +115,6 @@ int rx_napi_weight = 32;
|
|
module_param(rx_napi_weight, int, 0444);
|
|
module_param(rx_napi_weight, int, 0444);
|
|
MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter.");
|
|
MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter.");
|
|
|
|
|
|
-/*
|
|
|
|
- * The offset from mac_addr_base that should be used for the next port
|
|
|
|
- * that is configured. By convention, if any mgmt ports exist on the
|
|
|
|
- * chip, they get the first mac addresses, The ports controlled by
|
|
|
|
- * this driver are numbered sequencially following any mgmt addresses
|
|
|
|
- * that may exist.
|
|
|
|
- */
|
|
|
|
-static unsigned int cvm_oct_mac_addr_offset;
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* cvm_oct_poll_queue - Workqueue for polling operations.
|
|
* cvm_oct_poll_queue - Workqueue for polling operations.
|
|
*/
|
|
*/
|
|
@@ -176,7 +169,7 @@ static void cvm_oct_periodic_worker(struct work_struct *work)
|
|
queue_delayed_work(cvm_oct_poll_queue, &priv->port_periodic_work, HZ);
|
|
queue_delayed_work(cvm_oct_poll_queue, &priv->port_periodic_work, HZ);
|
|
}
|
|
}
|
|
|
|
|
|
-static __init void cvm_oct_configure_common_hw(void)
|
|
|
|
|
|
+static __devinit void cvm_oct_configure_common_hw(void)
|
|
{
|
|
{
|
|
/* Setup the FPA */
|
|
/* Setup the FPA */
|
|
cvmx_fpa_enable();
|
|
cvmx_fpa_enable();
|
|
@@ -396,23 +389,21 @@ static void cvm_oct_common_set_multicast_list(struct net_device *dev)
|
|
|
|
|
|
* Returns Zero on success
|
|
* Returns Zero on success
|
|
*/
|
|
*/
|
|
-static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
|
|
|
|
|
|
+static int cvm_oct_set_mac_filter(struct net_device *dev)
|
|
{
|
|
{
|
|
struct octeon_ethernet *priv = netdev_priv(dev);
|
|
struct octeon_ethernet *priv = netdev_priv(dev);
|
|
union cvmx_gmxx_prtx_cfg gmx_cfg;
|
|
union cvmx_gmxx_prtx_cfg gmx_cfg;
|
|
int interface = INTERFACE(priv->port);
|
|
int interface = INTERFACE(priv->port);
|
|
int index = INDEX(priv->port);
|
|
int index = INDEX(priv->port);
|
|
|
|
|
|
- memcpy(dev->dev_addr, addr + 2, 6);
|
|
|
|
-
|
|
|
|
if ((interface < 2)
|
|
if ((interface < 2)
|
|
&& (cvmx_helper_interface_get_mode(interface) !=
|
|
&& (cvmx_helper_interface_get_mode(interface) !=
|
|
CVMX_HELPER_INTERFACE_MODE_SPI)) {
|
|
CVMX_HELPER_INTERFACE_MODE_SPI)) {
|
|
int i;
|
|
int i;
|
|
- uint8_t *ptr = addr;
|
|
|
|
|
|
+ uint8_t *ptr = dev->dev_addr;
|
|
uint64_t mac = 0;
|
|
uint64_t mac = 0;
|
|
for (i = 0; i < 6; i++)
|
|
for (i = 0; i < 6; i++)
|
|
- mac = (mac << 8) | (uint64_t) (ptr[i + 2]);
|
|
|
|
|
|
+ mac = (mac << 8) | (uint64_t)ptr[i];
|
|
|
|
|
|
gmx_cfg.u64 =
|
|
gmx_cfg.u64 =
|
|
cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
|
|
cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
|
|
@@ -421,17 +412,17 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
|
|
|
|
|
|
cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac);
|
|
cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac);
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface),
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface),
|
|
- ptr[2]);
|
|
|
|
|
|
+ ptr[0]);
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface),
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface),
|
|
- ptr[3]);
|
|
|
|
|
|
+ ptr[1]);
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface),
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface),
|
|
- ptr[4]);
|
|
|
|
|
|
+ ptr[2]);
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface),
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface),
|
|
- ptr[5]);
|
|
|
|
|
|
+ ptr[3]);
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface),
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface),
|
|
- ptr[6]);
|
|
|
|
|
|
+ ptr[4]);
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface),
|
|
cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface),
|
|
- ptr[7]);
|
|
|
|
|
|
+ ptr[5]);
|
|
cvm_oct_common_set_multicast_list(dev);
|
|
cvm_oct_common_set_multicast_list(dev);
|
|
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
|
|
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
|
|
gmx_cfg.u64);
|
|
gmx_cfg.u64);
|
|
@@ -439,6 +430,15 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
|
|
|
|
+{
|
|
|
|
+ int r = eth_mac_addr(dev, addr);
|
|
|
|
+
|
|
|
|
+ if (r)
|
|
|
|
+ return r;
|
|
|
|
+ return cvm_oct_set_mac_filter(dev);
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* cvm_oct_common_init - per network device initialization
|
|
* cvm_oct_common_init - per network device initialization
|
|
* @dev: Device to initialize
|
|
* @dev: Device to initialize
|
|
@@ -448,26 +448,17 @@ static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
|
|
int cvm_oct_common_init(struct net_device *dev)
|
|
int cvm_oct_common_init(struct net_device *dev)
|
|
{
|
|
{
|
|
struct octeon_ethernet *priv = netdev_priv(dev);
|
|
struct octeon_ethernet *priv = netdev_priv(dev);
|
|
- struct sockaddr sa;
|
|
|
|
- u64 mac = ((u64)(octeon_bootinfo->mac_addr_base[0] & 0xff) << 40) |
|
|
|
|
- ((u64)(octeon_bootinfo->mac_addr_base[1] & 0xff) << 32) |
|
|
|
|
- ((u64)(octeon_bootinfo->mac_addr_base[2] & 0xff) << 24) |
|
|
|
|
- ((u64)(octeon_bootinfo->mac_addr_base[3] & 0xff) << 16) |
|
|
|
|
- ((u64)(octeon_bootinfo->mac_addr_base[4] & 0xff) << 8) |
|
|
|
|
- (u64)(octeon_bootinfo->mac_addr_base[5] & 0xff);
|
|
|
|
-
|
|
|
|
- mac += cvm_oct_mac_addr_offset;
|
|
|
|
- sa.sa_data[0] = (mac >> 40) & 0xff;
|
|
|
|
- sa.sa_data[1] = (mac >> 32) & 0xff;
|
|
|
|
- sa.sa_data[2] = (mac >> 24) & 0xff;
|
|
|
|
- sa.sa_data[3] = (mac >> 16) & 0xff;
|
|
|
|
- sa.sa_data[4] = (mac >> 8) & 0xff;
|
|
|
|
- sa.sa_data[5] = mac & 0xff;
|
|
|
|
-
|
|
|
|
- if (cvm_oct_mac_addr_offset >= octeon_bootinfo->mac_addr_count)
|
|
|
|
- printk(KERN_DEBUG "%s: Using MAC outside of the assigned range:"
|
|
|
|
- " %pM\n", dev->name, sa.sa_data);
|
|
|
|
- cvm_oct_mac_addr_offset++;
|
|
|
|
|
|
+ const u8 *mac = NULL;
|
|
|
|
+
|
|
|
|
+ if (priv->of_node)
|
|
|
|
+ mac = of_get_mac_address(priv->of_node);
|
|
|
|
+
|
|
|
|
+ if (mac && is_valid_ether_addr(mac)) {
|
|
|
|
+ memcpy(dev->dev_addr, mac, ETH_ALEN);
|
|
|
|
+ dev->addr_assign_type &= ~NET_ADDR_RANDOM;
|
|
|
|
+ } else {
|
|
|
|
+ eth_hw_addr_random(dev);
|
|
|
|
+ }
|
|
|
|
|
|
/*
|
|
/*
|
|
* Force the interface to use the POW send if always_use_pow
|
|
* Force the interface to use the POW send if always_use_pow
|
|
@@ -488,7 +479,7 @@ int cvm_oct_common_init(struct net_device *dev)
|
|
SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops);
|
|
SET_ETHTOOL_OPS(dev, &cvm_oct_ethtool_ops);
|
|
|
|
|
|
cvm_oct_phy_setup_device(dev);
|
|
cvm_oct_phy_setup_device(dev);
|
|
- dev->netdev_ops->ndo_set_mac_address(dev, &sa);
|
|
|
|
|
|
+ cvm_oct_set_mac_filter(dev);
|
|
dev->netdev_ops->ndo_change_mtu(dev, dev->mtu);
|
|
dev->netdev_ops->ndo_change_mtu(dev, dev->mtu);
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -595,22 +586,55 @@ static const struct net_device_ops cvm_oct_pow_netdev_ops = {
|
|
|
|
|
|
extern void octeon_mdiobus_force_mod_depencency(void);
|
|
extern void octeon_mdiobus_force_mod_depencency(void);
|
|
|
|
|
|
-static int __init cvm_oct_init_module(void)
|
|
|
|
|
|
+static struct device_node * __devinit cvm_oct_of_get_child(const struct device_node *parent,
|
|
|
|
+ int reg_val)
|
|
|
|
+{
|
|
|
|
+ struct device_node *node = NULL;
|
|
|
|
+ int size;
|
|
|
|
+ const __be32 *addr;
|
|
|
|
+
|
|
|
|
+ for (;;) {
|
|
|
|
+ node = of_get_next_child(parent, node);
|
|
|
|
+ if (!node)
|
|
|
|
+ break;
|
|
|
|
+ addr = of_get_property(node, "reg", &size);
|
|
|
|
+ if (addr && (be32_to_cpu(*addr) == reg_val))
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return node;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct device_node * __devinit cvm_oct_node_for_port(struct device_node *pip,
|
|
|
|
+ int interface, int port)
|
|
|
|
+{
|
|
|
|
+ struct device_node *ni, *np;
|
|
|
|
+
|
|
|
|
+ ni = cvm_oct_of_get_child(pip, interface);
|
|
|
|
+ if (!ni)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ np = cvm_oct_of_get_child(ni, port);
|
|
|
|
+ of_node_put(ni);
|
|
|
|
+
|
|
|
|
+ return np;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int __devinit cvm_oct_probe(struct platform_device *pdev)
|
|
{
|
|
{
|
|
int num_interfaces;
|
|
int num_interfaces;
|
|
int interface;
|
|
int interface;
|
|
int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
|
|
int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
|
|
int qos;
|
|
int qos;
|
|
|
|
+ struct device_node *pip;
|
|
|
|
|
|
octeon_mdiobus_force_mod_depencency();
|
|
octeon_mdiobus_force_mod_depencency();
|
|
pr_notice("cavium-ethernet %s\n", OCTEON_ETHERNET_VERSION);
|
|
pr_notice("cavium-ethernet %s\n", OCTEON_ETHERNET_VERSION);
|
|
|
|
|
|
- if (OCTEON_IS_MODEL(OCTEON_CN52XX))
|
|
|
|
- cvm_oct_mac_addr_offset = 2; /* First two are the mgmt ports. */
|
|
|
|
- else if (OCTEON_IS_MODEL(OCTEON_CN56XX))
|
|
|
|
- cvm_oct_mac_addr_offset = 1; /* First one is the mgmt port. */
|
|
|
|
- else
|
|
|
|
- cvm_oct_mac_addr_offset = 0;
|
|
|
|
|
|
+ pip = pdev->dev.of_node;
|
|
|
|
+ if (!pip) {
|
|
|
|
+ pr_err("Error: No 'pip' in /aliases\n");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
|
|
cvm_oct_poll_queue = create_singlethread_workqueue("octeon-ethernet");
|
|
cvm_oct_poll_queue = create_singlethread_workqueue("octeon-ethernet");
|
|
if (cvm_oct_poll_queue == NULL) {
|
|
if (cvm_oct_poll_queue == NULL) {
|
|
@@ -689,10 +713,11 @@ static int __init cvm_oct_init_module(void)
|
|
cvmx_helper_interface_get_mode(interface);
|
|
cvmx_helper_interface_get_mode(interface);
|
|
int num_ports = cvmx_helper_ports_on_interface(interface);
|
|
int num_ports = cvmx_helper_ports_on_interface(interface);
|
|
int port;
|
|
int port;
|
|
|
|
+ int port_index;
|
|
|
|
|
|
- for (port = cvmx_helper_get_ipd_port(interface, 0);
|
|
|
|
|
|
+ for (port_index = 0, port = cvmx_helper_get_ipd_port(interface, 0);
|
|
port < cvmx_helper_get_ipd_port(interface, num_ports);
|
|
port < cvmx_helper_get_ipd_port(interface, num_ports);
|
|
- port++) {
|
|
|
|
|
|
+ port_index++, port++) {
|
|
struct octeon_ethernet *priv;
|
|
struct octeon_ethernet *priv;
|
|
struct net_device *dev =
|
|
struct net_device *dev =
|
|
alloc_etherdev(sizeof(struct octeon_ethernet));
|
|
alloc_etherdev(sizeof(struct octeon_ethernet));
|
|
@@ -703,6 +728,7 @@ static int __init cvm_oct_init_module(void)
|
|
|
|
|
|
/* Initialize the device private structure. */
|
|
/* Initialize the device private structure. */
|
|
priv = netdev_priv(dev);
|
|
priv = netdev_priv(dev);
|
|
|
|
+ priv->of_node = cvm_oct_node_for_port(pip, interface, port_index);
|
|
|
|
|
|
INIT_DELAYED_WORK(&priv->port_periodic_work,
|
|
INIT_DELAYED_WORK(&priv->port_periodic_work,
|
|
cvm_oct_periodic_worker);
|
|
cvm_oct_periodic_worker);
|
|
@@ -787,7 +813,7 @@ static int __init cvm_oct_init_module(void)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-static void __exit cvm_oct_cleanup_module(void)
|
|
|
|
|
|
+static int __devexit cvm_oct_remove(struct platform_device *pdev)
|
|
{
|
|
{
|
|
int port;
|
|
int port;
|
|
|
|
|
|
@@ -835,10 +861,29 @@ static void __exit cvm_oct_cleanup_module(void)
|
|
if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
|
|
if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
|
|
cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
|
|
cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
|
|
CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
|
|
CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static struct of_device_id cvm_oct_match[] = {
|
|
|
|
+ {
|
|
|
|
+ .compatible = "cavium,octeon-3860-pip",
|
|
|
|
+ },
|
|
|
|
+ {},
|
|
|
|
+};
|
|
|
|
+MODULE_DEVICE_TABLE(of, cvm_oct_match);
|
|
|
|
+
|
|
|
|
+static struct platform_driver cvm_oct_driver = {
|
|
|
|
+ .probe = cvm_oct_probe,
|
|
|
|
+ .remove = __devexit_p(cvm_oct_remove),
|
|
|
|
+ .driver = {
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
+ .name = KBUILD_MODNAME,
|
|
|
|
+ .of_match_table = cvm_oct_match,
|
|
|
|
+ },
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+module_platform_driver(cvm_oct_driver);
|
|
|
|
+
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>");
|
|
MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>");
|
|
MODULE_DESCRIPTION("Cavium Networks Octeon ethernet driver.");
|
|
MODULE_DESCRIPTION("Cavium Networks Octeon ethernet driver.");
|
|
-module_init(cvm_oct_init_module);
|
|
|
|
-module_exit(cvm_oct_cleanup_module);
|
|
|