Commit a30d9715 authored by Alessandro Rubini's avatar Alessandro Rubini

Merge branch 'fmc-bus'

parents 27b4ad99 9b4b025a
This diff is collapsed.
This diff is collapsed.
LINUX ?= /lib/modules/$(shell uname -r)/build
obj-m = spec.o
obj-m += spec-wr-nic.o
obj-m += spec-fine-delay.o
ccflags-y = -I$M/include
obj-m = fmc.o
obj-m += spec.o
obj-m += fmc-trivial.o
obj-m += fmc-write-eeprom.o
obj-m += wr-nic.o
fmc-y = fmc-core.o
fmc-y += fmc-sdb.o
spec-y = spec-pci.o
spec-y += spec-fmc.o
spec-y += spec-i2c.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-$(CONFIG_GPIOLIB) += wr-nic-gpio.o
spec-objs = spec-core.o loader-ll.o
all modules:
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) modules
......
/*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/fmc.h>
#include "spec.h"
static int fmc_match(struct device *dev, struct device_driver *drv)
{
//struct fmc_driver *fdrv = to_fmc_driver(drv);
//struct fmc_device *fdev = to_fmc_device(dev);
//const struct fmc_device_id *t = fdrv->id_table;
/* Currently, return 1 every time, until we define policies */
return 1;
}
static int fmc_uevent(struct device *dev, struct kobj_uevent_env *env)
{
//struct fmc_device *fdev = to_fmc_device(dev);
/* FIXME: The MODALIAS */
add_uevent_var(env, "MODALIAS=%s", "fmc");
return 0;
}
static int fmc_probe(struct device *dev)
{
struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
struct fmc_device *fdev = to_fmc_device(dev);
return fdrv->probe(fdev);
}
static int fmc_remove(struct device *dev)
{
struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
struct fmc_device *fdev = to_fmc_device(dev);
return fdrv->remove(fdev);
}
static void fmc_shutdown(struct device *dev)
{
/* not implemented but mandatory */
}
static struct bus_type fmc_bus_type = {
.name = "fmc",
.match = fmc_match,
.uevent = fmc_uevent,
.probe = fmc_probe,
.remove = fmc_remove,
.shutdown = fmc_shutdown,
};
/* Every device must have a release method: provide a default */
static void __fmc_release(struct device *dev){ }
/* This is needed as parent for our devices and dir in sysfs */
struct device fmc_bus = {
.release = __fmc_release,
.init_name = "fmc",
};
/* Functions for client modules */
int fmc_driver_register(struct fmc_driver *drv)
{
drv->driver.bus = &fmc_bus_type;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL(fmc_driver_register);
void fmc_driver_unregister(struct fmc_driver *drv)
{
driver_unregister(&drv->driver);
}
EXPORT_SYMBOL(fmc_driver_unregister);
int fmc_device_register(struct fmc_device *fdev)
{
device_initialize(&fdev->dev);
if (!fdev->dev.release)
fdev->dev.release = __fmc_release;
if (!fdev->dev.parent)
fdev->dev.parent = &fmc_bus;
fdev->dev.bus = &fmc_bus_type;
{
static int i;
/* FIXME: the name */
dev_set_name(&fdev->dev, "fmc-%04x", i++);
}
return device_add(&fdev->dev);
}
EXPORT_SYMBOL(fmc_device_register);
void fmc_device_unregister(struct fmc_device *fdev)
{
device_del(&fdev->dev);
put_device(&fdev->dev);
}
EXPORT_SYMBOL(fmc_device_unregister);
/* Init and exit are trivial */
static int fmc_init(void)
{
int err;
err = device_register(&fmc_bus);
if (err)
return err;
err = bus_register(&fmc_bus_type);
if (err)
device_unregister(&fmc_bus);
return err;
}
static void fmc_exit(void)
{
bus_unregister(&fmc_bus_type);
device_unregister(&fmc_bus);
}
module_init(fmc_init);
module_exit(fmc_exit);
MODULE_LICENSE("GPL");
/*
* 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/slab.h>
#include <linux/fmc.h>
#include <linux/sdb.h>
#include <linux/err.h>
#include <linux/fmc-sdb.h>
#include <asm/byteorder.h>
static uint32_t __sdb_rd(struct fmc_device *fmc, unsigned long address,
int convert)
{
uint32_t res = fmc_readl(fmc, address);
if (convert)
return __be32_to_cpu(res);
return res;
}
static struct sdb_array *__fmc_scan_sdb_tree(struct fmc_device *fmc,
unsigned long address, int level)
{
uint32_t onew;
int i, j, n, convert = 0;
struct sdb_array *arr, *sub;
onew = fmc_readl(fmc, address);
if (onew == SDB_MAGIC) {
/* Uh! If we are little-endian, we must convert */
if (SDB_MAGIC != __be32_to_cpu(SDB_MAGIC))
convert = 1;
} else if (onew == __be32_to_cpu(SDB_MAGIC)) {
/* ok, don't convert */
} else {
return ERR_PTR(-ENOENT);
}
/* So, the magic was there: get the count from offset 4*/
onew = __sdb_rd(fmc, address + 4, convert);
n = __be16_to_cpu(*(uint16_t *)&onew);
dev_info(fmc->hwdev, "address %lx, %i items (%08x) - c %i\n", address,
n, onew, convert);
arr = kzalloc(sizeof(*arr), GFP_KERNEL);
if (arr) {
arr->record = kzalloc(sizeof(arr->record[0]) * n, GFP_KERNEL);
arr->subtree = kzalloc(sizeof(arr->subtree[0]) * n, GFP_KERNEL);
}
if (!arr || !arr->record || !arr->subtree) {
kfree(arr->record);
kfree(arr->subtree);
kfree(arr);
return ERR_PTR(-ENOMEM);
}
arr->len = n;
arr->level = level;
arr->fmc = fmc;
for (i = 0; i < n; i++) {
union sdb_record *r;
for (j = 0; j < sizeof(arr->record[0]); j += 4) {
*(uint32_t *)((void *)(arr->record + i) + j) =
__sdb_rd(fmc, address + (i * 64) + j, convert);
}
r = &arr->record[i];
arr->subtree[i] = ERR_PTR(-ENODEV);
if (r->empty.record_type == sdb_type_bridge) {
uint64_t subaddr = r->bridge.sdb_child;
struct sdb_component *c;
c = &r->bridge.sdb_component;
subaddr = __be64_to_cpu(subaddr);
sub = __fmc_scan_sdb_tree(fmc, subaddr, level + 1);
arr->subtree[i] = sub; /* may be error */
if (IS_ERR(sub))
continue;
sub->parent = arr;
sub->baseaddr = __be64_to_cpu(c->addr_first);
}
}
return arr;
}
int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address)
{
struct sdb_array *ret;
if (fmc->sdb)
return -EBUSY;
ret = __fmc_scan_sdb_tree(fmc, address, 0);
if (IS_ERR(ret))
return PTR_ERR(ret);
fmc->sdb = ret;
return 0;
}
EXPORT_SYMBOL(fmc_scan_sdb_tree);
static void __fmc_sdb_free(struct sdb_array *arr)
{
int i, n;
if (!arr) return;
n = arr->len;
for (i = 0; i < n; i++) {
if (IS_ERR(arr->subtree[i]))
continue;
__fmc_sdb_free(arr->subtree[i]);
}
kfree(arr->record);
kfree(arr->subtree);
kfree(arr);
}
int fmc_free_sdb_tree(struct fmc_device *fmc)
{
__fmc_sdb_free(fmc->sdb);
fmc->sdb = NULL;
return 0;
}
EXPORT_SYMBOL(fmc_free_sdb_tree);
static void __fmc_show_sdb_tree(struct sdb_array *arr)
{
int i, j, n = arr->len, level = arr->level;
struct sdb_array *ap;
for (i = 0; i < n; i++) {
unsigned long base;
union sdb_record *r;
struct sdb_product *p;
struct sdb_component *c;
for (j = 0; j < level; j++)
printk(" ");
r = &arr->record[i];
c = &r->dev.sdb_component;
p = &c->product;
base = 0;
for (ap = arr; ap; ap = ap->parent)
base += ap->baseaddr;
switch(r->empty.record_type) {
case sdb_type_interconnect:
printk("%08llx:%08x %.19s\n",
__be64_to_cpu(p->vendor_id),
__be32_to_cpu(p->device_id),
p->name);
break;
case sdb_type_device:
printk("%08llx:%08x %.19s (%08llx-%08llx)\n",
__be64_to_cpu(p->vendor_id),
__be32_to_cpu(p->device_id),
p->name,
__be64_to_cpu(c->addr_first) + base,
__be64_to_cpu(c->addr_last) + base);
break;
case sdb_type_bridge:
printk("%08llx:%08x %.19s (bridge: %08llx)\n",
__be64_to_cpu(p->vendor_id),
__be32_to_cpu(p->device_id),
p->name,
__be64_to_cpu(c->addr_first) + base);
if (IS_ERR(arr->subtree[i])) {
printk("(bridge error %li)\n",
PTR_ERR(arr->subtree[i]));
break;
}
__fmc_show_sdb_tree(arr->subtree[i]);
break;
case sdb_type_integration:
printk("integration\n");
break;
case sdb_type_empty:
printk("empty\n");
break;
default:
printk("UNKNOWN TYPE 0x%02x\n", r->empty.record_type);
break;
}
}
}
void fmc_show_sdb_tree(struct fmc_device *fmc)
{
if (!fmc->sdb)
return;
__fmc_show_sdb_tree(fmc->sdb);
}
EXPORT_SYMBOL(fmc_show_sdb_tree);
signed long fmc_find_sdb_device(struct sdb_array *tree,
uint64_t vid, uint32_t did)
{
signed long res = -ENODEV;
union sdb_record *r;
struct sdb_product *p;
struct sdb_component *c;
int i, n = tree->len;
/* FIXME: what if the first interconnect is not at zero? */
for (i = 0; i < n; i++) {
r = &tree->record[i];
c = &r->dev.sdb_component;
p = &c->product;
if (!IS_ERR(tree->subtree[i]))
res = fmc_find_sdb_device(tree->subtree[i], vid, did);
if (res > 0)
return res + tree->baseaddr;
if (r->empty.record_type != sdb_type_device)
continue;
if (__be64_to_cpu(p->vendor_id) != vid)
continue;
if (__be32_to_cpu(p->device_id) != did)
continue;
return __be64_to_cpu(c->addr_first);
}
return res;
}
EXPORT_SYMBOL(fmc_find_sdb_device);
/* A trivial fmc driver that can load a gateware file and reports interrupts */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/fmc.h>
#include "spec.h"
static struct fmc_driver t_drv; /* initialized later */
irqreturn_t t_handler(int irq, void *dev_id)
{
struct fmc_device *fmc = dev_id;
fmc->op->irq_ack(fmc);
printk("%s: irq %i\n", __func__, irq);
return IRQ_HANDLED;
}
int t_probe(struct fmc_device *fmc)
{
int ret;
int index;
index = fmc->op->validate(fmc, &t_drv);
if (index < 0)
return -EINVAL; /* not our device: invalid */
ret = fmc->op->irq_request(fmc, t_handler, "fmc-trivial", 0);
if (ret < 0)
return ret;
/* Reprogram, if asked to. ESRCH == no filename specified */
ret = fmc->op->reprogram(fmc, &t_drv,"");
if (ret == -ESRCH)
ret = 0;
if (ret < 0)
fmc->op->irq_free(fmc);
/* FIXME: reprogram LM32 too */
return ret;
}
int t_remove(struct fmc_device *fmc)
{
fmc->op->irq_free(fmc);
return 0;
}
static struct fmc_driver t_drv = {
.driver.name = KBUILD_MODNAME,
.probe = t_probe,
.remove = t_remove,
/* no table, as the current match just matches everything */
};
/* We accept the generic parameters */
FMC_PARAM_BUSID(t_drv);
FMC_PARAM_GATEWARE(t_drv);
static int t_init(void)
{
int ret;
ret = fmc_driver_register(&t_drv);
return ret;
}
static void t_exit(void)
{
fmc_driver_unregister(&t_drv);
}
module_init(t_init);
module_exit(t_exit);
MODULE_LICENSE("GPL and additional rights"); /* public domain */
/*
* 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/string.h>
#include <linux/firmware.h>
#include <linux/init.h>
#include <linux/fmc.h>
#include <asm/unaligned.h>
/*
* This module uses the firmware loader to program the whole or part
* of the FMC eeprom. The meat is in the _run functions. However, no
* default file name is provided, to avoid accidental mishaps. Also,
* you must pass the busid argument
*/
static struct fmc_driver fwe_drv;
FMC_PARAM_BUSID(fwe_drv);
/* The "file=" is like the generic "gateware=" used elsewhere */
static char *fwe_file[FMC_MAX_CARDS];
static int fwe_file_n;
module_param_array_named(file, fwe_file, charp, &fwe_file_n, 444);
static int fwe_run_tlv(struct fmc_device *fmc, const struct firmware *fw,
int write)
{
const uint8_t *p = fw->data;
int len = fw->size;
uint16_t thislen, thisaddr;
int err;
/* format is: 'w' addr16 len16 data... */
while (len > 5) {
thisaddr = get_unaligned_le16(p+1);
thislen = get_unaligned_le16(p+3);
if (p[0] != 'w' || thislen + 5 > len) {
dev_err(fmc->hwdev, "invalid tlv at offset %i\n",
p - fw->data);
return -EINVAL;
}
err = 0;
if (write) {
dev_info(fmc->hwdev, "write %i bytes at 0x%04x\n",
thislen, thisaddr);
err = fmc->op->write_ee(fmc, thisaddr, p + 5, thislen);
}
if (err < 0) {
dev_err(fmc->hwdev, "write failure @0x%04x\n",
thisaddr);
return err;
}
p += 5 + thislen;
len -= 5 + thislen;
}
if (write)
dev_info(fmc->hwdev, "write_eeprom: success\n");
return 0;
}
static int fwe_run_bin(struct fmc_device *fmc, const struct firmware *fw)
{
int ret;
dev_info(fmc->hwdev, "programming %i bytes\n", fw->size);
ret = fmc->op->write_ee(fmc, 0, (void *)fw->data, fw->size);
if (ret < 0) {
dev_info(fmc->hwdev, "write_eeprom: error %i\n", ret);
return ret;
}
dev_info(fmc->hwdev, "write_eeprom: success\n");
return 0;
}
static int fwe_run(struct fmc_device *fmc, const struct firmware *fw, char *s)
{
char *last4 = s + strlen(s) - 4;
int err;
if (!strcmp(last4,".bin"))
return fwe_run_bin(fmc, fw);
if (!strcmp(last4,".tlv")) {
err = fwe_run_tlv(fmc, fw, 0);
if (!err)
err = fwe_run_tlv(fmc, fw, 1);
return err;
}
dev_err(fmc->hwdev, "invalid file name \"%s\"\n", s);
return -EINVAL;
}
/*
* Programming is done at probe time. Morever, only those listed with
* busid= are programmed.
* card is probed for, only one is programmed. Unfortunately, it's
* difficult to know in advance when probing the first card if others
* are there.
*/
int fwe_probe(struct fmc_device *fmc)
{
int err, index;
const struct firmware *fw;
struct device *dev = fmc->hwdev;
char *s;
if (!fwe_drv.busid_n) {
dev_err(dev, "%s: no busid passed, refusing all cards\n",
KBUILD_MODNAME);
return -ENODEV;
}
index = fmc->op->validate(fmc, &fwe_drv);
s = fwe_file[index];
if (!s) {
dev_err(dev, "%s: no filename given: not programming\n",
KBUILD_MODNAME);
return -ENOENT;
}
err = request_firmware(&fw, s, dev);
if (err < 0) {
dev_err(dev, "request firmware \"%s\": error %i\n", s, err);
return err;
}
fwe_run(fmc, fw, s);
release_firmware(fw);
return 0;
}
int fwe_remove(struct fmc_device *fmc)
{
return 0;
}
static struct fmc_driver fwe_drv = {
.driver.name = KBUILD_MODNAME,
.probe = fwe_probe,
.remove = fwe_remove,
/* no table, as the current match just matches everything */
};
static int fwe_init(void)
{
int ret;
ret = fmc_driver_register(&fwe_drv);
return ret;
}
static void fwe_exit(void)
{
fmc_driver_unregister(&fwe_drv);
}
module_init(fwe_init);
module_exit(fwe_exit);
MODULE_LICENSE("GPL");
/*
Register definitions for slave core: WR Core System Controller
* File : wrc_syscon_regs.h
* Author : auto-generated by wbgen2 from wrc_syscon_wb.wb
* Created : Fri Feb 17 19:23:19 2012
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE wrc_syscon_wb.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_WRC_SYSCON_WB_WB
#define __WBGEN2_REGDEFS_WRC_SYSCON_WB_WB
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: Syscon reset register */
/* definitions for field: Reset trigger in reg: Syscon reset register */
#define SYSC_RSTR_TRIG_MASK WBGEN2_GEN_MASK(0, 28)
#define SYSC_RSTR_TRIG_SHIFT 0
#define SYSC_RSTR_TRIG_W(value) WBGEN2_GEN_WRITE(value, 0, 28)
#define SYSC_RSTR_TRIG_R(reg) WBGEN2_GEN_READ(reg, 0, 28)
/* definitions for field: Reset line state value in reg: Syscon reset register */
#define SYSC_RSTR_RST WBGEN2_GEN_MASK(28, 1)
/* definitions for register: GPIO Set/Readback Register */
/* definitions for field: Status LED in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_LED_STAT WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Link LED in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_LED_LINK WBGEN2_GEN_MASK(1, 1)
/* definitions for field: FMC I2C bitbanged SCL in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_FMC_SCL WBGEN2_GEN_MASK(2, 1)
/* definitions for field: FMC I2C bitbanged SDA in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_FMC_SDA WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Network AP reset in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_NET_RST WBGEN2_GEN_MASK(4, 1)
/* definitions for field: SPEC Pushbutton 1 state in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_BTN1 WBGEN2_GEN_MASK(5, 1)
/* definitions for field: SPEC Pushbutton 2 state in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_BTN2 WBGEN2_GEN_MASK(6, 1)
/* definitions for field: SFP detect (MOD_DEF0 signal) in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_SFP_DET WBGEN2_GEN_MASK(7, 1)
/* definitions for field: SFP I2C bitbanged SCL in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_SFP_SCL WBGEN2_GEN_MASK(8, 1)
/* definitions for field: SFP I2C bitbanged SDA in reg: GPIO Set/Readback Register */
#define SYSC_GPSR_SFP_SDA WBGEN2_GEN_MASK(9, 1)
/* definitions for register: GPIO Clear Register */
/* definitions for field: Status LED in reg: GPIO Clear Register */
#define SYSC_GPCR_LED_STAT WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Link LED in reg: GPIO Clear Register */
#define SYSC_GPCR_LED_LINK WBGEN2_GEN_MASK(1, 1)
/* definitions for field: FMC I2C bitbanged SCL in reg: GPIO Clear Register */
#define SYSC_GPCR_FMC_SCL WBGEN2_GEN_MASK(2, 1)
/* definitions for field: FMC I2C bitbanged SDA in reg: GPIO Clear Register */
#define SYSC_GPCR_FMC_SDA WBGEN2_GEN_MASK(3, 1)
/* definitions for field: SFP I2C bitbanged SCL in reg: GPIO Clear Register */
#define SYSC_GPCR_SFP_SCL WBGEN2_GEN_MASK(8, 1)
/* definitions for field: FMC I2C bitbanged SDA in reg: GPIO Clear Register */
#define SYSC_GPCR_SFP_SDA WBGEN2_GEN_MASK(9, 1)
/* definitions for register: Hardware Feature Register */
/* definitions for field: Memory size in reg: Hardware Feature Register */
#define SYSC_HWFR_MEMSIZE_MASK WBGEN2_GEN_MASK(0, 4)
#define SYSC_HWFR_MEMSIZE_SHIFT 0
#define SYSC_HWFR_MEMSIZE_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define SYSC_HWFR_MEMSIZE_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for register: Timer Control Register */
/* definitions for field: Timer Divider in reg: Timer Control Register */
#define SYSC_TCR_TDIV_MASK WBGEN2_GEN_MASK(0, 12)
#define SYSC_TCR_TDIV_SHIFT 0
#define SYSC_TCR_TDIV_W(value) WBGEN2_GEN_WRITE(value, 0, 12)
#define SYSC_TCR_TDIV_R(reg) WBGEN2_GEN_READ(reg, 0, 12)
/* definitions for field: Timer Enable in reg: Timer Control Register */
#define SYSC_TCR_ENABLE WBGEN2_GEN_MASK(31, 1)
/* definitions for register: Timer Counter Value Register */
/* [0x0]: REG Syscon reset register */
#define SYSC_REG_RSTR 0x00000000
/* [0x4]: REG GPIO Set/Readback Register */
#define SYSC_REG_GPSR 0x00000004
/* [0x8]: REG GPIO Clear Register */
#define SYSC_REG_GPCR 0x00000008
/* [0xc]: REG Hardware Feature Register */
#define SYSC_REG_HWFR 0x0000000c
/* [0x10]: REG Timer Control Register */
#define SYSC_REG_TCR 0x00000010
/* [0x14]: REG Timer Counter Value Register */
#define SYSC_REG_TVR 0x00000014
#endif
/*
* This file is separate from sdb.h, because I want that one to remain
* unchanged (as far as possible) from the official sdb distribution
*
* This file and associated functionality are a playground for me to
* understand stuff which will later be implemented in more generic places.
*/
#include <linux/sdb.h>
/* This is the union of all currently defined types */
union sdb_record {
struct sdb_interconnect ic;
struct sdb_device dev;
struct sdb_bridge bridge;
struct sdb_integration integr;
struct sdb_empty empty;
};
struct fmc_device;
/* Every sdb table is turned into this structure */
struct sdb_array {
int len;
int level;
unsigned long baseaddr;
struct fmc_device *fmc; /* the device that hosts it */
struct sdb_array *parent; /* NULL at root */
union sdb_record *record; /* copies of the struct */
struct sdb_array **subtree; /* only valid for bridge items */
};
extern int fmc_scan_sdb_tree(struct fmc_device *fmc, unsigned long address);
extern void fmc_show_sdb_tree(struct fmc_device *fmc);
extern signed long fmc_find_sdb_device(struct sdb_array *tree,
uint64_t vendor, uint32_t device);
extern int fmc_free_sdb_tree(struct fmc_device *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.
*/
#ifndef __LINUX_FMC_H__
#define __LINUX_FMC_H__
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
struct fmc_device;
struct fmc_driver;
struct fmc_device_id {
/* FIXME: the device ID must be defined according to eeprom contents */
uint64_t unique_id;
};
#define FMC_MAX_CARDS 16 /* That many with the same matching driver... */
/* The driver is a pretty simple thing */
struct fmc_driver {
struct device_driver driver;
int (*probe)(struct fmc_device *);
int (*remove)(struct fmc_device *);
const struct fmc_device_id *id_table;
/* What follows is for generic module parameters */
int busid_n;
int busid_val[FMC_MAX_CARDS];
int gw_n;
char *gw_val[FMC_MAX_CARDS];
};
#define to_fmc_driver(x) container_of((x), struct fmc_driver, driver)
/* These are the generic parameters, that drivers may instantiate */
#define FMC_PARAM_BUSID(_d) \
module_param_array_named(busid, _d.busid_val, int, &_d.busid_n, 0444)
#define FMC_PARAM_GATEWARE(_d) \
module_param_array_named(gateware, _d.gw_val, charp, &_d.gw_n, 0444)
/* To be carrier-independent, we need to abstract hardware access */
struct fmc_operations {
uint32_t (*readl)(struct fmc_device *fmc, int offset);
void (*writel)(struct fmc_device *fmc, uint32_t value, int offset);
int (*validate)(struct fmc_device *fmc, struct fmc_driver *drv);
int (*reprogram)(struct fmc_device *f, struct fmc_driver *d, char *gw);
int (*irq_request)(struct fmc_device *fmc, irq_handler_t h,
char *name, int flags);
void (*irq_ack)(struct fmc_device *fmc);
int (*irq_free)(struct fmc_device *fmc);
int (*read_ee)(struct fmc_device *fmc, int pos, void *d, int l);
int (*write_ee)(struct fmc_device *fmc, int pos, const void *d, int l);
};
/* The device reports all information needed to access hw */
struct fmc_device {
unsigned long flags;
struct fmc_device_id id; /* for the match function */
struct fmc_operations *op; /* carrier-provided */
int irq; /* according to host bus. 0 == none */
int eeprom_len; /* Usually 8kB, may be less */
uint8_t *eeprom; /* Full contents or leading part */
char *carrier_name; /* "SPEC" or similar, for special use */
void *carrier_data; /* "struct spec *" or equivalent */
__iomem void *base; /* May be NULL (Etherbone) */
struct device dev; /* For Linux use */
struct device *hwdev; /* The underlying hardware device */
struct sdb_array *sdb;
};
#define to_fmc_device(x) container_of((x), struct fmc_device, dev)
#define FMC_DEVICE_HAS_GOLDEN 1
#define FMC_DEVICE_HAS_CUSTOM 2
#define FMC_DEVICE_NO_MEZZANINE 4
/* If the carrier offers no readl/writel, use base address */
static inline uint32_t fmc_readl(struct fmc_device *fmc, int offset)
{
if (unlikely(fmc->op->readl))
return fmc->op->readl(fmc, offset);
return readl(fmc->base + offset);
}
static inline void fmc_writel(struct fmc_device *fmc, uint32_t val, int off)
{
if (unlikely(fmc->op->writel))
fmc->op->writel(fmc, val, off);
else
writel(val, fmc->base + off);
}
/* pci-like naming */
static inline void *fmc_get_drvdata(struct fmc_device *fmc)
{
return dev_get_drvdata(&fmc->dev);
}
static inline void fmc_set_drvdata(struct fmc_device *fmc, void *data)
{
dev_set_drvdata(&fmc->dev, data);
}
/* The 4 access points */
extern int fmc_driver_register(struct fmc_driver *drv);
extern void fmc_driver_unregister(struct fmc_driver *drv);
extern int fmc_device_register(struct fmc_device *tdev);
extern void fmc_device_unregister(struct fmc_device *tdev);
#endif /* __LINUX_FMC_H__ */
/*
* This is version 1.0 of sdb.h, as included the specification v1.0
*/
#ifndef __SDB_H__
#define __SDB_H__
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
/*
* All structures are 64 bytes long and are expected
* to live in an array, one for each interconnect.
* Most fields of the structures are shared among the
* various types, and most-specific fields are at the
* beginning (for alignment reasons, and to keep the
* magic number at the head of the interconnect record
*/
/* Product, 40 bytes at offset 24, 8-byte alignmed
*
* device_id is vendor-assigned; version is device-specific,
* date is hex (e.g 0x20120501), name is UTF-8, blank-filled
* and not terminated with a 0 byte.
*/
struct sdb_product {
uint64_t vendor_id; /* 0x18..0x1f */
uint32_t device_id; /* 0x20..0x23 */
uint32_t version; /* 0x24..0x27 */
uint32_t date; /* 0x28..0x2b */
uint8_t name[19]; /* 0x2c..0x3e */
uint8_t record_type; /* 0x3f */
};
/*
* Component, 56 bytes at offset 8, 8-byte aligned
*
* The address range is first to last, inclusive
* (for example 0x100000 - 0x10ffff)
*/
struct sdb_component {
uint64_t addr_first; /* 0x08..0x0f */
uint64_t addr_last; /* 0x10..0x17 */
struct sdb_product product; /* 0x18..0x3f */
};
/* Type of the SDB record */
enum sdb_record_type {
sdb_type_interconnect = 0x00,
sdb_type_device = 0x01,
sdb_type_bridge = 0x02,
sdb_type_integration = 0x80,
sdb_type_empty = 0xFF,
};
/* Type 0: interconnect (first of the array)
*
* sdb_records is the length of the table including this first
* record, version is 1. The bus type is enumerated later.
*/
#define SDB_MAGIC 0x5344422d /* "SDB-" */
struct sdb_interconnect {
uint32_t sdb_magic; /* 0x00-0x03 */
uint16_t sdb_records; /* 0x04-0x05 */
uint8_t sdb_version; /* 0x06 */
uint8_t sdb_bus_type; /* 0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 1: device
*
* class is 0 for "custom device", other values are
* to be standardized; ABI version is for the driver,
* bus-specific bits are defined by each bus (see below)
*/
struct sdb_device {
uint16_t abi_class; /* 0x00-0x01 */
uint8_t abi_ver_major; /* 0x02 */
uint8_t abi_ver_minor; /* 0x03 */
uint32_t bus_specific; /* 0x04-0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 2: bridge
*
* child is the address of the nested SDB table
*/
struct sdb_bridge {
uint64_t sdb_child; /* 0x00-0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 0x80: integration
*
* all types with bit 7 set are meta-information, so
* software can ignore the types it doesn't know. Here we
* just provide product information for an aggregate device
*/
struct sdb_integration {
uint8_t reserved[24]; /* 0x00-0x17 */
struct sdb_product product; /* 0x08-0x3f */
};
/* Type 0xff: empty
*
* this allows keeping empty slots during development,
* so they can be filled later with miminal efforts and
* no misleading description is ever shipped -- hopefully.
* It can also be used to pad a table to a desired length.
*/
struct sdb_empty {
uint8_t reserved[63]; /* 0x00-0x3e */
uint8_t record_type; /* 0x3f */
};
/* The type of bus, for bus-specific flags (currently only Wishbone) */
enum sdb_bus_type {
sdb_wishbone = 0x00
};
#define SDB_WB_WIDTH_MASK 0x0f
#define SDB_WB_ACCESS8 0x01
#define SDB_WB_ACCESS16 0x02
#define SDB_WB_ACCESS32 0x04
#define SDB_WB_ACCESS64 0x08
#define SDB_WB_LITTLE_ENDIAN 0x80
#endif /* __SDB_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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/ctype.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <asm/unaligned.h>
#include "spec.h"
#include "loader-ll.h"
static char *spec_name = "%b";
module_param_named(name, spec_name, charp, 0444);
static int spec_lm32_addr = -1;
module_param_named(lm32, spec_lm32_addr, int, 0444);
/*
* A procedure to build the names associated with the device. This
* copies the spec_name. With "spec-" prefix, expanding %P
* (vendor:device), %p (subv:subd), %b (bus:devfn). Then ".bin" is
* the gateware, "-lm32.bin" is the lm32 compiled code and ".ko"
* is the kernel module.
* Example for device in bus 2, slot 0:
* command: "insmod spec name=%b"
* gateware: "spec-B0002.bin"
* program: "spec-B0002-cpu.bin" -- will be .elf, hopefully
* other module requested: "spec-B0002.ko"
*/
static int spec_build_names(struct spec_dev *dev)
{
struct pci_dev *pdev = dev->pdev;
char basename[64];
char *si, *so;
int i;
static char *templates[] = {
[SPEC_NAME_FW] = "fmc/spec-%s.bin",
[SPEC_NAME_PROG] = "fmc/spec-%s-cpu.bin", /* will be .elf */
[SPEC_NAME_SUBMOD] = "fmc/spec-%s", /* .ko added by modprobe */
};
for (si = spec_name, so = basename; *si ; si++) {
if (so - basename >= sizeof(basename))
return -ENOSPC;
if (*si != '%') {
*so++ = *si;
continue;
}
si++; /* eat '%' */
if (so - basename + 5 >= sizeof(basename))
return -ENOSPC;
switch(*si) {
case 'b': /* BUS id */
so += sprintf(so, "B%04x", pdev->bus->number);
break;
case 's': /* slot-fn id */
so += sprintf(so, "S%04x", pdev->devfn);
break;
case '%':
*so++ = '%';
default:
return -EINVAL;
}
}
/* terminate and remove trailing spaces (includes newlines) */
*so = '\0';
while (isspace(*--so))
*so = '\0';
/* build the actual things */
for (i = 0; i < SPEC_NAMES; i++)
dev->names[i] = kasprintf(GFP_KERNEL, templates[i], basename);
return 0;
}
/* Load the FPGA. This bases on loader-ll.c, a kernel/user space thing */
static int spec_load_fpga(struct spec_dev *dev)
{
const struct firmware *fw;
unsigned long j;
int i, err, wrote, done;
err = request_firmware(&fw, dev->names[SPEC_NAME_FW], &dev->pdev->dev);
if (err < 0)
return err;
pr_info("%s: got binary file \"%s\", %i (0x%x) bytes\n", __func__,
dev->names[SPEC_NAME_FW], fw->size, fw->size);
/* loader_low_level is designed to run from user space too */
wrote = loader_low_level(0 /* unused fd */,
dev->remap[2], fw->data, fw->size);
j = jiffies + 2 * HZ;
/* Wait for DONE interrupt */
while(!done) {
i = readl(dev->remap[2] + FCL_IRQ);
if (i & 0x8) {
printk("%s: done after %i writes\n", __func__,
wrote);
done = 1;
} else if( (i & 0x4) && !done) {
printk("%s: error after %i writes\n", __func__,
wrote);
err = -ETIMEDOUT;
goto out;
}
if (time_after(jiffies, j)) {
printk("%s: timeout after %i writes\n", __func__,
wrote);
err = -ETIMEDOUT;
goto out;
}
}
out:
release_firmware(fw);
return err;
}
static int spec_load_submodule(struct spec_dev *dev)
{
int err;
err = request_module(dev->names[SPEC_NAME_SUBMOD]);
pr_info("%s: load \"%s\": %i\n", __func__,
dev->names[SPEC_NAME_SUBMOD], err);
return err;
}
int spec_load_lm32(struct spec_dev *dev)
{
const struct firmware *fw;
int err, off;
if (spec_lm32_addr < 0) {
/* Not loading lm32 code unless we get the parameter */
return 0;
}
if (spec_lm32_addr == 1) {
/* "insmod lm32=1" loads at the default address */
spec_lm32_addr = SPEC_DEFAULT_LM32_ADDR;
}
err = request_firmware(&fw, dev->names[SPEC_NAME_PROG],
&dev->pdev->dev);
if (err < 0)
return err;
pr_info("%s: got program file \"%s\", %i (0x%x) bytes\n", __func__,
dev->names[SPEC_NAME_PROG], fw->size, fw->size);
/* Reset the LM32 */
writel(0x1deadbee, dev->remap[0] + spec_lm32_addr + 0x20400);
/* Copy stuff over */
for (off = 0; off < fw->size; off += 4) {
uint32_t datum;
datum = get_unaligned_be32(fw->data + off);
writel(datum, dev->remap[0] + spec_lm32_addr + off);
}
/* Unreset the LM32 */
writel(0xdeadbee, dev->remap[0] + spec_lm32_addr + 0x20400);
/* MSC */
pr_info("LM32 has been restarted\n");
release_firmware(fw);
return 0;
}
/* A procedure to load the three names */
static int spec_load_files(struct spec_dev *dev)
{
int err;
printk("%s\n", __func__);
/*
* We need to load the three files and we are in process context
* (god has said: Documentation/PCI/pci.txt)
*/
if ( (err = spec_load_fpga(dev)) < 0) {
dev_err(&dev->pdev->dev, "Can't load firwmare \"%s\" - %i\n",
dev->names[SPEC_NAME_FW], err);
return err;
}
if ( (err = spec_load_lm32(dev)) < 0 ) {
dev_warn(&dev->pdev->dev, "Can't load program \"%s\" - %i\n",
dev->names[SPEC_NAME_PROG], err);
/* continue anyways */
}
if ( (err = spec_load_submodule(dev)) < 0 )
dev_warn(&dev->pdev->dev, "Can't load submodule \"%s\" - %i\n",
dev->names[SPEC_NAME_SUBMOD], err);
return 0;
}
struct list_head spec_list;
EXPORT_SYMBOL(spec_list);
/* The probe can be called in atomic context, so all loading is delayed */
static int spec_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct spec_dev *dev;
int i;
printk("%s (device %04x:%04x)\n", __func__, pdev->bus->number,
pdev->devfn);
printk("%s: current %i (%s)\n", __func__, current->pid, current->comm);
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->pdev = pdev;
pci_enable_device(pdev);
if ( (i = pci_enable_msi_block(pdev, 1)) < 0)
pr_err("%s: enable ms block: %i\n", __func__, i);
/* Remap our 3 bars */
for (i = 0; i < 3; i++) {
struct resource *r = pdev->resource + (2 * i);
if (!r->start)
continue;
dev->area[i] = r;
if (r->flags & IORESOURCE_MEM)
dev->remap[i] = ioremap(r->start,
r->end + 1 - r->start);
}
/* Build the names */
if (spec_build_names(dev) < 0) {
dev_warn(&pdev->dev, "can't build names with \"%s\"\n",
spec_name);
/* go on anyways */
}
/* Register the device in out list, so the submodule will find it */
pci_set_drvdata(pdev, dev);
list_add(&dev->list, &spec_list);
/* The probe function can sleep, so load firmware directly */
spec_load_files(dev);
/* Done */
return 0;
}
static void spec_remove(struct pci_dev *pdev)
{
struct spec_dev *dev = pci_get_drvdata(pdev);
int i;
printk("%s\n", __func__);
for (i = 0; i < 3; i++) {
iounmap(dev->remap[i]);
dev->remap[i] = NULL;
dev->area[i] = NULL;
}
list_del(&dev->list);
pci_set_drvdata(pdev, NULL);
for (i = 0; i < SPEC_NAMES; i++)
kfree(dev->names[i]);
kfree(dev);
pci_disable_msi(pdev);
pci_disable_device(pdev);
}
DEFINE_PCI_DEVICE_TABLE(spec_idtable) = {
{ PCI_DEVICE(PCI_VENDOR_ID_CERN, PCI_DEVICE_ID_SPEC) },
{ PCI_DEVICE(PCI_VENDOR_ID_GENNUM, PCI_DEVICE_ID_GN4124) },
{ 0,},
};
static struct pci_driver spec_driver = {
.name = "spec",
.id_table = spec_idtable,
.probe = spec_probe,
.remove = spec_remove,
};
static int spec_init(void)
{
INIT_LIST_HEAD(&spec_list);
return pci_register_driver(&spec_driver);
}
static void spec_exit(void)
{
pci_unregister_driver(&spec_driver);
}
module_init(spec_init);
module_exit(spec_exit);
MODULE_LICENSE("GPL");
/*
* 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/slab.h>
#include <linux/fmc.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <linux/fmc-sdb.h>
#include "spec.h"
static int spec_test_irq;
module_param_named(test_irq, spec_test_irq, int, 0444);
/* The main role of this file is offering the fmc_operations for the spec */
static int spec_validate(struct fmc_device *fmc, struct fmc_driver *drv)
{
struct spec_dev *spec = fmc->carrier_data;
struct pci_dev *pdev = spec->pdev;
int busid = (pdev->bus->number << 8) | pdev->devfn;
int i;
if (!drv->busid_n)
return 0; /* everyhing is valid */
for (i = 0; i < drv->busid_n; i++)
if (drv->busid_val[i] == busid)
return i;
return -ENOENT;
}
static int spec_reprogram(struct fmc_device *fmc, struct fmc_driver *drv,
char *gw)
{
const struct firmware *fw;
struct spec_dev *spec = fmc->carrier_data;
struct device *dev = fmc->hwdev;
int ret;
if (!gw)
gw = spec_fw_name;
if (!strlen(gw)) { /* use module parameters from the driver */
int index;
index = spec_validate(fmc, drv);
gw = drv->gw_val[index];
if (!gw)
return -ESRCH; /* the caller may accept this */
}
dev_info(fmc->hwdev, "reprogramming with %s\n", gw);
ret = request_firmware(&fw, gw, dev);
if (ret < 0) {
dev_warn(dev, "request firmware \"%s\": error %i\n", gw, ret);
goto out;
}
fmc_free_sdb_tree(fmc);
fmc->flags &= ~(FMC_DEVICE_HAS_GOLDEN | FMC_DEVICE_HAS_CUSTOM);
ret = spec_load_fpga(spec, fw->data, fw->size);
if (ret <0) {
dev_err(dev, "write firmware \"%s\": error %i\n", gw, ret);
goto out;
}
if (gw == spec_fw_name)
fmc->flags |= FMC_DEVICE_HAS_GOLDEN;
else
fmc->flags |= FMC_DEVICE_HAS_CUSTOM;
/* FIXME: load lm32 */
out:
release_firmware(fw);
return ret;
}
static int spec_irq_request(struct fmc_device *fmc, irq_handler_t handler,
char *name, int flags)
{
struct spec_dev *spec = fmc->carrier_data;
int ret;
u32 value;
ret = request_irq(fmc->irq, handler, flags, name, fmc);
if (ret) return ret;
value = gennum_readl(spec, GNPPCI_MSI_CONTROL);
if ((value & 0x810000) != 0x810000)
dev_err(&spec->pdev->dev, "invalid msi control: 0x%08x\n",
value);
value = 0xa50000 | (value & 0xffff);
gennum_writel(spec, value, GNPPCI_MSI_CONTROL);
/* Enable gpio interrupts:
* gpio6: tp8: output low
* gpio7: tp7: interrupt, raising edge
* gpio8: IRQ1 from FPGA: interrupt, raising edge
* gpio9: IRQ0 from FPGA: interrupt, raising edge
* gpio10: tp6: output low
* gpio11: tp5: interrupt, raising edge
*/
/* bypass = alternate function */
gennum_mask_val(spec, 0xfc, 0x00, GNGPIO_BYPASS_MODE);
/* direction 0 = output */
gennum_mask_val(spec, 0x44, 0x00, GNGPIO_DIRECTION_MODE);
gennum_mask_val(spec, 0xb8, 0xb8, GNGPIO_DIRECTION_MODE);
gennum_mask_val(spec, 0x44, 0x44, GNGPIO_OUTPUT_ENABLE);
gennum_mask_val(spec, 0xb8, 0x00, GNGPIO_INT_TYPE); /* 0 = edge */
gennum_mask_val(spec, 0xb8, 0xb8, GNGPIO_INT_VALUE); /* 1 = raising */
gennum_mask_val(spec, 0xb8, 0x00, GNGPIO_INT_ON_ANY);
gennum_writel(spec, 0xb8, GNGPIO_INT_MASK_CLR); /* enable */
return 0;
}
static void spec_irq_ack(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
/*
* Note: we only support gpio interrupts here, i.e. the
* 0x814 (INT_STAT) register is expected to only have bit 15 set.
* We also accept software-generated irq, but they need no ack.
*/
gennum_readl(spec, GNGPIO_INT_STATUS);
}
static int spec_irq_free(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
gennum_writel(spec, 0xffff, GNGPIO_INT_MASK_SET); /* disable */
free_irq(fmc->irq, fmc);
return 0;
}
/* The engines for this live in spec-i2c.c, we only shape arguments */
static int spec_read_ee(struct fmc_device *fmc, int pos, void *data, int len)
{
if (!(fmc->flags & FMC_DEVICE_HAS_GOLDEN))
return -ENOTSUPP;
return spec_eeprom_read(fmc, SPEC_I2C_EEPROM_ADDR, pos, data, len);
}
static int spec_write_ee(struct fmc_device *fmc, int pos,
const void *data, int len)
{
if (!(fmc->flags & FMC_DEVICE_HAS_GOLDEN))
return -ENOTSUPP;
return spec_eeprom_write(fmc, SPEC_I2C_EEPROM_ADDR, pos, data, len);
}
static struct fmc_operations spec_fmc_operations = {
/* no readl/writel because we have the base pointer */
.validate = spec_validate,
.reprogram = spec_reprogram,
.irq_request = spec_irq_request,
.irq_ack = spec_irq_ack,
.irq_free = spec_irq_free,
.read_ee = spec_read_ee,
.write_ee = spec_write_ee,
};
/*
* Since interrupts are a hairy thing with the gennum, make a test run
* of interrupt handling using its own internal "software interrupt"
*/
static irqreturn_t spec_test_handler(int irq, void *dev_id)
{
struct fmc_device *fmc = dev_id;
struct spec_dev *spec = fmc->carrier_data;
printk("got %i!\n", irq);
spec->irq_count++;
complete(&spec->compl);
fmc->op->irq_ack(fmc);
return IRQ_HANDLED;
}
/*
* Finally, the real init and exit
*/
static int spec_irq_init(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
uint32_t value;
int i;
/*
* Enable multiple-msi to work around a chip design bug.
* See http://blog.tftechpages.com/?p=595
*/
value = gennum_readl(spec, GNPPCI_MSI_CONTROL);
if ((value & 0x810000) != 0x810000)
dev_err(&spec->pdev->dev, "invalid msi control: 0x%08x\n",
value);
value = 0xa50000 | (value & 0xffff);
gennum_writel(spec, value, GNPPCI_MSI_CONTROL);
/*
* Now check the two least-significant bits of the msi-data register,
* then enable CFG_0 or .. CFG_3 accordingly, to get proper vector.
*/
value = gennum_readl(spec, GNPPCI_MSI_DATA);
for (i = 0; i < 7; i++)
gennum_writel(spec, 0, GNINT_CFG(i));
gennum_writel(spec, 0x800c, GNINT_CFG(value & 0x03));
/* Finally, ensure we are able to receive it -- if the user asked to */
if (spec_test_irq == 0)
return 0;
spec->irq_count = 0;
init_completion(&spec->compl);
fmc->op->irq_request(fmc, spec_test_handler, "spec-test", 0);
gennum_writel(spec, 8, GNINT_STAT);
gennum_writel(spec, 0, GNINT_STAT);
wait_for_completion_timeout(&spec->compl, msecs_to_jiffies(50));
fmc->op->irq_free(fmc);
if (!spec->irq_count) {
dev_err(&spec->pdev->dev, "Can't receive interrupt\n");
return -EIO;
}
dev_info(&spec->pdev->dev, "Interrupts work as expected\n");
/* FIXME: configure the GPIO pins to receive interrupts */
return 0;
}
static void spec_irq_exit(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
int i;
for (i = 0; i < 7; i++)
gennum_writel(spec, 0, GNINT_CFG(i));
fmc->op->irq_ack(fmc); /* just to be safe */
}
static int check_golden(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
/* poor man's SDB */
if (fmc_readl(fmc, 0x100) != 0x5344422d) {
dev_err(&spec->pdev->dev, "Can't find SDB magic\n");
return -ENODEV;
}
/* Offset 4 is number of records, version, bus type */
if (fmc_readl(fmc, 0x104) != 0x00020100) {
dev_err(&spec->pdev->dev, "unsexpected content of SDB\n");
return -ENODEV;
}
if (fmc_readl(fmc, 0x15c) != 0x0000ce42) {
dev_err(&spec->pdev->dev, "unsexpected vendor in SDB\n");
return -ENODEV;
}
if (fmc_readl(fmc, 0x160) != 0xff07fc47) {
dev_err(&spec->pdev->dev, "unsexpected device in SDB\n");
return -ENODEV;
}
return 0;
}
int spec_fmc_create(struct spec_dev *spec)
{
struct fmc_device *fmc;
int ret;
fmc = kzalloc(sizeof(*fmc), GFP_KERNEL);
if (!fmc)
return -ENOMEM;
/* FIXME: many fields of the device are still NULL */
fmc->carrier_name = "SPEC";
fmc->carrier_data = spec;
fmc->base = spec->remap[0] + 0x80000; /* 512k window at 512k offset */
fmc->irq = spec->pdev->irq;
fmc->op = &spec_fmc_operations;
fmc->hwdev = &spec->pdev->dev; /* for messages */
spec->fmc = fmc;
/* Check that the golden binary is actually correct */
ret = check_golden(fmc);
if (ret)
goto out_free;
ret = spec_i2c_init(fmc);
if (ret)
goto out_free;
ret = spec_irq_init(fmc);
if (ret)
goto out_free;
ret = fmc_device_register(fmc);
if (ret)
goto out_irq;
spec_gpio_init(fmc); /* May fail, we don't care */
return ret;
out_irq:
spec_irq_exit(fmc);
out_free:
spec->fmc = NULL;
kfree(fmc);
return ret;
}
void spec_fmc_destroy(struct spec_dev *spec)
{
spec_gpio_exit(spec->fmc);
fmc_device_unregister(spec->fmc);
spec_irq_exit(spec->fmc);
spec_i2c_exit(spec->fmc);
spec->fmc = NULL;
}
#include <linux/kernel.h>
#include <linux/fmc.h>
#include "spec.h"
/*
* If the host computer has no gpiolib, this default will apply
*/
int __weak spec_gpio_init(struct fmc_device *fmc)
{
printk("%s - %s\n", __FILE__, __func__);
return 0;
}
void __weak spec_gpio_exit(struct fmc_device *fmc)
{
printk("%s - %s\n", __FILE__, __func__);
}
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/fmc.h>
#include "spec.h"
int spec_gpio_init(struct fmc_device *fmc)
{
printk("%s - %s\n", __FILE__, __func__);
return 0;
}
void spec_gpio_exit(struct fmc_device *fmc)
{
printk("%s - %s\n", __FILE__, __func__);
}
/*
* I2C access (on-board EEPROM)
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@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 Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/moduleparam.h>
#include <linux/io.h>
#include <linux/time.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/fmc.h>
#include "spec.h"
#include "hw/wrc_syscon_regs.h"
static int spec_i2c_dump;
module_param_named(i2c_dump, spec_i2c_dump, int, 0444);
/* Stupid dumping tool */
static void dumpstruct(char *name, void *ptr, int size)
{
int i;
unsigned char *p = ptr;
printk("%s: (size 0x%x)\n", name, size);
for (i = 0; i < size; ) {
printk("%02x", p[i]);
i++;
printk(i & 3 ? " " : i & 0xf ? " " : "\n");
}
if (i & 0xf)
printk("\n");
}
static void set_sda(struct fmc_device *fmc, int val)
{
if (val)
fmc_writel(fmc, SYSC_GPSR_FMC_SDA, SYSC_REG_GPSR);
else
fmc_writel(fmc, SYSC_GPCR_FMC_SDA, SYSC_REG_GPCR);
}
static void set_scl(struct fmc_device *fmc, int val)
{
if (val)
fmc_writel(fmc, SYSC_GPSR_FMC_SCL, SYSC_REG_GPSR);
else
fmc_writel(fmc, SYSC_GPCR_FMC_SCL, SYSC_REG_GPCR);
}
static int get_sda(struct fmc_device *fmc)
{
return fmc_readl(fmc, SYSC_REG_GPSR) & SYSC_GPSR_FMC_SDA ? 1 : 0;
};
static void mi2c_start(struct fmc_device *fmc)
{
set_sda(fmc, 0);
set_scl(fmc, 0);
}
static void mi2c_stop(struct fmc_device *fmc)
{
set_sda(fmc, 0);
set_scl(fmc, 1);
set_sda(fmc, 1);
}
int mi2c_put_byte(struct fmc_device *fmc, int data)
{
int i;
int ack;
for (i = 0; i < 8; i++, data<<=1) {
set_sda(fmc, data & 0x80);
set_scl(fmc, 1);
set_scl(fmc, 0);
}
set_sda(fmc, 1);
set_scl(fmc, 1);
ack = get_sda(fmc);
set_scl(fmc, 0);
set_sda(fmc, 0);
return ack ? -EIO : 0; /* ack low == success */
}
int mi2c_get_byte(struct fmc_device *fmc, unsigned char *data, int sendack)
{
int i;
int indata = 0;
/* assert: scl is low */
set_scl(fmc, 0);
set_sda(fmc, 1);
for (i = 0; i < 8; i++) {
set_scl(fmc, 1);
indata <<= 1;
if (get_sda(fmc))
indata |= 0x01;
set_scl(fmc, 0);
}
set_sda(fmc, (sendack ? 0 : 1));
set_scl(fmc, 1);
set_scl(fmc, 0);
set_sda(fmc, 0);
*data= indata;
return 0;
}
void mi2c_init(struct fmc_device *fmc)
{
set_scl(fmc, 1);
set_sda(fmc, 1);
}
int mi2c_scan(struct fmc_device *fmc)
{
int i, found = 0;
for(i = 0; i < 256; i += 2) {
mi2c_start(fmc);
if(!mi2c_put_byte(fmc, i)) {
pr_info("%s: Found i2c device at 0x%x\n",
KBUILD_MODNAME, i >> 1);
found++;
}
mi2c_stop(fmc);
}
return found;
}
/* FIXME: this is very inefficient: read several bytes in a row instead */
int spec_eeprom_read(struct fmc_device *fmc, int i2c_addr, uint32_t offset,
void *buf, size_t size)
{
int i;
uint8_t *buf8 = buf;
unsigned char c;
for(i = 0; i < size; i++) {
mi2c_start(fmc);
if(mi2c_put_byte(fmc, i2c_addr << 1) < 0) {
mi2c_stop(fmc);
return -EIO;
}
mi2c_put_byte(fmc, (offset >> 8) & 0xff);
mi2c_put_byte(fmc, offset & 0xff);
offset++;
mi2c_stop(fmc);
mi2c_start(fmc);
mi2c_put_byte(fmc, (i2c_addr << 1) | 1);
mi2c_get_byte(fmc, &c, 0);
*buf8++ = c;
mi2c_stop(fmc);
}
return size;
}
int spec_eeprom_write(struct fmc_device *fmc, int i2c_addr, uint32_t offset,
const void *buf, size_t size)
{
int i, busy;
const uint8_t *buf8 = buf;
for(i = 0; i < size; i++) {
mi2c_start((fmc));
if(mi2c_put_byte(fmc, i2c_addr << 1) < 0) {
mi2c_stop(fmc);
return -1;
}
mi2c_put_byte(fmc, (offset >> 8) & 0xff);
mi2c_put_byte(fmc, offset & 0xff);
mi2c_put_byte(fmc, *buf8++);
offset++;
mi2c_stop(fmc);
do { /* wait until the chip becomes ready */
mi2c_start(fmc);
busy = mi2c_put_byte(fmc, i2c_addr << 1);
mi2c_stop(fmc);
} while(busy);
}
return size;
}
int spec_i2c_init(struct fmc_device *fmc)
{
struct spec_dev *spec = fmc->carrier_data;
void *buf;
int i, found;
found = mi2c_scan(fmc);
if (!found) {
fmc->flags |= FMC_DEVICE_NO_MEZZANINE;
return 0;
}
buf = kmalloc(SPEC_I2C_EEPROM_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
i = spec_eeprom_read(fmc, SPEC_I2C_EEPROM_ADDR, 0, buf,
SPEC_I2C_EEPROM_SIZE);
if (i != SPEC_I2C_EEPROM_SIZE) {
dev_err(&spec->pdev->dev, "EEPROM read error: retval is %i\n",
i);
kfree(buf);
return -EIO;
}
fmc->eeprom = buf;
fmc->eeprom_len = SPEC_I2C_EEPROM_SIZE;
if (spec_i2c_dump)
dumpstruct("eeprom", buf, SPEC_I2C_EEPROM_SIZE);
return 0;
}
void spec_i2c_exit(struct fmc_device *fmc)
{
kfree(fmc->eeprom);
fmc->eeprom = NULL;
fmc->eeprom_len = 0;
}
/*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/kmod.h>
#include <linux/sched.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <asm/unaligned.h>
#include "spec.h"
#include "loader-ll.h"
char *spec_fw_name = "fmc/spec-init.bin";
module_param_named(fw_name, spec_fw_name, charp, 0444);
/* Load the FPGA. This bases on loader-ll.c, a kernel/user space thing */
int spec_load_fpga(struct spec_dev *spec, const void *data, int size)
{
struct device *dev = &spec->pdev->dev;
int i, wrote;
unsigned long j;
/* loader_low_level is designed to run from user space too */
wrote = loader_low_level(0 /* unused fd */,
spec->remap[2], data, size);
j = jiffies + 2 * HZ;
/* Wait for DONE interrupt */
while(1) {
udelay(100);
i = readl(spec->remap[2] + FCL_IRQ);
if (i & 0x8) {
dev_info(dev, "FPGA programming successful\n");
return 0;
}
if(i & 0x4) {
dev_err(dev, "FPGA program error after %i writes\n",
wrote);
return -ETIMEDOUT;
}
if (time_after(jiffies, j)) {
dev_err(dev, "FPGA timeout after %i writes\n", wrote);
return -ETIMEDOUT;
}
}
}
int spec_load_fpga_file(struct spec_dev *spec, char *name)
{
struct device *dev = &spec->pdev->dev;
const struct firmware *fw;
int err = 0;
err = request_firmware(&fw, name, dev);
if (err < 0) {
dev_err(dev, "request firmware \"%s\": error %i\n", name, err);
return err;
}
dev_info(dev, "got file \"%s\", %i (0x%x) bytes\n",
spec_fw_name, fw->size, fw->size);
err = spec_load_fpga(spec, fw->data, fw->size);
release_firmware(fw);
return err;
}
static int __devinit spec_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct spec_dev *spec;
int i, ret;
dev_info(&pdev->dev, " probe for device %04x:%04x\n",
pdev->bus->number, pdev->devfn);
ret = pci_enable_device(pdev);
if (ret < 0)
return ret;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
return -ENOMEM;
spec->pdev = pdev;
if ( (ret = pci_enable_msi_block(pdev, 1)) < 0)
dev_err(&pdev->dev, "enable msi block: error %i\n", ret);
/* Remap our 3 bars */
for (i = ret = 0; i < 3; i++) {
struct resource *r = pdev->resource + (2 * i);
if (!r->start)
continue;
spec->area[i] = r;
if (r->flags & IORESOURCE_MEM) {
spec->remap[i] = ioremap(r->start,
r->end + 1 - r->start);
if (!spec->remap[i])
ret = -ENOMEM;
}
}
if (ret)
goto out_unmap;
/* Load the golden FPGA binary to read the eeprom */
ret = spec_load_fpga_file(spec, spec_fw_name);
if (ret)
goto out_unmap;
ret = spec_fmc_create(spec);
if (ret)
goto out_unmap;
/* Done */
pci_set_drvdata(pdev, spec);
return 0;
out_unmap:
for (i = 0; i < 3; i++) {
if (spec->remap[i])
iounmap(spec->remap[i]);
spec->remap[i] = NULL;
spec->area[i] = NULL;
}
pci_set_drvdata(pdev, NULL);
pci_disable_msi(pdev);
pci_disable_device(pdev);
kfree(spec);
return ret;
}
static void __devexit spec_remove(struct pci_dev *pdev)
{
struct spec_dev *spec = pci_get_drvdata(pdev);
int i;
dev_info(&pdev->dev, "remove\n");
spec_fmc_destroy(spec);
for (i = 0; i < 3; i++) {
if (spec->remap[i])
iounmap(spec->remap[i]);
spec->remap[i] = NULL;
spec->area[i] = NULL;
}
pci_set_drvdata(pdev, NULL);
kfree(spec);
pci_disable_msi(pdev);
pci_disable_device(pdev);
}
DEFINE_PCI_DEVICE_TABLE(spec_idtable) = {
{ PCI_DEVICE(PCI_VENDOR_ID_CERN, PCI_DEVICE_ID_SPEC) },
{ PCI_DEVICE(PCI_VENDOR_ID_GENNUM, PCI_DEVICE_ID_GN4124) },
{ 0,},
};
static struct pci_driver spec_driver = {
.name = "spec",
.id_table = spec_idtable,
.probe = spec_probe,
.remove = spec_remove,
};
static int __init spec_init(void)
{
return pci_register_driver(&spec_driver);
}
static void __exit spec_exit(void)
{
pci_unregister_driver(&spec_driver);
}
module_init(spec_init);
module_exit(spec_exit);
MODULE_LICENSE("GPL");
......@@ -10,10 +10,10 @@
#ifndef __SPEC_H__
#define __SPEC_H__
#include <linux/pci.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>
#include <linux/atomic.h>
#include <linux/list.h>
#include <linux/completion.h>
#include <linux/fmc.h>
#include <linux/gpio.h>
#define PCI_VENDOR_ID_CERN 0x10dc
#define PCI_DEVICE_ID_SPEC 0x018d
......@@ -22,59 +22,123 @@
#define SPEC_DEFAULT_LM32_ADDR 0x80000 /* used if "1" is passed */
#define SPEC_MAX_BOARDS 8
enum spec_names {
SPEC_NAME_FW,
SPEC_NAME_PROG,
SPEC_NAME_SUBMOD,
SPEC_NAMES,
};
/* Our device structure */
struct spec_dev {
struct pci_dev *pdev;
struct resource *area[3]; /* bar 0, 2, 4 */
void *remap[3]; /* ioremap of bar 0, 2, 4 */
char *names[SPEC_NAMES];
void __iomem *remap[3]; /* ioremap of bar 0, 2, 4 */
char *submod_name;
struct work_struct work;
const struct firmware *fw;
struct list_head list;
unsigned long irqcount;
atomic_t has_submod;
void *sub_priv;
struct fmc_device *fmc;
int irq_count; /* for mezzanine use too */
struct completion compl;
struct gpio_chip *gpio;
};
/* Used by sub-modules */
extern struct list_head spec_list;
/* Registers for GN4124 access */
enum {
/* page 106 */
GNPPCI_MSI_CONTROL = 0x48, /* actually, 3 smaller regs */
GNPPCI_MSI_ADDRESS_LOW = 0x4c,
GNPPCI_MSI_ADDRESS_HIGH = 0x50,
GNPPCI_MSI_DATA = 0x54,
/* page 130 ff */
GNINT_CTRL = 0x810,
GNINT_STAT = 0x814,
GNINT_CFG_0 = 0x820,
GNINT_CFG_1 = 0x824,
GNINT_CFG_2 = 0x828,
GNINT_CFG_3 = 0x82c,
GNINT_CFG_4 = 0x830,
GNINT_CFG_5 = 0x834,
GNINT_CFG_6 = 0x838,
GNINT_CFG_7 = 0x83c,
#define GNINT_CFG(x) (GNINT_CFG_0 + 4 * (x))
/* Registers from the gennum header files */
enum {
/* page 146 ff */
GNGPIO_BASE = 0xA00,
GNGPIO_DIRECTION_MODE = GNGPIO_BASE + 0x4,
GNGPIO_OUTPUT_ENABLE = GNGPIO_BASE + 0x8,
GNGPIO_OUTPUT_VALUE = GNGPIO_BASE + 0xC,
GNGPIO_INPUT_VALUE = GNGPIO_BASE + 0x10,
FCL_BASE = 0xB00,
FCL_CTRL = FCL_BASE,
FCL_STATUS = FCL_BASE + 0x4,
FCL_IODATA_IN = FCL_BASE + 0x8,
FCL_IODATA_OUT = FCL_BASE + 0xC,
FCL_EN = FCL_BASE + 0x10,
FCL_TIMER_0 = FCL_BASE + 0x14,
FCL_TIMER_1 = FCL_BASE + 0x18,
FCL_CLK_DIV = FCL_BASE + 0x1C,
FCL_IRQ = FCL_BASE + 0x20,
FCL_TIMER_CTRL = FCL_BASE + 0x24,
FCL_IM = FCL_BASE + 0x28,
FCL_TIMER2_0 = FCL_BASE + 0x2C,
FCL_TIMER2_1 = FCL_BASE + 0x30,
FCL_DBG_STS = FCL_BASE + 0x34,
FCL_FIFO = 0xE00,
PCI_SYS_CFG_SYSTEM = 0x800
GNGPIO_BYPASS_MODE = GNGPIO_BASE,
GNGPIO_DIRECTION_MODE = GNGPIO_BASE + 0x04, /* 0 == output */
GNGPIO_OUTPUT_ENABLE = GNGPIO_BASE + 0x08,
GNGPIO_OUTPUT_VALUE = GNGPIO_BASE + 0x0C,
GNGPIO_INPUT_VALUE = GNGPIO_BASE + 0x10,
GNGPIO_INT_MASK = GNGPIO_BASE + 0x14, /* 1 == disabled */
GNGPIO_INT_MASK_CLR = GNGPIO_BASE + 0x18, /* irq enable */
GNGPIO_INT_MASK_SET = GNGPIO_BASE + 0x1C, /* irq disable */
GNGPIO_INT_STATUS = GNGPIO_BASE + 0x20,
GNGPIO_INT_TYPE = GNGPIO_BASE + 0x24, /* 1 == level */
GNGPIO_INT_VALUE = GNGPIO_BASE + 0x28, /* 1 == high/rise */
GNGPIO_INT_ON_ANY = GNGPIO_BASE + 0x2C, /* both edges */
/* page 158 ff */
FCL_BASE = 0xB00,
FCL_CTRL = FCL_BASE,
FCL_STATUS = FCL_BASE + 0x04,
FCL_IODATA_IN = FCL_BASE + 0x08,
FCL_IODATA_OUT = FCL_BASE + 0x0C,
FCL_EN = FCL_BASE + 0x10,
FCL_TIMER_0 = FCL_BASE + 0x14,
FCL_TIMER_1 = FCL_BASE + 0x18,
FCL_CLK_DIV = FCL_BASE + 0x1C,
FCL_IRQ = FCL_BASE + 0x20,
FCL_TIMER_CTRL = FCL_BASE + 0x24,
FCL_IM = FCL_BASE + 0x28,
FCL_TIMER2_0 = FCL_BASE + 0x2C,
FCL_TIMER2_1 = FCL_BASE + 0x30,
FCL_DBG_STS = FCL_BASE + 0x34,
FCL_FIFO = 0xE00,
PCI_SYS_CFG_SYSTEM = 0x800
};
/* Access gennum registers in a "standard" way */
static inline uint32_t gennum_readl(struct spec_dev *spec, int reg)
{
return readl(spec->remap[2] + reg);
}
static inline void gennum_writel(struct spec_dev *spec, uint32_t val, int reg)
{
writel(val, spec->remap[2] + reg);
}
static inline void gennum_mask_val(struct spec_dev *spec,
uint32_t mask, uint32_t val, int reg)
{
uint32_t v = gennum_readl(spec, reg);
v &= ~mask;
v |= val;
gennum_writel(spec, v, reg);
}
/* Functions in spec-pci.c */
extern int spec_load_fpga(struct spec_dev *spec, const void *data, int size);
extern int spec_load_fpga_file(struct spec_dev *spec, char *name);
extern char *spec_fw_name;
/* Functions in spec-fmc.c, used by spec-pci.c */
extern int spec_fmc_create(struct spec_dev *spec);
extern void spec_fmc_destroy(struct spec_dev *spec);
/* Functions in spec-i2c.c, used by spec-fmc.c */
extern int spec_i2c_init(struct fmc_device *fmc);
extern void spec_i2c_exit(struct fmc_device *fmc);
extern int spec_eeprom_read(struct fmc_device *fmc, int i2c_addr,
uint32_t offset, void *buf, size_t size);
extern int spec_eeprom_write(struct fmc_device *fmc, int i2c_addr,
uint32_t offset, const void *buf, size_t size);
/* The eeprom is at address 0x50 */
#define SPEC_I2C_EEPROM_ADDR 0x50
#define SPEC_I2C_EEPROM_SIZE (8 * 1024)
/* Functions in spec-gpio.c */
extern int spec_gpio_init(struct fmc_device *fmc);
extern void spec_gpio_exit(struct fmc_device *fmc);
#endif /* __SPEC_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/interrupt.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include "wr-nic.h"
#include "spec.h"
static struct fmc_driver wrn_drv;
static char *wrn_filename = WRN_GATEWARE_DEFAULT_NAME;
module_param_named(file, wrn_filename, charp, 0444);
irqreturn_t wrn_handler(int irq, void *dev_id)
{
struct fmc_device *fmc = dev_id;
fmc->op->irq_ack(fmc);
printk("%s: irq %i\n", __func__, irq);
return IRQ_HANDLED;
}
int wrn_probe(struct fmc_device *fmc)
{
int ret = 0;
struct device *dev = fmc->hwdev;
struct wrn_drvdata *dd;
/* 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 (and lm32) within the spec */
ret = fmc->op->reprogram(fmc, &wrn_drv, WRN_GATEWARE_DEFAULT_NAME);
if (ret <0) {
dev_err(dev, "write firmware \"%s\": error %i\n",
wrn_filename, ret);
goto out;
}
/* Verify that we have SDB at offset 0x63000 */
if (fmc_readl(fmc, 0x63000) != 0x5344422d) {
dev_err(dev, "Can't find SDB magic\n");
ret = -ENODEV;
goto out;
}
dev_info(dev, "Gateware successfully loaded\n");
if ( (ret = fmc_scan_sdb_tree(fmc, 0x63000)) < 0) {
dev_err(dev, "scan fmc failed %i\n", ret);
goto out;
}
fmc_show_sdb_tree(fmc);
/* FIXME: load lm32 */
/* Register the gpio stuff, if we have kernel support */
ret = wrn_gpio_init(fmc);
if (ret < 0)
goto out;
/* The netword device */
ret = wrn_eth_init(fmc);
if (ret < 0)
goto out_gpio;
/* The interrupt */
ret = fmc->op->irq_request(fmc, wrn_handler, "wr-nic", 0);
if (ret < 0) {
dev_err(dev, "Can't request interrupt\n");
goto out_nic;
}
return 0;
out_nic:
wrn_eth_exit(fmc);
out_gpio:
wrn_gpio_exit(fmc);
out:
return ret;
}
int wrn_remove(struct fmc_device *fmc)
{
fmc->op->irq_free(fmc);
wrn_eth_exit(fmc);
wrn_gpio_exit(fmc);
fmc_free_sdb_tree(fmc);
return 0;
}
static struct fmc_driver wrn_drv = {
.driver.name = KBUILD_MODNAME,
.probe = wrn_probe,
.remove = wrn_remove,
/* no table, as the current match just matches everything */
};
static int wrn_init(void)
{
int ret;
ret = fmc_driver_register(&wrn_drv);
return ret;
}
static void wrn_exit(void)
{
fmc_driver_unregister(&wrn_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_LICENSE("GPL");
/*
* Copyright (C) 2010-2012 CERN (www.cern.ch)
* 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.
......@@ -7,31 +7,17 @@
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <linux/atomic.h>
#include <asm/unaligned.h>
#include <linux/fmc.h>
#include "wr-nic.h"
#include "spec.h"
static int wrn_init(void)
int wrn_eth_init(struct fmc_device *fmc)
{
return 0;
}
static void wrn_exit(void)
void wrn_eth_exit(struct fmc_device *fmc)
{
}
module_init(wrn_init);
module_exit(wrn_exit);
MODULE_LICENSE("GPL");
/*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/fmc.h>
#include "spec.h"
#include "wr-nic.h"
static inline struct fmc_device *gc_to_fmc(struct gpio_chip *gc)
{
struct device *dev = gc->dev;
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;
gc->dev = &fmc->dev;
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)
{
int ret;
struct wrn_drvdata *dd = fmc_get_drvdata(fmc);
struct gpio_chip *gc = dd->gc;
ret = gpiochip_remove(gc);
if (ret)
dev_err(fmc->hwdev, "DANGER %i! gpio chip can't be removed\n",
ret);
return;
}
#ifndef __WR_NIC_H__
#define __WR_NIC_H__
#include <linux/gpio.h>
/*
* This is the memory map of this beast, from "./top/spec/wr_nic_sdb_top.vhd"
* in the wr-nic ohwr project (git@ohwr.org:white-rabbit/wr-nic.git for us
* and git://ohwr.org/white-rabbit/wr-nic.git for everybody)
*
* -- Memory map:
* -- 0x00000000: WRPC
* -- 0x00000: WRPC I/D Memory
* -- 0x20000: WRPC Peripheral interconnect
* -- +0x000: WRPC Minic
* -- +0x100: WRPC Endpoint
* -- +0x200: WRPC Softpll
* -- +0x300: WRPC PPS gen
* -- +0x400: WRPC Syscon
* -- +0x500: WRPC UART
* -- +0x600: WRPC OneWire
* -- +0x700: WRPC Auxillary space (Etherbone config, etc)
* -- 0x00040000: WRSW NIC
* -- 0x00060000: VIC
* -- 0x00061000: TxTSU
* -- 0x00062000: DIO
* -- 0x000: DIO-ONEWIRE
* -- 0x100: DIO-I2C
* -- 0x200: DIO-GPIO
* -- 0x300: DIO-REGISTERS
* (plus, at 63000 there are the sdb records)
*/
#define WRN_GPIO 0x62200 /* "standard" GPIO registers */
#define WRN_DIO 0x62300 /* time-aware gpio registers */
#define WRN_SDB 0x63000
#define WRN_GATEWARE_DEFAULT_NAME "fmc/wr_nic_dio.bin"
struct wrn_drvdata {
struct gpio_chip *gc;
struct net_device *eth;
/* We also need the various base addresses here for fmc_writel/readl */
__iomem void *gpio_baseaddr;
};
/* wr-nic-eth.c */
extern int wrn_eth_init(struct fmc_device *fmc);
extern void wrn_eth_exit(struct fmc_device *fmc);
/* wr-nic-gpio.h */
extern int wrn_gpio_init(struct fmc_device *fmc);
extern void wrn_gpio_exit(struct fmc_device *fmc);
#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