[PATCH v2 1/3] drivers/fsi: Add slave interrupt polling
eajames
eajames at linux.vnet.ibm.com
Sat Feb 4 03:18:05 AEDT 2017
On 2017-02-02 19:11, Alistair Popple wrote:
> Hi Edward,
>
> Ideally this should also make use of as much existing kernel
> infrastructure as possible to avoid remiplementing interfaces that
> already exist. In the case of IRQs the kernel already has functions
> implementing interfaces like request_irq() to assign interrupt
> handlers, etc.
Hi,
The problem here is that there is no hardware irq. FSI is using GPIO
lines to access slave registers. The only indication that we have an
interrupt is a bit set in the "slave interrupt status register". So we
have to poll that register and handle the results. I'm not aware of any
way to turn that into a kernel irq and use the existing kernel
framework. But if it can be done, agreed, we should implement that!
Thanks,
Eddie
>
> To hook into these calls you need to implement an irqchip. For example
> arch/powerpc/platforms/powernv/opal-irqchip.c has a resonable example,
> although there are plenty others in the kernel including for I2C
> busses. Searching for "struct irq_chip" should turn up more examples.
>
> Regards,
>
> Alistair
>
> On Thu, 2 Feb 2017 05:26:02 PM eajames at linux.vnet.ibm.com wrote:
>> From: Christopher Bostic <cbostic at linux.vnet.ibm.com>
>>
>> Scan slaves present for asserting interrupt signals in the si1s
>> register and call a registered client's interrupt handler as
>> appropriate.
>>
>> Signed-off-by: Eddie James <eajames at us.ibm.com>
>> Signed-off-by: Christopher Bostic <cbostic at linux.vnet.ibm.com>
>> ---
>> drivers/fsi/fsi-core.c | 99
>> +++++++++++++++++++++++++++++++++++++++++++
>> drivers/fsi/fsi-master-gpio.c | 1 +
>> drivers/fsi/fsi-master.h | 2 +
>> include/linux/fsi.h | 5 +++
>> 4 files changed, 107 insertions(+)
>>
>> diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
>> index 72e0ca3..4df7218 100644
>> --- a/drivers/fsi/fsi-core.c
>> +++ b/drivers/fsi/fsi-core.c
>> @@ -15,11 +15,15 @@
>>
>> #include <linux/device.h>
>> #include <linux/fsi.h>
>> +#include <linux/kthread.h>
>> #include <linux/module.h>
>> #include <linux/slab.h>
>> +#include <linux/jiffies.h>
>>
>> #include "fsi-master.h"
>>
>> +#define DEBUG
>> +
>> #define FSI_N_SLAVES 4
>>
>> #define FSI_SLAVE_CONF_NEXT_MASK 0x80000000
>> @@ -36,7 +40,11 @@
>> #define FSI_PEEK_BASE 0x410
>> #define FSI_SLAVE_BASE 0x800
>>
>> +#define FSI_IPOLL_PERIOD msecs_to_jiffies(fsi_ipoll_period_ms)
>> +
>> static const int engine_page_size = 0x400;
>> +static struct task_struct *master_ipoll;
>> +static unsigned int fsi_ipoll_period_ms = 100;
>>
>> static atomic_t master_idx = ATOMIC_INIT(-1);
>>
>> @@ -60,6 +68,8 @@ static int fsi_slave_write(struct fsi_slave *slave,
>> uint32_t addr,
>> * FSI slave engine control register offsets
>> */
>> #define FSI_SMODE 0x0 /* R/W: Mode register */
>> +#define FSI_SI1M 0x18 /* R/W: IRQ mask */
>> +#define FSI_SI1S 0x1C /* R: IRQ status */
>>
>> /*
>> * SMODE fields
>> @@ -197,6 +207,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
>> uint32_t engine_addr;
>> uint32_t conf;
>> int rc, i;
>> + uint8_t si1s_bit = 1;
>>
>> INIT_LIST_HEAD(&slave->my_engines);
>>
>> @@ -253,6 +264,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
>> dev->unit = i;
>> dev->addr = engine_addr;
>> dev->size = slots * engine_page_size;
>> + dev->si1s_bit = si1s_bit++;
>>
>> dev_info(&slave->dev,
>> "engine[%i]: type %x, version %x, addr %x size %x\n",
>> @@ -507,6 +519,56 @@ static void fsi_master_unscan(struct fsi_master
>> *master)
>> master->slave_list = false;
>> }
>>
>> +static void fsi_master_irq(struct fsi_master *master, int link,
>> uint32_t si1s)
>> +{
>> + struct fsi_slave *slave;
>> + struct fsi_device *fsi_dev;
>> +
>> + if (list_empty(&master->my_slaves))
>> + return;
>> +
>> + slave = list_first_entry(&master->my_slaves, struct fsi_slave,
>> + list_link);
>> +
>> + list_for_each_entry(fsi_dev, &slave->my_engines, link) {
>> + if (si1s & (0x80000000 >> fsi_dev->si1s_bit) &&
>> + fsi_dev->irq_handler)
>> + fsi_dev->irq_handler(0, &fsi_dev->dev);
>> + }
>> +}
>> +
>> +static int fsi_master_ipoll(void *data)
>> +{
>> + int rc;
>> + uint32_t si1s;
>> + unsigned long elapsed = 0;
>> + unsigned long previous_jiffies = jiffies;
>> + struct fsi_master *master = data;
>> +
>> + while (!kthread_should_stop()) {
>> + if (!master->ipoll)
>> + goto done;
>> +
>> + /* Ignore errors for now */
>> + rc = master->read(master, 0, 0, FSI_SLAVE_BASE + FSI_SI1S,
>> + &si1s, sizeof(uint32_t));
>> + if (rc)
>> + goto done;
>> +
>> + if (si1s & master->ipoll)
>> + fsi_master_irq(master, 0, si1s);
>> +done:
>> + elapsed = jiffies - previous_jiffies;
>> + if (elapsed < FSI_IPOLL_PERIOD) {
>> + set_current_state(TASK_UNINTERRUPTIBLE);
>> + schedule_timeout(FSI_IPOLL_PERIOD - elapsed);
>> + }
>> + previous_jiffies = jiffies;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> int fsi_master_register(struct fsi_master *master)
>> {
>> if (!master || !master->dev)
>> @@ -524,9 +586,36 @@ void fsi_master_unregister(struct fsi_master
>> *master)
>> {
>> fsi_master_unscan(master);
>> put_device(master->dev);
>> + if (master_ipoll) {
>> + kthread_stop(master_ipoll);
>> + master_ipoll = NULL;
>> + }
>> }
>> EXPORT_SYMBOL_GPL(fsi_master_unregister);
>>
>> +/*
>> + * TODO: move this to master->start_ipoll( ) -each master may have
>> its
>> + * own way of doing this
>> + */
>> +int fsi_master_start_ipoll(struct fsi_master *master)
>> +{
>> + if (master_ipoll) {
>> + dev_err(master->dev, "Already polling for irqs\n");
>> + return -EALREADY;
>> + }
>> + master_ipoll = kthread_create(fsi_master_ipoll, master,
>> + "fsi_master_ipoll");
>> + if (IS_ERR(master_ipoll)) {
>> + dev_err(master->dev, "Couldn't create ipoll thread rc:%d\n",
>> + (int)PTR_ERR(master_ipoll));
>> + return PTR_ERR(master_ipoll);
>> + }
>> + wake_up_process(master_ipoll);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(fsi_master_start_ipoll);
>> +
>> /* FSI core & Linux bus type definitions */
>>
>> static int fsi_bus_match(struct device *dev, struct device_driver
>> *drv)
>> @@ -566,6 +655,16 @@ void fsi_driver_unregister(struct fsi_driver
>> *fsi_drv)
>> }
>> EXPORT_SYMBOL_GPL(fsi_driver_unregister);
>>
>> +int fsi_enable_irq(struct fsi_device *dev)
>> +{
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(fsi_enable_irq);
>> +
>> +void fsi_disable_irq(struct fsi_device *dev)
>> +{
>> +}
>> +
>> struct bus_type fsi_bus_type = {
>> .name = "fsi",
>> .match = fsi_bus_match,
>> diff --git a/drivers/fsi/fsi-master-gpio.c
>> b/drivers/fsi/fsi-master-gpio.c
>> index 83c7cf0..73d9985 100644
>> --- a/drivers/fsi/fsi-master-gpio.c
>> +++ b/drivers/fsi/fsi-master-gpio.c
>> @@ -457,6 +457,7 @@ static ssize_t store_scan(struct device *dev,
>> /* clear out any old scan data if present */
>> fsi_master_unregister(&master->master);
>> fsi_master_register(&master->master);
>> + fsi_master_start_ipoll(&master->master);
>>
>> return count;
>> }
>> diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
>> index 454af2b..3737404 100644
>> --- a/drivers/fsi/fsi-master.h
>> +++ b/drivers/fsi/fsi-master.h
>> @@ -25,6 +25,7 @@ struct fsi_master {
>> struct device *dev;
>> int idx;
>> int n_links;
>> + uint32_t ipoll;
>> int (*read)(struct fsi_master *, int link,
>> uint8_t slave, uint32_t addr,
>> void *val, size_t size);
>> @@ -37,6 +38,7 @@ struct fsi_master {
>>
>> extern int fsi_master_register(struct fsi_master *master);
>> extern void fsi_master_unregister(struct fsi_master *master);
>> +extern int fsi_master_start_ipoll(struct fsi_master *master);
>>
>> /**
>> * crc4 helper: Given a starting crc4 state @c, calculate the crc4
>> vaue of @x,
>> diff --git a/include/linux/fsi.h b/include/linux/fsi.h
>> index 2721255..d22d0c5 100644
>> --- a/include/linux/fsi.h
>> +++ b/include/linux/fsi.h
>> @@ -23,9 +23,11 @@ struct fsi_device {
>> u8 engine_type;
>> u8 version;
>> u8 unit;
>> + u8 si1s_bit;
>> struct fsi_slave *slave;
>> uint32_t addr;
>> uint32_t size;
>> + int (*irq_handler)(int, void *);
>> };
>>
>> extern int fsi_device_read(struct fsi_device *dev, uint32_t addr,
>> @@ -69,4 +71,7 @@ extern void fsi_driver_unregister(struct fsi_driver
>> *);
>>
>> extern struct bus_type fsi_bus_type;
>>
>> +extern int fsi_enable_irq(struct fsi_device *dev);
>> +extern void fsi_disable_irq(struct fsi_device *dev);
>> +
>> #endif /* LINUX_FSI_H */
>>
More information about the openbmc
mailing list