Commit cefbf7b2 authored by Tristan Gingold's avatar Tristan Gingold

drivers: add adc-x2

parent e78a5e9f
......@@ -7,16 +7,22 @@ endif
endif
# add versions of used submodules
KBUILD_EXTRA_SYMBOLS += $(FMC_EXTRA_SYMBOLS-y)
ccflags-y += -DADDITIONAL_VERSIONS="$(SUBMODULE_VERSIONS)"
ccflags-y += -DDRV_VERSION=\"$(DRV_VERSION)\"
ccflags-y += -Wall -Werror
ccflags-y += -I$(FMC_ABS)/include
ccflags-y += -I$(ADC_ABS)/software/kernel
obj-m := wrtd-ref-spec150t-adc.o
obj-m += wrtd-ref-svec-tdc-fd.o
obj-m += wrtd-ref-svec-tdc-x2.o
obj-m += wrtd-ref-svec-fd-x2.o
obj-m += wrtd-ref-svec-adc-x2.o
wrtd-ref-spec150t-adc-objs := wrtd-ref-spec150t-adc-core.o
wrtd-ref-svec-tdc-fd-objs := wrtd-ref-svec-tdc-fd-core.o
wrtd-ref-svec-tdc-x2-objs := wrtd-ref-svec-tdc-x2-core.o
wrtd-ref-svec-fd-x2-objs := wrtd-ref-svec-fd-x2-core.o
wrtd-ref-svec-adc-x2-objs := wrtd-ref-svec-adc-x2-core.o
......@@ -7,17 +7,29 @@ REPO_PARENT ?= $(shell /bin/pwd)/../..
LINUX ?= /lib/modules/$(shell uname -r)/build
ifdef REPO_PARENT
FMC ?= $(REPO_PARENT)/fmc-sw
endif
DRV_VERSION := $(shell git describe --always --dirty --long --tags)
FMC_ABS ?= $(abspath $(FMC))
FMC_EXTRA_SYMBOLS-y = $(FMC_ABS)/drivers/fmc/Module.symvers
ADC_ABS ?= $(abspath $(FETCHTO)/fmc-adc-100m14b4cha)
all: modules
.PHONY: all modules clean help install modules_install
modules help modules_install:
$(MAKE) -C $(LINUX) M=$(shell pwd) DRV_VERSION=$(DRV_VERSION) $@
$(MAKE) -C $(LINUX) M=$(shell pwd) DRV_VERSION=$(DRV_VERSION) \
FMC_ABS=$(FMC_ABS) ADC_ABS=$(ADC_ABS) \
FMC_EXTRA_SYMBOLS-y=$(FMC_EXTRA_SYMBOLS-y) $@
install:
$(MAKE) -C $(LINUX) M=$(shell pwd) DRV_VERSION=$(DRV_VERSION) modules_install
$(MAKE) -C $(LINUX) M=$(shell pwd) DRV_VERSION=$(DRV_VERSION) \
FMC_ABS=$(FMC_ABS) ADC_ABS=$(ADC_ABS) modules_install
# be able to run the "clean" rule even if $(LINUX) is not valid
clean:
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/mfd/core.h>
#include <linux/fmc.h>
#include "platform_data/fmc-adc-100m14b4cha.h"
#define SVEC_FMC_SLOTS 2
/*
* From SVEC but we do not want to add a dependency for these 4 registers
* which should never change by design. If they do, and you end up here:
* sorry! It shouldn't have happened.
*/
#define SVEC_BASE_REGS_CSR 0x40UL
#define SVEC_FPGA_CSR_DDR4_ADDR (SVEC_BASE_REGS_CSR + 0x18)
#define SVEC_FPGA_DDR4_DMA (0x2000)
#define SVEC_FPGA_CSR_DDR5_ADDR (SVEC_BASE_REGS_CSR + 0x1C)
#define SVEC_FPGA_DDR5_DMA (0x3000)
enum wrtd_adcx2_dev_offsets {
WRTD_ADCX2_ADC1_MEM_START = 0x00002000,
WRTD_ADCX2_ADC1_MEM_END = 0x00003fff,
WRTD_ADCX2_ADC2_MEM_START = 0x00004000,
WRTD_ADCX2_ADC2_MEM_END = 0x00005fff,
WRTD_ADCX2_TRTL_MEM_START = 0x0001C000,
WRTD_ADCX2_TRTL_MEM_END = 0x0003bfff,
};
static inline struct platform_device *platform_device_register_resndata_mask(
struct device *parent, const char *name, int id,
const struct resource *res, unsigned int num,
const void *data, size_t size, u64 dma_mask) {
struct platform_device_info pdevinfo = {
.parent = parent,
.name = name,
.id = id,
.res = res,
.num_res = num,
.data = data,
.size_data = size,
.dma_mask = dma_mask,
};
return platform_device_register_full(&pdevinfo);
}
static struct fmc_adc_platform_data wrtd_adcx2_pdata[] = {
{
.flags = FMC_ADC_BIG_ENDIAN |
FMC_ADC_SVEC |
FMC_ADC_NOSQUASH_SCATTERLIST,
.vme_reg_offset = SVEC_FPGA_CSR_DDR4_ADDR,
.vme_dma_offset = SVEC_FPGA_DDR4_DMA,
.calib_trig_time = 0,
.calib_trig_threshold = 0,
.calib_trig_internal = 0,
}, {
.flags = FMC_ADC_BIG_ENDIAN |
FMC_ADC_SVEC |
FMC_ADC_NOSQUASH_SCATTERLIST,
.vme_reg_offset = SVEC_FPGA_CSR_DDR5_ADDR,
.vme_dma_offset = SVEC_FPGA_DDR5_DMA,
.calib_trig_time = 0,
.calib_trig_threshold = 0,
.calib_trig_internal = 0,
}
};
static struct resource wrtd_adcx2_res1[] = {
{
.name = "fmc-adc-100m-mem.1",
.flags = IORESOURCE_MEM,
.start = WRTD_ADCX2_ADC1_MEM_START,
.end = WRTD_ADCX2_ADC1_MEM_END,
},
{
.name = "fmc-adc-100m-dma.1",
.flags = IORESOURCE_DMA,
},
{
.name = "fmc-adc-100m-irq.1",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = 0,
.end = 0,
},
};
static struct resource wrtd_adcx2_res2[] = {
{
.name = "fmc-adc-100m-mem.2",
.flags = IORESOURCE_MEM,
.start = WRTD_ADCX2_ADC2_MEM_START,
.end = WRTD_ADCX2_ADC2_MEM_END,
},
{
.name = "fmc-adc-100m-dma.2",
.flags = IORESOURCE_DMA,
},
{
.name = "fmc-adc-100m-irq.2",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = 1,
.end = 1,
},
};
static struct resource wrtd_adcx2_trtl_res[] = {
{
.name = "mock-turtle-mem",
.flags = IORESOURCE_MEM,
.start = WRTD_ADCX2_TRTL_MEM_START,
.end = WRTD_ADCX2_TRTL_MEM_END,
}, {
.name = "mock-turtle-irq_in",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = 2,
}, {
.name = "mock-turtle-irq_out",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = 3,
}, {
.name = "mock-turtle-irq_con",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = 4,
}, {
.name = "mock-turtle-irq_not",
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
.start = 5,
},
};
static struct resource *wrtd_adcx2_res[] = {
wrtd_adcx2_res1,
wrtd_adcx2_res2,
};
struct wrtd_adcx2_data {
struct platform_device *adc[2];
struct platform_device *trtl;
};
static int wrtd_adcx2_probe(struct platform_device *pdev)
{
struct wrtd_adcx2_data *pdev_data;
struct resource *rmem;
int irq;
int i;
rmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!rmem) {
dev_err(&pdev->dev, "Missing memory resource\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Missing IRQ number\n");
return -EINVAL;
}
pdev_data = kmalloc(sizeof(*pdev_data), GFP_KERNEL);
if (!pdev_data)
return -ENOMEM;
for (i = 0; i < SVEC_FMC_SLOTS; ++i) {
unsigned int res_n = ARRAY_SIZE(wrtd_adcx2_res1);
struct resource res[res_n];
struct fmc_slot *slot = fmc_slot_get(pdev->dev.parent, i + 1);
int present;
if (IS_ERR(slot)) {
dev_err(&pdev->dev,
"Can't find FMC slot %d err: %ld\n",
i + 1, PTR_ERR(slot));
continue;
}
present = fmc_slot_present(slot);
fmc_slot_put(slot);
dev_dbg(&pdev->dev, "FMC slot: %d, present: %d\n",
i + 1, present);
if (!present)
continue;
memcpy(res, wrtd_adcx2_res[i], sizeof(res));
res[0].parent = rmem;
res[0].start += rmem->start;
res[0].end += rmem->start;
res[2].start += irq;
pdev_data->adc[i] = platform_device_register_resndata_mask(&pdev->dev,
"fmc-adc-100m",
PLATFORM_DEVID_AUTO,
res,
res_n,
&wrtd_adcx2_pdata[i],
sizeof(wrtd_adcx2_pdata[i]),
DMA_BIT_MASK(32));
if (IS_ERR(pdev_data->adc[i])) {
dev_err(&pdev->dev,
"Faild to register ADC instance %d\n",
i);
pdev_data->adc[i] = NULL;
}
}
{
unsigned int res_n = ARRAY_SIZE(wrtd_adcx2_trtl_res);
struct resource res[res_n];
memcpy(res, wrtd_adcx2_trtl_res, sizeof(res));
res[0].parent = rmem;
res[0].start += rmem->start;
res[0].end += rmem->start;
res[1].start += irq;
res[2].start += irq;
res[3].start += irq;
res[4].start += irq;
pdev_data->trtl = platform_device_register_resndata(&pdev->dev,
"mock-turtle",
PLATFORM_DEVID_AUTO,
res, res_n,
NULL, 0);
if (IS_ERR(pdev_data->trtl)) {
dev_err(&pdev->dev,
"Faild to register TRTL instance\n");
pdev_data->trtl = NULL;
}
}
platform_set_drvdata(pdev, pdev_data);
return 0;
}
static int wrtd_adcx2_remove(struct platform_device *pdev)
{
struct wrtd_adcx2_data *pdev_data = platform_get_drvdata(pdev);
int i;
if (!pdev_data)
return 0;
for (i = 0; i < SVEC_FMC_SLOTS; ++i)
if (pdev_data->adc[i])
platform_device_unregister(pdev_data->adc[i]);
platform_device_unregister(pdev_data->trtl);
kfree(pdev_data);
return 0;
}
/**
* List of supported platform
*/
enum wrtd_adcx2_version {
WRTD_ADCX2_VER = 0,
};
static const struct platform_device_id wrtd_adcx2_id_table[] = {
{
.name = "wrtd-adcx2",
.driver_data = WRTD_ADCX2_VER,
}, {
.name = "id:000010DC57544E05",
.driver_data = WRTD_ADCX2_VER,
}, {
.name = "id:000010dc57544e05",
.driver_data = WRTD_ADCX2_VER,
},
{},
};
static struct platform_driver wrtd_adcx2_driver = {
.driver = {
.name = "wrtd-adcx2",
.owner = THIS_MODULE,
},
.id_table = wrtd_adcx2_id_table,
.probe = wrtd_adcx2_probe,
.remove = wrtd_adcx2_remove,
};
module_platform_driver(wrtd_adcx2_driver);
MODULE_AUTHOR("Federico Vaga <federico.vaga@cern.ch>");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
MODULE_DESCRIPTION("Driver for the WRTD SVEC ADCx2");
MODULE_DEVICE_TABLE(platform, wrtd_adcx2_id_table);
MODULE_SOFTDEP("pre: svec_fmc_carrier fmc-adc-100m14b4ch");
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