Commit 54a77d73 authored by Federico Vaga's avatar Federico Vaga

drv: convert to platform

fmc-bus has been replaced by a simpler platform driver, this means
that:
- IRQ needs to come through the standard kernel API
- Calibration data cannot be read from the fmc-bus, the user (through an
  udev rule for example) should pass this information to the driver
  instance. By default the card will run uncalibrated.
- registration must happen by other means and not from fmc-bus like
  before.
- FPGA must be programmed before, the driver cannot do it anymore
Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent ec1583c6
[submodule "zio"]
path = zio
url = https://ohwr.org/project/zio.git
[submodule "fmc-bus"]
path = fmc-bus
url = https://ohwr.org/project/fmc-bus.git
......@@ -6,7 +6,6 @@ REPO_PARENT ?= $(CURDIR)/..
all: kernel tools
FMC_BUS ?= fmc-bus
ZIO ?= zio
SVEC_SW ?= svec-sw
VMEBUS ?= $(REPO_PARENT)/vmebridge
......@@ -45,7 +44,7 @@ $(DIRS):
$(MAKE) -C $@ $(TARGET)
SUBMOD = $(FMC_BUS_ABS) $(ZIO_ABS)
SUBMOD = $(ZIO_ABS)
prereq_install_warn:
@test -f .prereq_installed || \
......@@ -55,13 +54,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) )
Subproject commit eb86efcf4e19a31a25471c4ddf3fd9fef8df02ec
......@@ -3,7 +3,6 @@ CONFIG_FMC_ADC_SVEC ?= CONFIG_VME
VMEBUS_EXTRA_SYMBOLS-$(CONFIG_FMC_ADC_SVEC) := $(VMEBUS_ABS)/driver/Module.symvers
KBUILD_EXTRA_SYMBOLS := \
$(ZIO_ABS)/Module.symvers \
$(FMC_BUS_ABS)/kernel/Module.symvers \
$(VMEBUS_EXTRA_SYMBOLS-y)
# add versions of supermodule. It is useful when fine-delay-sw is included as sub-module
......@@ -14,23 +13,16 @@ SUBMODULE_VERSIONS-y += MODULE_INFO(version_$(CONFIG_SUPER_REPO),\"$(CONFIG_SUPE
endif
endif
# add versions of used submodules
SUBMODULE_VERSIONS-y += MODULE_INFO(version_fmc_bus,\"$(FMC_BUS_VERSION)\");
SUBMODULE_VERSIONS-y += MODULE_INFO(version_zio,\"$(ZIO_VERSION)\");
ccflags-y += -DADDITIONAL_VERSIONS="$(SUBMODULE_VERSIONS-y)"
# 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 $(LINUXINCLUDE)
ccflags-y += -DGIT_VERSION=\"$(GIT_VERSION)\" \
-I$(ZIO_ABS)/include \
-I $(VMEBUS_ABS)/driver \
-I$(src)
ccflags-$(CONFIG_FMC_ADC_SVEC) += -I$(SVEC_SW_ABS)/kernel
ccflags-$(CONFIG_FMC_ADC_SVEC) += -I$(VMEBUS_ABS)/include
ccflags-$(CONFIG_FMC_ADC_DEBUG) += -DDEBUG
ccflags-$(CONFIG_FMC_ADC_SVEC) += -DCONFIG_FMC_ADC_SVEC
# Extract ZIO minimum compatible version
ccflags-y += -D__ZIO_MIN_MAJOR_VERSION=$(shell echo $(ZIO_VERSION) | cut -d '-' -f 2 | cut -d '.' -f 1; )
......@@ -48,7 +40,6 @@ fmc-adc-100m14b-y += fa-irq.o
fmc-adc-100m14b-y += fa-debug.o
fmc-adc-100m14b-y += onewire.o
fmc-adc-100m14b-y += spi.o
fmc-adc-100m14b-y += fmc-util.o
fmc-adc-100m14b-y += fa-spec-core.o
fmc-adc-100m14b-y += fa-spec-regtable.o
fmc-adc-100m14b-y += fa-spec-dma.o
......
......@@ -7,30 +7,23 @@ REPO_PARENT ?= $(CURDIR)/../..
KVERSION ?= $(shell uname -r)
LINUX ?= /lib/modules/$(KVERSION)/build
FMC_BUS ?= ../fmc-bus
ZIO ?= ../zio
SVEC_SW ?= ../svec-sw
VMEBUS ?= $(REPO_PARENT)/../vmebridge
# FMC_BUS_ABS and ZIO_ABS has to be absolut path,
# due to beeing passed to the Kbuild
FMC_BUS_ABS ?= $(abspath $(FMC_BUS) )
ZIO_ABS ?= $(abspath $(ZIO) )
SVEC_SW_ABS ?= $(abspath $(SVEC_SW) )
VMEBUS_ABS ?= $(abspath $(VMEBUS) )
GIT_VERSION = $(shell git describe --always --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:
$(MAKE) -C $(LINUX) M=$(CURDIR) FMC_BUS_ABS=$(FMC_BUS_ABS) \
ZIO_ABS=$(ZIO_ABS) SVEC_SW_ABS=$(SVEC_SW_ABS) \
$(MAKE) -C $(LINUX) M=$(CURDIR) ZIO_ABS=$(ZIO_ABS) \
VMEBUS_ABS=$(VMEBUS_ABS) modules
install modules_install: modules
......
......@@ -98,12 +98,13 @@ static void fa_calib_cpu_to_le16s(struct fa_calib *calib)
cpu_to_le16s(p + i); /* s == in situ */
}
void fa_read_eeprom_calib(struct fa_dev *fa)
void fa_identity_calib_set(struct fa_dev *fa)
{
/* Retrieve calibration data from the eeprom, then verify it */
memcpy(&fa->calib, fa->fmc->eeprom + FA_CAL_OFFSET, sizeof(fa->calib));
memcpy(&fa->calib, &fa_identity_calib, sizeof(fa->calib));
fa_calib_le16_to_cpus(&fa->calib);
fa_verify_calib(&fa->fmc->dev, &fa->calib, &fa_identity_calib);
fa_verify_calib(&fa->pdev->dev, &fa->calib, &fa_identity_calib);
dev_info(fa->msgdev, "%s succeeds.\n", __func__);
}
/**
......
......@@ -11,16 +11,18 @@
#include "fmc-adc-100m14b4cha.h"
/* Module parameters */
static struct fmc_driver fa_dev_drv;
FMC_PARAM_BUSID(fa_dev_drv);
FMC_PARAM_GATEWARE(fa_dev_drv);
static int fa_enable_test_data_fpga;
module_param_named(enable_test_data_fpga, fa_enable_test_data_fpga, int, 0444);
int fa_enable_test_data_adc = 0;
module_param_named(enable_test_data_adc, fa_enable_test_data_adc, int, 0444);
struct fa_memory_ops memops = {
.read = NULL,
.write = NULL,
};
static const int zfad_hw_range[] = {
[FA100M14B4C_RANGE_10V_CAL] = 0x44,
[FA100M14B4C_RANGE_1V_CAL] = 0x40,
......@@ -333,66 +335,27 @@ int zfad_fsm_command(struct fa_dev *fa, uint32_t command)
return 0;
}
/* Extract from SDB the base address of the core components */
/* which are not carrier specific */
static int __fa_sdb_get_device(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
int ret;
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;
}
if (ret < 0) {
dev_err(fa->msgdev,
"%s: no SDB in the bitstream."
"Are you sure you've provided the correct one?\n",
KBUILD_MODNAME);
return ret;
}
/* Now use SDB to find the base addresses */
fa->fa_irq_vic_base = fmc_find_sdb_device(fmc->sdb, 0xce42,
0x13, NULL);
fa->fa_adc_csr_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x608,
fmc->slot_id, NULL);
fa->fa_irq_adc_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x26ec6086,
fmc->slot_id, NULL);
fa->fa_utc_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x604, fmc->slot_id, NULL);
fa->fa_spi_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42, 0xe503947e,
fmc->slot_id, NULL);
fa->fa_ow_base = fmc_find_sdb_device_ext(fmc->sdb, 0xce42, 0x779c5443,
fmc->slot_id, NULL);
return ret;
}
/*
* Specific check and init
*/
static int __fa_init(struct fa_dev *fa)
{
struct device *hwdev = fa->fmc->hwdev;
struct zio_device *zdev = fa->zdev;
int i, addr;
/* Check if hardware supports 64-bit DMA */
if (dma_set_mask(hwdev, DMA_BIT_MASK(64))) {
if (dma_set_mask(fa->pdev->dev.parent, DMA_BIT_MASK(64))) {
/* Check if hardware supports 32-bit DMA */
if (dma_set_mask(hwdev, DMA_BIT_MASK(32))) {
dev_err(fa->msgdev, "32-bit DMA addressing not available\n");
if (dma_set_mask(fa->pdev->dev.parent, DMA_BIT_MASK(32))) {
dev_err(fa->msgdev,
"32-bit DMA addressing not available\n");
return -EINVAL;
}
}
/* Use identity calibration */
fa_read_eeprom_calib(fa);
fa_identity_calib_set(fa);
fa->mshot_max_samples = fa_readl(fa, fa->fa_adc_csr_base,
&zfad_regs[ZFA_MULT_MAX_SAMP]);
......@@ -456,87 +419,130 @@ static struct fa_modlist mods[] = {
{"debug", fa_debug_init, fa_debug_exit},
};
static int fa_resource_validation(struct platform_device *pdev)
{
struct resource *r;
r = platform_get_resource(pdev, IORESOURCE_IRQ, ADC_IRQ_TRG);
if (!r) {
dev_err(&pdev->dev,
"The ADC needs an interrupt number for the IRQ\n");
return -ENXIO;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, ADC_MEM_BASE);
if (!r) {
dev_err(&pdev->dev,
"The ADC needs base address\n");
return -ENXIO;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, ADC_CARR_MEM_BASE);
if (!r) {
dev_err(&pdev->dev,
"The ADC needs the carrier base address\n");
return -ENXIO;
}
r = platform_get_resource(pdev, IORESOURCE_BUS, ADC_BUS_FMC_SLOT);
if (!r) {
dev_err(&pdev->dev,
"The ADC needs to be assigned to an FMC slot\n");
return -ENXIO;
}
/* Special Configurations */
switch (pdev->id_entry->driver_data) {
case ADC_VER_SPEC:
r = platform_get_resource(pdev, IORESOURCE_IRQ, ADC_IRQ_DMA);
if (!r) {
dev_err(&pdev->dev,
"The ADC needs an interrupt number for the DMA\n");
return -ENXIO;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, ADC_CARR_DMA);
if (!r) {
dev_err(&pdev->dev,
"The ADC needs address to SPEC DMA engine\n");
return -ENXIO;
}
break;
#ifdef CONFIG_FMC_ADC_SVEC
case ADC_VER_SVEC:
r = platform_get_resource(pdev, IORESOURCE_BUS,
ADC_CARR_VME_ADDR);
if (!r) {
dev_err(&pdev->dev,
"The ADC needs a DMA ADDR register\n");
return -ENXIO;
}
break;
#endif
default:
break;
}
return 0;
}
/* probe and remove are called by fa-spec.c */
int fa_probe(struct fmc_device *fmc)
int fa_probe(struct platform_device *pdev)
{
struct fa_modlist *m = NULL;
struct fa_dev *fa;
struct resource *r;
int err, i = 0;
char *fwname;
/* Validate the new FMC device */
i = fmc_validate(fmc, &fa_dev_drv);
if (i < 0) {
dev_info(&fmc->dev, "not using \"%s\" according to "
"modparam\n", KBUILD_MODNAME);
return -ENODEV;
}
err = fa_resource_validation(pdev);
if (err)
return err;
/* Driver data */
fa = devm_kzalloc(&fmc->dev, sizeof(struct fa_dev), GFP_KERNEL);
fa = devm_kzalloc(&pdev->dev, sizeof(struct fa_dev), GFP_KERNEL);
if (!fa)
return -ENOMEM;
fmc_set_drvdata(fmc, fa);
fa->fmc = fmc;
fa->msgdev = &fa->fmc->dev;
/* apply carrier-specific hacks and workarounds */
fa->carrier_op = NULL;
if (!strcmp(fmc->carrier_name, "SPEC")) {
platform_set_drvdata(pdev, fa);
fa->pdev = pdev;
fa->msgdev = &fa->pdev->dev;
/* Assign IO operation */
switch (pdev->id_entry->driver_data) {
case ADC_VER_SPEC:
memops.read = ioread32;
memops.write = iowrite32;
fa->carrier_op = &fa_spec_op;
} else if (!strcmp(fmc->carrier_name, "SVEC")) {
break;
#ifdef CONFIG_FMC_ADC_SVEC
case ADC_VER_SVEC:
memops.read = ioread32be;
memops.write = iowrite32be;
fa->carrier_op = &fa_svec_op;
break;
#endif
default:
dev_err(fa->msgdev, "Unknow version %lu\n",
pdev->id_entry->driver_data);
return -EINVAL;
}
/*
* Check if carrier operations exists. Otherwise it means that the
* driver was compiled without enable any carrier, so it cannot work
*/
if (!fa->carrier_op) {
dev_err(fa->msgdev,
"This binary doesn't support the '%s' carrier\n",
fmc->carrier_name);
return -ENODEV;
}
r = platform_get_resource(pdev, IORESOURCE_MEM, ADC_MEM_BASE);
fa->fa_top_level = ioremap(r->start, resource_size(r));
fa->fa_adc_csr_base = fa->fa_top_level + 0x1000;
fa->fa_irq_adc_base = fa->fa_top_level + 0x1500;
fa->fa_ow_base = fa->fa_top_level + 0x1700;
fa->fa_spi_base = fa->fa_top_level + 0x1800;
fa->fa_utc_base = fa->fa_top_level + 0x1900;
/*
* 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 || fa_dev_drv.gw_n) {
if (fa_dev_drv.gw_n)
fwname = ""; /* reprogram will pick from module parameter */
else
fwname = fa->carrier_op->get_gwname();
/* We first write a new binary (and lm32) within the carrier */
err = fmc_reprogram(fmc, &fa_dev_drv, fwname, 0x0);
if (err) {
dev_err(fa->msgdev, "write firmware \"%s\": error %i\n",
fwname, err);
goto out;
}
} else {
dev_info(fa->msgdev,
"Gateware already there. Set the \"gateware\" parameter to overwrite the current gateware\n");
}
/* Extract whisbone core base address fron SDB */
err = __fa_sdb_get_device(fa);
if (err < 0)
goto out;
r = platform_get_resource(fa->pdev, IORESOURCE_MEM, ADC_CARR_MEM_BASE);
fa->fa_carrier_csr_base = ioremap(r->start, resource_size(r));
err = fa->carrier_op->init(fa);
if (err < 0)
goto out;
err = fa->carrier_op->reset_core(fa);
if (err < 0)
goto out;
/* init all subsystems */
for (i = 0, m = mods; i < ARRAY_SIZE(mods); i++, m++) {
dev_dbg(fa->msgdev, "Calling init for \"%s\"\n", m->name);
......@@ -556,14 +562,8 @@ int fa_probe(struct fmc_device *fmc)
if (err < 0)
goto out_irq;
/* Pin the carrier */
if (!try_module_get(fmc->owner))
goto out_mod;
return 0;
out_mod:
fa_free_irqs(fa);
out_irq:
out:
while (--m, --i >= 0)
......@@ -572,9 +572,9 @@ out:
return err;
}
int fa_remove(struct fmc_device *fmc)
int fa_remove(struct platform_device *pdev)
{
struct fa_dev *fa = fmc_get_drvdata(fmc);
struct fa_dev *fa = platform_get_drvdata(pdev);
struct fa_modlist *m;
int i = ARRAY_SIZE(mods);
......@@ -589,27 +589,31 @@ int fa_remove(struct fmc_device *fmc)
fa->carrier_op->exit(fa);
/* Release the carrier */
module_put(fmc->owner);
return 0;
}
static struct fmc_fru_id fa_fru_id[] = {
static const struct platform_device_id fa_id[] = {
{
.name = "adc-100m-spec",
.driver_data = ADC_VER_SPEC,
},
#ifdef CONFIG_FMC_ADC_SVEC
{
.product_name = "FmcAdc100m14b4cha",
.name = "adc-100m-svec",
.driver_data = ADC_VER_SVEC,
},
#endif
/* TODO we should support different version */
};
static struct fmc_driver fa_dev_drv = {
.version = FMC_VERSION,
.driver.name = KBUILD_MODNAME,
static struct platform_driver fa_dev_drv = {
.driver = {
.name = KBUILD_MODNAME,
},
.probe = fa_probe,
.remove = fa_remove,
.id_table = {
.fru_id = fa_fru_id,
.fru_id_nr = ARRAY_SIZE(fa_fru_id),
},
.id_table = fa_id,
};
static int fa_init(void)
......@@ -637,7 +641,7 @@ static int fa_init(void)
goto out2;
/* Finally the fmc driver, whose probe instantiates zio devices */
ret = fmc_driver_register(&fa_dev_drv);
ret = platform_driver_register(&fa_dev_drv);
if (ret)
goto out3;
......@@ -655,7 +659,7 @@ out1:
static void fa_exit(void)
{
fmc_driver_unregister(&fa_dev_drv);
platform_driver_unregister(&fa_dev_drv);
fa_zio_unregister();
fa_trig_exit();
if (fa_workqueue != NULL)
......
......@@ -11,6 +11,7 @@
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include "fmc-adc-100m14b4cha.h"
#include "fa-spec.h"
......@@ -271,9 +272,6 @@ end:
dev_dbg(fa->msgdev, "Automatic start\n");
zfad_fsm_command(fa, FA100M14B4C_CMD_START);
}
/* ack the irq */
fmc_irq_ack(fa->fmc);
}
/*
......@@ -284,25 +282,23 @@ end:
* Get irq and clear the register. To clear an interrupt we have to write 1
* on the handled interrupt. We handle all interrupt so we clear all interrupts
*/
static void fa_get_irq_status(struct fa_dev *fa, int irq_core_base,
uint32_t *irq_status)
static void fa_get_irq_status(struct fa_dev *fa, uint32_t *irq_status)
{
/* Get current interrupts status */
*irq_status = fa_readl(fa, irq_core_base, &zfad_regs[ZFA_IRQ_ADC_SRC]);
*irq_status = fa_readl(fa, fa->fa_irq_adc_base, &zfad_regs[ZFA_IRQ_ADC_SRC]);
dev_dbg(fa->msgdev,
"IRQ 0x%x fired an interrupt. IRQ status register: 0x%x\n",
irq_core_base, *irq_status);
"IRQ fired an interrupt. IRQ status register: 0x%x\n",
*irq_status);
if (*irq_status)
/* Clear current interrupts status */
fa_writel(fa, irq_core_base, &zfad_regs[ZFA_IRQ_ADC_SRC],
fa_writel(fa, fa->fa_irq_adc_base, &zfad_regs[ZFA_IRQ_ADC_SRC],
*irq_status);
}
/*
* fa_irq_handler
* @irq:
* @ptr: pointer to fmc_device
* @arg: pointer to fa_dev
*
* The ADC svec firmware fires interrupt from a single wishbone core
* and throught the VIC ACQ_END and TRIG events. Note about "TRIG"
......@@ -314,22 +310,20 @@ static void fa_get_irq_status(struct fa_dev *fa, int irq_core_base,
* of small number of samples and makes the retry loop in the hanlder
* obsolete.
*/
irqreturn_t fa_irq_handler(int irq_core_base, void *dev_id)
irqreturn_t fa_irq_handler(int irq, void *arg)
{
struct fmc_device *fmc = dev_id;
struct fa_dev *fa = fmc_get_drvdata(fmc);
struct fa_dev *fa = arg;
struct zio_cset *cset = fa->zdev->cset;
uint32_t status;
unsigned long flags;
struct zfad_block *zfad_block;
/* irq to handle */
fa_get_irq_status(fa, irq_core_base, &status);
fa_get_irq_status(fa, &status);
if (!status)
return IRQ_NONE; /* No interrupt fired by this mezzanine */
dev_dbg(fa->msgdev, "Handle ADC interrupts fmc slot: %d\n",
fmc->slot_id);
dev_dbg(fa->msgdev, "Handle ADC interrupts\n");
if (status & FA_IRQ_ADC_ACQ_END) {
/*
......@@ -352,14 +346,12 @@ irqreturn_t fa_irq_handler(int irq_core_base, void *dev_id)
queue_work(fa_workqueue, &fa->irq_work);
/* register the core firing the IRQ in order to */
/* check right IRQ seq.: ACQ_END followed by DMA_END */
fa->last_irq_core_src = irq_core_base;
} else /* current Acquiistion has been stopped */
fmc_irq_ack(fmc);
fa->last_irq_core_src = irq;
}
} else { /* unexpected interrupt we have to ack anyway */
dev_err(fa->msgdev,
"%s unexpected interrupt 0x%x\n",
__func__, status);
fmc_irq_ack(fmc);
}
return IRQ_HANDLED;
......@@ -368,31 +360,18 @@ irqreturn_t fa_irq_handler(int irq_core_base, void *dev_id)
int fa_setup_irqs(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
struct resource *r;
int err;
/* Request IRQ */
dev_dbg(fa->msgdev, "%s request irq fmc slot: %d\n",
__func__, fa->fmc->slot_id);
/* VIC svec setup */
fa_writel(fa, fa->fa_irq_vic_base,
&zfad_regs[ZFA_IRQ_VIC_CTRL],
0x3);
fa_writel(fa, fa->fa_irq_vic_base,
&zfad_regs[ZFA_IRQ_VIC_ENABLE_MASK],
0x3);
/* trick : vic needs the base address of teh core firing the irq
* It cannot provided throught irq_request() call therefore the trick
* is to set it by means of the field irq provided by the fmc device
*/
fmc->irq = fa->fa_irq_adc_base;
err = fmc_irq_request(fmc, fa_irq_handler,
"fmc-adc-100m14b",
0 /*VIC is used */);
if (err) {
dev_err(fa->msgdev, "can't request irq %i (error %i)\n",
fa->fmc->irq, err);
dev_dbg(fa->msgdev, "Request irq\n");
r = platform_get_resource(fa->pdev, IORESOURCE_IRQ, ADC_IRQ_TRG);
err = request_any_context_irq(r->start, fa_irq_handler, 0,
r->name, fa);
if (err < 0) {
dev_err(fa->msgdev, "can't request irq %lli (error %i)\n",
r->start, err);
return err;
}
/* workqueue is required to execute DMA transaction */
......@@ -409,8 +388,6 @@ int fa_setup_irqs(struct fa_dev *fa)
int fa_free_irqs(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
/*
* When we unload the driver the FPGA is still running so it may
* rises interrupts. Disable IRQs in order to prevent spurious
......@@ -423,16 +400,14 @@ int fa_free_irqs(struct fa_dev *fa)
fa->carrier_op->free_irqs(fa);
/* Release ADC IRQs */
fmc->irq = fa->fa_irq_adc_base;
fmc_irq_free(fmc);
free_irq(platform_get_irq(fa->pdev, ADC_IRQ_TRG), fa);
return 0;
}
int fa_enable_irqs(struct fa_dev *fa)
{
dev_dbg(fa->msgdev, "%s Enable interrupts fmc slot:%d\n",
__func__, fa->fmc->slot_id);
dev_dbg(fa->msgdev, "Enable interrupts\n");
fa_writel(fa, fa->fa_irq_adc_base,
&zfad_regs[ZFA_IRQ_ADC_ENABLE_MASK],
......@@ -445,8 +420,7 @@ int fa_enable_irqs(struct fa_dev *fa)
int fa_disable_irqs(struct fa_dev *fa)
{
dev_dbg(fa->msgdev, "%s Disable interrupts fmc slot:%d\n",
__func__, fa->fmc->slot_id);
dev_dbg(fa->msgdev, "Disable interrupts\n");
fa_writel(fa, fa->fa_irq_adc_base,
&zfad_regs[ZFA_IRQ_ADC_DISABLE_MASK],
......
......@@ -11,31 +11,22 @@
#include "fmc-adc-100m14b4cha.h"
#include "fa-spec.h"
static char *fa_spec_get_gwname(void)
{
return FA_GATEWARE_SPEC;
}
static int fa_spec_init(struct fa_dev *fa)
{
struct resource *r;
struct fa_spec_data *cdata;
uint32_t val;
fa->fa_carrier_csr_base = fmc_find_sdb_device(fa->fmc->sdb, 0xce42,
0x603, NULL);
cdata = kzalloc(sizeof(struct fa_spec_data), GFP_KERNEL);
if (!cdata)
return -ENOMEM;
/* SDB carrier specific */
cdata->fa_dma_base =
fmc_find_sdb_device(fa->fmc->sdb, 0xce42, 0x601, NULL);
cdata->fa_irq_dma_base =
fmc_find_sdb_device(fa->fmc->sdb, 0xce42, 0xd5735ab4, NULL);
r = platform_get_resource(fa->pdev, IORESOURCE_MEM, ADC_CARR_DMA);
cdata->fa_dma_base = ioremap(r->start, resource_size(r));
cdata->fa_irq_dma_base = cdata->fa_dma_base + 0x0200;
dev_info(fa->msgdev,
"Spec Base addrs: irq_dmma:0x%x, dma_ctrl:0x%x, csr:0x%x\n",
"Spec Base addrs: irq_dmma: %p, dma_ctrl: %p, csr: %p\n",
cdata->fa_irq_dma_base, cdata->fa_dma_base,
fa->fa_carrier_csr_base);
......@@ -94,42 +85,40 @@ static void fa_spec_exit(struct fa_dev *fa)
}
/* Unfortunately, on the spec this is GPIO9, i.e. IRQ(1) */
static struct fmc_gpio fa_gpio_on[] = {
{
.gpio = FMC_GPIO_IRQ(0),
.mode = GPIOF_DIR_IN,
.irqmode = IRQF_TRIGGER_RISING,
}
};
static struct fmc_gpio fa_gpio_off[] = {
{
.gpio = FMC_GPIO_IRQ(0),
.mode = GPIOF_DIR_IN,
.irqmode = 0,
}
};
/* FIXME find a way to get rid of fmc here
* This is used only by the SPEC design, is it not possible to avoid it
* and let the VHDL configure the GPIO?
*/
/* static struct fmc_gpio fa_gpio_on[] = { */
/* { */
/* .gpio = FMC_GPIO_IRQ(0), */
/* .mode = GPIOF_DIR_IN, */
/* .irqmode = IRQF_TRIGGER_RISING, */
/* } */
/* }; */
/* static struct fmc_gpio fa_gpio_off[] = { */
/* { */
/* .gpio = FMC_GPIO_IRQ(0), */
/* .mode = GPIOF_DIR_IN, */
/* .irqmode = 0, */
/* } */
/* }; */
static int fa_spec_setup_irqs(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
struct fa_spec_data *spec_data = fa->carrier_data;
struct resource *r;
int err;
/* Request IRQ
* trick : vic needs the base address of teh core firing the irq
* It cannot provided throught irq_request() call therefore the trick
* is to set it by means of the field irq provided by the fmc device
*/
fmc->irq = spec_data->fa_irq_dma_base;
err = fmc_irq_request(fmc, fa_spec_irq_handler,
"fmc-adc-100m14b", 0);
if (err) {
dev_err(fa->msgdev, "can't request irq 0x%x (error %i)\n",
fmc->irq, err);
r = platform_get_resource(fa->pdev, IORESOURCE_IRQ, ADC_IRQ_DMA);
err = request_any_context_irq(r->start, fa_spec_irq_handler, 0,
r->name, fa);
if (err < 0) {
dev_err(fa->msgdev, "can't request irq 0x%llx (error %i)\n",
r->start, err);
return err;
}
fmc_gpio_config(fmc, fa_gpio_on, ARRAY_SIZE(fa_gpio_on));
//fmc_gpio_config(fmc, fa_gpio_on, ARRAY_SIZE(fa_gpio_on));
dev_info(fa->msgdev, "spec::%s successfully executed\n", __func__);
/* Add SPEC specific IRQ sources to listen */
......@@ -140,14 +129,10 @@ static int fa_spec_setup_irqs(struct fa_dev *fa)
static int fa_spec_free_irqs(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
struct fa_spec_data *spec_data = fa->carrier_data;
/* Release DMA IRQs */
fmc->irq = spec_data->fa_irq_dma_base;
fmc_irq_free(fmc);
free_irq(platform_get_irq(fa->pdev, ADC_IRQ_DMA), fa);
fmc_gpio_config(fmc, fa_gpio_off, ARRAY_SIZE(fa_gpio_off));
/* fmc_gpio_config(fmc, fa_gpio_off, ARRAY_SIZE(fa_gpio_off)); */
return 0;
}
......@@ -180,7 +165,6 @@ static int fa_spec_ack_irq(struct fa_dev *fa, int irq_id)
}
struct fa_carrier_op fa_spec_op = {
.get_gwname = fa_spec_get_gwname,
.init = fa_spec_init,
.reset_core = fa_spec_reset,
.exit = fa_spec_exit,
......
......@@ -94,7 +94,7 @@ int fa_spec_dma_start(struct zio_cset *cset)
for (i = 0; i < fa->n_shots; ++i)
blocks[i] = zfad_block[i].block;
fa->zdma = zio_dma_alloc_sg(interleave, fa->fmc->hwdev, blocks,
fa->zdma = zio_dma_alloc_sg(interleave, fa->pdev->dev.parent, blocks,
fa->n_shots, GFP_ATOMIC);
if (IS_ERR(fa->zdma))
return PTR_ERR(fa->zdma);
......
......@@ -25,18 +25,20 @@
* Get irq and clear the register. To clear an interrupt we have to write 1
* on the handled interrupt. We handle all interrupt so we clear all interrupts
*/
static void fa_get_irq_status(struct fa_dev *fa, int irq_core_base,
uint32_t *irq_status)
static void fa_get_irq_status(struct fa_dev *fa, uint32_t *irq_status)
{
struct fa_spec_data *cdata = fa->carrier_data;
/* Get current interrupts status */
*irq_status = fa_readl(fa, irq_core_base,
*irq_status = fa_readl(fa, cdata->fa_irq_dma_base,
&fa_spec_regs[ZFA_IRQ_DMA_SRC]);
dev_dbg(fa->msgdev,
"core DMA: 0x%x fired an interrupt. IRQ status register: 0x%x\n",
irq_core_base, *irq_status);
"core DMA: %p fired an interrupt. IRQ status register: 0x%x\n",
cdata->fa_irq_dma_base, *irq_status);
if (*irq_status)
/* Clear current interrupts status */
fa_writel(fa, irq_core_base,
fa_writel(fa, cdata->fa_irq_dma_base,
&fa_spec_regs[ZFA_IRQ_DMA_SRC], *irq_status);
}
......@@ -55,15 +57,14 @@ static void fa_get_irq_status(struct fa_dev *fa, int irq_core_base,
* of small number of samples and makes the retry loop in the hanlder
* obsolete.
*/
irqreturn_t fa_spec_irq_handler(int irq_core_base, void *ptr)
irqreturn_t fa_spec_irq_handler(int irq, void *arg)
{
struct fmc_device *fmc = ptr;
struct fa_dev *fa = fmc_get_drvdata(fmc);
struct fa_dev *fa = arg;
struct zio_cset *cset = fa->zdev->cset;
uint32_t status;
/* irq to handle */
fa_get_irq_status(fa, irq_core_base, &status);
fa_get_irq_status(fa, &status);
if (!status)
return IRQ_NONE;
......@@ -81,15 +82,16 @@ irqreturn_t fa_spec_irq_handler(int irq_core_base, void *ptr)
goto out;
}
if (unlikely(fa->last_irq_core_src == irq_core_base)) {
WARN(1, "Cannot handle two consecutives %s interrupt."
"The ADC doesn't behave properly\n",
(irq_core_base == fa->fa_irq_adc_base) ? "ACQ" : "DMA");
/* Stop Acquisition, ADC it is not working properly */
zfad_fsm_command(fa, FA100M14B4C_CMD_STOP);
fa->last_irq_core_src = FA_SPEC_IRQ_SRC_NONE;
goto out;
}
/* FIXME handle it better */
/* if (unlikely(fa->last_irq_core_src == irq_core_base)) { */
/* WARN(1, "Cannot handle two consecutives %s interrupt." */
/* "The ADC doesn't behave properly\n", */
/* (irq_core_base == fa->fa_irq_adc_base) ? "ACQ" : "DMA"); */
/* /\* Stop Acquisition, ADC it is not working properly *\/ */
/* zfad_fsm_command(fa, FA100M14B4C_CMD_STOP); */
/* fa->last_irq_core_src = FA_SPEC_IRQ_SRC_NONE; */
/* goto out; */
/* } */
dev_dbg(fa->msgdev, "Handle ADC interrupts\n");
......@@ -100,7 +102,8 @@ irqreturn_t fa_spec_irq_handler(int irq_core_base, void *ptr)
/* register the core which just fired the IRQ */
/* check proper sequence of IRQ in case of multi IRQ (ACQ + DMA)*/
fa->last_irq_core_src = irq_core_base;
/* FIXME */
/* fa->last_irq_core_src = irq_core_base; */
out:
/*
......@@ -111,8 +114,5 @@ out:
cset->flags &= ~ZIO_CSET_HW_BUSY;
spin_unlock(&cset->lock);
/* ack the irq */
fmc_irq_ack(fa->fmc);
return IRQ_HANDLED;
}
......@@ -85,8 +85,8 @@ enum fa_spec_irq {
/* specific carrier data */
struct fa_spec_data {
/* DMA attributes */
unsigned int fa_dma_base;
unsigned int fa_irq_dma_base;
void *fa_dma_base;
void *fa_irq_dma_base;
struct fa_dma_item *items;
dma_addr_t dma_list_item;
unsigned int n_dma_err; /* statistics */
......
......@@ -8,51 +8,44 @@
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <svec.h>
#include "fmc-adc-100m14b4cha.h"
#include "fa-svec.h"
static char *fa_svec_get_gwname(void)
{
return FA_GATEWARE_SVEC;
}
static int fa_svec_init(struct fa_dev *fa)
{
struct fmc_device *fmc = fa->fmc;
struct fa_svec_data *cdata;
struct svec_dev *svec = fmc->carrier_data;
struct resource *r;
unsigned int res_i;
cdata = kzalloc(sizeof(struct fa_svec_data), GFP_KERNEL);
if (!cdata)
return -ENOMEM;
cdata->vme_base = svec->cfg_cur.vme_base;
fa->fa_carrier_csr_base = fmc_find_sdb_device(fmc->sdb, 0xce42,
0x6603, NULL);
cdata->fa_dma_ddr_addr = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x10006611,
fmc->slot_id, NULL);
cdata->fa_dma_ddr_data = fmc_find_sdb_device_ext(fmc->sdb, 0xce42,
0x10006610,
fmc->slot_id, NULL);
/* Reset the FMC slot*/
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_svec_regfield[FA_CAR_FMC0_RES + fmc->slot_id], 1);
r = platform_get_resource(fa->pdev, IORESOURCE_BUS, ADC_CARR_VME_ADDR);
cdata->vme_ddr_data = r->start;
r = platform_get_resource(fa->pdev, IORESOURCE_BUS, ADC_BUS_FMC_SLOT);
switch(r->start) {
case 1:
res_i = FA_CAR_FMC0_RES;
break;
case 2:
res_i = FA_CAR_FMC1_RES;
break;
default:
return -EINVAL;
}
cdata->fa_dma_ddr_addr = fa->fa_top_level + 0x2000;
fa_writel(fa, fa->fa_carrier_csr_base, &fa_svec_regfield[res_i], 1);
mdelay(50);
fa_writel(fa, fa->fa_carrier_csr_base,
&fa_svec_regfield[FA_CAR_FMC0_RES + fmc->slot_id], 0);
fa_writel(fa, fa->fa_carrier_csr_base, &fa_svec_regfield[res_i], 0);
mdelay(50);
/* register carrier data */
fa->carrier_data = cdata;
return 0;
}
static int fa_svec_reset(struct fa_dev *fa)
{
return 0;
}
......@@ -62,9 +55,7 @@ static void fa_svec_exit(struct fa_dev *fa)
}
struct fa_carrier_op fa_svec_op = {
.get_gwname = fa_svec_get_gwname,
.init = fa_svec_init,
.reset_core = fa_svec_reset,
.exit = fa_svec_exit,
.dma_start = fa_svec_dma_start,
.dma_done = fa_svec_dma_done,
......
......@@ -93,9 +93,6 @@ int fa_svec_dma_start(struct zio_cset *cset)
struct zfad_block *fa_dma_block = interleave->priv_d;
int i;
struct vme_dma desc; /* Vme driver DMA structure */
unsigned long vme_addr;
vme_addr = svec_data->vme_base + svec_data->fa_dma_ddr_data;
/*
* write the data address in the ddr_addr register: this
......@@ -107,14 +104,18 @@ int fa_svec_dma_start(struct zio_cset *cset)
fa_writel(fa, svec_data->fa_dma_ddr_addr,
&fa_svec_regfield[FA_DMA_DDR_ADDR],
fa_dma_block[0].dev_mem_off/4);
pr_info("%s:%d 0x%x\n", __func__, __LINE__,
fa_dma_block[0].dev_mem_off/4);
/* Execute DMA shot by shot */
for (i = 0; i < fa->n_shots; ++i) {
dev_dbg(fa->msgdev,
dev_info(fa->msgdev,
"configure DMA descriptor shot %d "
"vme addr: 0x%llx destination address: 0x%p len: %d\n",
i, (long long)vme_addr, fa_dma_block[i].block->data,
i, (long long)svec_data->vme_ddr_data,
fa_dma_block[i].block->data,
(int)fa_dma_block[i].block->datalen);
build_dma_desc(&desc, vme_addr,
memset(fa_dma_block[i].block->data, 5, fa_dma_block[i].block->datalen);
build_dma_desc(&desc, svec_data->vme_ddr_data,
fa_dma_block[i].block->data,
fa_dma_block[i].block->datalen);
......
......@@ -32,9 +32,8 @@ enum fa_spec_regs_id {
/* specific carrier data */
struct fa_svec_data {
/* DMA attributes */
unsigned long vme_base;
unsigned int fa_dma_ddr_data; /* offset */
unsigned int fa_dma_ddr_addr; /* offset */
unsigned long vme_ddr_data; /* offset */
void *fa_dma_ddr_addr; /* offset */
unsigned int n_dma_err; /* statistics */
};
......
......@@ -169,7 +169,7 @@ static int zfad_conf_set(struct device *dev, struct zio_attribute *zattr,
uint32_t usr_val)
{
struct fa_dev *fa = get_zfadc(dev);
unsigned int baseoff = fa->fa_adc_csr_base;
void *baseoff = fa->fa_adc_csr_base;
struct zio_channel *chan;
int i, range, err = 0, reg_index;
......@@ -322,7 +322,7 @@ static int zfad_info_get(struct device *dev, struct zio_attribute *zattr,
uint32_t *usr_val)
{
struct fa_dev *fa = get_zfadc(dev);
unsigned int baseoff = fa->fa_adc_csr_base;
void *baseoff = fa->fa_adc_csr_base;
int i, reg_index;
i = FA100M14B4C_NCHAN;
......@@ -677,7 +677,7 @@ int fa_zio_init(struct fa_dev *fa)
/* Register the hardware zio_device */
err = zio_register_device(fa->hwzdev, "adc-100m14b",
fa->fmc->device_id);
fa->pdev->id);
if (err) {
dev_err(fa->msgdev, "Cannot register ZIO device fmc-adc-100m14b\n");
zio_free_device(fa->hwzdev);
......
......@@ -36,6 +36,10 @@
#define FA100M14B4C_TRG_POL_CH4 FA100M14B4C_TRG_SRC_CH4
#define FA100M14B4C_TRG_POL_CHx(_x) (FA100M14B4C_TRG_POL_CH1 << ((_x) - 1))
enum fa_versions {
ADC_VER_SPEC = 0,
ADC_VER_SVEC,
};
/*
* Trigger Extended Attribute Enumeration
......@@ -150,9 +154,8 @@ struct fa_calib {
#include <linux/scatterlist.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include <linux/zio.h>
#include <linux/zio-dma.h>
#include <linux/zio-sysfs.h>
......@@ -163,6 +166,29 @@ struct fa_calib {
extern int fa_enable_test_data_adc;
enum fa_irq_resource {
ADC_IRQ_TRG = 0,
ADC_IRQ_DMA,
};
enum fa_mem_resource {
ADC_MEM_BASE = 0,
ADC_CARR_MEM_BASE,
ADC_CARR_DMA, /* SPEC only, remove it when we support DMA engine */
};
enum fa_bus_resource {
ADC_BUS_FMC_SLOT = 0,
ADC_CARR_VME_ADDR,
};
struct fa_memory_ops {
u32 (*read)(void *addr);
void (*write)(u32 value, void *addr);
};
extern struct fa_memory_ops memops;
/*
* ZFA_CHx_MULT : the trick which requires channel regs id grouped and ordered
* address offset between two registers of the same type on consecutive channel
......@@ -369,7 +395,7 @@ struct fa_carrier_op {
/*
* fa_dev: is the descriptor of the FMC ADC mezzanine
*
* @fmc: the pointer to the fmc_device generic structure
* @pdev: the pointer to the fmc_device generic structure
* @zdev: is the pointer to the real zio_device in use
* @hwzdev: is the pointer to the fake zio_device, used to initialize and
* to remove a zio_device
......@@ -383,21 +409,22 @@ struct fa_carrier_op {
*/
struct fa_dev {
struct device *msgdev; /**< device used to print messages */
/* the pointer to the fmc_device generic structure */
struct fmc_device *fmc;
/* the pointer to the platform_device generic structure */
struct platform_device *pdev;
/* the pointer to the real zio_device in use */
struct zio_device *zdev;
/* the pointer to the fake zio_device, used for init/remove */
struct zio_device *hwzdev;
/* carrier common base offset addresses obtained from SDB */
unsigned int fa_adc_csr_base;
unsigned int fa_spi_base;
unsigned int fa_ow_base;
unsigned int fa_carrier_csr_base;
unsigned int fa_irq_vic_base;
unsigned int fa_irq_adc_base;
unsigned int fa_utc_base;
void *fa_adc_csr_base;
void *fa_spi_base;
void *fa_ow_base;
void *fa_top_level;
void *fa_carrier_csr_base;
void *fa_irq_vic_base;
void *fa_irq_adc_base;
void *fa_utc_base;
/* DMA description */
struct zio_dma_sgt *zdma;
......@@ -493,23 +520,23 @@ static inline struct fa_dev *get_zfadc(struct device *dev)
return NULL;
}
static inline u32 fa_ioread(struct fa_dev *fa, unsigned long addr)
static inline u32 fa_ioread(struct fa_dev *fa, void *addr)
{
return fmc_readl(fa->fmc, addr);
return memops.read(addr);
}
static inline void fa_iowrite(struct fa_dev *fa, u32 value, unsigned long addr)
static inline void fa_iowrite(struct fa_dev *fa, u32 value, void *addr)
{
fmc_writel(fa->fmc, value, addr);
memops.write(value, addr);
}
static inline uint32_t fa_readl(struct fa_dev *fa,
unsigned int base_off,
void *base_off,
const struct zfa_field_desc *field)
{
uint32_t cur;
cur = fa_ioread(fa, base_off+field->offset);
cur = fa_ioread(fa, base_off + field->offset);
if (field->is_bitfield) {
/* apply mask and shift right accordlying to the mask */
cur &= field->mask;
......@@ -517,11 +544,12 @@ static inline uint32_t fa_readl(struct fa_dev *fa,
} else {
cur &= field->mask; /* bitwise and with the mask */
}
return cur;
}
static inline void fa_writel(struct fa_dev *fa,
unsigned int base_off,
void *base_off,
const struct zfa_field_desc *field,
uint32_t usr_val)
{
......@@ -536,12 +564,12 @@ static inline void fa_writel(struct fa_dev *fa,
val = usr_val * (field->mask & -(field->mask));
if (val & ~field->mask)
dev_warn(fa->msgdev,
"addr 0x%lx: value 0x%x doesn't fit mask 0x%x\n",
"addr %p: value 0x%x doesn't fit mask 0x%x\n",
base_off+field->offset, val, field->mask);
val &= field->mask;
val |= cur;
}
fa_iowrite(fa, val, base_off+field->offset);
fa_iowrite(fa, val, base_off + field->offset);
}
extern struct bin_attribute dev_attr_calibration;
......@@ -601,13 +629,8 @@ extern int fa_spi_xfer(struct fa_dev *fa, int cs, int num_bits,
extern int fa_spi_init(struct fa_dev *fd);
extern void fa_spi_exit(struct fa_dev *fd);
/* fmc extended function */
signed long fmc_find_sdb_device_ext(struct sdb_array *tree,
uint64_t vid, uint32_t did, int index,
unsigned long *sz);
/* function exporetd by fa-calibration.c */
extern void fa_read_eeprom_calib(struct fa_dev *fa);
extern void fa_identity_calib_set(struct fa_dev *fa);
/* functions exported by fa-debug.c */
extern int fa_debug_init(struct fa_dev *fa);
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Some utility functions not supported in the current version of fmc-bus.
*
* Copyright (C) 2012-2019 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*/
#include <linux/fmc.h>
#include <linux/fmc-sdb.h>
#include <linux/err.h>
#include <asm/byteorder.h>
/* Finds index-th SDB device that matches (vid/did) pair. */
signed long fmc_find_sdb_device_ext(struct sdb_array *tree,
uint64_t vid, uint32_t did, int index,
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;
int ci = 0;
/* 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])) {
/* FIXME: this index SHOULD be recursive, too */
res = fmc_find_sdb_device(tree->subtree[i],
vid, did, sz);
if (res >= 0 && ci++ == index)
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);
if (ci++ == index)
return first + tree->baseaddr;
}
return res;
}
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