Commit 4a0799ec authored by Federico Vaga's avatar Federico Vaga

kernel: replace FMC with platform

Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent dafa2e49
[submodule "zio"]
path = zio
url = git://ohwr.org/misc/zio.git
[submodule "fmc-bus"]
path = fmc-bus
url = git://ohwr.org/fmc-projects/fmc-bus.git
# include parent_common.mk for buildsystem's defines
# use absolute path for REPO_PARENT
REPO_PARENT=$(shell /bin/pwd)/..
REPO_PARENT ?= $(shell /bin/pwd)/..
-include $(REPO_PARENT)/parent_common.mk
all: kernel lib tools
......@@ -10,24 +10,22 @@ CONFIG_WR_NIC=n
export CONFIG_WR_NIC
# The user can override, using environment variables, all these three:
FMC_BUS ?= fmc-bus
ZIO ?= zio
# FMC_BUS_ABS and ZIO_ABS has to be absolut path, due to beeing
# ZIO_ABS has to be absolut path, due to beeing
# passed to the Kbuild
FMC_BUS_ABS ?= $(abspath $(FMC_BUS) )
ZIO_ABS ?= $(abspath $(ZIO) )
export FMC_BUS_ABS
export ZIO_ABS
ZIO_VERSION = $(shell cd $(ZIO_ABS); git describe --always --dirty --long --tags)
export ZIO_VERSION
DIRS = $(FMC_BUS_ABS) $(ZIO_ABS) kernel lib tools
DIRS = $(ZIO_ABS) kernel lib tools
kernel: $(FMC_BUS_ABS) $(ZIO_ABS)
$(SPEC_SW_ABS):
kernel: $(ZIO_ABS)
lib: $(ZIO_ABS)
tools: lib
......@@ -50,7 +48,7 @@ $(DIRS):
$(MAKE) -C $@ $(TARGET)
SUBMOD = $(FMC_BUS_ABS) $(ZIO_ABS)
SUBMOD = $(ZIO_ABS)
prereq_install_warn:
@test -f .prereq_installed || \
......@@ -60,13 +58,8 @@ prereq_install:
for d in $(SUBMOD); do $(MAKE) -C $$d modules_install || exit 1; done
touch .prereq_installed
$(FMC_BUS_ABS): fmc-bus-init_repo
$(ZIO_ABS): zio-init_repo
# init submodule if missing
fmc-bus-init_repo:
@test -d $(FMC_BUS_ABS)/doc || ( echo "Checking out submodule $(FMC_BUS_ABS)"; git submodule update --init $(FMC_BUS_ABS) )
# init submodule if missing
zio-init_repo:
@test -d $(ZIO_ABS)/doc || ( echo "Checking out submodule $(ZIO_ABS)" && git submodule update --init $(ZIO_ABS) )
......
fmc-bus @ eb86efcf
Subproject commit eb86efcf4e19a31a25471c4ddf3fd9fef8df02ec
KBUILD_EXTRA_SYMBOLS := \
$(ZIO_ABS)/Module.symvers \
$(FMC_BUS_ABS)/kernel/Module.symvers
$(ZIO_ABS)/Module.symvers
# add versions of supermodule. It is useful when fine-delay-sw is included as sub-module
# of a bigger project that we want to track
......@@ -11,18 +10,13 @@ SUBMODULE_VERSIONS += MODULE_INFO(version_$(CONFIG_SUPER_REPO),\"$(CONFIG_SUPER_
endif
endif
# add versions of used submodules
SUBMODULE_VERSIONS += MODULE_INFO(version_fmc_bus,\"$(FMC_BUS_VERSION)\");
SUBMODULE_VERSIONS += MODULE_INFO(version_zio,\"$(ZIO_VERSION)\");
ccflags-y += -DADDITIONAL_VERSIONS="$(SUBMODULE_VERSIONS)"
# The library includes <sdb.h>, so point -I directtly there
# include our header before to avoid conflicts with the kernel
LINUXINCLUDE := -I$(FMC_BUS_ABS)/kernel/include -I$(FMC_BUS_ABS)/kernel/include/linux $(LINUXINCLUDE)
ccflags-y += \
-I$(ZIO_ABS)/include \
-I$(FMC_BUS_ABS) \
-I$(src)
ccflags-y += -DGIT_VERSION=\"$(GIT_VERSION)\"
......@@ -37,8 +31,6 @@ subdirs-ccflags-y = $(ccflags-y)
obj-m := fmc-fine-delay.o
fmc-fine-delay-objs = fd-zio.o fd-irq.o fd-core.o
fmc-fine-delay-objs += onewire.o spi.o i2c.o gpio.o
fmc-fine-delay-objs += onewire.o spi.o gpio.o
fmc-fine-delay-objs += acam.o calibrate.o pll.o time.o
fmc-fine-delay-objs += calibration.o fmc-util.o
fmc-fine-delay-objs += fmc-bus-link/sdb-lib/access.o
fmc-fine-delay-objs += fmc-bus-link/sdb-lib/glue.o
fmc-fine-delay-objs += calibration.o
# include parent_common.mk for buildsystem's defines
# use absolute path for REPO_PARENT
REPO_PARENT?=$(shell /bin/pwd)/../..
REPO_PARENT ?= $(shell /bin/pwd)/../..
-include $(REPO_PARENT)/parent_common.mk
LINUX ?= /lib/modules/$(shell uname -r)/build
FMC_BUS ?= ../fmc-bus
ZIO ?= ../zio
# FMC_BUS_ABS and ZIO_ABS has to be absolut path, due to beeing
# ZIO_ABS has to be absolut path, due to beeing
# passed to the Kbuild
FMC_BUS_ABS ?= $(abspath $(FMC_BUS) )
ZIO_ABS ?= $(abspath $(ZIO) )
GIT_VERSION = $(shell git describe --dirty --long --tags)
export GIT_VERSION
FMC_BUS_VERSION ?= $(shell cd $(FMC_BUS_ABS); git describe --always --dirty --long --tags)
ZIO_VERSION ?= $(shell cd $(ZIO_ABS); git describe --always --dirty --long --tags)
export FMC_BUS_VERSION
export ZIO_VERSION
all modules:
rm -f fmc-bus-link
ln -s $(FMC_BUS_ABS) fmc-bus-link
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) FMC_BUS_ABS=$(FMC_BUS_ABS) ZIO_ABS=$(ZIO_ABS) modules
rm -f fmc-bus-link
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) ZIO_ABS=$(ZIO_ABS) modules
install modules_install: modules
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) modules_install
......
......@@ -64,7 +64,7 @@ static uint64_t output_delay_ps(struct fd_dev *fd, int ch, int fine, int n,
uint64_t *results;
uint64_t res, acc = 0;
int rem;
struct device *dev = &fd->fmc->dev;
struct device *dev = &fd->pdev->dev;
results = kmalloc(n * sizeof(*results), GFP_KERNEL);
if (!results)
......@@ -146,7 +146,7 @@ static int fd_find_8ns_tap(struct fd_dev *fd, int ch)
int l = 0, mid, r = FD_NUM_TAPS - 1;
uint64_t bias, dly;
struct delay_stats stats;
struct device *dev = &fd->fmc->dev;
struct device *dev = &fd->pdev->dev;
/*
* Measure the delay at zero setting, so it can be further
......@@ -203,7 +203,7 @@ int fd_calibrate_outputs(struct fd_dev *fd)
fd_ch_writel(fd, ch, new, FD_REG_FRR);
fd->ch[ch].frr_cur = new;
if (fd->verbose > 1) {
dev_info(&fd->fmc->dev,
dev_info(&fd->pdev->dev,
"%s: ch%i: 8ns @%i (f %i, off %i, t %i.%02i)\n",
__func__, FD_CH_EXT(ch),
new, fitted, fd->ch[ch].frr_offset,
......@@ -231,7 +231,7 @@ void fd_update_calibration(unsigned long arg)
fd_ch_writel(fd, ch, new, FD_REG_FRR);
fd->ch[ch].frr_cur = new;
dev_dbg(&fd->fmc->dev,
dev_dbg(&fd->pdev->dev,
"%s: ch%i: 8ns @%i (f %i, off %i, t %i.%02i)\n",
__func__, FD_CH_EXT(ch),
new, fitted, fd->ch[ch].frr_offset,
......@@ -241,4 +241,3 @@ void fd_update_calibration(unsigned long arg)
mod_timer(&fd->temp_timer, jiffies + HZ * fd_calib_period_s);
}
......@@ -10,63 +10,11 @@
* option, any later version.
*/
#include <linux/moduleparam.h>
#include <linux/time.h>
#include <linux/firmware.h>
#include <linux/jhash.h>
#include <linux/stat.h>
#include "fine-delay.h"
#include <sdb-lib/libsdbfs.h>
/* At factory config time, it's possible to load a file and/or write eeprom */
static char *calibration_load;
static int calibration_save;
static int calibration_check;
static int calibration_default;
module_param(calibration_load, charp, 0444);
module_param(calibration_default, int, 0444);
module_param(calibration_save, int, 0444);
module_param(calibration_check, 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");
}
/* The user requested to load the configuration from file */
static void fd_i2c_load_calib(struct fd_dev *fd,
struct fd_calibration *calib, char *name)
{
const struct firmware *fw;
int err;
err = request_firmware(&fw, name, fd->fmc->hwdev);
if (err < 0) {
dev_warn(fd->fmc->hwdev, "can't load \"%s\"\n", name);
return;
}
if (fw->size != sizeof(*calib)) {
dev_warn(fd->fmc->hwdev, "File \"%s\" has size != %zi\n",
name, sizeof(*calib));
} else {
memcpy(calib, fw->data, fw->size);
dev_info(fd->fmc->hwdev,
"calibration data loaded from \"%s\"\n", name);
}
release_firmware(fw);
return;
}
static struct fd_calibration fd_calib_default = {
.magic = 0xf19ede1a,
......@@ -78,101 +26,50 @@ static struct fd_calibration fd_calib_default = {
.vcxo_default_tune = 41711,
};
/* sdbfs-related function */
static int fd_read_calibration_eeprom(struct fmc_device *fmc,
struct fd_calibration *calib)
{
int i, ret;
static struct sdbfs fs;
fs.data = fmc->eeprom;
fs.datalen = fmc->eeprom_len;
/* Look for sdb entry point at powers of 2 and onwards */
for (i = 0x40; i < 0x1000; i *= 2) {
fs.entrypoint = i;
ret = sdbfs_dev_create(&fs, 0);
if (ret == 0)
break;
}
if (ret)
return ret;
/* Open "cali" as a device id, vendor is "FileData" -- big endian */
ret = sdbfs_open_id(&fs,
be64_to_cpu(0x46696c6544617461LL),
be32_to_cpu(0x63616c69) /* "cali" */);
if (ret)
return ret;
ret = sdbfs_fread(&fs, 0, (void *)calib, sizeof(*calib));
sdbfs_dev_destroy(&fs);
return ret;
}
/* This is the only thing called by outside */
int fd_handle_eeprom_calibration(struct fd_dev *fd)
int fd_handle_calibration(struct fd_dev *fd, struct fd_calibration *calib)
{
struct fd_calibration *calib;
struct device *d = &fd->fmc->dev;
struct device *d = &fd->pdev->dev;
u32 horig = 0, hash = 0;
int i;
u32 hash, horig;
/* Retrieve and validate the calibration */
calib = &fd->calib;
i = fd_read_calibration_eeprom(fd->fmc, calib);
if (i != sizeof(*calib))
dev_warn(d, "Calibration NOT read from eeprom (got %i)\n", i);
if (calibration_check)
dumpstruct("Calibration data from eeprom:", calib,
sizeof(*calib));
/* Verify hash (used later) */
horig = be32_to_cpu(calib->hash);
calib->hash = 0;
hash = jhash(calib, sizeof(*calib), 0);
if (calib) {
/* save old hash and compute it again before fixing endianess */
horig = be32_to_cpu(calib->hash);
calib->hash = 0;
hash = jhash(calib, sizeof(*calib), 0);
/* fix endianess from SDBFS */
calib->magic = be32_to_cpu(calib->magic);
calib->size = be16_to_cpu(calib->size);
calib->version = be16_to_cpu(calib->version);
calib->date = be32_to_cpu(calib->date);
for (i = 0; i < ARRAY_SIZE(calib->frr_poly); i++)
calib->frr_poly[i] = be64_to_cpu(calib->frr_poly[i]);
for (i = 0; i < ARRAY_SIZE(calib->zero_offset); i++)
calib->zero_offset[i] = be32_to_cpu(calib->zero_offset[i]);
calib->tdc_zero_offset = be32_to_cpu(calib->tdc_zero_offset);
calib->vcxo_default_tune = be32_to_cpu(calib->vcxo_default_tune);
/* FIXME: validate with gateware version */
if (calibration_load) {
fd_i2c_load_calib(fd, calib, calibration_load);
hash = horig; /* whatever it is */
}
/* convert endianneess */
calib->magic = be32_to_cpu(calib->magic);
calib->size = be16_to_cpu(calib->size);
calib->version = be16_to_cpu(calib->version);
calib->date = be32_to_cpu(calib->date);
for (i = 0; i < ARRAY_SIZE(calib->frr_poly); i++)
calib->frr_poly[i] = be64_to_cpu(calib->frr_poly[i]);
for (i = 0; i < ARRAY_SIZE(calib->zero_offset); i++)
calib->zero_offset[i] = be32_to_cpu(calib->zero_offset[i]);
calib->tdc_zero_offset = be32_to_cpu(calib->tdc_zero_offset);
calib->vcxo_default_tune = be32_to_cpu(calib->vcxo_default_tune);
if (calibration_default) {
} else {
dev_info(d, "calibration: overriding with default values\n");
*calib = fd_calib_default;
hash = horig; /* whatever it is */
calib = &fd_calib_default;
}
if (fd->verbose) {
dev_info(d, "calibration: version %i, date %08x\n",
calib->version, calib->date);
if (calibration_check) {
/* dump human-readable values */
dev_info(d, "calib: magic 0x%08x\n", calib->magic);
for (i = 0; i < ARRAY_SIZE(calib->frr_poly); i++)
dev_info(d, "calib: poly[%i] = %lli\n", i,
(long long)calib->frr_poly[i]);
for (i = 0; i < ARRAY_SIZE(calib->zero_offset); i++)
dev_info(d, "calib: offset[%i] = %li\n", i,
(long)calib->zero_offset[i]);
dev_info(d, "calib: tdc_offset %i\n", calib->tdc_zero_offset);
dev_info(d, "calib: vcxo %i\n", calib->vcxo_default_tune);
}
/* dump human-readable values */
dev_info(d, "calib: magic 0x%08x\n", calib->magic);
for (i = 0; i < ARRAY_SIZE(calib->frr_poly); i++)
dev_info(d, "calib: poly[%i] = %lli\n", i,
(long long)calib->frr_poly[i]);
for (i = 0; i < ARRAY_SIZE(calib->zero_offset); i++)
dev_info(d, "calib: offset[%i] = %li\n", i,
(long)calib->zero_offset[i]);
dev_info(d, "calib: tdc_offset %i\n",
calib->tdc_zero_offset);
dev_info(d, "calib: vcxo %i\n", calib->vcxo_default_tune);
}
if (hash != horig) {
......@@ -182,14 +79,46 @@ int fd_handle_eeprom_calibration(struct fd_dev *fd)
}
if (calib->version < 3) {
dev_err(d, "Calibration version %i < 3: refusing to work\n",
fd->calib.version);
calib->version);
return -EINVAL;
}
if (calibration_save) {
/* FIXME: save to eeprom: re-convert endianness and hash */
dev_warn(d, "Saving is not supported\n");
}
fd->calib = *calib;
fd->calib.hash = hash;
return 0;
}
static ssize_t fd_write_eeprom(struct file *file, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct platform_device *pdev = to_platform_device(dev);
struct fd_dev *fd = platform_get_drvdata(pdev);
struct fd_calibration *calib = (struct fd_calibration *)(buf + 0x240);
int ret;
if (off >= (4 * 1024)) {
/*
* We do care only about the data in the first 4K of
* the eeprom. Just, acknowledge any other page
*/
return count;
}
if (count < 0x240 + sizeof(struct fd_calibration)) {
dev_err(dev, "Invalid eeprom size\n");
return -EINVAL;
}
ret = fd_handle_calibration(fd, calib);
return ret ? ret : count;
}
struct bin_attribute dev_attr_eeprom = {
.attr = {
.name = "eeprom",
.mode = S_IWUSR,
},
.size = (8 * 1024), /* 8 KiB */
.write = fd_write_eeprom,
};
......@@ -21,21 +21,20 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include <linux/platform_device.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
struct memory_ops memops = {
.read = NULL,
.write = NULL,
};
/* Module parameters */
static int fd_verbose = 0;
module_param_named(verbose, fd_verbose, int, 0444);
static struct fmc_driver fd_drv; /* forward declaration */
FMC_PARAM_BUSID(fd_drv);
FMC_PARAM_GATEWARE(fd_drv);
/* FIXME: add parameters "file=" and "wrc=" like wr-nic-core does */
/**
......@@ -97,7 +96,7 @@ int fd_reset_again(struct fd_dev *fd)
msleep(10);
}
if (time_after_eq(jiffies, j)) {
dev_err(&fd->fmc->dev,
dev_err(&fd->pdev->dev,
"%s: timeout waiting for GCR lock bit\n", __func__);
return -EIO;
}
......@@ -124,90 +123,85 @@ static struct fd_modlist mods[] = {
{"reset-again", fd_reset_again},
SUBSYS(acam),
SUBSYS(time),
SUBSYS(i2c),
SUBSYS(zio),
};
/* probe and remove are called by the FMC bus core */
int fd_probe(struct fmc_device *fmc)
static int fd_resource_validation(struct platform_device *pdev)
{
struct fd_modlist *m;
struct fd_dev *fd;
struct device *dev = &fmc->dev;
char *fwname = "invalid";
int i, index, ret, ch, ord;
/* Validate the new FMC device */
index = fmc->op->validate(fmc, &fd_drv);
if (index < 0) {
dev_info(dev, "not using \"%s\" according to "
"modparam\n", KBUILD_MODNAME);
return -ENODEV;
}
struct resource *r;
fd = devm_kzalloc(&fmc->dev, sizeof(*fd), GFP_KERNEL);
if (!fd) {
dev_err(dev, "can't allocate device\n");
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_IRQ, FD_IRQ);
if (!r) {
dev_err(&pdev->dev,
"The Fine-Delay needs an interrupt number\n");
return -ENXIO;
}
/*
* If the carrier is still using the golden bitstream or the user is
* asking for a particular one, then program our bistream, otherwise
* we already have our bitstream
*/
if (fmc->flags & FMC_DEVICE_HAS_GOLDEN || fd_drv.gw_n) {
if (fd_drv.gw_n)
fwname = ""; /* reprogram will pick from module parameter */
else if (!strcmp(fmc->carrier_name, "SVEC"))
fwname = FDELAY_GATEWARE_NAME_SVEC;
else if (!strcmp(fmc->carrier_name, "SPEC"))
fwname = FDELAY_GATEWARE_NAME_SPEC;
dev_info(fmc->hwdev, "Gateware (%s)\n", fwname);
/* We first write a new binary (and lm32) within the carrier */
ret = fmc_reprogram(fmc, &fd_drv, fwname, 0 /* SDB entry point */);
if (ret < 0) {
dev_err(fmc->hwdev, "write firmware \"%s\": error %i\n",
fwname, ret);
if (ret == -ESRCH) {
dev_info(fmc->hwdev, "%s: no gateware at index %i\n",
KBUILD_MODNAME, index);
return -ENODEV;
}
return ret;
}
dev_info(fmc->hwdev, "Gateware successfully loaded\n");
} else {
dev_info(fmc->hwdev,
"Gateware already there. Set the \"gateware\" parameter to overwrite the current gateware\n");
r = platform_get_resource(pdev, IORESOURCE_MEM, FD_MEM_BASE);
if (!r) {
dev_err(&pdev->dev,
"The Fine-Delay needs base address\n");
return -ENXIO;
}
ret = fmc_scan_sdb_tree(fmc, 0);
if (ret == -EBUSY) {
/* Not a problem, it's already there. We assume that
it's the correct one */
ret = 0;
r = platform_get_resource(pdev, IORESOURCE_BUS, FD_BUS_FMC_SLOT);
if (!r) {
dev_err(&pdev->dev,
"The Fine-Delay needs to be assigned to an FMC slot\n");
return -ENXIO;
}
/* Now use SDB to find the base addresses */
return 0;
}
ord = fmc->slot_id;
fd->fd_regs_base = fmc_sdb_find_nth_device ( fmc->sdb, 0xce42, 0xf19ede1a, &ord, NULL );
if( (signed long)fd->fd_regs_base < 0)
{
dev_err(dev, "Can't find the FD core. Wrong gateware?\n");
/* probe and remove are called by the FMC bus core */
int fd_probe(struct platform_device *pdev)
{
struct fd_modlist *m;
struct fd_dev *fd;
struct device *dev = &pdev->dev;
int i, ret, ch, err;
struct resource *r;
err = fd_resource_validation(pdev);
if (err)
return err;
/* Assign IO operation */
switch (pdev->id_entry->driver_data) {
case FD_VER_SPEC:
memops.read = ioread32;
memops.write = iowrite32;
break;
case FD_VER_SVEC:
memops.read = ioread32be;
memops.write = iowrite32be;
break;
default:
dev_err(&pdev->dev, "Unknow version %lu\n",
pdev->id_entry->driver_data);
return -EINVAL;
}
fd = devm_kzalloc(&pdev->dev, sizeof(*fd), GFP_KERNEL);
if (!fd) {
dev_err(dev, "can't allocate device\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, fd);
fd->pdev = pdev;
fd->verbose = fd_verbose;
dev_dbg(dev, "fd_regs_base is %x\n", fd->fd_regs_base);
r = platform_get_resource(pdev, IORESOURCE_MEM, FD_MEM_BASE);
fd->fd_regs_base = ioremap(r->start, resource_size(r));
fd->fd_owregs_base = fd->fd_regs_base + 0x500;
spin_lock_init(&fd->lock);
fmc->mezzanine_data = fd;
fd->fmc = fmc;
fd->verbose = fd_verbose;
/* Check the binary is there */
if (fd_readl(fd, FD_REG_IDR) != FD_MAGIC_FPGA) {
......@@ -216,39 +210,10 @@ int fd_probe(struct fmc_device *fmc)
}
/* Retrieve calibration from the eeprom, and validate */
ret = fd_handle_eeprom_calibration(fd);
ret = fd_handle_calibration(fd, NULL);
if (ret < 0)
return ret;
/*
* Now, check the release field of the sdb record for our core.
* Unfortunately we have no fmc primitive to see the device record.
* So keep at the first level, knowing fdelay leaves there (FIXME)
*/
for (i = 0; i < fmc->sdb->len; i++) {
union sdb_record *r;
struct sdb_product *p;
struct sdb_component *c;
r = fmc->sdb->record + i;
if (r->empty.record_type != sdb_type_device)
continue;
c = &r->dev.sdb_component;
p = &c->product;
if (be64_to_cpu(p->vendor_id) != 0xce42LL)
continue;
if (be32_to_cpu(p->device_id) != 0xf19ede1a)
continue;
if (be32_to_cpu(p->version) < 3) {
dev_err(dev, "Core version %i < 3; refusing to work\n",
be32_to_cpu(p->version));
return -EINVAL;
}
break;
}
if (i == fmc->sdb->len)
dev_warn(dev, "Can't find 0xf19ede1a dev for version check\n");
/* First, hardware reset */
fd_do_reset(fd, 1);
......@@ -269,6 +234,12 @@ int fd_probe(struct fmc_device *fmc)
if (ret < 0)
goto err;
ret = device_create_bin_file(&fd->pdev->dev, &dev_attr_eeprom);
if (ret) {
dev_warn(&fd->pdev->dev,
"Cannot create EEPROM sysfs attribute, use default calibration data\n");
}
if (0) {
struct timespec ts1, ts2, ts3;
/* Temporarily, test the time stuff */
......@@ -288,14 +259,8 @@ int fd_probe(struct fmc_device *fmc)
for (ch = 1; ch <= FD_CH_NUMBER; ch++)
fd_gpio_set(fd, FD_GPIO_OUTPUT_EN(ch));
/* Pin the carrier */
if (!try_module_get(fmc->owner))
goto out_mod;
return 0;
out_mod:
fd_irq_exit(fd);
err:
while (--m, --i >= 0)
if (m->exit)
......@@ -303,15 +268,17 @@ err:
return ret;
}
int fd_remove(struct fmc_device *fmc)
int fd_remove(struct platform_device *pdev)
{
struct fd_modlist *m;
struct fd_dev *fd = fmc->mezzanine_data;
struct fd_dev *fd = platform_get_drvdata(pdev);
int i = ARRAY_SIZE(mods);
if (!test_bit(FD_FLAG_INITED, &fd->flags)) /* FIXME: ditch this */
return 0; /* No init, no exit */
device_remove_bin_file(&fd->pdev->dev, &dev_attr_eeprom);
fd_irq_exit(fd);
while (--i >= 0) {
m = mods + i;
......@@ -319,29 +286,32 @@ int fd_remove(struct fmc_device *fmc)
m->exit(fd);
}
/* Release the carrier */
module_put(fmc->owner);
return 0;
}
static struct fmc_fru_id fd_fru_id[] = {
static const struct platform_device_id fd_id[] = {
{
.product_name = "FmcDelay1ns4cha",
.name = "fdelay-tdc-spec",
.driver_data = FD_VER_SPEC,
}, {
.name = "fdelay-tdc-svec",
.driver_data = FD_VER_SVEC,
},
/* TODO we should support different version */
};
static struct fmc_driver fd_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
static struct platform_driver fd_platform_driver = {
.driver = {
.name = KBUILD_MODNAME,
},
.probe = fd_probe,
.remove = fd_remove,
.id_table = {
.fru_id = fd_fru_id,
.fru_id_nr = ARRAY_SIZE(fd_fru_id),
},
.id_table = fd_id,
};
static int fd_init(void)
{
int ret;
......@@ -349,7 +319,7 @@ static int fd_init(void)
ret = fd_zio_register();
if (ret < 0)
return ret;
ret = fmc_driver_register(&fd_drv);
ret = platform_driver_register(&fd_platform_driver);
if (ret < 0) {
fd_zio_unregister();
return ret;
......@@ -359,7 +329,7 @@ static int fd_init(void)
static void fd_exit(void)
{
fmc_driver_unregister(&fd_drv);
platform_driver_unregister(&fd_platform_driver);
fd_zio_unregister();
}
......
......@@ -24,8 +24,6 @@
#include <linux/zio-buffer.h>
#include <linux/zio-trigger.h>
#include <linux/fmc.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
#include "hw/fd_channel_regs.h"
......@@ -216,7 +214,7 @@ static int fd_read_hw_fifo(struct fd_dev *fd)
BUG_ON(diff < 0);
if (diff >= fd_sw_fifo_len)
dev_dbg(fd->fmc->hwdev, "Fifo overflow: "
dev_dbg(&fd->pdev->dev, "Fifo overflow: "
" dropped %i samples (%li -> %li == %li)\n",
fd_sw_fifo_len / 2,
fd->sw_fifo.tail, fd->sw_fifo.head, diff);
......@@ -264,10 +262,9 @@ static void fd_tlet(unsigned long arg)
* you read the whole fifo buffer. It is useless to clear the interrupt
* in EIC_ISR
*/
irqreturn_t fd_irq_handler(int irq, void *dev_id)
irqreturn_t fd_irq_handler(int irq, void *arg)
{
struct fmc_device *fmc = dev_id;
struct fd_dev *fd = fmc->mezzanine_data;
struct fd_dev *fd = arg;
if ((fd_readl(fd, FD_REG_TSBCR) & FD_TSBCR_EMPTY))
goto out_unexpected; /* bah! */
......@@ -282,23 +279,17 @@ irqreturn_t fd_irq_handler(int irq, void *dev_id)
tasklet_schedule(&fd->tlet);
out_unexpected:
/*
* This may be an unexpected interrupt (it may not even be
* use, but still on the SPEC we must ack, or the system locks
* up, entering the interrupt again and again
*/
fmc->op->irq_ack(fmc);
return IRQ_HANDLED;
}
int fd_irq_init(struct fd_dev *fd)
{
struct fmc_device *fmc = fd->fmc;
int rv;
/* Check that the sw fifo size is a power of two */
if (fd_sw_fifo_len & (fd_sw_fifo_len - 1)) {
dev_err(&fd->fmc->dev,
dev_err(&fd->pdev->dev,
"fifo len must be a power of 2 (not %d = 0x%x)\n",
fd_sw_fifo_len, fd_sw_fifo_len);
return -EINVAL;
......@@ -316,21 +307,20 @@ int fd_irq_init(struct fd_dev *fd)
if (fd_timer_period_ms) {
setup_timer(&fd->fifo_timer, fd_tlet, (unsigned long)fd);
fd_timer_period_jiffies = msecs_to_jiffies(fd_timer_period_ms);
dev_dbg(&fd->fmc->dev,"Using a timer for input (%i ms)\n",
dev_dbg(&fd->pdev->dev,"Using a timer for input (%i ms)\n",
jiffies_to_msecs(fd_timer_period_jiffies));
mod_timer(&fd->fifo_timer, jiffies + fd_timer_period_jiffies);
} else {
dev_dbg(fd->fmc->hwdev, "Using interrupts for input\n");
dev_dbg(&fd->pdev->dev, "Using interrupts for input\n");
/* Disable interrupts */
fd_writel(fd, ~0, FD_REG_EIC_IDR);
tasklet_init(&fd->tlet, fd_tlet, (unsigned long)fd);
fmc->irq = fd->fd_regs_base;
rv = fmc->op->irq_request(fmc, fd_irq_handler, "fine-delay", 0);
rv = request_irq(platform_get_irq(fd->pdev, 0), fd_irq_handler, 0,
dev_name(&fd->pdev->dev), fd);
if (rv < 0) {
dev_err(&fd->fmc->dev,
dev_err(&fd->pdev->dev,
"Failed to request the VIC interrupt\n");
goto out_irq_request;
}
......@@ -359,7 +349,6 @@ out_irq_request:
void fd_irq_exit(struct fd_dev *fd)
{
struct fmc_device *fmc = fd->fmc;
/* Stop input */
fd_writel(fd, 0, FD_REG_GCR);
......@@ -368,8 +357,7 @@ void fd_irq_exit(struct fd_dev *fd)
del_timer_sync(&fd->fifo_timer);
} else {
fd_writel(fd, ~0, FD_REG_EIC_IDR);
fmc->irq = fd->fd_regs_base;
fmc->op->irq_free(fmc);
free_irq(platform_get_irq(fd->pdev, 0), fd);
}
kfree(fd->sw_fifo.t);
}
......@@ -23,8 +23,6 @@
#include <linux/zio-buffer.h>
#include <linux/zio-trigger.h>
#include <linux/fmc.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
#include "hw/fd_channel_regs.h"
......@@ -660,7 +658,7 @@ static int fd_zio_output(struct zio_cset *cset)
ctrl = zio_get_ctrl(cset->chan->active_block);
if (fd->verbose > 1) {
dev_info(&fd->fmc->dev,
dev_info(&fd->pdev->dev,
"%s: attrs for cset %i: ", __func__, cset->index);
for (i = FD_ATTR_DEV__LAST; i < FD_ATTR_OUT__LAST; i++)
printk("%08x%c", ctrl->attr_channel.ext_val[i],
......@@ -867,7 +865,6 @@ static void __fd_init_outputs(struct fd_dev *fd)
int fd_zio_init(struct fd_dev *fd)
{
int err = 0;
int dev_id;
fd->hwzdev = zio_allocate_device();
if (IS_ERR(fd->hwzdev))
......@@ -877,9 +874,7 @@ int fd_zio_init(struct fd_dev *fd)
fd->hwzdev->owner = THIS_MODULE;
fd->hwzdev->priv_d = fd;
dev_id = fd->fmc->device_id;
err = zio_register_device(fd->hwzdev, "fd", dev_id);
err = zio_register_device(fd->hwzdev, "fd", fd->pdev->id);
if (err) {
zio_free_device(fd->hwzdev);
return err;
......
#ifndef __FINE_DELAY_H__
#define __FINE_DELAY_H__
#define FDELAY_GATEWARE_NAME_SPEC "fmc/spec-fine-delay.bin"
#define FDELAY_GATEWARE_NAME_SVEC "fmc/svec-fine-delay.bin"
enum fd_versions {
FD_VER_SPEC = 0,
FD_VER_SVEC,
};
enum fd_mem_resource {
FD_MEM_BASE = 0,
};
enum fd_bus_resource {
FD_BUS_FMC_SLOT = 0,
};
enum fd_irq_resource {
FD_IRQ = 0,
};
#define FDELAY_VERSION 2 /* version of the layout of registers */
/*
......@@ -114,8 +128,9 @@ struct fd_time {
#ifdef __KERNEL__ /* All the rest is only of kernel users */
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/fmc.h>
#include <linux/platform_device.h>
#include <linux/version.h>
#include <linux/interrupt.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,25)
#include <linux/math64.h>
#else
......@@ -127,6 +142,13 @@ static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)
}
#endif
struct memory_ops {
u32 (*read)(void *addr);
void (*write)(u32 value, void *addr);
};
extern struct memory_ops memops;
/* This is somehow generic, but I find no better place at this time */
#ifndef SET_HI32
# if BITS_PER_LONG > 32
......@@ -186,9 +208,9 @@ struct fd_sw_fifo {
struct fd_dev {
spinlock_t lock;
unsigned long flags;
int fd_regs_base; /* sdb_find_device(cern, f19ede1a) */
int fd_owregs_base; /* regs_base + 0x500 */
struct fmc_device *fmc;
void *fd_regs_base;
void *fd_owregs_base; /* regs_base + 0x500 */
struct platform_device *pdev;
struct zio_device *zdev, *hwzdev;
struct timer_list fifo_timer;
struct timer_list temp_timer;
......@@ -230,15 +252,15 @@ static inline void fd_split_pico(uint64_t pico,
*frac = (*frac << 12) / 8000;
}
static inline u32 ft_ioread(struct fmctdc_dev *ft, void *addr)
static inline u32 ft_ioread(struct fd_dev *ft, void *addr)
{
return fmc_readl(fd->fmc, addr);
return memops.read(addr);
}
static inline void ft_iowrite(struct fmctdc_dev *ft,
static inline void ft_iowrite(struct fd_dev *ft,
u32 value, void *addr)
{
fmc_writel(fd->fmc, value, addr);
memops.write(value, addr);
}
static inline uint32_t fd_readl(struct fd_dev *fd, unsigned long reg)
......@@ -392,8 +414,9 @@ extern int fd_eeprom_write(struct fd_dev *fd, int i2c_addr, uint32_t offset,
void *buf, size_t size);
/* Function exported by calibration.c */
int fd_handle_eeprom_calibration(struct fd_dev *fd);
signed long fmc_sdb_find_nth_device (struct sdb_array *tree, uint64_t vid, uint32_t did, int *ordinal, uint32_t *size );
extern int fd_handle_calibration(struct fd_dev *fd,
struct fd_calibration *calib);
extern struct bin_attribute dev_attr_eeprom;
#endif /* __KERNEL__ */
#endif /* __FINE_DELAY_H__ */
/*
* Some utility functions not supported in the current version of fmc-bus.
*
* Copyright (C) 2012-2014 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/fmc.h>
#include <linux/fmc-sdb.h>
#include <linux/err.h>
#include <asm/byteorder.h>
#include "fine-delay.h"
typedef int (*sdb_traverse_cb) (uint32_t address, uint32_t size, uint64_t vid, uint32_t did, void *data);
static int traverse_sdb_devices(struct sdb_array *tree,
sdb_traverse_cb cb,
void *data)
{
union sdb_record *r;
struct sdb_product *p;
struct sdb_component *c;
int i, n = tree->len, rv;
uint64_t last, first, vid;
uint32_t did, size;
/* 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]))
{
rv = traverse_sdb_devices ( tree->subtree[i], cb, data );
if(rv > 0)
return 1;
}
if (r->empty.record_type != sdb_type_device)
continue;
/* record is a device?*/
last = __be64_to_cpu(c->addr_last);
first = __be64_to_cpu(c->addr_first);
vid = __be64_to_cpu(p->vendor_id);
did = __be32_to_cpu(p->device_id);
size = (uint32_t) (last + 1 - first);
if (cb (first + tree->baseaddr, size, vid, did, data))
return 1;
}
return 0;
}
struct callback_state {
int n;
int *ordinal;
uint32_t current_address;
uint32_t current_size;
uint64_t did;
uint32_t vid;
};
static int callback (uint32_t address, uint32_t size, uint64_t vid_, uint32_t did_, void *data)
{
struct callback_state *st = (struct callback_state *) data;
if(vid_ == st->vid && did_ == st->did)
{
st->n++;
st->current_address = address;
st->current_size = size;
if(!st->ordinal || st->n == *st->ordinal)
{
return 1;
}
}
return 0; /* continue scanning */
}
/* Finds the Nth SDB device that matches (vid/did) pair, where N <= *ordinal.
If N < *ordinal, the value of N is stored at *ordinal.
This magic is used to handle hybrid bistreams (with two or more different
mezzanines). */
signed long fmc_sdb_find_nth_device (struct sdb_array *tree, uint64_t vid, uint32_t did, int *ordinal, uint32_t *size )
{
struct callback_state st;
st.n = -1;
st.ordinal = ordinal;
st.vid = vid;
st.did = did;
traverse_sdb_devices (tree, callback, &st);
if (st.n >= 0)
{
if(size)
*size = st.current_size;
if(ordinal)
*ordinal = st.n;
return st.current_address;
}
return -ENODEV;
}
......@@ -47,7 +47,7 @@ static int gpio_writel_with_retry(struct fd_dev *fd, int val, int reg)
if(rv >= 0 && (rv == val))
{
if(SPI_RETRIES-1-retries > 0)
dev_info(&fd->fmc->dev,
dev_info(&fd->pdev->dev,
"%s: succeded after %d retries\n",
__func__, SPI_RETRIES - 1 - retries);
return 0;
......@@ -87,7 +87,7 @@ void fd_gpio_set_clr(struct fd_dev *fd, int mask, int set)
int fd_gpio_init(struct fd_dev *fd)
{
int i, val;
struct device *dev = &fd->fmc->dev;
struct device *dev = &fd->pdev->dev;
fd->mcp_iodir = 0xffff;
fd->mcp_olat = 0;
......
/*
* 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/io.h>
#include <linux/time.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/random.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
static void set_sda(struct fd_dev *fd, int val)
{
uint32_t reg;
reg = fd_readl(fd, FD_REG_I2CR) & ~FD_I2CR_SDA_OUT;
if (val)
reg |= FD_I2CR_SDA_OUT;
fd_writel(fd, reg, FD_REG_I2CR);
ndelay(2000);
}
static void set_scl(struct fd_dev *fd, int val)
{
uint32_t reg;
reg = fd_readl(fd, FD_REG_I2CR) & ~FD_I2CR_SCL_OUT;
if (val)
reg |= FD_I2CR_SCL_OUT;
fd_writel(fd, reg, FD_REG_I2CR);
ndelay(2000);
}
static int get_sda(struct fd_dev *fd)
{
return fd_readl(fd, FD_REG_I2CR) & FD_I2CR_SDA_IN ? 1 : 0;
};
static void mi2c_start(struct fd_dev *fd)
{
set_sda(fd, 0);
set_scl(fd, 0);
}
static void mi2c_stop(struct fd_dev *fd)
{
set_sda(fd, 0);
set_scl(fd, 1);
set_sda(fd, 1);
}
int mi2c_put_byte(struct fd_dev *fd, int data)
{
int i;
int ack;
for (i = 0; i < 8; i++, data<<=1) {
set_sda(fd, data & 0x80);
set_scl(fd, 1);
set_scl(fd, 0);
}
set_sda(fd, 1);
set_scl(fd, 1);
ack = get_sda(fd);
set_scl(fd, 0);
set_sda(fd, 0);
return ack ? -EIO : 0; /* ack low == success */
}
int mi2c_get_byte(struct fd_dev *fd, unsigned char *data, int sendack)
{
int i;
int indata = 0;
/* assert: scl is low */
set_scl(fd, 0);
set_sda(fd, 1);
for (i = 0; i < 8; i++) {
set_scl(fd, 1);
indata <<= 1;
if (get_sda(fd))
indata |= 0x01;
set_scl(fd, 0);
}
set_sda(fd, (sendack ? 0 : 1));
set_scl(fd, 1);
set_scl(fd, 0);
set_sda(fd, 0);
*data= indata;
return 0;
}
void mi2c_init(struct fd_dev *fd)
{
set_scl(fd, 1);
set_sda(fd, 1);
}
void mi2c_scan(struct fd_dev *fd)
{
int i;
for(i = 0; i < 256; i += 2) {
mi2c_start(fd);
if(!mi2c_put_byte(fd, i) && fd->verbose)
dev_info(&fd->fmc->dev,
"%s: Found i2c device at 0x%x\n",
KBUILD_MODNAME, i >> 1);
mi2c_stop(fd);
}
}
/* FIXME: this is very inefficient: read several bytes in a row instead */
int fd_eeprom_read(struct fd_dev *fd, 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(fd);
if(mi2c_put_byte(fd, i2c_addr << 1) < 0) {
mi2c_stop(fd);
return -EIO;
}
mi2c_put_byte(fd, (offset >> 8) & 0xff);
mi2c_put_byte(fd, offset & 0xff);
offset++;
mi2c_stop(fd);
mi2c_start(fd);
mi2c_put_byte(fd, (i2c_addr << 1) | 1);
mi2c_get_byte(fd, &c, 0);
*buf8++ = c;
mi2c_stop(fd);
}
return size;
}
int fd_eeprom_write(struct fd_dev *fd, int i2c_addr, uint32_t offset,
void *buf, size_t size)
{
int i, busy;
uint8_t *buf8 = buf;
for(i = 0; i < size; i++) {
mi2c_start(fd);
if(mi2c_put_byte(fd, i2c_addr << 1) < 0) {
mi2c_stop(fd);
return -1;
}
mi2c_put_byte(fd, (offset >> 8) & 0xff);
mi2c_put_byte(fd, offset & 0xff);
mi2c_put_byte(fd, *buf8++);
offset++;
mi2c_stop(fd);
do { /* wait until the chip becomes ready */
mi2c_start(fd);
busy = mi2c_put_byte(fd, i2c_addr << 1);
mi2c_stop(fd);
} while(busy);
}
return size;
}
int fd_i2c_init(struct fd_dev *fd)
{
mi2c_scan(fd);
return 0;
}
void fd_i2c_exit(struct fd_dev *fd)
{
/* nothing to do */
}
......@@ -142,7 +142,7 @@ static int ow_read_block(struct fd_dev *fd, int port, uint8_t *block, int len)
static int ds18x_read_serial(struct fd_dev *fd)
{
if(!ow_reset(fd, 0)) {
dev_err(&fd->fmc->dev, "Failure in resetting one-wire channel\n");
dev_err(&fd->pdev->dev, "Failure in resetting one-wire channel\n");
return -EIO;
}
......@@ -165,7 +165,7 @@ static int ds18x_access(struct fd_dev *fd)
return ow_write_byte(fd, FD_OW_PORT, CMD_ROM_SKIP);
}
out:
dev_err(&fd->fmc->dev, "Failure in one-wire communication\n");
dev_err(&fd->pdev->dev, "Failure in one-wire communication\n");
return -EIO;
}
......@@ -185,7 +185,7 @@ int fd_read_temp(struct fd_dev *fd, int verbose)
int i, temp;
unsigned long j;
uint8_t data[9];
struct device *dev = &fd->fmc->dev;
struct device *dev = &fd->pdev->dev;
/* If first conversion, ask for it first */
if (fd->next_t == 0)
......@@ -238,7 +238,7 @@ int fd_onewire_init(struct fd_dev *fd)
return -EIO;
if (fd->verbose) {
dev_info(&fd->fmc->dev, "%s: Found DS18xx sensor: ", __func__);
dev_info(&fd->pdev->dev, "%s: Found DS18xx sensor: ", __func__);
for (i = 0; i < 8; i++)
printk("%02x%c", fd->ds18_id[i], i == 7 ? '\n' : ':');
}
......
......@@ -39,7 +39,7 @@ int fd_pll_init(struct fd_dev *fd)
int i;
unsigned long j;
const struct ad9516_reg *r;
struct device *dev = &fd->fmc->dev;
struct device *dev = &fd->pdev->dev;
if (pll_writel(fd, 0x99, 0x000) < 0)
goto out;
......
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