Commit 04eb2376 authored by Alessandro Rubini's avatar Alessandro Rubini

Merge branch 'fmc-as-submodule'

parents 7a1fb9bc 43ee322e
[submodule "fmc-bus"]
path = fmc-bus
url = git://ohwr.org/fmc-projects/fmc-bus.git
DIRS = kernel tools doc
DIRS = fmc-bus/kernel kernel tools doc
all clean modules install modules_install:
for d in $(DIRS); do $(MAKE) -C $$d $@ || exit 1; done
This diff is collapsed.
This diff is collapsed.
fmc-bus @ 53298896
Subproject commit 532988962e856188fb26890b2912c1171c4d3695
LINUX ?= /lib/modules/$(shell uname -r)/build
ccflags-y += -I$M/include
KBUILD_EXTRA_SYMBOLS := $M/../fmc-bus/kernel/Module.symvers
ccflags-y += -I$M/include -I$M/../fmc-bus/kernel/include
ccflags-y += $(WR_NIC_CFLAGS)
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
......
/*
* 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_check_version(unsigned long version, const char *name)
{
if (__FMC_MAJOR(version) != FMC_MAJOR) {
pr_err("%s: \"%s\" has wrong major (has %li, expected %i)\n",
__func__, name, __FMC_MAJOR(version), FMC_MAJOR);
return -EINVAL;
}
if (__FMC_MINOR(version) != FMC_MINOR)
pr_info("%s: \"%s\" has wrong minor (has %li, expected %i)\n",
__func__, name, __FMC_MINOR(version), FMC_MINOR);
return 0;
}
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)
{
if (fmc_check_version(drv->version, drv->driver.name))
return -EINVAL;
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)
{
if (fmc_check_version(fdev->version, fdev->carrier_name))
return -EINVAL;
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);
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 = address + 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 fmc_device *fmc, 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;
r = &arr->record[i];
c = &r->dev.sdb_component;
p = &c->product;
base = 0;
for (ap = arr; ap; ap = ap->parent)
base += ap->baseaddr;
dev_info(fmc->hwdev, "SDB: ");
for (j = 0; j < level; j++)
printk(" ");
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(fmc, 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, fmc->sdb);
}
EXPORT_SYMBOL(fmc_show_sdb_tree);
signed long fmc_find_sdb_device(struct sdb_array *tree,
uint64_t vid, uint32_t did, unsigned long *sz)
{
signed long res = -ENODEV;
union sdb_record *r;
struct sdb_product *p;
struct sdb_component *c;
int i, n = tree->len;
uint64_t last, first;
/* 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, sz);
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;
/* found */
last = __be64_to_cpu(c->addr_last);
first = __be64_to_cpu(c->addr_first);
if (sz) *sz = (typeof(*sz))(last + 1 -first);
return first + tree->baseaddr;
}
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/gpio.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);
dev_info(fmc->hwdev, "received irq %i\n", irq);
return IRQ_HANDLED;
}
struct fmc_gpio t_gpio[] = {
{
.gpio = FMC_GPIO_IRQ(0),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}, {
.gpio = FMC_GPIO_IRQ(1),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}
};
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", IRQF_SHARED);
if (ret < 0)
return ret;
/* ignore error code of call below, we really don't care */
fmc->op->gpio_config(fmc, t_gpio, ARRAY_SIZE(t_gpio));
/* 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 = {
.version = FMC_VERSION,
.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 = {
.version = FMC_VERSION,
.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");
/*
* 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, unsigned long *sz);
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;
/*
* This bus abstraction is developed separately from drivers, so we need
* to check the version of the data structures we receive.
*/
#define FMC_MAJOR 1
#define FMC_MINOR 0
#define FMC_VERSION ((FMC_MAJOR << 16) | FMC_MINOR)
#define __FMC_MAJOR(x) ((x) >> 16)
#define __FMC_MINOR(x) ((x) & 0xffff)
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 {
unsigned long version;
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)
/*
* Drivers may need to configure gpio pins in the carrier. To read input
* (a very uncommon opeation, and definitely not in the hot paths), just
* configure one gpio only and get 0 or 1 as retval of the config method
*/
struct fmc_gpio {
char *carrier_name; /* name or NULL for virtual pins */
int gpio;
int _gpio; /* internal use by the carrier */
int mode; /* GPIOF_DIR_OUT etc, from <linux/gpio.h> */
int irqmode; /* IRQF_TRIGGER_LOW and so on */
};
/* The numbering of gpio pins allows access to raw pins or virtual roles */
#define FMC_GPIO_RAW(x) (x) /* 4096 of them */
#define __FMC_GPIO_IS_RAW(x) ((x) < 0x1000)
#define FMC_GPIO_IRQ(x) ((x) + 0x1000) /* 256 of them */
#define FMC_GPIO_LED(x) ((x) + 0x1100) /* 256 of them */
#define FMC_GPIO_KEY(x) ((x) + 0x1200) /* 256 of them */
#define FMC_GPIO_TP(x) ((x) + 0x1300) /* 256 of them */
#define FMC_GPIO_USER(x) ((x) + 0x1400) /* 256 of them */
/* We may add SCL and SDA, or other roles if the need arises */
/*
* The operations are offered by each carrier and should make driver
* design completely independent of th carrier. Named GPIO pins may be
* the exception.
*/
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 (*gpio_config)(struct fmc_device *fmc, struct fmc_gpio *gpio,
int ngpio);
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 version;
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;
void *mezzanine_data;
};
#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__ */
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