Commit 7af7f374 authored by Alessandro Rubini's avatar Alessandro Rubini

nic: current status of affairs

This is nothing to be excited about, but it's compiling and the
basic layout is there. There are quite some FIXME notes inside,
and each of them explains what is missing in the specific point.
Some files are still empty, with no FIXME either, but they must
clearly be filled with code.
parent 43a5018d
/*
* Device initialization and cleanup for White-Rabbit switch network interface
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Parts are from previous work by Tomasz Wlostowski <tomasz.wlostowsk@cern.ch>
* Parts are from previous work by Emilio G. Cota <cota@braap.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include "wr-nic.h"
/* The remove function is used by probe, so it's not __devexit */
static int __devexit wrn_remove(struct platform_device *pdev)
{
struct wrn_dev *wrn = pdev->dev.platform_data;
struct resource *res;
int i;
for (i = 0; i < WRN_NR_ENDPOINTS; i++) {
if (wrn->dev[i]) {
wrn_endpoint_remove(wrn->dev[i]);
free_netdev(wrn->dev[i]);
wrn->dev[i] = NULL;
}
}
if (wrn->base_nic) iounmap(wrn->base_nic);
if (wrn->base_ppsg) iounmap(wrn->base_ppsg);
if (wrn->base_calib) iounmap(wrn->base_calib);
if (wrn->base_stamp) iounmap(wrn->base_stamp);
if (wrn->irq_registered) {
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
free_irq(res->start, wrn);
}
return 0;
}
/* This helper is used by probe below, to avoid code replication */
static int __devinit __wrn_ioremap(struct platform_device *pdev, int n,
void __iomem **ptr)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, n);
if (!res) {
dev_err(&pdev->dev, "No resource %i defined\n", n);
return -ENOMEM;
}
*ptr = ioremap(res->start, res->end + 1 - res->start);
if (!*ptr) {
dev_err(&pdev->dev, "Ioremap for resource %i failed\n", n);
return -ENOMEM;
}
return 0;
}
static int __devinit wrn_probe(struct platform_device *pdev)
{
struct net_device *netdev;
struct wrn_devpriv *priv;
struct wrn_dev *wrn = pdev->dev.platform_data;
struct resource *res;
int i, err = 0;
/* no need to lock_irq: we only protect count and continue unlocked */
spin_lock(&wrn->lock);
if (++wrn->use_count != 1) {
--wrn->use_count;
spin_unlock(&wrn->lock);
return -EBUSY;
}
spin_unlock(&wrn->lock);
/* These memory regions are mapped once for all endpoints */
if ( (err = __wrn_ioremap(pdev, WRN_RES_MEM_NIC, &wrn->base_nic)) )
goto out;
if ( (err = __wrn_ioremap(pdev, WRN_RES_MEM_PPSG, &wrn->base_ppsg)) )
goto out;
if ( (err = __wrn_ioremap(pdev, WRN_RES_MEM_CALIB, &wrn->base_calib)) )
goto out;
if ( (err = __wrn_ioremap(pdev, WRN_RES_MEM_STAMP, &wrn->base_stamp)) )
goto out;
/* get the interrupt number from the resource */
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
err = request_irq(res->start, wrn_interrupt,
IRQF_TRIGGER_LOW | IRQF_SHARED,
DRV_NAME,
wrn);
if (err) goto out;
wrn->irq_registered = 1;
/* Finally, register one interface per endpoint */
memset(wrn->dev, 0, sizeof(wrn->dev));
for (i = 0; i < WRN_NR_ENDPOINTS; i++) {
netdev = alloc_etherdev(sizeof(struct wrn_devpriv));
if (!netdev) {
dev_err(&pdev->dev, "Etherdev alloc failed.\n");
err = -ENOMEM;
goto out;
}
priv = netdev_priv(netdev);
priv->wrn = wrn;
priv->ep_number = i;
/* The netdevice thing is registered from the endpoint */
err = wrn_endpoint_probe(netdev);
if (err == -ENODEV)
break;
if (err)
goto out;
/* This endpoint went in properly */
wrn->dev[i] = netdev;
}
err = 0;
out:
if (err) {
/* Call the remove function to avoid duplicating code */
wrn_remove(pdev);
}
return err;
}
/* This is not static as ./module.c is going to register it */
struct platform_driver wrn_driver = {
.probe = wrn_probe,
.remove = wrn_remove, /* not __exit_p as probe calls it */
/* No suspend or resume by now */
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};
/*
* Endoint-specific operations in the White-Rabbit switch network interface
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Parts are from previous work by Tomasz Wlostowski <tomasz.wlostowsk@cern.ch>
* Parts are from previous work by Emilio G. Cota <cota@braap.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/io.h>
#include "wr-nic.h"
/* Called for each endpoint, with a valid priv structure in place */
int wrn_endpoint_probe(struct net_device *netdev)
{
struct wrn_devpriv *priv = netdev_priv(netdev);
int err;
/* FIXME: check if the endpoint does exist or not */
if (0 /* not existent -- means no more exist after this one */)
return -ENODEV;
/* Errors different from -ENODEV are fatal to insmod */
priv->base = ioremap(FPGA_BASE_EP(priv->ep_number), WRN_EP_MEM_SIZE);
if (!priv->base) {
printk(KERN_ERR DRV_NAME ": ioremap failed for EP %i\n",
priv->ep_number);
return -ENOMEM;
}
/* build the device name (FIXME: up or downlink?) */
dev_alloc_name(netdev, "wru%d");
wrn_netops_init(netdev); /* function in ./nic-core.c */
wrn_ethtool_init(netdev); /* function in ./ethtool.c */
/* Napi is not supported on this device */
/* FIXME: mii -- copy from minic */
/* randomize a MAC address, so lazy users can avoid ifconfig */
random_ether_addr(netdev->dev_addr);
err = register_netdev(netdev);
if (err) {
printk(KERN_ERR DRV_NAME "Can't register dev %s\n",
netdev->name);
iounmap(priv->base);
/* ENODEV means "no more" for the caller */
return err == -ENODEV ? -EIO : err;
}
return 0;
}
/* Called for each endpoint, with a valid priv structure. The caller frees */
void wrn_endpoint_remove(struct net_device *netdev)
{
struct wrn_devpriv *priv = netdev_priv(netdev);
unregister_netdev(netdev);
iounmap(priv->base);
}
/*
* Ethtool operations for White-Rabbit switch network interface
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Parts are from previous work by Tomasz Wlostowski <tomasz.wlostowsk@cern.ch>
* Parts are from previous work by Emilio G. Cota <cota@braap.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/spinlock.h>
#include "wr-nic.h"
static int wrn_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct wrn_dev *wrn = netdev_priv(dev);
int ret;
spin_lock_irq(&wrn->lock);
ret = mii_ethtool_gset(&wrn->mii, cmd);
spin_unlock_irq(&wrn->lock);
cmd->supported=
SUPPORTED_FIBRE | /* FIXME: copper sfp? */
SUPPORTED_Autoneg |
SUPPORTED_1000baseKX_Full;
cmd->advertising =
ADVERTISED_1000baseKX_Full |
ADVERTISED_Autoneg;
cmd->port = PORT_FIBRE;
cmd->speed = SPEED_1000;
cmd->duplex = DUPLEX_FULL;
cmd->autoneg = AUTONEG_ENABLE;
return ret;
}
static int wrn_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct wrn_dev *wrn = netdev_priv(dev);
int ret;
spin_lock_irq(&wrn->lock);
ret = mii_ethtool_sset(&wrn->mii, cmd);
spin_unlock_irq(&wrn->lock);
return ret;
}
static int wrn_nwayreset(struct net_device *dev)
{
struct wrn_dev *wrn = netdev_priv(dev);
int ret;
spin_lock_irq(&wrn->lock);
ret = mii_nway_restart(&wrn->mii);
spin_unlock_irq(&wrn->lock);
return ret;
}
static void wrn_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
strlcpy(info->bus_info, dev_name(dev->dev.parent),
sizeof(info->bus_info));
}
/*
* These are the operations we support. No coalescing is there since
* most of the traffic will just happen within the FPGA switching core.
* Similarly, other funcionality like ringparam are not used.
* get_eeprom/set_eeprom may be useful for a simple MAC address management.
*/
static const struct ethtool_ops wrn_ethtool_ops = {
.get_settings = wrn_get_settings,
.set_settings = wrn_set_settings,
.get_drvinfo = wrn_get_drvinfo,
.nway_reset = wrn_nwayreset,
/* Some of the default methods apply for us */
.get_link = ethtool_op_get_link,
/* FIXME: get_regs_len and get_regs may be useful for debugging */
};
int wrn_ethtool_init(struct net_device *netdev)
{
netdev->ethtool_ops = &wrn_ethtool_ops;
return 0;
}
/*
* Module-related material for wr-nic: load and unload
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Parts are from previous work by Tomasz Wlostowski <tomasz.wlostowsk@cern.ch>
* Parts are from previous work by Emilio G. Cota <cota@braap.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include "wr-nic.h"
/* Our platform data is actually the device itself, and we have 1 only */
static struct wrn_dev wrn_dev;
/* The WRN_RES_ names are defined in the header file. Each is 64kB */
#define __RES(x) .start = x, .end = x + 0x10000 - 1, .flags = IORESOURCE_MEM
static struct resource wrn_resources[] = {
[WRN_RES_MEM_NIC] = { __RES( FPGA_BASE_NIC ) },
[WRN_RES_MEM_PPSG] = { __RES( FPGA_BASE_PPSG ) },
[WRN_RES_MEM_CALIB] = { __RES( FPGA_BASE_CALIB ) },
[WRN_RES_MEM_STAMP] = { __RES( FPGA_BASE_STAMP ) },
/* Last is the interrupt */
[WRN_RES_IRQ] = {
.start = WRN_INTERRUPT,
.end = WRN_INTERRUPT,
.flags = IORESOURCE_IRQ,
}
};
#undef __RES
static struct platform_device wrn_device = {
.name = DRV_NAME,
.id = 0,
.resource = wrn_resources,
.num_resources = ARRAY_SIZE(wrn_resources),
.dev = {
.platform_data = &wrn_dev,
/* dma_mask not used, as we make no DMA */
},
};
/*
* Module init and exit stuff. Here we register the platform data
* as well, but the driver itself is in device.c
*/
int __init wrn_init(void)
{
/* A few fields must be initialized at run time */
spin_lock_init(&wrn_dev.lock);
platform_device_register(&wrn_device);
platform_driver_register(&wrn_driver);
return -EAGAIN;
}
void __exit wrn_exit(void)
{
platform_driver_unregister(&wrn_driver);
platform_device_unregister(&wrn_device);
return;
}
module_init(wrn_init);
module_exit(wrn_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:wr-nic");
/*
* Core file for White-Rabbit switch network interface
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Parts are from previous work by Tomasz Wlostowski <tomasz.wlostowsk@cern.ch>
* Parts are from previous work by Emilio G. Cota <cota@braap.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include "wr-nic.h"
/* These are the standard netword device operations */
static int wrn_open(struct net_device *netdev)
{
struct wrn_devpriv *priv = netdev_priv(netdev);
struct wrn_dev *wrn = priv->wrn;
netdev_dbg(netdev, "%s\n", __func__);
if (!is_valid_ether_addr(netdev->dev_addr))
return -EADDRNOTAVAIL;
#if 0 /* FIXME: the whole open method is missing */
minic_writel(nic, MINIC_REG_MCR, 0);
minic_disable_irq(nic, 0xffffffff);
ep_enable(netdev);
netif_carrier_off(netdev);
init_timer(&nic->link_timer);
nic->link_timer.data = (unsigned long)netdev;
nic->link_timer.function = minic_check_link;
mod_timer(&nic->link_timer, jiffies + LINK_POLL_INTERVAL);
nic->synced = false;
nic->syncing_counters = false;
nic->rx_base = MINIC_PBUF_SIZE >> 1;
nic->rx_size = MINIC_PBUF_SIZE >> 1;
nic->tx_base = 0;
nic->tx_size = MINIC_PBUF_SIZE >> 1;
nic->tx_hwtstamp_enable = 0;
nic->rx_hwtstamp_enable = 0;
nic->tx_hwtstamp_oob = 1;
minic_new_rx_buffer(nic);
minic_enable_irq(nic, MINIC_EIC_IER_RX); // enable RX irq
minic_writel(nic, MINIC_REG_MCR, MINIC_MCR_RX_EN); // enable RX
if (netif_queue_stopped(nic->netdev)) {
netif_wake_queue(netdev);
} else {
netif_start_queue(netdev);
}
nic->iface_up = 0;
#endif
return 0;
}
static int wrn_close(struct net_device *netdev)
{
/* FIXME: the close method is missing */
return 0;
}
static int wrn_set_mac_address(struct net_device *netdev, void* addr)
{
/* FIXME: set_mac_address is missing */
return 0;
}
static int wrn_start_xmit(struct sk_buff *skb, struct net_device *netdev)
{
/* FIXME: start_xmit is missing */
return -ENODEV;
}
struct net_device_stats *wrn_get_stats(struct net_device *netdev)
{
/* FIXME: getstats is missing */
return NULL;
}
static int wrn_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
{
/* FIXME: ioctl is missing */
return -ENOIOCTLCMD;
}
static const struct net_device_ops wrn_netdev_ops = {
.ndo_open = wrn_open,
.ndo_stop = wrn_close,
.ndo_start_xmit = wrn_start_xmit,
.ndo_validate_addr = eth_validate_addr,
.ndo_get_stats = wrn_get_stats,
.ndo_set_mac_address = wrn_set_mac_address,
.ndo_do_ioctl = wrn_ioctl,
#if 0
/* Missing ops, possibly to add later (FIXME?) */
.ndo_set_multicast_list = wrn_set_multicast_list,
.ndo_change_mtu = wrn_change_mtu,
/* There are several more, but not really useful for us */
#endif
};
int wrn_netops_init(struct net_device *netdev)
{
netdev->netdev_ops = &wrn_netdev_ops;
return 0;
}
irqreturn_t wrn_interrupt(int irq, void *dev_id)
{
/* FIXME */
return IRQ_HANDLED;
}
#ifndef __WR_NIC_HARDWARE_H__
#define __WR_NIC_HARDWARE_H__
/* Our host CPU is this one, no way out of it */
#include <mach/at91sam9263.h>
#include "wr_nic.wb.h" /* wbgen2 file, with local fixes (64KB of area) */
/* FIXME: This one is wrong, should stack on wr_vic.c instead */
#define WRN_INTERRUPT AT91SAM9263_ID_IRQ0
/* Physical memory addresses. Should we import from other places? */
/* FIXME: Where is the wr_nic 64kB area in all this? */
/* The following is fake, just to make stuff compile - FIXME FIXME FIXME */
#define FPGA_BASE_NIC _FPGA_BUS_2_ADDR(4)
#define FPGA_BASE_PPSG _FPGA_BUS_2_ADDR(5)
#define FPGA_BASE_CALIB _FPGA_BUS_2_ADDR(6)
#define FPGA_BASE_STAMP _FPGA_BUS_2_ADDR(7)
#define FPGA_BASE_EP(x) _FPGA_BUS_2_ADDR(8+(x))
/* Following defines come straight from board-whiterabbit-mch.c */
#define _FPGA_BUS_2_ADDR(bus) (0x70000000 + (bus) * 0x10000)
#define FPGA_BASE_REVID _FPGA_BUS_2_ADDR(0)
#define FPGA_BASE_GPIO _FPGA_BUS_2_ADDR(1)
#define FPGA_BASE_SPIM _FPGA_BUS_2_ADDR(2)
#define FPGA_BASE_VIC _FPGA_BUS_2_ADDR(3)
#define FPGA_BASE_MINIC_UP1 _FPGA_BUS_2_ADDR(4)
#define FPGA_BASE_MINIC_UP0 _FPGA_BUS_2_ADDR(5)
#define FPGA_BASE_MINIC_DP0 _FPGA_BUS_2_ADDR(6)
#define FPGA_BASE_MINIC_DP1 _FPGA_BUS_2_ADDR(7)
#define FPGA_BASE_MINIC_DP2 _FPGA_BUS_2_ADDR(8)
#define FPGA_BASE_MINIC_DP3 _FPGA_BUS_2_ADDR(9)
#define FPGA_BASE_MINIC_DP4 _FPGA_BUS_2_ADDR(10)
#define FPGA_BASE_MINIC_DP5 _FPGA_BUS_2_ADDR(11)
#define FPGA_BASE_MINIC_DP6 _FPGA_BUS_2_ADDR(12)
#define FPGA_BASE_MINIC_DP7 _FPGA_BUS_2_ADDR(13)
#define FPGA_BASE_PPS_GEN _FPGA_BUS_2_ADDR(14)
#define FPGA_BASE_CALIBRATOR _FPGA_BUS_2_ADDR(15)
#define FPGA_BASE_RTU _FPGA_BUS_2_ADDR(16)
#define FPGA_BASE_RTU_TESTUNIT _FPGA_BUS_2_ADDR(17)
#define WR_MINIC_SIZE 0x10000
#endif /* __WR_NIC_HARDWARE_H__ */
/*
* wr-nic definitions, structures and prototypes
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Parts are from previous work by Tomasz Wlostowski <tomasz.wlostowsk@cern.ch>
* Parts are from previous work by Emilio G. Cota <cota@braap.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __WR_NIC_H__
#define __WR_NIC_H__
#include <linux/mii.h>
#include <linux/irqreturn.h>
#include <linux/spinlock.h>
#include "nic-hardware.h" /* Magic numbers: please fix them as needed */
#define DRV_NAME "wr-nic" /* Used in messages and device/driver names */
#define DRV_VERSION "0.1" /* For ethtool->get_drvinfo -- FIXME: auto-vers */
/* Structures we are using but may remain opaque */
struct net_device;
/*
* This is the main data structure for our NIC device
*/
#define WRN_NR_ENDPOINTS 24
struct wrn_dev {
/* Base addresses for the various areas */
void __iomem *base_nic; /* nic includes packet RAM */
void __iomem *base_ppsg;
void __iomem *base_calib;
void __iomem *base_stamp;
/* The base for each endpoint is ioremapped by the EP itself */
spinlock_t lock;
struct net_device *dev[WRN_NR_ENDPOINTS];
/* FIXME: all dev fields must be verified */
//int tx_hwtstamp_enable;
//int rx_hwtstamp_enable;
//int tx_hwtstamp_oob;
//struct platform_device *pdev;
//struct device *dev;
//struct net_device *netdev;
//struct net_device_stats stats;
//struct napi_struct napi;
//struct sk_buff *current_skb;
//struct timer_list link_timer;
//unsigned int rx_head, rx_avail, rx_base, rx_size;
//unsigned int tx_head, tx_avail, tx_base, tx_size;
//bool synced;
//bool syncing_counters;
//int iface_up;
//u32 cur_rx_desc;
struct mii_if_info mii; /* for ethtool operations */
int use_count; /* only used at probe time */
int irq_registered;
};
/* Each network device has one such priv structure */
struct wrn_devpriv {
struct wrn_dev *wrn;
void __iomem *base; /* each EP has its own memory area */
int ep_number;
int flags; /* uplink/downlink and possibly more */
/* FIXME: other fields needed for endpoint-specific struct? */
};
#define WRN_EP_MEM_SIZE 0x10000 /* 64k like other areas */
/* Our resources. The names are used as indexes in the resource array */
enum wrn_resnames {
WRN_RES_MEM_NIC = 0,
WRN_RES_MEM_PPSG,
WRN_RES_MEM_CALIB,
WRN_RES_MEM_STAMP,
/* Irq is last, so platform_get_resource() can use previous enums */
WRN_RES_IRQ,
};
/* Following functions in in nic-core.c */
extern irqreturn_t wrn_interrupt(int irq, void *dev_id);
extern int wrn_netops_init(struct net_device *netdev);
/* Following data in device.c */
struct platform_driver;
extern struct platform_driver wrn_driver;
/* Following functions in ethtool.c */
extern int wrn_ethtool_init(struct net_device *netdev);
/* Following functions in endpoint.c */
extern int wrn_endpoint_probe(struct net_device *netdev);
extern void wrn_endpoint_remove(struct net_device *netdev);
#endif /* __WR_NIC_H__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment