1056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini/*
2056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini * Copyright (C) 2012 CERN (www.cern.ch)
3056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini * Author: Alessandro Rubini <rubini@gnudd.com>
4056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini *
5056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini * Permission to use, copy, modify, and/or distribute this software for any
6056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini * purpose with or without fee is hereby granted, provided that the above
7056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini * copyright notice and this permission notice appear in all copies.
8056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini *
9056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini * The software is provided "as is"; the copyright holders disclaim
10056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini * all warranties and liabilities, to the extent permitted by
11056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini * applicable law.
12056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini */
13056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
14056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini/* A trivial fmc driver that can load a gateware file and reports interrupts */
15056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini#include <linux/module.h>
16056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini#include <linux/init.h>
17056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini#include <linux/interrupt.h>
18056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini#include <linux/gpio.h>
19056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini#include <linux/fmc.h>
20056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
21056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubinistatic struct fmc_driver t_drv; /* initialized later */
22056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
23056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubinistatic irqreturn_t t_handler(int irq, void *dev_id)
24056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini{
25056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	struct fmc_device *fmc = dev_id;
26056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
27056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	fmc->op->irq_ack(fmc);
28056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	dev_info(&fmc->dev, "received irq %i\n", irq);
29056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	return IRQ_HANDLED;
30056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini}
31056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
32056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubinistatic struct fmc_gpio t_gpio[] = {
33056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	{
34056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		.gpio = FMC_GPIO_IRQ(0),
35056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		.mode = GPIOF_DIR_IN,
36056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		.irqmode = IRQF_TRIGGER_RISING,
37056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	}, {
38056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		.gpio = FMC_GPIO_IRQ(1),
39056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		.mode = GPIOF_DIR_IN,
40056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		.irqmode = IRQF_TRIGGER_RISING,
41056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	}
42056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini};
43056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
44056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubinistatic int t_probe(struct fmc_device *fmc)
45056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini{
46056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	int ret;
47056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	int index = 0;
48056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
49056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	if (fmc->op->validate)
50056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		index = fmc->op->validate(fmc, &t_drv);
51056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	if (index < 0)
52056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		return -EINVAL; /* not our device: invalid */
53056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
54056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	ret = fmc->op->irq_request(fmc, t_handler, "fmc-trivial", IRQF_SHARED);
55056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	if (ret < 0)
56056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		return ret;
57056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	/* ignore error code of call below, we really don't care */
58056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	fmc->op->gpio_config(fmc, t_gpio, ARRAY_SIZE(t_gpio));
59056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
60056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	/* Reprogram, if asked to. ESRCH == no filename specified */
61056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	ret = -ESRCH;
62056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	if (fmc->op->reprogram)
63056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		ret = fmc->op->reprogram(fmc, &t_drv, "");
64056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	if (ret == -ESRCH)
65056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		ret = 0;
66056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	if (ret < 0)
67056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini		fmc->op->irq_free(fmc);
68056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
69056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	/* FIXME: reprogram LM32 too */
70056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	return ret;
71056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini}
72056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
73056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubinistatic int t_remove(struct fmc_device *fmc)
74056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini{
75056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	fmc->op->irq_free(fmc);
76056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	return 0;
77056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini}
78056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
79056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubinistatic struct fmc_driver t_drv = {
80056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	.version = FMC_VERSION,
81056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	.driver.name = KBUILD_MODNAME,
82056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	.probe = t_probe,
83056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	.remove = t_remove,
84056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	/* no table, as the current match just matches everything */
85056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini};
86056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
87056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini /* We accept the generic parameters */
88056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro RubiniFMC_PARAM_BUSID(t_drv);
89056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro RubiniFMC_PARAM_GATEWARE(t_drv);
90056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
91056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubinistatic int t_init(void)
92056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini{
93056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	int ret;
94056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
95056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	ret = fmc_driver_register(&t_drv);
96056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	return ret;
97056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini}
98056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
99056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubinistatic void t_exit(void)
100056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini{
101056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini	fmc_driver_unregister(&t_drv);
102056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini}
103056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
104056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubinimodule_init(t_init);
105056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubinimodule_exit(t_exit);
106056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro Rubini
107056d83f3c30c398f14eb879f1d1707e3a7808f4aAlessandro RubiniMODULE_LICENSE("Dual BSD/GPL");
108