On 2017-01-04 15:13, Peter Rosin wrote: > On 2017-01-04 10:29, Phil Reid wrote: >> Various muxes can aggregate multiple interrupts from each i2c bus. >> All of the muxes with interrupt support combine the active low irq lines >> using an internal 'and' function and generate a combined active low >> output. The muxes do provide the ability to read a control register to >> determine which irq is active. By making the mux an irq controller isr >> can potentially be reduced by reading the status register and then only >> calling the registered isr on that bus segment. >> >> As there is no irq masking on the mux irq are disabled until irq_unmask is >> called at least once. > > Irqs are not my strong point, I would prefer if someone else also had > a look. And there are some comments below... > > Cheers, > peda > >> Signed-off-by: Phil Reid <preid@xxxxxxxxxxxxxxxxx> >> --- >> drivers/i2c/muxes/i2c-mux-pca954x.c | 126 ++++++++++++++++++++++++++++++++++++ >> 1 file changed, 126 insertions(+) >> >> diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c >> index 981d145..f2dba7c 100644 >> --- a/drivers/i2c/muxes/i2c-mux-pca954x.c >> +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c >> @@ -40,14 +40,19 @@ >> #include <linux/i2c.h> >> #include <linux/i2c-mux.h> >> #include <linux/i2c/pca954x.h> >> +#include <linux/interrupt.h> >> +#include <linux/irq.h> >> #include <linux/module.h> >> #include <linux/of.h> >> #include <linux/of_device.h> >> +#include <linux/of_irq.h> >> #include <linux/pm.h> >> #include <linux/slab.h> >> >> #define PCA954X_MAX_NCHANS 8 >> >> +#define PCA954X_IRQ_OFFSET 4 >> + >> enum pca_type { >> pca_9540, >> pca_9542, >> @@ -62,6 +67,7 @@ enum pca_type { >> struct chip_desc { >> u8 nchans; >> u8 enable; /* used for muxes only */ >> + u8 has_irq; >> enum muxtype { >> pca954x_ismux = 0, >> pca954x_isswi >> @@ -74,6 +80,9 @@ struct pca954x { >> u8 last_chan; /* last register value */ >> u8 deselect; >> struct i2c_client *client; >> + >> + struct irq_domain *irq; >> + unsigned int irq_mask; >> }; >> >> /* Provide specs for the PCA954x types we know about */ >> @@ -86,19 +95,23 @@ struct pca954x { >> [pca_9542] = { >> .nchans = 2, >> .enable = 0x4, >> + .has_irq = 1, >> .muxtype = pca954x_ismux, >> }, >> [pca_9543] = { >> .nchans = 2, >> + .has_irq = 1, >> .muxtype = pca954x_isswi, >> }, >> [pca_9544] = { >> .nchans = 4, >> .enable = 0x4, >> + .has_irq = 1, >> .muxtype = pca954x_ismux, >> }, >> [pca_9545] = { >> .nchans = 4, >> + .has_irq = 1, >> .muxtype = pca954x_isswi, >> }, >> [pca_9547] = { >> @@ -203,6 +216,104 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan) >> return pca954x_reg_write(muxc->parent, client, data->last_chan); >> } >> >> +static irqreturn_t pca954x_irq_handler(int irq, void *dev_id) >> +{ >> + struct pca954x *data = dev_id; >> + unsigned int child_irq; >> + int ret, i, handled; >> + >> + ret = i2c_smbus_read_byte(data->client); >> + if (ret < 0) >> + return IRQ_NONE; >> + >> + for (i = 0; i < data->chip->nchans; i++) { >> + if (ret & BIT(PCA954X_IRQ_OFFSET+i)) { > > Spaces around the + > >> + child_irq = irq_linear_revmap(data->irq, i); >> + handle_nested_irq(child_irq); >> + handled++; >> + } >> + } >> + return handled ? IRQ_HANDLED : IRQ_NONE; >> +} >> + >> +static void pca954x_irq_mask(struct irq_data *idata) >> +{ >> + struct pca954x *data = irq_data_get_irq_chip_data(idata); >> + unsigned int pos = idata->hwirq; >> + >> + data->irq_mask &= ~BIT(pos); >> + if (!data->irq_mask) >> + disable_irq(data->client->irq); >> +} >> + >> +static void pca954x_irq_unmask(struct irq_data *idata) >> +{ >> + struct pca954x *data = irq_data_get_irq_chip_data(idata); >> + unsigned int pos = idata->hwirq; >> + >> + if (!data->irq_mask) >> + enable_irq(data->client->irq); >> + data->irq_mask |= BIT(pos); >> +} >> + >> +static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type) >> +{ >> + if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_LOW) >> + return -EINVAL; >> + return 0; >> +} >> + >> +static struct irq_chip pca954x_irq_chip = { >> + .name = "i2c-mux-pca954x", >> + .irq_mask = pca954x_irq_mask, >> + .irq_unmask = pca954x_irq_unmask, >> + .irq_set_type = pca954x_irq_set_type, >> +}; >> + >> +static int pca953x_irq_setup(struct i2c_mux_core *muxc) Ooop, pca954x_irq_setup >> +{ >> + struct pca954x *data = i2c_mux_priv(muxc); >> + struct i2c_client *client = data->client; >> + int c, err, irq; >> + >> + if (!data->chip->has_irq || client->irq <= 0) >> + return 0; >> + >> + data->irq = irq_domain_add_linear(client->dev.of_node, >> + data->chip->nchans, >> + &irq_domain_simple_ops, data); >> + if (!data->irq) { >> + err = -ENODEV; >> + goto err_irq_domain; > > This label is not needed, just return -ENODEV > >> + } >> + >> + for (c = 0; c < data->chip->nchans; c++) { >> + irq = irq_create_mapping(data->irq, c); >> + irq_set_chip_data(irq, data); >> + irq_set_chip_and_handler(irq, &pca954x_irq_chip, >> + handle_simple_irq); >> + } >> + >> + err = devm_request_threaded_irq(&client->dev, data->client->irq, NULL, >> + pca954x_irq_handler, >> + IRQF_ONESHOT | IRQF_SHARED, >> + "pca953x", data); > > "pca954x" > >> + if (err) >> + goto err_req_irq; >> + >> + disable_irq(data->client->irq); >> + >> + return 0; >> +err_req_irq: >> + for (c = 0; c < data->chip->nchans; c++) { >> + irq = irq_find_mapping(data->irq, c); >> + irq_dispose_mapping(irq); >> + } >> + irq_domain_remove(data->irq); >> +err_irq_domain: >> + return err; >> +} >> + >> /* >> * I2C init/probing/exit functions >> */ >> @@ -258,6 +369,10 @@ static int pca954x_probe(struct i2c_client *client, >> idle_disconnect_dt = of_node && >> of_property_read_bool(of_node, "i2c-mux-idle-disconnect"); >> >> + ret = pca953x_irq_setup(muxc); >> + if (ret) >> + goto irq_setup_failed; >> + >> /* Now create an adapter for each channel */ >> for (num = 0; num < data->chip->nchans; num++) { >> bool idle_disconnect_pd = false; >> @@ -294,6 +409,7 @@ static int pca954x_probe(struct i2c_client *client, >> >> return 0; >> >> +irq_setup_failed: >> virt_reg_failed: > > Rename the virt_reg_failed label to name what is reversed instead of what > happened to fail. E.g. fail_del_adapters, or something. > >> i2c_mux_del_adapters(muxc); >> return ret; >> @@ -302,6 +418,16 @@ static int pca954x_probe(struct i2c_client *client, >> static int pca954x_remove(struct i2c_client *client) >> { >> struct i2c_mux_core *muxc = i2c_get_clientdata(client); >> + struct pca954x *data = i2c_mux_priv(muxc); >> + int c, irq; >> + >> + if (data->irq) { >> + for (c = 0; c < data->chip->nchans; c++) { >> + irq = irq_find_mapping(data->irq, c); >> + irq_dispose_mapping(irq); >> + } >> + irq_domain_remove(data->irq); >> + } >> >> i2c_mux_del_adapters(muxc); >> return 0; >> > -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html