Commit 26f5d737 authored by Alessandro Rubini's avatar Alessandro Rubini

Merge branch 'wr-nic-unify'

This branch is unifying the wr-nic driver in wr-swithc-sw and the
one in spec-sw.  The gateware cell is the same, so we are aiming
at a single driver, maybe SDB-driven in the future.

A few differences still remain, but they are very minor details, and
we'll fix them in further commits, without a specific branch.
parents 01d90b2c ad6c1652
# This Makefile is meant to be used by the wr-switch. When built for wr-node
# the relevant Makefile lived in the parent directory.
ccflags-y += -DWR_SWITCH
obj-m := wr-nic.o
wr-nic-objs := module.o device.o nic-core.o endpoint.o ethtool.o \
pps.o timestamp.o dmtd.o
pps.o timestamp.o
# accept WRN_DEBUG from the environment. It turns pr_debug() into printk.
ifdef WRN_DEBUG
......
......@@ -11,6 +11,7 @@
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
......@@ -23,15 +24,31 @@
#include "wr-nic.h"
#include "nic-mem.h"
#if WR_IS_NODE /* Our platform_data is different in node vs switch */
#include "../spec-nic.h"
static inline struct wrn_dev *wrn_from_pdev(struct platform_device *pdev)
{
struct wrn_drvdata *drvdata = pdev->dev.platform_data;
return drvdata->wrn;
}
#else /* WR_IS_SWITCH */
static inline struct wrn_dev *wrn_from_pdev(struct platform_device *pdev)
{
return pdev->dev.platform_data;
}
#endif
/* The remove function is used by probe, so it's not __devexit */
static int __devexit wrn_remove(struct platform_device *pdev)
static int wrn_remove(struct platform_device *pdev)
{
struct wrn_dev *wrn = pdev->dev.platform_data;
struct wrn_dev *wrn = wrn_from_pdev(pdev);
int i;
spin_lock(&wrn->lock);
--wrn->use_count; /* Hmmm... looks like overkill... */
spin_unlock(&wrn->lock);
if (WR_IS_SWITCH) {
spin_lock(&wrn->lock);
--wrn->use_count; /* Hmmm... looks like overkill... */
spin_unlock(&wrn->lock);
}
/* First of all, stop any transmission */
writel(0, &wrn->regs->CR);
......@@ -39,6 +56,7 @@ static int __devexit wrn_remove(struct platform_device *pdev)
/* Then remove devices, memory maps, interrupts */
for (i = 0; i < WRN_NR_ENDPOINTS; i++) {
if (wrn->dev[i]) {
wrn_mezzanine_exit(wrn->dev[i]);
wrn_endpoint_remove(wrn->dev[i]);
free_netdev(wrn->dev[i]);
wrn->dev[i] = NULL;
......@@ -61,12 +79,12 @@ static int __devexit wrn_remove(struct platform_device *pdev)
}
/* This helper is used by probe below */
static int __devinit __wrn_map_resources(struct platform_device *pdev)
static int __wrn_map_resources(struct platform_device *pdev)
{
int i;
struct resource *res;
void __iomem *ptr;
struct wrn_dev *wrn = pdev->dev.platform_data;
struct wrn_dev *wrn = wrn_from_pdev(pdev);
/*
* The memory regions are mapped once for all endpoints.
......@@ -78,23 +96,23 @@ static int __devinit __wrn_map_resources(struct platform_device *pdev)
continue;
ptr = ioremap(res->start, res->end + 1 - res->start);
if (!ptr) {
dev_err(&pdev->dev, "Remap for res %i (%08x) failed\n",
dev_err(&pdev->dev, "Remap for res %i (%pa) failed\n",
i, res->start);
return -ENOMEM;
}
/* Hack: find the block number and fill the array */
pr_debug("Remapped %08x (block %i) to %p\n",
pr_debug("Remapped %pa (block %i) to %p\n",
res->start, i, ptr);
wrn->bases[i] = ptr;
}
return 0;
}
static int __devinit wrn_probe(struct platform_device *pdev)
static int wrn_probe(struct platform_device *pdev)
{
struct net_device *netdev;
struct wrn_ep *ep;
struct wrn_dev *wrn = pdev->dev.platform_data;
struct wrn_dev *wrn = wrn_from_pdev(pdev);
int i, err = 0;
/* Lazily: irqs are not in the resource list */
......@@ -103,13 +121,15 @@ static int __devinit wrn_probe(struct platform_device *pdev)
static irq_handler_t irq_handlers[] = WRN_IRQ_HANDLERS;
/* No need to lock_irq: we only protect count and continue unlocked */
spin_lock(&wrn->lock);
if (++wrn->use_count != 1) {
--wrn->use_count;
if (WR_IS_SWITCH) {
spin_lock(&wrn->lock);
if (++wrn->use_count != 1) {
--wrn->use_count;
spin_unlock(&wrn->lock);
return -EBUSY;
}
spin_unlock(&wrn->lock);
return -EBUSY;
}
spin_unlock(&wrn->lock);
/* Map our resource list and instantiate the shortcut pointers */
if ( (err = __wrn_map_resources(pdev)) )
......@@ -121,24 +141,26 @@ static int __devinit wrn_probe(struct platform_device *pdev)
wrn->rxd = ((void *)wrn->regs) + 0x100; /* was: RX1_D1 */
wrn->databuf = (void *)wrn->regs + offsetof(struct NIC_WB, MEM);
tasklet_init(&wrn->rx_tlet, wrn_rx_interrupt, (unsigned long)wrn);
printk("regs %p, txd %p, rxd %p, buffer %p\n",
wrn->regs, wrn->txd, wrn->rxd, wrn->databuf);
/* Register the interrupt handlers (not shared) */
for (i = 0; i < ARRAY_SIZE(irq_names); i++) {
err = request_irq(irqs[i], irq_handlers[i],
IRQF_TRIGGER_LOW, irq_names[i], wrn);
if (err) goto out;
wrn->irq_registered |= 1 << i;
if (0)
printk("regs %p, txd %p, rxd %p, buffer %p\n",
wrn->regs, wrn->txd, wrn->rxd, wrn->databuf);
if (WR_IS_SWITCH) {
/* Register the interrupt handlers (not shared) */
for (i = 0; i < ARRAY_SIZE(irq_names); i++) {
err = request_irq(irqs[i], irq_handlers[i],
IRQF_TRIGGER_LOW, irq_names[i], wrn);
if (err)
goto out;
wrn->irq_registered |= 1 << i;
}
}
/* Reset the device, just to be sure, before making anything */
writel(0, &wrn->regs->CR);
mdelay(10);
/* 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_ep));
netdev->dev.parent = &pdev->dev;
if (!netdev) {
dev_err(&pdev->dev, "Etherdev alloc failed.\n");
err = -ENOMEM;
......@@ -148,7 +170,6 @@ static int __devinit wrn_probe(struct platform_device *pdev)
ep = netdev_priv(netdev);
ep->wrn = wrn;
ep->ep_regs = wrn->bases[WRN_FB_EP] + i * FPGA_SIZE_EACH_EP;
printk("ep %p, regs %i = %p\n", ep, i, ep->ep_regs);
ep->ep_number = i;
#if 0 /* FIXME: UPlink or not? */
if (i < WRN_NR_UPLINK)
......@@ -163,7 +184,13 @@ static int __devinit wrn_probe(struct platform_device *pdev)
goto out;
/* This endpoint went in properly */
wrn->dev[i] = netdev;
err = wrn_mezzanine_init(netdev);
if (err)
dev_err(&pdev->dev, "Init mezzanine code: "
"error %i\n", err);
}
if (i == 0)
return -ENODEV; /* no endpoints */
for (i = 0; i < WRN_NR_TXDESC; i++) { /* Clear all tx descriptors */
struct wrn_txd *tx;
......@@ -190,7 +217,6 @@ static int __devinit wrn_probe(struct platform_device *pdev)
writel(NIC_CR_RX_EN | NIC_CR_TX_EN, &wrn->regs->CR);
writel(WRN_IRQ_ALL, (void *)wrn->regs + 0x24 /* EIC_IER */);
printk("imr: %08x\n", readl((void *)wrn->regs + 0x28 /* EIC_IMR */));
wrn_tstamp_init(wrn);
err = 0;
......@@ -210,7 +236,7 @@ struct platform_driver wrn_driver = {
.remove = wrn_remove, /* not __exit_p as probe calls it */
/* No suspend or resume by now */
.driver = {
.name = DRV_NAME,
.name = KBUILD_MODNAME,
.owner = THIS_MODULE,
},
};
/*
* DMTD calibration procedures
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Partly from previous work by Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Partly 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 "wr-nic.h"
int wrn_phase_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct wrn_phase_req phase_req;
struct wrn_ep *ep = netdev_priv(dev);
u32 dmsr;
s32 ph;
phase_req.phase = 0;
phase_req.ready = 0;
dmsr = readl(&ep->ep_regs->DMSR);
if(dmsr & EP_DMSR_PS_RDY) {
ph = EP_DMSR_PS_VAL_R(dmsr);
/* Sign-extend the 24-bit value */
if(ph & 0x800000)
ph |= 0xff << 24;
/* Divide by nsamples (average) */
ph /= WRN_DMTD_AVG_SAMPLES;
/* Put it back in the proper range */
ph = (ph + WRN_DMTD_MAX_PHASE) % WRN_DMTD_MAX_PHASE;
phase_req.phase = ph;
phase_req.ready = 1;
}
if (copy_to_user(rq->ifr_data, &phase_req, sizeof(phase_req)))
return -EFAULT;
return 0;
}
int wrn_calib_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct wrn_calibration_req cal_req;
struct wrn_ep *ep = netdev_priv(dev);
u32 tmp;
if (copy_from_user(&cal_req, rq->ifr_data, sizeof(cal_req)))
return -EFAULT;
if (0) { /* FIXME: what' coming out of this thing? */
if (!(ep->ep_flags & BIT(WRN_EP_UP)))
return -EIO; /* was -EFAULT in minic */
}
switch(cal_req.cmd) {
case WRN_CAL_TX_ON:
tmp = wrn_phy_read(dev, 0, WRN_MDIO_WR_SPEC);
wrn_phy_write(dev, 0, WRN_MDIO_WR_SPEC,
tmp | WRN_MDIO_WR_SPEC_TX_CAL);
break;
case WRN_CAL_TX_OFF:
tmp = wrn_phy_read(dev, 0, WRN_MDIO_WR_SPEC);
wrn_phy_write(dev, 0, WRN_MDIO_WR_SPEC,
tmp & (~WRN_MDIO_WR_SPEC_TX_CAL));
break;
case WRN_CAL_RX_ON:
tmp = wrn_phy_read(dev, 0, WRN_MDIO_WR_SPEC);
wrn_phy_write(dev, 0, WRN_MDIO_WR_SPEC,
tmp | WRN_MDIO_WR_SPEC_CAL_CRST);
break;
case WRN_CAL_RX_OFF:
// do nothing.....
break;
case WRN_CAL_RX_CHECK:
tmp = wrn_phy_read(dev, 0, WRN_MDIO_WR_SPEC);
cal_req.cal_present = tmp & WRN_MDIO_WR_SPEC_RX_CAL_STAT
? 1 : 0;
if (copy_to_user(rq->ifr_data,&cal_req,
sizeof(cal_req)))
return -EFAULT;
break;
}
return 0;
}
......@@ -23,27 +23,27 @@ static char *macaddr = "00:00:00:00:00:00";
module_param(macaddr, charp, 0444);
/* Copied from kernel 3.6 net/utils.c, it converts from MAC string to u8 array */
static int mac_pton(const char *s, u8 *mac)
__weak int mac_pton(const char *s, u8 *mac)
{
int i;
/* XX:XX:XX:XX:XX:XX */
if (strlen(s) < 3 * ETH_ALEN - 1)
return -EINVAL;
return 0;
/* Don't dirty result unless string is valid MAC. */
for (i = 0; i < ETH_ALEN; i++) {
if (!strchr("0123456789abcdefABCDEF", s[i * 3]))
return -EINVAL;
return 0;
if (!strchr("0123456789abcdefABCDEF", s[i * 3 + 1]))
return -EINVAL;
return 0;
if (i != ETH_ALEN - 1 && s[i * 3 + 2] != ':')
return -EINVAL;
return 0;
}
for (i = 0; i < ETH_ALEN; i++) {
mac[i] = (hex_to_bin(s[i * 3]) << 4) | hex_to_bin(s[i * 3 + 1]);
}
return 0;
return 1;
}
/*
......@@ -55,6 +55,16 @@ int wrn_phy_read(struct net_device *dev, int phy_id, int location)
struct wrn_ep *ep = netdev_priv(dev);
u32 val;
if (WR_IS_NODE) {
/*
* We cannot access the phy from Linux, because the phy
* is managed by the lm32 core. However, network manager
* insists on doing that, so we'd better not warn about it
*/
//WARN_ON(1); /* SPEC: no access */
return -1;
}
wrn_ep_write(ep, MDIO_CR, EP_MDIO_CR_ADDR_W(location));
while( (wrn_ep_read(ep, MDIO_ASR) & EP_MDIO_ASR_READY) == 0)
;
......@@ -67,6 +77,17 @@ void wrn_phy_write(struct net_device *dev, int phy_id, int location,
int value)
{
struct wrn_ep *ep = netdev_priv(dev);
if (WR_IS_NODE) {
/*
* We cannot access the phy from Linux, because the phy
* is managed by the lm32 core. However, network manager
* insists on doing that, so we'd better not warn about it
*/
//WARN_ON(1); /* SPEC: no access */
return;
}
wrn_ep_write(ep, MDIO_CR,
EP_MDIO_CR_ADDR_W(location)
| EP_MDIO_CR_DATA_W(value)
......@@ -157,6 +178,11 @@ int wrn_ep_open(struct net_device *dev)
unsigned long timerarg = (unsigned long)dev;
int prio, prio_map;
if (WR_IS_NODE) {
netif_carrier_on(dev);
return 0; /* No access to EP registers in the SPEC */
}
/* Prepare hardware registers: first config, then bring up */
writel(0
| EP_VCR0_QMODE_W(0x3) /* unqualified port */
......@@ -185,17 +211,13 @@ int wrn_ep_open(struct net_device *dev)
| EP_ECR_RX_EN,
&ep->ep_regs->ECR);
/* Setup DMCR */
writel(0
| EP_DMCR_EN
| EP_DMCR_N_AVG_W(256 /* DMTD_AVG_SAMPLES */),
&ep->ep_regs->DMCR);
wrn_phy_write(dev, 0, MII_LPA, 0);
wrn_phy_write(dev, 0, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
/* Prepare the timer for link-up notifications */
setup_timer(&ep->ep_link_timer, wrn_ep_check_link, timerarg);
/* Not on spec. On spec this part of the function is never reached
* due to return in if(WR_IS_NODE) */
mod_timer(&ep->ep_link_timer, jiffies + WRN_LINK_POLL_INTERVAL);
return 0;
}
......@@ -204,6 +226,13 @@ int wrn_ep_close(struct net_device *dev)
{
struct wrn_ep *ep = netdev_priv(dev);
if (WR_IS_NODE)
return 0; /* No access to EP registers in the SPEC */
/*
* Beware: the system loops in the del_timer_sync below if timer_setup
* had not been called either (see "if (WR_IS_NODE)" in ep_open above)
*/
writel(0, &ep->ep_regs->ECR);
del_timer_sync(&ep->ep_link_timer);
return 0;
......@@ -225,24 +254,50 @@ int wrn_endpoint_probe(struct net_device *dev)
{
struct wrn_ep *ep = netdev_priv(dev);
static u8 wraddr[6];
int epnum, err;
int err;
u32 val;
if (is_zero_ether_addr(wraddr)) {
err = mac_pton(macaddr, wraddr);
if (err)
if (!err)
pr_err("wr_nic: probably invalid MAC address \"%s\".\n"
"Use format XX:XX:XX:XX:XX:XX\n", macaddr);
}
if (WR_IS_NODE) {
/* If address is not provided as parameter read from lm32 */
if (is_zero_ether_addr(wraddr)) {
/* on the SPEC the lm32 already configured the mac address */
val = readl(&ep->ep_regs->MACH);
put_unaligned_be16(val, wraddr);
val = readl(&ep->ep_regs->MACL);
put_unaligned_be32(val, wraddr+2);
}
}
if (WR_IS_SWITCH) {
/* If the MAC address is 0, then randomize the first MAC */
/* Do not randomize for SPEC */
if (is_zero_ether_addr(wraddr)) {
pr_warn("wr_nic: missing MAC address, randomize\n");
/* randomize a MAC address, so lazy users can avoid ifconfig */
random_ether_addr(wraddr);
/* Clear the MSB on fourth octect to prevent bit overflow on OUI */
wraddr[3] &= 0x7F;
}
}
if (ep->ep_number == 0)
pr_info("WR-nic: Using address %pM\n", wraddr);
epnum = ep->ep_number;
/* Use wraddr as MAC */
memcpy(dev->dev_addr, wraddr, ETH_ALEN);
pr_debug("wr_nic: assign MAC %pM to wr%d\n", dev->dev_addr, ep->ep_number);
/* Check whether the ep has been sinthetized or not */
val = readl(&ep->ep_regs->IDCODE);
if (val != WRN_EP_MAGIC) {
pr_info(DRV_NAME "EP%i (%s) has not been sintethized\n",
pr_info(KBUILD_MODNAME " EP%i (%s) has not been sintethized\n",
ep->ep_number, dev->name);
return -ENODEV;
}
......@@ -264,19 +319,6 @@ int wrn_endpoint_probe(struct net_device *dev)
ep->mii.advertising = ADVERTISE_1000XFULL;
ep->mii.full_duplex = 1;
/* If the MAC address is 0, then randomize the first MAC */
if (is_zero_ether_addr(wraddr)) {
pr_warn("wr_nic: missing MAC address, randomize\n");
/* randomize a MAC address, so lazy users can avoid ifconfig */
random_ether_addr(wraddr);
/* Clear the MSB on fourth octect to prevent bit overflow on OUI */
wraddr[3] &= 0x7F;
}
/* Use wraddr as MAC */
memcpy(dev->dev_addr, wraddr, ETH_ALEN);
pr_debug("wr_nic: assign MAC %pM to wr%d\n", dev->dev_addr, epnum);
/* Finally, register and succeed, or fail and undo */
err = register_netdev(dev);
......@@ -285,7 +327,7 @@ int wrn_endpoint_probe(struct net_device *dev)
put_unaligned_be32(val + 1, wraddr + 2);
if (err) {
printk(KERN_ERR DRV_NAME "Can't register dev %s\n",
printk(KERN_ERR KBUILD_MODNAME ": Can't register dev %s\n",
dev->name);
__wrn_endpoint_shutdown(ep);
/* ENODEV means "no more" for the caller, so avoid it */
......
......@@ -70,7 +70,7 @@ static int wrn_nwayreset(struct net_device *dev)
static void wrn_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
strlcpy(info->bus_info, dev_name(dev->dev.parent),
sizeof(info->bus_info));
......
......@@ -45,7 +45,7 @@ static void wrn_release(struct device *dev)
}
static struct platform_device wrn_device = {
.name = DRV_NAME,
.name = KBUILD_MODNAME,
.id = 0,
.resource = wrn_resources,
.num_resources = ARRAY_SIZE(wrn_resources),
......
......@@ -20,7 +20,9 @@
#include <asm/unaligned.h>
#include "wr-nic.h"
#if WR_IS_SWITCH
#include "wr_pstats.h"
#endif
#include "nic-mem.h"
/*
......@@ -39,11 +41,13 @@ static int wrn_open(struct net_device *dev)
if (!is_valid_ether_addr(dev->dev_addr))
return -EADDRNOTAVAIL;
/* MACH gets the first two bytes, MACL the rest */
val = get_unaligned_be16(dev->dev_addr);
writel(val, &ep->ep_regs->MACH);
val = get_unaligned_be32(dev->dev_addr+2);
writel(val, &ep->ep_regs->MACL);
if (WR_IS_SWITCH) {
/* MACH gets the first two bytes, MACL the rest */
val = get_unaligned_be16(dev->dev_addr);
writel(val, &ep->ep_regs->MACH);
val = get_unaligned_be32(dev->dev_addr+2);
writel(val, &ep->ep_regs->MACL);
}
/* Mark it as down, and start the ep-specific polling timer */
clear_bit(WRN_EP_UP, &ep->ep_flags);
......@@ -183,6 +187,8 @@ static int wrn_start_xmit(struct sk_buff *skb, struct net_device *dev)
spin_lock_irqsave(&wrn->lock, flags);
desc = __wrn_alloc_tx_desc(wrn);
id = (wrn->id++) & 0xffff;
if (id == 0) /* 0 cannot be used in the SPEC; irrelevant in WRS */
id = wrn->id++;
spin_unlock_irqrestore(&wrn->lock, flags);
if (desc < 0) /* error */
......@@ -198,7 +204,7 @@ static int wrn_start_xmit(struct sk_buff *skb, struct net_device *dev)
__func__);
}
wrn->skb_desc[desc].skb = skb; /* Save for tx irq and stamping */
wrn->skb_desc[desc].id = id; /* Save for tx irq and stamping */
wrn->skb_desc[desc].frame_id = id; /* Save for tx irq and stamping */
//netif_stop_queue(dev); /* Queue stopped until tx is over (FIXME?) */
......@@ -220,17 +226,20 @@ static int wrn_start_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
#if WR_IS_SWITCH
int (*wr_nic_pstats_callback)(int epnum,
unsigned int ctr[PSTATS_CNT_PP]);
EXPORT_SYMBOL(wr_nic_pstats_callback);
static unsigned int nic_counters[PSTATS_CNT_PP];
static DEFINE_SPINLOCK(nic_counters_lock);
#endif
struct net_device_stats *wrn_get_stats(struct net_device *dev)
{
struct wrn_ep *ep = netdev_priv(dev);
#if WR_IS_SWITCH
if (wr_nic_pstats_callback) {
int i;
......@@ -259,11 +268,31 @@ struct net_device_stats *wrn_get_stats(struct net_device *dev)
}
spin_unlock(&nic_counters_lock);
}
#endif
return &ep->stats;
return NULL;
}
/*
* If we have a mezzanine, we need the ioctl as well as init/exit. Provide
* three weak functions here, so to link even if no mezzanine is there.
*/
int __weak wrn_mezzanine_ioctl(struct net_device *dev, struct ifreq *rq,
int cmd)
{
return -ENOIOCTLCMD;
}
int __weak wrn_mezzanine_init(struct net_device *dev)
{
return 0;
}
void __weak wrn_mezzanine_exit(struct net_device *dev)
{
return;
}
static int wrn_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct wrn_ep *ep = netdev_priv(dev);
......@@ -274,9 +303,8 @@ static int wrn_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
case SIOCSHWTSTAMP:
return wrn_tstamp_ioctl(dev, rq, cmd);
case PRIV_IOCGCALIBRATE:
return wrn_calib_ioctl(dev, rq, cmd);
case PRIV_IOCGGETPHASE:
return wrn_phase_ioctl(dev, rq, cmd);
return -EOPNOTSUPP;
case PRIV_IOCREADREG:
if (get_user(reg, (u32 *)rq->ifr_data) < 0)
return -EFAULT;
......@@ -300,6 +328,11 @@ static int wrn_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
return -EFAULT;
return 0;
case PRIV_MEZZANINE_ID:
case PRIV_MEZZANINE_CMD:
/* Pass this to the mezzanine driver, or use internal weak */
return wrn_mezzanine_ioctl(dev, rq, cmd);
default:
spin_lock_irq(&ep->lock);
res = generic_mii_ioctl(&ep->mii, if_mii(rq), cmd, NULL);
......@@ -396,28 +429,38 @@ static void __wrn_rx_descriptor(struct wrn_dev *wrn, int desc)
wrn_ppsg_read_time(wrn, &counter_ppsg, &utc);
if(counter_ppsg < REFCLK_FREQ/4 && ts_r > 3*REFCLK_FREQ/4)
utc--;
if (WR_IS_NODE)
if (counter_ppsg < ts_r)
utc--;
if (WR_IS_SWITCH)
if (counter_ppsg < REFCLK_FREQ/4 && ts_r > 3*REFCLK_FREQ/4)
utc--;
ts.tv_sec = (s32)utc & 0x7fffffff;
cntr_diff = (ts_r & 0xf) - ts_f;
/* the bit says the rising edge cnter is 1tick ahead */
if(cntr_diff == 1 || cntr_diff == (-0xf))
if (cntr_diff == 1 || cntr_diff == (-0xf))
ts.tv_sec |= 0x80000000;
ts.tv_nsec = ts_r * NSEC_PER_TICK;
pr_debug("Timestamp: %li:%li, ahead = %d\n",
ts.tv_sec & 0x7fffffff,
ts.tv_nsec & 0x7fffffff,
ts.tv_sec & 0x80000000 ? 1 :0);
ts.tv_sec & 0x80000000 ? 1 : 0);
if (WR_IS_NODE) {
/* SPEC: don't do the strange stuff for wr-ptp */
ts.tv_sec &= ~0x80000000;
ts.tv_nsec &= 0x7fffffff;
}
/* If the timestamp was reported as incorrect, pass 0 instead */
if (! (r1 & NIC_RX1_D1_TS_INCORRECT)) /* FIXME: bit name possibly? */
{
if (!(r1 & NIC_RX1_D1_TS_INCORRECT)) {
hwts = skb_hwtstamps(skb);
hwts->hwtstamp = timespec_to_ktime(ts);
}
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
dev->last_rx = jiffies;
......@@ -486,7 +529,7 @@ static void wrn_tx_interrupt(struct wrn_dev *wrn)
/* hardware timestamping is enabled */
info->tx_flags |= SKBTX_IN_PROGRESS;
pr_debug("%s: %i -- in progress\n", __func__, __LINE__);
wrn_tstamp_find_skb(wrn, i);
wrn_tx_tstamp_skb(wrn, i);
/* It has been freed if found; otherwise keep it */
} else {
dev_kfree_skb_irq(skb);
......
/*
* hardware-specific definitions for the White Rabbit NIC
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Copyright (C) 2010-2014 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or modify
......@@ -10,7 +10,11 @@
*/
#ifndef __WR_NIC_HARDWARE_H__
#define __WR_NIC_HARDWARE_H__
#if (!defined WR_IS_NODE) && (!defined WR_IS_SWITCH)
#error "WR_NODE and WR_SWITCH not defined!"
#endif
#if WR_IS_SWITCH
/* This is the clock used in internal counters. */
#define REFCLK_FREQ (125000000 / 2)
#define NSEC_PER_TICK (NSEC_PER_SEC / REFCLK_FREQ)
......@@ -53,6 +57,70 @@
#define FPGA_SIZE_VIC 0x00001000
#define FPGA_BASE_TS 0x10051000
#define FPGA_SIZE_TS 0x00001000
#endif /* WR_IS_SWITCH */
#if WR_IS_NODE
/* This is the clock used in internal counters. */
#define REFCLK_FREQ (125000000)
#define NSEC_PER_TICK (NSEC_PER_SEC / REFCLK_FREQ)
/* The interrupt is one of those managed by our WRVIC device */
#define WRN_IRQ_BASE 0 /* FIXME: relative to pci dev */
#define WRN_IRQ_NIC (WRN_IRQ_BASE + 0)
#define WRN_IRQ_TSTAMP /* (WRN_IRQ_BASE + 1) -- not used here */
//#define WRN_IRQ_PPSG (WRN_IRQ_BASE + )
//#define WRN_IRQ_RTU (WRN_IRQ_BASE + )
//#define WRN_IRQ_RTUT (WRN_IRQ_BASE + )
/*
* spec-wr-nic memory map (from SDB dump):
*
* 00000651:e6a542c9 WB4-Crossbar-GSI
* 0000ce42:00000011 WR-CORE (bridge: 00000000)
* 00000651:e6a542c9 WB4-Crossbar-GSI
* 0000ce42:66cfeb52 WB4-BlockRAM (00000000-00015fff)
* 00000651:eef0b198 WB4-Bridge-GSI (bridge: 00020000)
* 00000651:e6a542c9 WB4-Crossbar-GSI
* 0000ce42:ab28633a WR-Mini-NIC (00020000-000200ff)
* 0000ce42:650c2d4f WR-Endpoint (00020100-000201ff)
* 0000ce42:65158dc0 WR-Soft-PLL (00020200-000202ff)
* 0000ce42:de0d8ced WR-PPS-Generator (00020300-000203ff)
* 0000ce42:ff07fc47 WR-Periph-Syscon (00020400-000204ff)
* 0000ce42:e2d13d04 WR-Periph-UART (00020500-000205ff)
* 0000ce42:779c5443 WR-Periph-1Wire (00020600-000206ff)
* 0000ce42:779c5443 WR-Periph-1Wire (00020700-000207ff)
* 0000ce42:00000012 WR-NIC (00040000-0005ffff)
* 0000ce42:00000013 WB-VIC-Int.Control (00060000-000600ff)
* 0000ce42:00000014 WR-TXTSU (00061000-000610ff)
* 000075cb:00000002 WR-DIO-Core (bridge: 00062000)
* 00000651:e6a542c9 WB4-Crossbar-GSI
* 0000ce42:779c5443 WR-1Wire-master (00062000-000620ff)
* 0000ce42:123c5443 WB-I2C-Master (00062100-000621ff)
* 0000ce42:441c5143 WB-GPIO-Port (00062200-000622ff)
* 000075cb:00000001 WR-DIO-Registers (00062300-000623ff)
*
*/
/* This is the base address of memory regions (gennum bridge, bar 0) */
#define FPGA_BASE_LM32 0x00000000
#define FPGA_SIZE_LM32 0x00016000
#define FPGA_BASE_NIC 0x00020000
#define FPGA_SIZE_NIC 0x00000100
#define FPGA_BASE_EP 0x00020100
#define FPGA_SIZE_EP 0x00000100
#define FPGA_SIZE_EACH_EP 0x100 /* There is one only */
#define FPGA_BASE_PPSG 0x00020300
#define FPGA_SIZE_PPSG 0x00000100
#define FPGA_BASE_VIC 0x00060000 /* not used here */
#define FPGA_SIZE_VIC 0x00000100
#define FPGA_BASE_TS 0x00061000
#define FPGA_SIZE_TS 0x0000 100
#endif /* ifdef WR_IS_NODE */
enum fpga_blocks {
WRN_FB_NIC,
......@@ -63,7 +131,12 @@ enum fpga_blocks {
};
/* In addition to the above enumeration, we scan for those many endpoints */
#define WRN_NR_ENDPOINTS 18
#if WR_IS_NODE
# define WRN_NR_ENDPOINTS 1
#endif
#if WR_IS_SWITCH
# define WRN_NR_ENDPOINTS 18
#endif
/* 8 tx and 8 rx descriptors */
#define WRN_NR_DESC 8
......
......@@ -15,40 +15,29 @@
#include "wr-nic.h"
/* This looks for an skb in the already-received stamp list */
void wrn_tstamp_find_skb(struct wrn_dev *wrn, int desc)
/* This checks if we already received the timestamp interrupt */
void wrn_tx_tstamp_skb(struct wrn_dev *wrn, int desc)
{
struct skb_shared_hwtstamps *hwts;
struct sk_buff *skb = wrn->skb_desc[desc].skb;
struct wrn_desc_pending *d = wrn->skb_desc + desc;
struct sk_buff *skb = d->skb;
struct timespec ts;
int id = wrn->skb_desc[desc].id;
u32 counter_ppsg; /* PPS generator nanosecond counter */
u32 utc;
int i; /* FIXME: use list for faster access */
for(i = 0; i < WRN_TS_BUF_SIZE; i++)
if(wrn->ts_buf[i].valid && wrn->ts_buf[i].frame_id == id)
break;
if (i == WRN_TS_BUF_SIZE) {
pr_debug("%s: not found\n", __func__);
if (!wrn->skb_desc[desc].valid)
return;
}
pr_debug("%s: found\n", __func__);
/* so we found the skb, do the timestamping magic */
/* already reported by hardware: do the timestamping magic */
wrn_ppsg_read_time(wrn, &counter_ppsg, &utc);
/* The timestamp nanoseconds value is closer to the end of previous second, but the UTC time
read from PPSG is at the beginning of the next second: adjust UTC seconds to avoid 1 sec
"jump" */
if(counter_ppsg < REFCLK_FREQ/4 && wrn->ts_buf[i].ts > 3*REFCLK_FREQ/4)
/* We may be at the beginning og the next second */
if (counter_ppsg < d->cycles)
utc--;
ts.tv_sec = (s32)utc & 0x7fffffff;
ts.tv_nsec = wrn->ts_buf[i].ts * NSEC_PER_TICK;
if (! (wrn->ts_buf[i].valid & TS_INVALID))
{
ts.tv_nsec = d->cycles * NSEC_PER_TICK;
if (!(d->valid & TS_INVALID)) {
hwts = skb_hwtstamps(skb);
hwts->hwtstamp = timespec_to_ktime(ts);
skb_tstamp_tx(skb, hwts);
......@@ -56,68 +45,50 @@ void wrn_tstamp_find_skb(struct wrn_dev *wrn, int desc)
dev_kfree_skb_irq(skb);
/* release both the descriptor and the tstamp entry */
wrn->skb_desc[desc].skb = 0;
wrn->ts_buf[i].valid = 0;
d->skb = 0;
d->valid = 0;
}
/* This function records the timestamp in a list -- called from interrupt */
/* This function, called by txtsu records the timestamp for the descriptor */
static int record_tstamp(struct wrn_dev *wrn, u32 tsval, u32 idreg, u32 r2)
{
int port_id = TXTSU_TSF_R1_PID_R(idreg);
int frame_id = TXTSU_TSF_R1_FID_R(idreg);
int ts_incorrect = r2 & TXTSU_TSF_R2_INCORRECT;
struct skb_shared_hwtstamps *hwts;
struct timespec ts;
struct sk_buff *skb;
u32 utc, counter_ppsg; /* PPS generator nanosecond counter */
int i; /* FIXME: use list for faster access */
/*printk("%s: Got TS: %x pid %d fid %d\n", __func__,
tsval, port_id, frame_id);*/
int i;
/* First of all look if the skb is already pending */
/* Find the skb in the descriptor array */
for (i = 0; i < WRN_NR_DESC; i++)
if (wrn->skb_desc[i].skb && wrn->skb_desc[i].id == frame_id)
if (wrn->skb_desc[i].skb
&& wrn->skb_desc[i].frame_id == frame_id)
break;
if (i < WRN_NR_DESC) {
/*printk("%s: found\n", __func__);*/
skb = wrn->skb_desc[i].skb;
wrn_ppsg_read_time(wrn, &counter_ppsg, &utc);
if(counter_ppsg < (tsval & 0xfffffff))
utc--;
ts.tv_sec = (s32)utc & 0x7fffffff;
ts.tv_nsec = (tsval & 0xfffffff) * NSEC_PER_TICK;
/* Provide the timestamp for the userland only if we're 100% sure about its correctness */
if (!ts_incorrect)
{
hwts = skb_hwtstamps(skb);
hwts->hwtstamp = timespec_to_ktime(ts);
skb_tstamp_tx(skb, hwts);
}
dev_kfree_skb_irq(skb);
wrn->skb_desc[i].skb = 0;
if (i == WRN_NR_DESC) {
/* Not found: Must be a PTP frame sent from the SPEC! */
return 0;
}
/* Otherwise, save it to the list, in an empty slot */
for(i = 0; i < WRN_TS_BUF_SIZE; i++)
if(!wrn->ts_buf[i].valid)
break;
if (i == WRN_TS_BUF_SIZE) {
pr_debug("%s: ENOMEM\n", __func__);
return -ENOMEM;
skb = wrn->skb_desc[i].skb;
wrn_ppsg_read_time(wrn, &counter_ppsg, &utc);
if (counter_ppsg < (tsval & 0xfffffff))
utc--;
ts.tv_sec = (s32)utc & 0x7fffffff;
ts.tv_nsec = (tsval & 0xfffffff) * NSEC_PER_TICK;
/* Provide the timestamp only if 100% sure about its correctness */
if (!ts_incorrect) {
hwts = skb_hwtstamps(skb);
hwts->hwtstamp = timespec_to_ktime(ts);
skb_tstamp_tx(skb, hwts);
}
pr_debug("%s: save to slot %i\n", __func__, i);
wrn->ts_buf[i].ts = tsval;
wrn->ts_buf[i].port_id = port_id;
wrn->ts_buf[i].frame_id = frame_id;
wrn->ts_buf[i].valid = TS_PRESENT;
if (ts_incorrect)
wrn->ts_buf[i].valid |= TS_INVALID;
dev_kfree_skb_irq(skb);
wrn->skb_desc[i].skb = 0;
return 0;
}
......@@ -133,11 +104,7 @@ irqreturn_t wrn_tstamp_interrupt(int irq, void *dev_id)
r1 = readl(&regs->TSF_R1);
r2 = readl(&regs->TSF_R2);
if(record_tstamp(wrn, r0, r1, r2) < 0) {
printk("%s: ENOMEM in the TS buffer. Disabling TX stamping.\n",
__func__);
writel(TXTSU_EIC_IER_NEMPTY, &wrn->txtsu_regs->EIC_IDR);
}
record_tstamp(wrn, r0, r1, r2);
writel(TXTSU_EIC_IER_NEMPTY, &wrn->txtsu_regs->EIC_ISR); /* ack irq */
return IRQ_HANDLED;
}
......@@ -194,7 +161,6 @@ int wrn_tstamp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
void wrn_tstamp_init(struct wrn_dev *wrn)
{
memset(wrn->ts_buf, 0, sizeof(wrn->ts_buf));
/* enable TXTSU irq */
writel(TXTSU_EIC_IER_NEMPTY, &wrn->txtsu_regs->EIC_IER);
}
......
......@@ -12,6 +12,30 @@
*/
#ifndef __WR_NIC_H__
#define __WR_NIC_H__
/* Private ioctls, (the first 2 are the same as they were in wr_minic.c */
#define PRIV_IOCGCALIBRATE (SIOCDEVPRIVATE + 1)
#define PRIV_IOCGGETPHASE (SIOCDEVPRIVATE + 2)
#define PRIV_IOCREADREG (SIOCDEVPRIVATE + 3)
#define PRIV_IOCPHYREG (SIOCDEVPRIVATE + 4)
/* The last two available are used for mezzanine-private stuff */
#define PRIV_MEZZANINE_ID (SIOCDEVPRIVATE + 14)
#define PRIV_MEZZANINE_CMD (SIOCDEVPRIVATE + 15)
#ifdef __KERNEL__ /* The rest is kernel-only */
/* The NIC can build for both the switch and the node. Prefer if to ifdef */
#if defined WR_NODE
# define WR_IS_NODE 1
# define WR_IS_SWITCH 0
#elif defined WR_SWITCH
# define WR_IS_NODE 0
# define WR_IS_SWITCH 1
#else
# error "Please define WR_NODE or WR_SWITCH"
#endif
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/mii.h> /* Needed for stuct mii_if_info in wrn_dev */
......@@ -20,7 +44,6 @@
#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 */
/*
......@@ -42,27 +65,21 @@
#define WRN_IRQ_NAMES {"wr-nic", "wr-tstamp"}
#define WRN_IRQ_HANDLERS {wrn_interrupt, wrn_tstamp_interrupt}
#define WRN_TS_BUF_SIZE 1024 /* array of timestamp structures */
struct wrn_ep; /* Defined later */
/* A timestamping structure to keep information for user-space */
struct wrn_tx_tstamp {
/* We must remember skb, id and tstamp for each pending descriptor, */
struct wrn_desc_pending {
struct sk_buff *skb;
u8 valid;
u8 port_id;
u16 frame_id;
u32 ts;
u32 cycles;
};
/* bits for "valid" field */
#define TS_PRESENT 1
#define TS_INVALID 2 /* as reported by hw: we return 0 as timestamp */
/* We must remember both skb and id for each pending descriptor */
struct wrn_desc_pending {
struct sk_buff *skb;
u32 id; /* only 16 bits, actually */
};
/*
* This is the main data structure for our NIC device. As for locking,
* the rule is that _either_ the wrn _or_ the endpoint is locked. Not both.
......@@ -88,7 +105,6 @@ struct wrn_dev {
int id;
struct net_device *dev[WRN_NR_ENDPOINTS];
struct wrn_tx_tstamp ts_buf[WRN_TS_BUF_SIZE];
/* FIXME: all dev fields must be verified */
......@@ -167,12 +183,6 @@ enum wrn_resnames {
#define wrn_ep_read(ep, reg) __raw_readl(&(ep)->ep_regs->reg)
#define wrn_ep_write(ep, reg, val) __raw_writel((val), &(ep)->ep_regs->reg)
/* Private ioctls, (the first 2 are the same as they were in wr_minic.c */
#define PRIV_IOCGCALIBRATE (SIOCDEVPRIVATE + 1)
#define PRIV_IOCGGETPHASE (SIOCDEVPRIVATE + 2)
#define PRIV_IOCREADREG (SIOCDEVPRIVATE + 3)
#define PRIV_IOCPHYREG (SIOCDEVPRIVATE + 4)
#define NIC_READ_PHY_CMD(addr) (((addr) & 0xff) << 16)
#define NIC_RESULT_DATA(val) ((val) & 0xffff)
#define NIC_WRITE_PHY_CMD(addr, value) ((((addr) & 0xff) << 16) \
......@@ -230,14 +240,22 @@ extern int wrn_endpoint_probe(struct net_device *netdev);
extern void wrn_endpoint_remove(struct net_device *netdev);
/* Following functions from timestamp.c */
extern void wrn_tstamp_find_skb(struct wrn_dev *wrn, int i);
extern void wrn_tx_tstamp_skb(struct wrn_dev *wrn, int desc);
extern int wrn_tstamp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
extern irqreturn_t wrn_tstamp_interrupt(int irq, void *dev_id);
extern void wrn_tstamp_init(struct wrn_dev *wrn);
/* Following functions from dmtd.c */
/* Following functions from dmtd.c and pps.c */
extern int wrn_phase_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
extern int wrn_calib_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
extern void wrn_ppsg_read_time(struct wrn_dev *wrn, u32 *fine_cnt, u32 *utc);
/* Locally weak, designed for a mezzanine driver to implement */
extern int wrn_mezzanine_ioctl(struct net_device *dev, struct ifreq *rq,
int cmd);
extern int wrn_mezzanine_init(struct net_device *dev);
extern void wrn_mezzanine_exit(struct net_device *dev);
#endif /* __KERNEL__ */
#endif /* __WR_NIC_H__ */
......@@ -14,6 +14,10 @@
#include <sys/stat.h>
#include "wrsSnmp.h"
/* defines for nic-hardware.h */
#define WR_SWITCH
#define WR_IS_NODE 0
#define WR_IS_SWITCH 1
#include "../../kernel/wr_nic/nic-hardware.h"
#include "../../kernel/wbgen-regs/ppsg-regs.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