Commit 27ed4476 authored by Federico Vaga's avatar Federico Vaga

sw:drv: make GN412X GPIO independent

Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent e3b26a42
......@@ -23,9 +23,10 @@ dkms-src: dkms-tree
$(eval $@_src := $(shell git ls-tree -r --name-only HEAD $(TOP_DIR) | grep "kernel" | tr '\n' ' '))
$(eval $@_dir := $(BUILD_DKMSSOURCE)/$(DRIVER_NAME)-$(VERSION))
@mkdir -p $($@_dir)
@cp $($@_src) $(TOP_DIR)/distribution/dkms.conf $($@_dir)
@cp $(TOP_DIR)/LICENSES/GPL-2.0.txt $($@_dir)/LICENSE
@mkdir -p $($@_dir)/platform_data
@cp -a $($@_src) $(TOP_DIR)/distribution/dkms.conf $($@_dir)
@mv $($@_dir)/gn412x-gpio.h $($@_dir)/platform_data
@cp -a $(TOP_DIR)/LICENSES/GPL-2.0.txt $($@_dir)/LICENSE
@sed -r -i -e "s/^VERSION\s=\s.*/VERSION = $(VERSION)/" $($@_dir)/Makefile
@sed -r -i -e "s/@PKGNAME@/$(DRIVER_NAME)/" $($@_dir)/dkms.conf
@sed -r -i -e "s/@PKGVER@/$(VERSION)/" $($@_dir)/dkms.conf
......
......@@ -2,6 +2,9 @@ PACKAGE_NAME="@PKGNAME@"
PACKAGE_VERSION="@PKGVER@"
CLEAN="make clean"
MAKE[0]="make KVERSION=$kernelver all"
MAKE[1]="make KVERSION=$kernelver all"
BUILT_MODULE_NAME[0]="@PKGNAME@"
BUILT_MODULE_NAME[1]="gn412x-gpio"
DEST_MODULE_LOCATION[0]="/updates"
DEST_MODULE_LOCATION[1]="/updates"
AUTOINSTALL="yes"
......@@ -28,6 +28,7 @@ endif
KBUILD_EXTRA_SYMBOLS += $(FMC_ABS)/drivers/fmc/Module.symvers
obj-m := spec-fmc-carrier.o
obj-m += gn412x-gpio.o
spec-fmc-carrier-objs := spec-core.o
spec-fmc-carrier-objs += spec-core-fpga.o
......@@ -35,5 +36,4 @@ spec-fmc-carrier-objs += spec-fpga.o
spec-fmc-carrier-objs += spec-dbg.o
spec-fmc-carrier-objs += spec-fmc.o
spec-fmc-carrier-objs += spec-compat.o
spec-fmc-carrier-objs += gn412x-gpio.o
spec-fmc-carrier-objs += spec-gpio.o
......@@ -7,9 +7,30 @@
#include <linux/gpio/driver.h>
#include "spec.h"
#include "spec-compat.h"
#include "gn412x.h"
#include "platform_data/gn412x-gpio.h"
#define GN412X_INT_CFG_MAX 7
/**
* struct gn412x_gpio_dev GN412X device descriptor
* @compl: for IRQ testing
* @int_cfg_gpio: INT_CFG used for GPIO interrupts
*/
struct gn412x_gpio_dev {
void __iomem *mem;
struct gpio_chip gpiochip;
struct completion compl;
struct gn412x_platform_data *pdata;
};
static struct gn412x_platform_data gn412x_gpio_pdata_default = {
.int_cfg = 0,
};
static inline struct gn412x_gpio_dev *to_gn412x_gpio_dev_gpio(struct gpio_chip *chip)
{
return container_of(chip, struct gn412x_gpio_dev, gpiochip);
}
enum gn412x_gpio_versions {
GN412X_VER = 0,
......@@ -19,12 +40,12 @@ enum htvic_mem_resources {
GN412X_MEM_BASE = 0,
};
static uint32_t gn412x_ioread32(struct gn412x_dev *gn412x, int reg)
static uint32_t gn412x_ioread32(struct gn412x_gpio_dev *gn412x, int reg)
{
return ioread32(gn412x->mem + reg);
}
static void gn412x_iowrite32(struct gn412x_dev *gn412x, uint32_t val, int reg)
static void gn412x_iowrite32(struct gn412x_gpio_dev *gn412x, uint32_t val, int reg)
{
return iowrite32(val, gn412x->mem + reg);
}
......@@ -33,7 +54,7 @@ static void gn412x_iowrite32(struct gn412x_dev *gn412x, uint32_t val, int reg)
static int gn412x_gpio_reg_read(struct gpio_chip *chip,
int reg, unsigned offset)
{
struct gn412x_dev *gn412x = to_gn412x_dev_gpio(chip);
struct gn412x_gpio_dev *gn412x = to_gn412x_gpio_dev_gpio(chip);
return gn412x_ioread32(gn412x, reg) & BIT(offset);
}
......@@ -41,7 +62,7 @@ static int gn412x_gpio_reg_read(struct gpio_chip *chip,
static void gn412x_gpio_reg_write(struct gpio_chip *chip,
int reg, unsigned offset, int value)
{
struct gn412x_dev *gn412x = to_gn412x_dev_gpio(chip);
struct gn412x_gpio_dev *gn412x = to_gn412x_gpio_dev_gpio(chip);
uint32_t regval;
regval = gn412x_ioread32(gn412x, reg);
......@@ -52,6 +73,36 @@ static void gn412x_gpio_reg_write(struct gpio_chip *chip,
gn412x_iowrite32(gn412x, regval, reg);
}
/**
* Enable GPIO interrupts
* @gn412x gn412x device
*
* Return: 0 on success, otherwise a negative error number
*/
static void gn412x_gpio_int_cfg_enable(struct gn412x_gpio_dev *gn412x)
{
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->pdata->int_cfg));
int_cfg |= GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->pdata->int_cfg));
}
/**
* Disable GPIO interrupts from a single configuration space
* @gn412x gn412x device
*
* Return: 0 on success, otherwise a negative error number
*/
static void gn412x_gpio_int_cfg_disable(struct gn412x_gpio_dev *gn412x)
{
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->pdata->int_cfg));
int_cfg &= ~GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->pdata->int_cfg));
}
static int gn412x_gpio_request(struct gpio_chip *chip, unsigned offset)
{
int val;
......@@ -109,14 +160,13 @@ static void gn412x_gpio_set(struct gpio_chip *chip,
gn412x_gpio_reg_write(chip, GNGPIO_OUTPUT_VALUE, offset, value);
}
/**
* (disable)
*/
static void gn412x_gpio_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gn412x_dev *gn412x = to_gn412x_dev_gpio(gc);
struct gn412x_gpio_dev *gn412x = to_gn412x_gpio_dev_gpio(gc);
gn412x_iowrite32(gn412x, BIT(d->hwirq), GNGPIO_INT_MASK_SET);
}
......@@ -128,7 +178,7 @@ static void gn412x_gpio_irq_mask(struct irq_data *d)
static void gn412x_gpio_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct gn412x_dev *gn412x = to_gn412x_dev_gpio(gc);
struct gn412x_gpio_dev *gn412x = to_gn412x_gpio_dev_gpio(gc);
gn412x_iowrite32(gn412x, BIT(d->hwirq), GNGPIO_INT_MASK_CLR);
}
......@@ -231,7 +281,7 @@ static struct irq_chip gn412x_gpio_irq_chip = {
*/
static irqreturn_t spec_irq_sw_handler(int irq, void *arg)
{
struct gn412x_dev *gn412x = arg;
struct gn412x_gpio_dev *gn412x = arg;
/* Ack the interrupts */
gn412x_ioread32(gn412x, GNINT_STAT);
......@@ -242,72 +292,15 @@ static irqreturn_t spec_irq_sw_handler(int irq, void *arg)
return IRQ_HANDLED;
}
/**
* Enable GPIO interrupts
* @gn412x gn412x device
* @cfg_n interrupt configuration register number
*
* Return: 0 on success, otherwise a negative error number
*/
int gn412x_int_gpio_enable(struct gn412x_dev *gn412x, unsigned int cfg_n)
{
uint32_t int_cfg;
if (WARN(cfg_n > GN412X_INT_CFG_MAX, "Unexistent GN412X INT_CFG(%d)",
cfg_n))
return -EINVAL;
gn412x->int_cfg_gpio = cfg_n;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->int_cfg_gpio));
int_cfg |= GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->int_cfg_gpio));
return 0;
}
/**
* Disable GPIO interrupts from a single configuration space
* @gn412x gn412x device
*
* Return: 0 on success, otherwise a negative error number
*/
void gn412x_int_gpio_disable(struct gn412x_dev *gn412x)
{
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->int_cfg_gpio));
int_cfg &= ~GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->int_cfg_gpio));
}
/**
* Disable GPIO interrupts from all configuration spaces
* @gn412x gn412x device
*/
static void gn412x_int_gpio_disable_all(struct gn412x_dev *gn412x)
{
int i;
for (i = 0; i <= GN412X_INT_CFG_MAX; ++i) {
uint32_t int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(i));
int_cfg &= ~GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(i));
}
}
/**
* Handle IRQ from the GPIO block
*/
static irqreturn_t gn412x_gpio_irq_handler_t(int irq, void *arg)
{
struct gn412x_dev *gn412x = arg;
struct gn412x_gpio_dev *gn412x = arg;
struct gpio_chip *gc = &gn412x->gpiochip;
unsigned int cascade_irq;
uint32_t gpio_int_status, int_cfg;
uint32_t gpio_int_status;
unsigned long loop;
irqreturn_t ret = IRQ_NONE;
int i;
......@@ -330,9 +323,7 @@ static irqreturn_t gn412x_gpio_irq_handler_t(int irq, void *arg)
out_enable_irq:
/* Re-enable the GPIO interrupts, we are done here */
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->int_cfg_gpio));
int_cfg |= GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->int_cfg_gpio));
gn412x_gpio_int_cfg_enable(gn412x);
return ret;
}
......@@ -340,10 +331,10 @@ out_enable_irq:
static irqreturn_t gn412x_gpio_irq_handler_h(int irq, void *arg)
{
struct gn412x_dev *gn412x = arg;
struct gn412x_gpio_dev *gn412x = arg;
uint32_t int_stat, int_cfg;
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->int_cfg_gpio));
int_cfg = gn412x_ioread32(gn412x, GNINT_CFG(gn412x->pdata->int_cfg));
int_stat = gn412x_ioread32(gn412x, GNINT_STAT);
if (unlikely(!(int_stat & int_cfg)))
return IRQ_NONE;
......@@ -360,14 +351,13 @@ static irqreturn_t gn412x_gpio_irq_handler_h(int irq, void *arg)
* let's disable interrupts. Within the thread we will re-enable them
* when we are ready (like IRQF_ONESHOT).
*/
int_cfg &= ~GNINT_STAT_GPIO;
gn412x_iowrite32(gn412x, int_cfg, GNINT_CFG(gn412x->int_cfg_gpio));
gn412x_gpio_int_cfg_disable(gn412x);
return IRQ_WAKE_THREAD;
}
static void gn412x_gpio_irq_set_nested_thread(struct gn412x_dev *gn412x,
static void gn412x_gpio_irq_set_nested_thread(struct gn412x_gpio_dev *gn412x,
unsigned int gpio, bool nest)
{
int irq;
......@@ -376,7 +366,7 @@ static void gn412x_gpio_irq_set_nested_thread(struct gn412x_dev *gn412x,
irq_set_nested_thread(irq, nest);
}
static void gn412x_gpio_irq_set_nested_thread_all(struct gn412x_dev *gn412x,
static void gn412x_gpio_irq_set_nested_thread_all(struct gn412x_gpio_dev *gn412x,
bool nest)
{
int i;
......@@ -385,30 +375,53 @@ static void gn412x_gpio_irq_set_nested_thread_all(struct gn412x_dev *gn412x,
gn412x_gpio_irq_set_nested_thread(gn412x, i, nest);
}
int gn412x_gpio_init(struct device *parent, struct gn412x_dev *gn412x)
static int gn412x_gpio_probe(struct platform_device *pdev)
{
int err, irq;
struct gn412x_gpio_dev *gn412x;
struct resource *r;
int err;
memset(&gn412x->gpiochip, 0, sizeof(gn412x->gpiochip));
gn412x->gpiochip.dev = parent;
gn412x = devm_kzalloc(&pdev->dev, sizeof(*gn412x), GFP_KERNEL);
if (!gn412x)
return -ENOMEM;
platform_set_drvdata(pdev, gn412x);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(&pdev->dev, "Missing memory resource\n");
err = -EINVAL;
goto err_res_mem;
}
gn412x->mem = devm_ioremap(&pdev->dev, r->start, resource_size(r));
if (!gn412x->mem) {
err = -EADDRNOTAVAIL;
goto err_map;
}
gn412x->pdata = dev_get_platdata(&pdev->dev);
if (!gn412x->pdata) {
dev_warn(&pdev->dev, "Missing platform data, use default\n");
gn412x->pdata = &gn412x_gpio_pdata_default;
}
gn412x->gpiochip.dev = &pdev->dev;
gn412x->gpiochip.label = "gn412x-gpio";
gn412x->gpiochip.owner = THIS_MODULE;
gn412x->gpiochip.request = gn412x_gpio_request;
gn412x->gpiochip.free = gn412x_gpio_free;
gn412x->gpiochip.get_direction = gn412x_gpio_get_direction,
gn412x->gpiochip.direction_input = gn412x_gpio_direction_input,
gn412x->gpiochip.direction_output = gn412x_gpio_direction_output,
gn412x->gpiochip.get = gn412x_gpio_get,
gn412x->gpiochip.set = gn412x_gpio_set,
gn412x->gpiochip.base = -1,
gn412x->gpiochip.ngpio = GN4124_GPIO_MAX,
gn412x->gpiochip.get_direction = gn412x_gpio_get_direction;
gn412x->gpiochip.direction_input = gn412x_gpio_direction_input;
gn412x->gpiochip.direction_output = gn412x_gpio_direction_output;
gn412x->gpiochip.get = gn412x_gpio_get;
gn412x->gpiochip.set = gn412x_gpio_set;
gn412x->gpiochip.base = -1;
gn412x->gpiochip.ngpio = GN4124_GPIO_MAX;
gn412x->gpiochip.can_sleep = 0;
err = gpiochip_add(&gn412x->gpiochip);
if (err)
goto err_add;
gn412x_int_gpio_disable_all(gn412x);
gn412x_gpio_int_cfg_disable(gn412x);
gn412x_iowrite32(gn412x, 0xFFFF, GNGPIO_INT_MASK_SET);
err = gpiochip_irqchip_add(&gn412x->gpiochip,
&gn412x_gpio_irq_chip,
......@@ -418,8 +431,8 @@ int gn412x_gpio_init(struct device *parent, struct gn412x_dev *gn412x)
goto err_add_irq;
gn412x_gpio_irq_set_nested_thread_all(gn412x, true);
irq = to_pci_dev(gn412x->gpiochip.dev->parent)->irq;
err = request_threaded_irq(irq,
err = request_threaded_irq(platform_get_irq(pdev, 0),
gn412x_gpio_irq_handler_h,
gn412x_gpio_irq_handler_t,
IRQF_SHARED,
......@@ -427,29 +440,59 @@ int gn412x_gpio_init(struct device *parent, struct gn412x_dev *gn412x)
gn412x);
if (err) {
dev_err(gn412x->gpiochip.dev, "Can't request IRQ %d (%d)\n",
irq, err);
platform_get_irq(pdev, 0), err);
goto err_req;
}
gn412x_gpio_int_cfg_enable(gn412x);
return 0;
err_req:
gn412x_gpio_irq_set_nested_thread_all(gn412x, false);
gpiochip_irqchip_remove(&gn412x->gpiochip);
err_add_irq:
gpiochip_remove(&gn412x->gpiochip);
err_add:
devm_iounmap(&pdev->dev, gn412x->mem);
err_map:
err_res_mem:
devm_kfree(&pdev->dev, gn412x);
return err;
}
void gn412x_gpio_exit(struct gn412x_dev *gn412x)
static int gn412x_gpio_remove(struct platform_device *pdev)
{
int irq;
struct gn412x_gpio_dev *gn412x = platform_get_drvdata(pdev);
gn412x_int_gpio_disable_all(gn412x);
gn412x_gpio_int_cfg_disable(gn412x);
irq = to_pci_dev(gn412x->gpiochip.dev->parent)->irq;
free_irq(irq, gn412x);
free_irq(platform_get_irq(pdev, 0), gn412x);
gn412x_gpio_irq_set_nested_thread_all(gn412x, false);
gpiochip_irqchip_remove(&gn412x->gpiochip);
gpiochip_remove(&gn412x->gpiochip);
return 0;
}
static const struct platform_device_id gn412x_gpio_id[] = {
{
.name = "gn412x-gpio",
},
{ .name = "" }, /* last */
};
static struct platform_driver gn412x_gpio_platform_driver = {
.driver = {
.name = KBUILD_MODNAME,
},
.probe = gn412x_gpio_probe,
.remove = gn412x_gpio_remove,
.id_table = gn412x_gpio_id,
};
module_platform_driver(gn412x_gpio_platform_driver);
MODULE_AUTHOR("Federico Vaga <federico.vaga@cern.ch>");
MODULE_DESCRIPTION("GN412X GPIO driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);
MODULE_DEVICE_TABLE(platform, gn412x_gpio_id);
//SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2010-2019 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#ifndef __GN412X_H__
#define __GN412X_H__
#define GNINT_STAT_GPIO BIT(15)
#define GNINT_STAT_SW0 BIT(2)
#define GNINT_STAT_SW1 BIT(3)
#define GNINT_STAT_SW_ALL (GNINT_STAT_SW0 | GNINT_STAT_SW1)
/* Registers for GN4124 access */
enum {
/* page 106 */
GNPPCI_MSI_CONTROL = 0x48, /* actually, 3 smaller regs */
GNPPCI_MSI_ADDRESS_LOW = 0x4c,
GNPPCI_MSI_ADDRESS_HIGH = 0x50,
GNPPCI_MSI_DATA = 0x54,
GNPCI_SYS_CFG_SYSTEM = 0x800,
/* page 130 ff */
GNINT_CTRL = 0x810,
GNINT_STAT = 0x814,
GNINT_CFG_0 = 0x820,
GNINT_CFG_1 = 0x824,
GNINT_CFG_2 = 0x828,
GNINT_CFG_3 = 0x82c,
GNINT_CFG_4 = 0x830,
GNINT_CFG_5 = 0x834,
GNINT_CFG_6 = 0x838,
GNINT_CFG_7 = 0x83c,
#define GNINT_CFG(x) (GNINT_CFG_0 + 4 * (x))
/* page 146 ff */
GNGPIO_BASE = 0xA00,
GNGPIO_BYPASS_MODE = GNGPIO_BASE,
GNGPIO_DIRECTION_MODE = GNGPIO_BASE + 0x04, /* 0 == output */
GNGPIO_OUTPUT_ENABLE = GNGPIO_BASE + 0x08,
GNGPIO_OUTPUT_VALUE = GNGPIO_BASE + 0x0C,
GNGPIO_INPUT_VALUE = GNGPIO_BASE + 0x10,
GNGPIO_INT_MASK = GNGPIO_BASE + 0x14, /* 1 == disabled */
GNGPIO_INT_MASK_CLR = GNGPIO_BASE + 0x18, /* irq enable */
GNGPIO_INT_MASK_SET = GNGPIO_BASE + 0x1C, /* irq disable */
GNGPIO_INT_STATUS = GNGPIO_BASE + 0x20,
GNGPIO_INT_TYPE = GNGPIO_BASE + 0x24, /* 1 == level */
GNGPIO_INT_VALUE = GNGPIO_BASE + 0x28, /* 1 == high/rise */
GNGPIO_INT_ON_ANY = GNGPIO_BASE + 0x2C, /* both edges */
/* page 158 ff */
FCL_BASE = 0xB00,
FCL_CTRL = FCL_BASE,
FCL_STATUS = FCL_BASE + 0x04,
FCL_IODATA_IN = FCL_BASE + 0x08,
FCL_IODATA_OUT = FCL_BASE + 0x0C,
FCL_EN = FCL_BASE + 0x10,
FCL_TIMER_0 = FCL_BASE + 0x14,
FCL_TIMER_1 = FCL_BASE + 0x18,
FCL_CLK_DIV = FCL_BASE + 0x1C,
FCL_IRQ = FCL_BASE + 0x20,
FCL_TIMER_CTRL = FCL_BASE + 0x24,
FCL_IM = FCL_BASE + 0x28,
FCL_TIMER2_0 = FCL_BASE + 0x2C,
FCL_TIMER2_1 = FCL_BASE + 0x30,
FCL_DBG_STS = FCL_BASE + 0x34,
FCL_FIFO = 0xE00,
PCI_SYS_CFG_SYSTEM = 0x800
};
#endif
#ifndef __GN412X_GPIO_H__
#define __GN412X_GPIO_H__
struct gn412x_platform_data {
unsigned int int_cfg;
};
#endif
......@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/version.h>
#include <linux/gpio/driver.h>
#include "spec-compat.h"
int compat_get_fpga_last_word_size(struct fpga_image_info *info, size_t count)
......@@ -216,17 +217,4 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table)
mutex_unlock(gpio_lookup_lock_p);
}
void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
{
void (*gpiochip_irqchip_remove_p)(struct gpio_chip *gpiochip);
gpiochip_irqchip_remove_p = (void *) kallsyms_lookup_name("gpiochip_irqchip_remove");
if (gpiochip_irqchip_remove_p)
gpiochip_irqchip_remove_p(gpiochip);
else
WARN(1, "Cannot find 'gpiochip_irqchip_remove'");
}
#endif
......@@ -6,6 +6,7 @@
#include <linux/fpga/fpga-mgr.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/gpio/driver.h>
#include "spec.h"
#if KERNEL_VERSION(4,10,0) <= LINUX_VERSION_CODE
......@@ -81,5 +82,4 @@ extern int compat_gpiod_add_lookup_table(struct gpiod_lookup_table *table);
#if KERNEL_VERSION(4, 3, 0) > LINUX_VERSION_CODE
extern void gpiod_remove_lookup_table(struct gpiod_lookup_table *table);
extern void gpiochip_irqchip_remove(struct gpio_chip *gpiochip);
#endif
......@@ -13,10 +13,14 @@
#include <linux/pci.h>
#include <linux/firmware.h>
#include <linux/moduleparam.h>
#include <linux/mfd/core.h>
#include "platform_data/gn412x-gpio.h"
#include "spec.h"
#include "spec-compat.h"
static int mfd_id; /* FIXME look for something better */
static char *spec_fw_name_45t = "spec-init-45T.bin";
static char *spec_fw_name_100t = "spec-init-100T.bin";
static char *spec_fw_name_150t = "spec-init-150T.bin";
......@@ -24,6 +28,37 @@ static char *spec_fw_name_150t = "spec-init-150T.bin";
char *spec_fw_name = "";
module_param_named(fw_name, spec_fw_name, charp, 0444);
static struct gn412x_platform_data gn412x_gpio_pdata = {
.int_cfg = 0,
};
static struct resource gn412x_gpio_res[] = {
{
.name = "gn412x-gpio-mem",
.flags = IORESOURCE_MEM,
.start = 0,
.end = 0x1000 - 1,
}, {
.name = "gn412x-gpio-irq",
.flags = IORESOURCE_IRQ,
.start = 0,
.end = 0,
}
};
enum spec_mfd_enum {
SPEC_MFD_GN412X_GPIO = 0,
};
static const struct mfd_cell spec_mfd_devs[] = {
[SPEC_MFD_GN412X_GPIO] = {
.name = "gn412x-gpio",
.platform_data = &gn412x_gpio_pdata,
.pdata_size = sizeof(gn412x_gpio_pdata),
.num_resources = ARRAY_SIZE(gn412x_gpio_res),
.resources = gn412x_gpio_res,
},
};
/**
* Return the SPEC defult FPGA firmware name based on PCI ID
* @spec: SPEC device
......@@ -100,6 +135,7 @@ static const struct device_type spec_dev_type = {
.uevent = spec_uevent,
};
static int spec_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
......@@ -146,10 +182,12 @@ static int spec_probe(struct pci_dev *pdev,
/* This virtual device is assciated with this driver */
spec->dev.driver = pdev->dev.driver;
spec->gn412x.mem = spec->remap[2];
err = gn412x_gpio_init(&spec->dev, &spec->gn412x);
err = mfd_add_devices(&spec->dev, mfd_id++,
spec_mfd_devs,
ARRAY_SIZE(spec_mfd_devs),
&pdev->resource[4], pdev->irq, NULL);
if (err)
goto err_ggpio;
goto err_mfd;
err = spec_gpio_init(spec);
if (err)
......@@ -185,8 +223,8 @@ err_fw:
err_fpga:
spec_gpio_exit(spec);
err_sgpio:
gn412x_gpio_exit(&spec->gn412x);
err_ggpio:
mfd_remove_devices(&spec->dev);
err_mfd:
device_unregister(&spec->dev);
err_dev:
err_name:
......@@ -212,7 +250,8 @@ static void spec_remove(struct pci_dev *pdev)
spec_core_fpga_exit(spec);
spec_fpga_exit(spec);
spec_gpio_exit(spec);
gn412x_gpio_exit(&spec->gn412x);
mfd_remove_devices(&spec->dev);
for (i = 0; i < 3; i++)
if (spec->remap[i])
......
......@@ -66,13 +66,15 @@ static inline size_t spec_gpiod_table_size(void)
int spec_gpio_init(struct spec_dev *spec)
{
struct gpiod_lookup_table *lookup;
int err;
int err = 0;
lookup = devm_kzalloc(&spec->dev,
spec_gpiod_table_size(),
GFP_KERNEL);
if (!lookup)
return -ENOMEM;
if (!lookup) {
err = -ENOMEM;
goto err_alloc;
}
memcpy(lookup, &spec_gpiod_table, spec_gpiod_table_size());
......@@ -148,8 +150,9 @@ err_lookup:
err_dup:
devm_kfree(&spec->dev, lookup);
spec->gpiod_table = NULL;
err_alloc:
return -ENODEV;
return err;
}
void spec_gpio_exit(struct spec_dev *spec)
......
......@@ -7,7 +7,6 @@
*/
#ifndef __SPEC_H__
#define __SPEC_H__
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/fpga/fpga-mgr.h>
......@@ -15,10 +14,11 @@
#include <linux/irqdomain.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/gpio/driver.h>
#include <linux/spinlock.h>
#include <linux/fmc.h>
#include "gn412x.h"
#define SPEC_FMC_SLOTS 1
/* On FPGA components */
......@@ -58,65 +58,6 @@ enum spec_fpga_select {
SPEC_FPGA_SELECT_SPI,
};
/* Registers for GN4124 access */
enum {
/* page 106 */
GNPPCI_MSI_CONTROL = 0x48, /* actually, 3 smaller regs */
GNPPCI_MSI_ADDRESS_LOW = 0x4c,
GNPPCI_MSI_ADDRESS_HIGH = 0x50,
GNPPCI_MSI_DATA = 0x54,
GNPCI_SYS_CFG_SYSTEM = 0x800,
/* page 130 ff */
GNINT_CTRL = 0x810,
GNINT_STAT = 0x814,
GNINT_CFG_0 = 0x820,
GNINT_CFG_1 = 0x824,
GNINT_CFG_2 = 0x828,
GNINT_CFG_3 = 0x82c,
GNINT_CFG_4 = 0x830,
GNINT_CFG_5 = 0x834,
GNINT_CFG_6 = 0x838,
GNINT_CFG_7 = 0x83c,
#define GNINT_CFG(x) (GNINT_CFG_0 + 4 * (x))
/* page 146 ff */
GNGPIO_BASE = 0xA00,
GNGPIO_BYPASS_MODE = GNGPIO_BASE,
GNGPIO_DIRECTION_MODE = GNGPIO_BASE + 0x04, /* 0 == output */
GNGPIO_OUTPUT_ENABLE = GNGPIO_BASE + 0x08,
GNGPIO_OUTPUT_VALUE = GNGPIO_BASE + 0x0C,
GNGPIO_INPUT_VALUE = GNGPIO_BASE + 0x10,
GNGPIO_INT_MASK = GNGPIO_BASE + 0x14, /* 1 == disabled */
GNGPIO_INT_MASK_CLR = GNGPIO_BASE + 0x18, /* irq enable */
GNGPIO_INT_MASK_SET = GNGPIO_BASE + 0x1C, /* irq disable */
GNGPIO_INT_STATUS = GNGPIO_BASE + 0x20,
GNGPIO_INT_TYPE = GNGPIO_BASE + 0x24, /* 1 == level */
GNGPIO_INT_VALUE = GNGPIO_BASE + 0x28, /* 1 == high/rise */
GNGPIO_INT_ON_ANY = GNGPIO_BASE + 0x2C, /* both edges */
/* page 158 ff */
FCL_BASE = 0xB00,
FCL_CTRL = FCL_BASE,
FCL_STATUS = FCL_BASE + 0x04,
FCL_IODATA_IN = FCL_BASE + 0x08,
FCL_IODATA_OUT = FCL_BASE + 0x0C,
FCL_EN = FCL_BASE + 0x10,
FCL_TIMER_0 = FCL_BASE + 0x14,
FCL_TIMER_1 = FCL_BASE + 0x18,
FCL_CLK_DIV = FCL_BASE + 0x1C,
FCL_IRQ = FCL_BASE + 0x20,
FCL_TIMER_CTRL = FCL_BASE + 0x24,
FCL_IM = FCL_BASE + 0x28,
FCL_TIMER2_0 = FCL_BASE + 0x2C,
FCL_TIMER2_1 = FCL_BASE + 0x30,
FCL_DBG_STS = FCL_BASE + 0x34,
FCL_FIFO = 0xE00,
PCI_SYS_CFG_SYSTEM = 0x800
};
enum {
/* Metadata */
......@@ -143,7 +84,6 @@ enum {
#define SPEC_META_CAP_DMA BIT(0)
#define SPEC_META_CAP_WR BIT(0)
/**
* struct spec_meta_id Metadata
*/
......@@ -157,30 +97,6 @@ struct spec_meta_id {
uint32_t uuid[4];
};
#define GNINT_STAT_GPIO BIT(15)
#define GNINT_STAT_SW0 BIT(2)
#define GNINT_STAT_SW1 BIT(3)
#define GNINT_STAT_SW_ALL (GNINT_STAT_SW0 | GNINT_STAT_SW1)
/**
* struct gn412x_dev GN412X device descriptor
* @compl: for IRQ testing
* @int_cfg_gpio: INT_CFG used for GPIO interrupts
*/
struct gn412x_dev {
void __iomem *mem;
struct gpio_chip gpiochip;
struct completion compl;
int int_cfg_gpio;
};
static inline struct gn412x_dev *to_gn412x_dev_gpio(struct gpio_chip *chip)
{
return container_of(chip, struct gn412x_dev, gpiochip);
}
/**
* struct spec_dev - SPEC instance
* It describes a SPEC device instance.
......@@ -215,8 +131,6 @@ struct spec_dev {
#define SPEC_DBG_META_NAME "fpga_device_metadata"
struct dentry *dbg_meta;
struct gn412x_dev gn412x;
struct gpiod_lookup_table *gpiod_table;
struct gpio_desc *gpiod[GN4124_GPIO_MAX];
};
......@@ -267,12 +181,6 @@ static inline void gennum_mask_val(struct spec_dev *spec,
gennum_writel(spec, v, reg);
}
extern int gn412x_gpio_init(struct device *parent, struct gn412x_dev *spec);
extern void gn412x_gpio_exit(struct gn412x_dev *spec);
extern int gn412x_int_gpio_enable(struct gn412x_dev *gn412x,
unsigned int cfg_n);
extern void gn412x_int_gpio_disable(struct gn412x_dev *gn412x);
extern int spec_fpga_init(struct spec_dev *spec);
extern void spec_fpga_exit(struct spec_dev *spec);
......
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