Commit b33dab32 authored by Miguel Jimenez Lopez's avatar Miguel Jimenez Lopez

kernel: Remove NIC and DIO related files.

parent f2e7ef89
......@@ -20,30 +20,11 @@ ccflags-y += $(WR_NIC_CFLAGS)
ccflags-y += -DGIT_VERSION=\"$(GIT_VERSION)\"
ccflags-y += -DWR_NODE
# this is a bad hack. Sometimes we are a submodule, and wr-nic can
# only compile with recent versions, so let the caller disable it
# FIXME: this is incorrect if we get copied to the kernel proper.
CONFIG_WR_NIC ?= m
obj-m += spec.o
obj-$(CONFIG_WR_NIC) += wr-nic.o
spec-y = spec-pci.o
spec-y += spec-fmc.o
spec-y += spec-i2c.o
spec-y += spec-vic.o
spec-y += loader-ll.o
spec-y += spec-gpio-no.o
spec-$(CONFIG_GPIOLIB) += spec-gpio.o
wr-nic-y = wr-nic-core.o
wr-nic-y += wr-nic-eth.o
wr-nic-y += wr-nic-dio.o
wr-nic-y += wr_nic/device.o
wr-nic-y += wr_nic/endpoint.o
wr-nic-y += wr_nic/ethtool.o
wr-nic-y += wr_nic/nic-core.o
wr-nic-y += wr_nic/timestamp.o
wr-nic-y += wr_nic/pps.o
wr-nic-$(CONFIG_GPIOLIB) += wr-nic-gpio.o
spec-$(CONFIG_GPIOLIB) += spec-gpio.o
\ No newline at end of file
/**
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Released according to the GNU GPL, version 2 or any later version
*
* Driver for SPEC (Simple PCI Express FMC carrier) board.
* VIC (Vectored Interrupt Controller) support code.
*/
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include "spec.h"
#include "hw/vic_regs.h"
#define VIC_MAX_VECTORS 32
#define VIC_SDB_VENDOR 0xce42
#define VIC_SDB_DEVICE 0x0013
/* A Vectored Interrupt Controller object */
struct vic_irq_controller {
/* It protects the handlers' vector */
spinlock_t vec_lock;
/* already-initialized flag */
int initialized;
/* Base address (FPGA-relative) */
uint32_t base;
/* Mapped base address of the VIC */
void *kernel_va;
/* Vector table */
struct vector {
/* Saved ID of the vector (for autodetection purposes) */
int saved_id;
/* Pointer to the assigned handler */
irq_handler_t handler;
/* FMC device that owns the interrupt */
struct fmc_device *requestor;
} vectors[VIC_MAX_VECTORS];
};
static inline void vic_writel(struct vic_irq_controller *vic, uint32_t value,
uint32_t offset)
{
writel(value, vic->kernel_va + offset);
}
static inline uint32_t vic_readl(struct vic_irq_controller *vic,
uint32_t offset)
{
return readl(vic->kernel_va + offset);
}
static int spec_vic_init(struct spec_dev *spec, struct fmc_device *fmc)
{
int i;
signed long vic_base;
struct vic_irq_controller *vic;
/*
* Try to look up the VIC in the SDB tree - note that IRQs
* shall be requested after the FMC driver has scanned the SDB tree
*/
vic_base =
fmc_find_sdb_device(fmc->sdb, VIC_SDB_VENDOR, VIC_SDB_DEVICE, NULL);
if (vic_base < 0) {
dev_err(&spec->pdev->dev,
"VIC controller not found, but a VIC interrupt requested. Wrong gateware?\n");
return -ENODEV;
}
dev_info(&spec->pdev->dev, "Found VIC @ 0x%lx\n", vic_base);
vic = kzalloc(sizeof(struct vic_irq_controller), GFP_KERNEL);
if (!vic)
return -ENOMEM;
spin_lock_init(&vic->vec_lock);
vic->kernel_va = spec->remap[0] + vic_base;
vic->base = (uint32_t) vic_base;
/* disable all IRQs, copy the vector table with pre-defined IRQ ids */
vic_writel(vic, 0xffffffff, VIC_REG_IDR);
for (i = 0; i < VIC_MAX_VECTORS; i++)
vic->vectors[i].saved_id =
vic_readl(vic, VIC_IVT_RAM_BASE + 4 * i);
/* config the VIC output: active high, edge, width = 256 tick (4 us) */
vic_writel(vic,
VIC_CTL_ENABLE | VIC_CTL_POL | VIC_CTL_EMU_EDGE |
VIC_CTL_EMU_LEN_W(250), VIC_REG_CTL);
vic->initialized = 1;
spec->vic = vic;
return 0;
}
static void spec_vic_exit(struct vic_irq_controller *vic)
{
if (!vic)
return;
/* Disable all irq lines and the VIC in general */
vic_writel(vic, 0xffffffff, VIC_REG_IDR);
vic_writel(vic, 0, VIC_REG_CTL);
kfree(vic);
}
/* NOTE: this function must be called while holding irq_lock */
irqreturn_t spec_vic_irq_dispatch(struct spec_dev *spec)
{
struct vic_irq_controller *vic = spec->vic;
int index, rv;
struct vector *vec;
if (unlikely(!vic))
goto fail;
/*
* Our parent IRQ handler: read the index value
* from the Vector Address Register, and find matching handler
*/
index = vic_readl(vic, VIC_REG_VAR) & 0xff;
if (index >= VIC_MAX_VECTORS) {
dev_err(&spec->pdev->dev, "Invalid VIC index %d (max %d)\n",
index, VIC_MAX_VECTORS);
goto fail;
}
vec = &vic->vectors[index];
if (!vec->handler) {
dev_err(&spec->pdev->dev,
"Handler not found for vector %d\n",
index);
goto fail;
}
rv = vec->handler(vec->saved_id, vec->requestor);
return rv;
fail:
return 0;
}
/* NOTE: this function must be called while holding irq_lock */
int spec_vic_irq_request(struct spec_dev *spec, struct fmc_device *fmc,
unsigned long id, irq_handler_t handler)
{
struct vic_irq_controller *vic;
int rv = 0, i;
/* First interrupt to be requested? Look up and init the VIC */
if (!spec->vic) {
rv = spec_vic_init(spec, fmc);
if (rv)
return rv;
}
vic = spec->vic;
for (i = 0; i < VIC_MAX_VECTORS; i++) {
/* find vector in stored table, assign handle, enable */
if (vic->vectors[i].saved_id == id) {
spin_lock(&spec->vic->vec_lock);
vic_writel(vic, i, VIC_IVT_RAM_BASE + 4 * i);
vic->vectors[i].requestor = fmc;
vic->vectors[i].handler = handler;
vic_writel(vic, (1 << i), VIC_REG_IER);
spin_unlock(&spec->vic->vec_lock);
return 0;
}
}
return -EINVAL;
}
int vic_is_managed(struct vic_irq_controller *vic, unsigned long id)
{
int i, ret;
if (!vic)
return 0;
for (i = 0; i < VIC_MAX_VECTORS; i++) {
if (vic->vectors[i].saved_id != id)
continue;
ret = vic_readl(vic, VIC_REG_IMR) & (1 << i);
return !!ret;
}
return 0;
}
/*
* vic_handler_count
* It counts how many handlers are registered within the VIC controller
*/
static inline int vic_handler_count(struct vic_irq_controller *vic)
{
int i, count;
for (i = 0, count = 0; i < VIC_MAX_VECTORS; ++i)
if (vic->vectors[i].handler)
count++;
return count;
}
/* NOTE: this function must be called while holding irq_lock */
void spec_vic_irq_free(struct spec_dev *spec, unsigned long id)
{
int i;
for (i = 0; i < VIC_MAX_VECTORS; i++) {
if (spec->vic->vectors[i].saved_id == id) {
spin_lock(&spec->vic->vec_lock);
vic_writel(spec->vic, 1 << i, VIC_REG_IDR);
vic_writel(spec->vic, id, VIC_IVT_RAM_BASE + 4 * i);
spec->vic->vectors[i].handler = NULL;
spin_unlock(&spec->vic->vec_lock);
}
}
/* Clean up the VIC if there are no more handlers */
if (!vic_handler_count(spec->vic)) {
spec_vic_exit(spec->vic);
spec->vic = NULL;
}
}
void spec_vic_irq_ack(struct spec_dev *spec, unsigned long id)
{
vic_writel(spec->vic, 0, VIC_REG_EOIR); /* ack the irq */
}
......@@ -38,8 +38,6 @@ struct spec_dev {
int irq_count; /* for mezzanine use too */
struct completion compl;
struct gpio_chip *gpio;
struct vic_irq_controller *vic;
spinlock_t irq_lock;
struct miscdevice mdev;
char name[SPEC_NAME_LEN];
......@@ -153,13 +151,5 @@ extern int spec_eeprom_write(struct fmc_device *fmc, uint32_t offset,
extern int spec_gpio_init(struct fmc_device *fmc);
extern void spec_gpio_exit(struct fmc_device *fmc);
/* Functions in spec-vic.c */
/* NOTE: these functions must be called while holding irq_lock */
int spec_vic_irq_request(struct spec_dev *spec, struct fmc_device *fmc,
unsigned long id, irq_handler_t handler);
void spec_vic_irq_free(struct spec_dev *spec, unsigned long id);
irqreturn_t spec_vic_irq_dispatch(struct spec_dev *spec);
extern void spec_vic_irq_ack(struct spec_dev *spec, unsigned long id);
extern int vic_is_managed(struct vic_irq_controller *vic, unsigned long id);
#endif /* __SPEC_H__ */
/*
* Copyright (C) 2010-2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __WR_DIO_H__
#define __WR_DIO_H__
/* This should be included by both the kernel and the tools */
#ifdef __KERNEL__
#include "wbgen-regs/wr-dio-regs.h"
/* For GPIO we have no wb-gen header */
struct wrn_gpio_block {
uint32_t clear;
uint32_t set;
uint32_t dir;
uint32_t status;
};
/* And this is our bit mapping */
#define WRN_GPIO_VALUE(bit) (1 << ((4 * (bit)) + 0))
extern irqreturn_t wrn_dio_interrupt(struct fmc_device *fmc);
#endif /* __KERNEL__ */
enum wr_dio_cmd_name {
WR_DIO_CMD_PULSE,
WR_DIO_CMD_STAMP,
WR_DIO_CMD_DAC,
WR_DIO_CMD_INOUT,
};
/*
* This is how parameters are used (K == reply from kernel):
*
* CMD_PULSE:
* cmd->flags: F_NOW, F_REL, F_LOOP
* cmd->channel: the channel or the mask
* cmd->t[]: either 2 or 3 values (start, duration, loop)
* cmd->value: count of loops (0 to turn off)
*
* CMD_STAMP:
* cmd->flags: F_MASK, F_WAIT
* cmd->channel: the channel or the mask
* K: cmd->channel: the channel where we had stamps
* K: cmd->nstamp: number of valid stamps
* K: cmd->t[]: the stamps
*
* CMD_DAC:
* cmd->flags: none
* cmd->channel: which one
* cmd->value: the value
*
* CMD_INOUT:
* cmd->flags: F_MASK
* cmd->channel: the channel or the mask
* cmd->value: bits 0..4: WR-DIO, 8..12 value, 16..20 OEN, 24..28 term
*
*/
#define WR_DIO_INOUT_DIO (1 << 0)
#define WR_DIO_INOUT_VALUE (1 << 8)
#define WR_DIO_INOUT_OUTPUT (1 << 16)
#define WR_DIO_INOUT_TERM (1 << 24)
#define WR_DIO_N_STAMP 16 /* At least 5 * 3 */
struct wr_dio_cmd {
uint16_t command; /* from user */
uint16_t channel; /* 0..4 or mask from user */
uint32_t value; /* for DAC or I/O */
uint32_t flags;
uint32_t nstamp; /* from kernel, if IN_STAMP */
struct timespec t[WR_DIO_N_STAMP]; /* may be from user */
};
#define WR_DIO_F_NOW 0x01 /* Output is now, t[0] ignored */
#define WR_DIO_F_REL 0x02 /* t[0].tv_sec is relative */
#define WR_DIO_F_MASK 0x04 /* Channel is 0x00..0x1f */
#define WR_DIO_F_LOOP 0x08 /* Output should loop: t[2] is looping*/
#define WR_DIO_F_WAIT 0x10 /* Wait for event */
#endif /* __WR_DIO_H__ */
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <linux/delay.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include "spec-nic.h"
#include "wr_nic/wr-nic.h"
static struct fmc_driver wrn_drv;
FMC_PARAM_BUSID(wrn_drv);
FMC_PARAM_GATEWARE(wrn_drv);
static char *wrn_filename = WRN_GATEWARE_DEFAULT_NAME;
module_param_named(file, wrn_filename, charp, 0444);
static char *wrn_wrc_filename = WRN_WRC_DEFAULT_NAME;
module_param_named(wrc, wrn_wrc_filename, charp, 0444);
static int wrn_show_sdb;
module_param_named(show_sdb, wrn_show_sdb, int, 0444);
static int wrn_load_wrc(struct fmc_device *fmc, char *name,
unsigned long ram, unsigned long ramsize)
{
const struct firmware *fw;
struct device *dev = fmc->hwdev;
int ret, count;
const uint32_t *p;
ret = request_firmware(&fw, name, dev);
if (ret < 0) {
dev_err(dev, "request firmware \"%s\": error %i\n",
name, ret);
return ret;
}
if (fw->size > ramsize) {
dev_err(dev, "firmware \"%s\" longer than ram (0x%lx)\n",
name, ramsize);
ret = -ENOMEM;
goto out;
}
for (count = 0, p = (void *)fw->data; count < fw->size; count += 4, p++)
fmc_writel(fmc, __cpu_to_be32(*p), ram + count);
out:
release_firmware(fw);
return ret;
}
int wrn_fmc_probe(struct fmc_device *fmc)
{
int index, need_wrc = 0, ret = 0;
struct device *dev = fmc->hwdev;
struct wrn_drvdata *dd;
signed long ram, syscon;
unsigned long ramsize;
char *filename;
/* We accept busid= to limit to some spec only */
index = fmc->op->validate(fmc, &wrn_drv);
if (index < 0) {
dev_info(fmc->hwdev, "not using \"%s\" according to "
"modparam\n", KBUILD_MODNAME);
return -ENODEV;
}
/* Driver data */
dd = devm_kzalloc(&fmc->dev, sizeof(*dd), GFP_KERNEL);
if (!dd)
return -ENOMEM;
fmc_set_drvdata(fmc, dd);
/*
* We first write a new binary within the spec. If the user
* passed "gateware=", use the per-board names instead of the
* global name
*/
if (wrn_drv.gw_n)
ret = fmc_reprogram(fmc, &wrn_drv, "", WRN_SDB_ADDR);
else
ret = fmc_reprogram(fmc, &wrn_drv, wrn_filename, WRN_SDB_ADDR);
if (ret <0) {
if (ret == -ESRCH) {
dev_info(fmc->hwdev, "%s: no gateware at index %i\n",
KBUILD_MODNAME, index);
return -ENODEV;
}
dev_err(dev, "write firmware \"%s\": error %i\n",
wrn_filename, ret);
return ret;
}
dev_info(dev, "Gateware successfully loaded\n");
/* FIXME: remove this parameter, fmc.ko shows it already */
if (wrn_show_sdb)
fmc_show_sdb_tree(fmc);
/*
* The gateware may not be including the WRC code, or the
* user may have asked for a specific file name. If so, load.
*/
ram = fmc_find_sdb_device(fmc->sdb, SDB_CERN, WRN_SDB_RAM, &ramsize);
syscon = fmc_find_sdb_device(fmc->sdb, SDB_CERN, WRN_SDB_SYSCON, NULL);
if (ram >= 0 && fmc_readl(fmc, ram) != 0x98000000)
need_wrc = 1;
filename = wrn_wrc_filename;
if (strcmp(wrn_wrc_filename, WRN_WRC_DEFAULT_NAME)) {
need_wrc = 1;
/*
* If the user changed it, use the new name.
* But "1" means "do load the default"
*/
if (!strcmp(wrn_wrc_filename, "1"))
filename = WRN_WRC_DEFAULT_NAME;
}
if (need_wrc && ((ram < 0) || (syscon < 0))) {
dev_err(dev, "can't reprogram WRC: SDB failure\n");
goto out;
}
if (need_wrc) {
unsigned long j = jiffies + HZ/2;
fmc_writel(fmc, 0x1deadbee, syscon);
while ( !(fmc_readl(fmc, syscon) & (1 << 28)) )
if (time_after(jiffies, j))
break;
if (time_after(jiffies, j)) {
dev_err(dev, "can't reset LM32\n");
fmc_writel(fmc, 0x0deadbee, syscon);
goto out;
}
ret = wrn_load_wrc(fmc, filename, ram, ramsize);
fmc_writel(fmc, 0x0deadbee, syscon);
if (ret)
goto out; /* message already reported */
if (fmc_readl(fmc, ram) != 0x98000000)
dev_warn(dev, "possible failure in wrc load\n");
else
dev_info(dev, "WRC program reloaded from \"%s\"\n",
filename);
}
/* After the LM32 started, give it time to set up */
msleep(200);
/* Register the gpio stuff, if we have kernel support */
ret = wrn_gpio_init(fmc);
if (ret < 0)
goto out;
/* The network device */
ret = wrn_eth_init(fmc);
if (ret < 0)
wrn_gpio_exit(fmc);
out:
return ret;
}
int wrn_fmc_remove(struct fmc_device *fmc)
{
wrn_eth_exit(fmc);
wrn_gpio_exit(fmc);
fmc_free_sdb_tree(fmc);
return 0;
}
static struct fmc_fru_id fd_fru_id[] = {
{
.product_name = "FmcDio5cha",
},
};
static struct fmc_driver wrn_fmc_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
.probe = wrn_fmc_probe,
.remove = wrn_fmc_remove,
.id_table = {
.fru_id = fd_fru_id,
.fru_id_nr = ARRAY_SIZE(fd_fru_id),
},
};
static int wrn_init(void)
{
int ret;
ret = fmc_driver_register(&wrn_fmc_drv);
if (ret < 0)
return ret;
platform_driver_register(&wrn_driver);
if (ret < 0)
fmc_driver_unregister(&wrn_fmc_drv);
return ret;
}
static void wrn_exit(void)
{
platform_driver_unregister(&wrn_driver);
fmc_driver_unregister(&wrn_fmc_drv);
}
module_init(wrn_init);
module_exit(wrn_exit);
/* If no gpio lib is there, this weak applies */
int __weak wrn_gpio_init(struct fmc_device *fmc)
{
return 0;
}
void __weak wrn_gpio_exit(struct fmc_device *fmc)
{
}
MODULE_VERSION(GIT_VERSION);
MODULE_LICENSE("GPL");
ADDITIONAL_VERSIONS;
This diff is collapsed.
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include "spec.h"
#include "spec-nic.h"
#include "wr-dio.h"
#include "wr_nic/wr-nic.h"
#include "wbgen-regs/vic-regs.h"
/*
* nic-device.c defines a platform driver. We need to allocate
* a platform device each time this init function is called
* (part of the code is from wr_nic/module.c).
*/
static void wrn_release(struct device *dev)
{
/* nothing to do, but mandatory function */
pr_debug("%s\n", __func__);
}
static struct platform_device wrn_pdev = {
/* other fields filled after it's copied: see wrn_eth_init() */
.name = KBUILD_MODNAME,
.dev.release = &wrn_release,
};
#define WRN_ALL_MASK (WRN_VIC_MASK_NIC | WRN_VIC_MASK_TXTSU | WRN_VIC_MASK_DIO)
/* This is the interrupt handler, that uses the VIC to know which is which */
irqreturn_t wrn_handler(int irq, void *dev_id)
{
struct fmc_device *fmc = dev_id;
struct platform_device *pdev = fmc->mezzanine_data;
struct wrn_drvdata *drvdata;
struct VIC_WB *vic;
uint32_t vector;
irqreturn_t ret = IRQ_HANDLED;
if (!pdev) {
/* too early, just do nothing */
dev_info(fmc->hwdev, "early irq %i, ignoring\n", irq);
fmc->op->irq_ack(fmc);
return ret;
}
drvdata = pdev->dev.platform_data;
vic = (typeof(vic)) drvdata->vic_base;
/*
* VIC operation algorithm:
* - when a peripheral interrupt arrives (as seen in RISR register),
* it is masked by IMR and latched in bit of ISR register:
*
* ISR |= (RISR & IMR);
* if (ISR != 0) {
* int current_irq = priority_decode(ISR)
* VAR = IVT_RAM[current_irq];
* MASTER_IRQ = CTL.POL;
*
* wait (write to EOIR)
*
* if (CTL.EMU_ENA)
* pulse(MASTER_IRQ, CTL.EMU_LEN, !CTL.POL)
* } else {
* MASTER_IRQ = !CTL.POL
* }
*
* The VIC was inspired by the original VIC used in NXP's ARM MCUs.
*/
/* read pending vector address - the index of currently pending IRQ. */
vector = readl(&vic->VAR);
if (vector == WRN_VIC_ID_NIC)
ret = wrn_interrupt(irq, drvdata->wrn);
else if (vector == WRN_VIC_ID_TXTSU)
ret = wrn_tstamp_interrupt(irq, drvdata->wrn);
else if (vector == WRN_VIC_ID_DIO)
ret = wrn_dio_interrupt(fmc /* different arg! */);
fmc->op->irq_ack(fmc);
writel(0, &vic->EOIR);
return ret;
}
static int wrn_vic_init(struct fmc_device *fmc)
{
struct platform_device *pdev = fmc->mezzanine_data;
struct wrn_drvdata *drvdata = pdev->dev.platform_data;
struct VIC_WB *vic = (typeof(vic))drvdata->vic_base;
/* fill the vector table */
writel(WRN_VIC_ID_TXTSU, &vic->IVT_RAM[WRN_VIC_ID_TXTSU]);
writel(WRN_VIC_ID_NIC, &vic->IVT_RAM[WRN_VIC_ID_NIC]);
writel(WRN_VIC_ID_DIO, &vic->IVT_RAM[WRN_VIC_ID_DIO]);
/* 4us edge emulation timer (counts in 16ns steps) */
writel(VIC_CTL_ENABLE | VIC_CTL_POL | VIC_CTL_EMU_EDGE | \
VIC_CTL_EMU_LEN_W(4000 / 16), &vic->CTL);
writel(WRN_ALL_MASK, &vic->IER);
return 0;
}
static void wrn_vic_exit(struct fmc_device *fmc)
{
struct platform_device *pdev = fmc->mezzanine_data;
struct wrn_drvdata *drvdata = pdev->dev.platform_data;
struct VIC_WB *vic = (typeof(vic))drvdata->vic_base;
writel(0xff, &vic->IDR);
/* Interrupt line is !POL when EN == 0, so leave polarity bit on */
writel(VIC_CTL_POL, &vic->CTL);
}
struct wrn_core {
const char *name;
uint64_t vendor;
uint32_t device;
int offset;
};
/* These cores are used by the nic driver itself */
static struct wrn_core wrn_cores[] = {
[WRN_FB_NIC] = {"NIC", SDB_CERN, WRN_SDB_NIC},
[WRN_FB_EP] = {"Endpoint", SDB_CERN, WRN_SDB_EP},
[WRN_FB_PPSG] = {"PPS-Gen", SDB_CERN, WRN_SDB_PPSG},
[WRN_FB_TS] = {"Tx-Stamp", SDB_CERN, WRN_SDB_TS},
};
/* These cores are used by the wr-nic fmc device */
static struct wrn_core wrn_cores2[] = {
{
"VIC", SDB_CERN, WRN_SDB_VIC,
offsetof(struct wrn_drvdata, vic_base)
},{
"GPIO", SDB_CERN, WRN_SDB_GPIO,
offsetof(struct wrn_drvdata, gpio_base)
},{
"WR-DIO", SDB_7SOL, WRN_SDB_WRDIO,
offsetof(struct wrn_drvdata, wrdio_base)
},{
"PPS-Gen", SDB_CERN, WRN_SDB_PPSG,
offsetof(struct wrn_drvdata, ppsg_base)
}
};
struct fmc_gpio wrn_gpio_cfg[] = {
{
.gpio = FMC_GPIO_IRQ(1),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}
};
int wrn_eth_init(struct fmc_device *fmc)
{
struct device *dev = fmc->hwdev;
struct resource *resarr;
struct platform_device *pdev;
struct wrn_drvdata *drvdata;
struct wrn_dev *wrn;
struct wrn_core *c;
struct spec_dev *spec = fmc->carrier_data;
signed long start;
unsigned long size;
int i, ret;
ret = fmc->op->irq_request(fmc, wrn_handler, "wr-nic", IRQF_SHARED);
if (ret < 0) {
dev_err(dev, "Can't request interrupt\n");
return ret;
}
/* FIXME: we should request irq0, self-test and then move to irq1 */
fmc->op->gpio_config(fmc, wrn_gpio_cfg, ARRAY_SIZE(wrn_gpio_cfg));
/* Make a copy of the platform device and register it */
ret = -ENOMEM;
pdev = kmemdup(&wrn_pdev, sizeof(wrn_pdev), GFP_KERNEL);
resarr = kzalloc(sizeof(*resarr) * ARRAY_SIZE(wrn_cores), GFP_KERNEL);
drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
wrn = kzalloc(sizeof(*wrn), GFP_KERNEL);
if (!pdev || !resarr || !drvdata || !wrn)
goto out_mem;
for (i = 0, c = wrn_cores; i < ARRAY_SIZE(wrn_cores); i++, c++) {
start = fmc_find_sdb_device(fmc->sdb, c->vendor, c->device,
&size);
if (start < 0) {
dev_err(dev, "Can't find sdb core \"%s\"\n", c->name);
goto out_mem;
}
resarr[i].name = c->name;
resarr[i].flags = IORESOURCE_MEM;
/* FIXME: spec-specific for the io area to be remapped */
resarr[i].start = spec->area[0]->start + start;
resarr[i].end = resarr[i].start + size - 1;
resarr[i].parent = spec->area[0];
}
for (i = 0, c = wrn_cores2; i < ARRAY_SIZE(wrn_cores2); i++, c++) {
start = fmc_find_sdb_device(fmc->sdb, c->vendor, c->device,
&size);
if (start < 0) {
dev_err(dev, "Can't find sdb core \"%s\"\n", c->name);
continue;
}
/* use c->offset to copy and already-remapped value */
*((void **)((u8 *)drvdata + c->offset)) =
fmc->fpga_base + start;
}
pdev->resource = resarr;
pdev->num_resources = ARRAY_SIZE(wrn_cores);
drvdata->wrn = wrn;
drvdata->fmc = fmc;
pdev->dev.platform_data = drvdata;
fmc->mezzanine_data = pdev;
platform_device_register(pdev);
wrn_vic_init(fmc);
wrn_pdev.id++; /* for the next one */
return 0;
out_mem:
kfree(wrn);
kfree(drvdata);
kfree(resarr);
kfree(pdev);
fmc->op->irq_free(fmc);
return ret;
}
void wrn_eth_exit(struct fmc_device *fmc)
{
struct platform_device *pdev = fmc->mezzanine_data;
struct wrn_drvdata *drvdata;
wrn_vic_exit(fmc);
if (pdev)
platform_device_unregister(pdev);
if (pdev) {
drvdata = pdev->dev.platform_data;
kfree(drvdata->wrn);
kfree(drvdata);
kfree(pdev->resource);
kfree(pdev);
}
fmc->mezzanine_data = NULL;
fmc->op->irq_free(fmc);
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/fmc.h>
#include "spec-nic.h"
static inline struct fmc_device *gc_to_fmc(struct gpio_chip *gc)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
struct device *dev = gc->dev;
#else
struct device *dev = gc->parent;
#endif
return container_of(dev, struct fmc_device, dev);
}
static int wrn_gpio_input(struct gpio_chip *chip, unsigned offset)
{
//struct fmc_device *fmc = gc_to_fmc(chip);
//struct wrn_drvdata *dd = fmc_get_drvdata(fmc);
//fmc_writel(fmc, ...); /* FIXME */
return -EAGAIN;
}
static int wrn_gpio_output(struct gpio_chip *chip, unsigned offset, int value)
{
return -EAGAIN;
}
int wrn_gpio_get(struct gpio_chip *chip, unsigned offset)
{
return -EAGAIN;
}
void wrn_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
return;
}
static const char *wrn_gpio_names[] = {
"dire", "fare", "baciare", "lettera", "testamento"
};
static struct gpio_chip wrn_gpio_template = {
.label = "wr-nic",
.owner = THIS_MODULE,
/* FIXME: request, free, for multi-function operation */
.direction_input = wrn_gpio_input,
.direction_output = wrn_gpio_output,
.get = wrn_gpio_get,
.set = wrn_gpio_set,
.base = -1, /* request dynamic */
.ngpio = 5,
.names = wrn_gpio_names,
};
int wrn_gpio_init(struct fmc_device *fmc)
{
struct wrn_drvdata *dd = fmc_get_drvdata(fmc);
struct gpio_chip *gc;
int ret;
gc = devm_kzalloc(&fmc->dev, sizeof(*gc), GFP_KERNEL);
if (!gc)
return -ENOMEM;
*gc = wrn_gpio_template;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
gc->dev = &fmc->dev;
#else
gc->parent = &fmc->dev;
#endif
ret = gpiochip_add(gc);
if (ret < 0)
goto out_free;
dd->gc = gc;
/* FIXME: program the DAC for each port (sysfs attributes?) */
return 0;
out_free:
kfree(gc);
return ret;
}
void wrn_gpio_exit(struct fmc_device *fmc)
{
struct wrn_drvdata *dd = fmc_get_drvdata(fmc);
struct gpio_chip *gc = dd->gc;
#if LINUX_VERSION_CODE > KERNEL_VERSION(3,17,0)
gpiochip_remove(gc);
#else
int ret;
ret = gpiochip_remove(gc);
if (ret)
dev_err(fmc->hwdev, "DANGER %i! gpio chip can't be removed\n",
ret);
#endif
return;
}
/*
* Device initialization and cleanup for White-Rabbit switch network interface
*
* 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/module.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/delay.h>
#include <linux/io.h>
#include "wr-nic.h"
#include "nic-mem.h"
#if WR_IS_NODE
#include "../spec-nic.h"
#endif
/* The remove function is used by probe, so it's not __devexit */
static int wrn_remove(struct platform_device *pdev)
{
#if WR_IS_NODE
struct wrn_drvdata *drvdata = pdev->dev.platform_data;
struct wrn_dev *wrn = drvdata->wrn;
#endif
#if WR_IS_SWITCH
struct wrn_dev *wrn = pdev->dev.platform_data;
#endif
int i;
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);
/* 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;
}
}
for (i = 0; i < ARRAY_SIZE(wrn->bases); i++) {
if (wrn->bases[i])
iounmap(wrn->bases[i]);
}
/* Unregister all interrupts that were registered */
for (i = 0; wrn->irq_registered; i++) {
static int irqs[] = WRN_IRQ_NUMBERS;
if (wrn->irq_registered & (1 << i))
free_irq(irqs[i], wrn);
wrn->irq_registered &= ~(1 << i);
}
return 0;
}
/* This helper is used by probe below */
static int __wrn_map_resources(struct platform_device *pdev)
{
int i;
struct resource *res;
void __iomem *ptr;
#if WR_IS_NODE
struct wrn_drvdata *drvdata = pdev->dev.platform_data;
struct wrn_dev *wrn = drvdata->wrn;
#endif
#if WR_IS_SWITCH
struct wrn_dev *wrn = pdev->dev.platform_data;
#endif
/*
* The memory regions are mapped once for all endpoints.
* We don't populate the whole array, but use the resource list
*/
for (i = 0; i < pdev->num_resources; i++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
if (!res || !res->start)
continue;
ptr = ioremap(res->start, res->end + 1 - res->start);
if (!ptr) {
dev_err(&pdev->dev, "Remap for res %i (%pa) failed\n",
i, (void *) res->start);
return -ENOMEM;
}
/* Hack: find the block number and fill the array */
pr_debug("Remapped %pa (block %i) to %p\n",
(void *) res->start, i, ptr);
wrn->bases[i] = ptr;
}
return 0;
}
static int wrn_probe(struct platform_device *pdev)
{
struct net_device *netdev;
struct wrn_ep *ep;
#if WR_IS_NODE
struct wrn_drvdata *drvdata = pdev->dev.platform_data;
struct wrn_dev *wrn = drvdata->wrn;
#endif
#if WR_IS_SWITCH
struct wrn_dev *wrn = pdev->dev.platform_data;
#endif
int i, err = 0;
/* Lazily: irqs are not in the resource list */
static int irqs[] = WRN_IRQ_NUMBERS;
static char *irq_names[] = WRN_IRQ_NAMES;
static irq_handler_t irq_handlers[] = WRN_IRQ_HANDLERS;
/* No need to lock_irq: we only protect count and continue unlocked */
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);
dev_err(&pdev->dev, "use count %i\n", wrn->use_count);
return -EBUSY;
}
/* Map our resource list and instantiate the shortcut pointers */
if ( (err = __wrn_map_resources(pdev)) )
goto out;
wrn->regs = wrn->bases[WRN_FB_NIC];
wrn->txtsu_regs = wrn->bases[WRN_FB_TS];
wrn->ppsg_regs = wrn->bases[WRN_FB_PPSG];
wrn->txd = ((void *)wrn->regs) + 0x80; /* was: TX1_D1 */
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);
if (0)
dev_info(&pdev->dev, "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;
goto out;
}
/* The ep structure is filled before calling ep_probe */
ep = netdev_priv(netdev);
ep->wrn = wrn;
ep->ep_regs = wrn->bases[WRN_FB_EP] + i * FPGA_SIZE_EACH_EP;
ep->ep_number = i;
#if 0 /* FIXME: UPlink or not? */
if (i < WRN_NR_UPLINK)
set_bit(WRN_EP_IS_UPLINK, &ep->ep_flags);
#endif
/* 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 = 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;
tx = wrn->txd + i;
writel(0, &tx->tx1);
}
/* Now, prepare RX descriptors */
for (i = 0; i < WRN_NR_RXDESC; i++) {
struct wrn_rxd *rx;
int offset;
rx = wrn->rxd + i;
offset = __wrn_desc_offset(wrn, WRN_DDIR_RX, i);
writel( (2000 << 16) | offset, &rx->rx3);
writel(NIC_RX1_D1_EMPTY, &rx->rx1);
}
/*
* make sure all head/tail are 0 -- not needed here, but if we
* disable and then re-enable, this _is_ needed
*/
wrn->next_tx_head = wrn->next_tx_tail = wrn->next_rx = 0;
writel(NIC_CR_RX_EN | NIC_CR_TX_EN, &wrn->regs->CR);
writel(WRN_IRQ_ALL, (void *)wrn->regs + 0x24 /* EIC_IER */);
wrn_tstamp_init(wrn);
err = 0;
out:
if (err) {
/* Call the remove function to avoid duplicating code */
wrn_remove(pdev);
} else {
dev_info(&pdev->dev, "White Rabbit NIC driver\n");
}
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 = KBUILD_MODNAME,
.owner = THIS_MODULE,
},
};
This diff is collapsed.
/*
* Ethtool operations for White-Rabbit switch network interface
*
* 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/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_ep *ep = netdev_priv(dev);
int ret;
spin_lock_irq(&ep->lock);
ret = mii_ethtool_gset(&ep->mii, cmd);
spin_unlock_irq(&ep->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_ep *ep = netdev_priv(dev);
int ret;
spin_lock_irq(&ep->lock);
ret = mii_ethtool_sset(&ep->mii, cmd);
spin_unlock_irq(&ep->lock);
return ret;
}
static int wrn_nwayreset(struct net_device *dev)
{
struct wrn_ep *ep = netdev_priv(dev);
int ret;
spin_lock_irq(&ep->lock);
ret = mii_nway_restart(&ep->mii);
spin_unlock_irq(&ep->lock);
return ret;
}
static void wrn_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
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));
}
/*
* 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;
}
This diff is collapsed.
/*
* hardware-specific definitions for the White Rabbit NIC
*
* 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
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#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)
/* The interrupt is one of those managed by our WRVIC device */
#define WRN_IRQ_BASE 192
#define WRN_IRQ_NIC (WRN_IRQ_BASE + 0)
#define WRN_IRQ_TSTAMP (WRN_IRQ_BASE + 1)
//#define WRN_IRQ_PPSG (WRN_IRQ_BASE + )
//#define WRN_IRQ_RTU (WRN_IRQ_BASE + )
//#define WRN_IRQ_RTUT (WRN_IRQ_BASE + )
/*
* V3 Memory map, temporarily (Jan 2012)
*
* 0x00000 - 0x1ffff: RT Subsystem
* 0x00000 - 0x0ffff: RT Subsystem Program Memory (16 - 64 kB)
* 0x10000 - 0x100ff: RT Subsystem UART
* 0x10100 - 0x101ff: RT Subsystem SoftPLL-adv
* 0x10200 - 0x102ff: RT Subsystem SPI Master
* 0x10300 - 0x103ff: RT Subsystem GPIO
* 0x10500 - 0x105ff: PPS gen
* 0x20000 - 0x3ffff: NIC
* 0x20000 - 0x20fff NIC control regs and descriptor area
* 0x28000 - 0x2bfff NIC packet buffer (16k)
* 0x30000 - 0x4ffff: Endpoints
* 0x30000 + N * 0x400 Endpoint N control registers
* 0x50000 - 0x50fff: VIC
* 0x51000 - 0x51fff: Tstamp unit
*/
/* This is the base address of all the FPGA regions (EBI1, CS0) */
#define FPGA_BASE_PPSG 0x10010500
#define FPGA_SIZE_PPSG 0x00000100
#define FPGA_BASE_NIC 0x10020000
#define FPGA_SIZE_NIC 0x00010000
#define FPGA_BASE_EP 0x10030000
#define FPGA_SIZE_EP 0x00010000
#define FPGA_SIZE_EACH_EP 0x400
#define FPGA_BASE_VIC 0x10050000 /* not used here */
#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,
WRN_FB_EP,
WRN_FB_PPSG,
WRN_FB_TS,
WRN_NR_OF_BLOCKS,
};
/* In addition to the above enumeration, we scan for those many endpoints */
#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
#define WRN_NR_TXDESC WRN_NR_DESC
#define WRN_NR_RXDESC WRN_NR_DESC
/* Magic number for endpoint */
#define WRN_EP_MAGIC 0xcafebabe
/*
* The following headers include the register lists, and have been
* generated by wbgen from .wb source files in svn
*/
#include "../wbgen-regs/endpoint-regs.h"
#include "../wbgen-regs/ppsg-regs.h"
#include "../wbgen-regs/nic-regs.h"
#include "../wbgen-regs/tstamp-regs.h"
/*
* To make thins easier, define the descriptor structures, for tx and rx
* Use functions in nic-mem.h to get pointes to them
*/
struct wrn_txd {
uint32_t tx1;
uint32_t tx2;
uint32_t tx3;
uint32_t unused;
};
struct wrn_rxd {
uint32_t rx1;
uint32_t rx2;
uint32_t rx3;
uint32_t unused;
};
/* Some more constants */
#define WRN_MTU 1540
#define WRN_DDATA_OFFSET 2 /* data in descriptors is offset by that much */
#endif /* __WR_NIC_HARDWARE_H__ */
/*
* This file hosts memory-specific inlines, separated for easier review
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* 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 "wr-nic.h"
#include <asm/unaligned.h>
/* Descriptor direction, used to locate descriptror data memory */
enum wrn_ddir {
WRN_DDIR_RX,
WRN_DDIR_TX
};
/* We place the descriptor memory in fixed positions (2kB each) */
static inline int __wrn_desc_offset(struct wrn_dev *wrn,
enum wrn_ddir dir, int nr)
{
if (dir == WRN_DDIR_RX) nr += WRN_NR_TXDESC;
return 0x800 * nr;
}
static inline u32 __iomem *__wrn_desc_mem(struct wrn_dev *wrn,
enum wrn_ddir dir, int nr)
{
/* note: this is a void pointer, but then we return a u32 ptr */
void __iomem *ptr = wrn->databuf;
return ptr + __wrn_desc_offset(wrn, dir, nr);
}
/* The two copy functions take arguments in the same order as memcpy */
static inline void __wrn_copy_out(u32 __iomem *to, void *from, int size)
{
u32 i;
from -= WRN_DDATA_OFFSET;
size += WRN_DDATA_OFFSET;
while (size > 0) {
i = get_unaligned_le32(from);
writel(i, to);
to++; from += sizeof(i);
size -= sizeof(i);
}
}
static inline void __wrn_copy_in(void *to, u32 __iomem *from, int size)
{
u32 i;
to -= WRN_DDATA_OFFSET;
size += WRN_DDATA_OFFSET;
while (size > 0) {
i = readl(from);
put_unaligned_le32(i, to);
to += sizeof(i); from++;
size -= sizeof(i);
}
}
/*
* Pulse per second management for White-Rabbit switch network interface
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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/sockios.h>
#include <linux/net_tstamp.h>
#include "wr-nic.h"
void wrn_ppsg_read_time(struct wrn_dev *wrn, u32 *fine_counter, u32 *utc)
{
u32 utc1, utc2, cnt;
utc1 = readl(&wrn->ppsg_regs->CNTR_UTCLO);
cnt = readl(&wrn->ppsg_regs->CNTR_NSEC);
utc2 = readl(&wrn->ppsg_regs->CNTR_UTCLO);
if (utc2 != utc1)
cnt = readl(&wrn->ppsg_regs->CNTR_NSEC);
*utc = utc2;
*fine_counter = cnt;
}
/*\
* Timestamping routines for WR Switch
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* 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/sockios.h>
#include <linux/net_tstamp.h>
#include "wr-nic.h"
/* 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 wrn_desc_pending *d = wrn->skb_desc + desc;
struct sk_buff *skb = d->skb;
struct timespec ts;
u32 counter_ppsg; /* PPS generator nanosecond counter */
u32 utc;
if (!wrn->skb_desc[desc].valid)
return;
/* already reported by hardware: do the timestamping magic */
wrn_ppsg_read_time(wrn, &counter_ppsg, &utc);
/* 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 = 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);
}
dev_kfree_skb_irq(skb);
/* release both the descriptor and the tstamp entry */
d->skb = 0;
d->valid = 0;
}
/* 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 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;
/* 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].frame_id == frame_id)
break;
if (i == WRN_NR_DESC) {
/* Not found: Must be a PTP frame sent from the SPEC! */
return 0;
}
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);
}
dev_kfree_skb_irq(skb);
wrn->skb_desc[i].skb = 0;
return 0;
}
irqreturn_t wrn_tstamp_interrupt(int irq, void *dev_id)
{
struct wrn_dev *wrn = dev_id;
struct TXTSU_WB *regs = wrn->txtsu_regs;
u32 r0, r1, r2;
if (!regs)
return IRQ_NONE; /* early interrupt? */
/* printk("%s: %i\n", __func__, __LINE__); */
/* FIXME: locking */
r0 = readl(&regs->TSF_R0);
r1 = readl(&regs->TSF_R1);
r2 = readl(&regs->TSF_R2);
record_tstamp(wrn, r0, r1, r2);
writel(TXTSU_EIC_IER_NEMPTY, &wrn->txtsu_regs->EIC_ISR); /* ack irq */
return IRQ_HANDLED;
}
int wrn_tstamp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct wrn_ep *ep = netdev_priv(dev);
struct hwtstamp_config config;
if (copy_from_user(&config, rq->ifr_data, sizeof(config)))
return -EFAULT;
if (0)
netdev_dbg(dev, "%s: tx type %i, rx filter %i\n",
__func__, config.tx_type, config.rx_filter);
switch (config.tx_type) {
/* Set up time stamping on transmission */
case HWTSTAMP_TX_ON:
set_bit(WRN_EP_STAMPING_TX, &ep->ep_flags);
/* FIXME: enable timestamp on tx in hardware */
break;
case HWTSTAMP_TX_OFF:
/* FIXME: disable timestamp on tx in hardware */
clear_bit(WRN_EP_STAMPING_TX, &ep->ep_flags);
break;
default:
return -ERANGE;
}
/*
* For the time being, make this really simple and stupid: either
* time-tag _all_ the incoming packets or none of them.
*/
switch (config.rx_filter) {
case HWTSTAMP_FILTER_NONE:
/* FIXME: disable rx in hardware */
clear_bit(WRN_EP_STAMPING_RX, &ep->ep_flags);
break;
default: /* All other case: activate stamping */
/* FIXME: enable rx in hardware */
set_bit(WRN_EP_STAMPING_RX, &ep->ep_flags);
break;
}
/* FIXME: minic_update_ts_config(nic); */
if (copy_to_user(rq->ifr_data, &config, sizeof(config)))
return -EFAULT;
return 0;
}
void wrn_tstamp_init(struct wrn_dev *wrn)
{
/* enable TXTSU irq */
writel(TXTSU_EIC_IER_NEMPTY, &wrn->txtsu_regs->EIC_IER);
}
/*
* wr-nic definitions, structures and prototypes
*
* 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.
*/
#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 */
#include <linux/netdevice.h> /* Needed for net_device_stats in wrn_dev */
#include <linux/timer.h> /* Needed for struct time_list in wrn_dev*/
#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 */
/*
* Interrupt information should be hidden in resource lists,
* but the looping code would be hairy. So let's define three
* arrays of the same size, and code loops over these
*/
#if 0
#define WRN_IRQ_NUMBERS \
{WRN_IRQ_PPSG, WRN_IRQ_NIC, WRN_IRQ_RTU, WRN_IRQ_RTUT, WRN_IRQ_TSTAMP}
#define WRN_IRQ_NAMES \
{"wr-ppsg", "wr-nic", "wr-rtu", "wr-rtut", "wr-tstamp"}
#define WRN_IRQ_HANDLERS \
{NULL, wrn_interrupt, NULL, NULL, wrn_tstamp_interrupt}
#endif
/* Temporarily, two handlers only */
#define WRN_IRQ_NUMBERS {WRN_IRQ_NIC, WRN_IRQ_TSTAMP}
#define WRN_IRQ_NAMES {"wr-nic", "wr-tstamp"}
#define WRN_IRQ_HANDLERS {wrn_interrupt, wrn_tstamp_interrupt}
struct wrn_ep; /* Defined later */
/* 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 cycles;
};
/* bits for "valid" field */
#define TS_PRESENT 1
#define TS_INVALID 2 /* as reported by hw: we return 0 as timestamp */
/*
* 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.
*/
struct wrn_dev {
/* Base addresses. It's easier with an array, but not all are used */
void __iomem *bases[WRN_NR_OF_BLOCKS];
struct NIC_WB __iomem *regs; /* shorthand for NIC-block registers */
struct TXTSU_WB __iomem *txtsu_regs; /* ... and the same for TXTSU */
struct PPSG_WB __iomem *ppsg_regs; /* ... */
spinlock_t lock;
struct tasklet_struct rx_tlet;
struct wrn_txd __iomem *txd;
struct wrn_rxd __iomem *rxd;
void __iomem *databuf; /* void to ease pointer arith */
int next_tx_head, next_tx_tail;
int next_rx;
/* For TX descriptors, we must keep track of the ownwer */
struct wrn_desc_pending skb_desc[WRN_NR_TXDESC];
int id;
struct net_device *dev[WRN_NR_ENDPOINTS];
/* FIXME: all dev fields must be verified */
//unsigned int rx_head, rx_avail, rx_base, rx_size;
//unsigned int tx_head, tx_avail, tx_base, tx_size;
//u32 cur_rx_desc;
int use_count; /* only used at probe time */
int irq_registered;
};
/* We need to disable the rx-complete interrupt, so get the masks */
#define WRN_IRQ_ALL (~0)
#define WRN_IRQ_ALL_BUT_RX (~NIC_EIC_IER_RCOMP)
#define WRN_IRQ_NONE 0
/* Each network device (endpoint) has one such priv structure */
struct wrn_ep {
struct wrn_dev *wrn;
struct EP_WB __iomem *ep_regs; /* each EP has its own memory */
spinlock_t lock;
struct timer_list ep_link_timer;
volatile unsigned long ep_flags;
struct mii_if_info mii; /* for ethtool operations */
int ep_number;
int pkt_count; /* used for tx stamping ID */
struct net_device_stats stats;
//struct sk_buff *current_skb;
//bool synced;
//bool syncing_counters;
//u32 cur_rx_desc;
};
#define WRN_LINK_POLL_INTERVAL (HZ/5)
enum ep_flags { /* only used in the ep_flags register */
WRN_EP_UP = 0,
WRN_EP_IS_UPLINK = 1,
WRN_EP_STAMPING_TX = 2,
WRN_EP_STAMPING_RX = 3,
};
/* Our resources. */
enum wrn_resnames {
/*
* The names are used as indexes in the resource array. Note that
* they are unrelated with the memory addresses: we can't have
* holes in the memory list, so these are _different_ values
*/
WRN_RES_MEM_EP_UP0,
WRN_RES_MEM_EP_UP1,
WRN_RES_MEM_EP_DP0,
WRN_RES_MEM_EP_DP1,
WRN_RES_MEM_EP_DP2,
WRN_RES_MEM_EP_DP3,
WRN_RES_MEM_EP_DP4,
WRN_RES_MEM_EP_DP5,
WRN_RES_MEM_EP_DP6,
WRN_RES_MEM_EP_DP7,
WRN_RES_MEM_PPSG,
WRN_RES_MEM_CALIBRATOR,
WRN_RES_MEM_NIC,
WRN_RES_MEM_TSTAMP,
/* Irq is last, so platform_get_resource() can use previous enums */
WRN_RES_IRQ,
};
/*
* Register access may be needed outside of specific files.
* Please note that this takes a register *name*, uppercase with no prefix.
*/
#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)
#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) \
| (1 << 31) \
| ((value) & 0xffff))
/* Structures straight from wr_minic.c -- should user-space include this? */
struct wrn_calibration_req {
int cmd;
int cal_present;
};
struct wrn_phase_req {
int ready;
u32 phase;
};
#define WRN_DMTD_AVG_SAMPLES 256
#define WRN_DMTD_MAX_PHASE 16384
#define WRN_CAL_TX_ON 1
#define WRN_CAL_TX_OFF 2
#define WRN_CAL_RX_ON 3
#define WRN_CAL_RX_OFF 4
#define WRN_CAL_RX_CHECK 5
/* This a WR-specific register in the mdio space */
#define WRN_MDIO_WR_SPEC 0x00000010
#define WRN_MDIO_WR_SPEC_TX_CAL 0x01 /* TX calib pattern */
#define WRN_MDIO_WR_SPEC_RX_CAL_STAT 0x02 /* RX calib status */
#define WRN_MDIO_WR_SPEC_CAL_CRST 0x04 /* Reset calibration counter */
/* Following functions are in nic-core.c */
extern irqreturn_t wrn_interrupt(int irq, void *dev_id);
extern int wrn_netops_init(struct net_device *netdev);
extern void wrn_rx_interrupt(unsigned long arg); /* tasklet */
/* 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_phy_read(struct net_device *dev, int phy_id, int location);
extern void wrn_phy_write(struct net_device *dev, int phy_id, int loc, int v);
extern int wrn_ep_open(struct net_device *dev);
extern int wrn_ep_close(struct net_device *dev);
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_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 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__ */
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