Merge branch 'i2c-mux/for-next' of https://github.com/peda-r/i2c-mux into i2c/for-4.11
This commit is contained in:
commit
465b2c4ab8
|
@ -19,7 +19,14 @@ Optional Properties:
|
||||||
- i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all
|
- i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all
|
||||||
children in idle state. This is necessary for example, if there are several
|
children in idle state. This is necessary for example, if there are several
|
||||||
multiplexers on the bus and the devices behind them use same I2C addresses.
|
multiplexers on the bus and the devices behind them use same I2C addresses.
|
||||||
|
- interrupt-parent: Phandle for the interrupt controller that services
|
||||||
|
interrupts for this device.
|
||||||
|
- interrupts: Interrupt mapping for IRQ.
|
||||||
|
- interrupt-controller: Marks the device node as an interrupt controller.
|
||||||
|
- #interrupt-cells : Should be two.
|
||||||
|
- first cell is the pin number
|
||||||
|
- second cell is used to specify flags.
|
||||||
|
See also Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -29,6 +36,11 @@ Example:
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
reg = <0x74>;
|
reg = <0x74>;
|
||||||
|
|
||||||
|
interrupt-parent = <&ipic>;
|
||||||
|
interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <2>;
|
||||||
|
|
||||||
i2c@2 {
|
i2c@2 {
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
|
|
|
@ -41,14 +41,20 @@
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c-mux.h>
|
#include <linux/i2c-mux.h>
|
||||||
#include <linux/i2c/pca954x.h>
|
#include <linux/i2c/pca954x.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
#define PCA954X_MAX_NCHANS 8
|
#define PCA954X_MAX_NCHANS 8
|
||||||
|
|
||||||
|
#define PCA954X_IRQ_OFFSET 4
|
||||||
|
|
||||||
enum pca_type {
|
enum pca_type {
|
||||||
pca_9540,
|
pca_9540,
|
||||||
pca_9542,
|
pca_9542,
|
||||||
|
@ -63,6 +69,7 @@ enum pca_type {
|
||||||
struct chip_desc {
|
struct chip_desc {
|
||||||
u8 nchans;
|
u8 nchans;
|
||||||
u8 enable; /* used for muxes only */
|
u8 enable; /* used for muxes only */
|
||||||
|
u8 has_irq;
|
||||||
enum muxtype {
|
enum muxtype {
|
||||||
pca954x_ismux = 0,
|
pca954x_ismux = 0,
|
||||||
pca954x_isswi
|
pca954x_isswi
|
||||||
|
@ -75,6 +82,10 @@ struct pca954x {
|
||||||
u8 last_chan; /* last register value */
|
u8 last_chan; /* last register value */
|
||||||
u8 deselect;
|
u8 deselect;
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
|
||||||
|
struct irq_domain *irq;
|
||||||
|
unsigned int irq_mask;
|
||||||
|
spinlock_t lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Provide specs for the PCA954x types we know about */
|
/* Provide specs for the PCA954x types we know about */
|
||||||
|
@ -84,17 +95,26 @@ static const struct chip_desc chips[] = {
|
||||||
.enable = 0x4,
|
.enable = 0x4,
|
||||||
.muxtype = pca954x_ismux,
|
.muxtype = pca954x_ismux,
|
||||||
},
|
},
|
||||||
|
[pca_9542] = {
|
||||||
|
.nchans = 2,
|
||||||
|
.enable = 0x4,
|
||||||
|
.has_irq = 1,
|
||||||
|
.muxtype = pca954x_ismux,
|
||||||
|
},
|
||||||
[pca_9543] = {
|
[pca_9543] = {
|
||||||
.nchans = 2,
|
.nchans = 2,
|
||||||
|
.has_irq = 1,
|
||||||
.muxtype = pca954x_isswi,
|
.muxtype = pca954x_isswi,
|
||||||
},
|
},
|
||||||
[pca_9544] = {
|
[pca_9544] = {
|
||||||
.nchans = 4,
|
.nchans = 4,
|
||||||
.enable = 0x4,
|
.enable = 0x4,
|
||||||
|
.has_irq = 1,
|
||||||
.muxtype = pca954x_ismux,
|
.muxtype = pca954x_ismux,
|
||||||
},
|
},
|
||||||
[pca_9545] = {
|
[pca_9545] = {
|
||||||
.nchans = 4,
|
.nchans = 4,
|
||||||
|
.has_irq = 1,
|
||||||
.muxtype = pca954x_isswi,
|
.muxtype = pca954x_isswi,
|
||||||
},
|
},
|
||||||
[pca_9547] = {
|
[pca_9547] = {
|
||||||
|
@ -110,7 +130,7 @@ static const struct chip_desc chips[] = {
|
||||||
|
|
||||||
static const struct i2c_device_id pca954x_id[] = {
|
static const struct i2c_device_id pca954x_id[] = {
|
||||||
{ "pca9540", pca_9540 },
|
{ "pca9540", pca_9540 },
|
||||||
{ "pca9542", pca_9540 },
|
{ "pca9542", pca_9542 },
|
||||||
{ "pca9543", pca_9543 },
|
{ "pca9543", pca_9543 },
|
||||||
{ "pca9544", pca_9544 },
|
{ "pca9544", pca_9544 },
|
||||||
{ "pca9545", pca_9545 },
|
{ "pca9545", pca_9545 },
|
||||||
|
@ -124,7 +144,7 @@ MODULE_DEVICE_TABLE(i2c, pca954x_id);
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
static const struct acpi_device_id pca954x_acpi_ids[] = {
|
static const struct acpi_device_id pca954x_acpi_ids[] = {
|
||||||
{ .id = "PCA9540", .driver_data = pca_9540 },
|
{ .id = "PCA9540", .driver_data = pca_9540 },
|
||||||
{ .id = "PCA9542", .driver_data = pca_9540 },
|
{ .id = "PCA9542", .driver_data = pca_9542 },
|
||||||
{ .id = "PCA9543", .driver_data = pca_9543 },
|
{ .id = "PCA9543", .driver_data = pca_9543 },
|
||||||
{ .id = "PCA9544", .driver_data = pca_9544 },
|
{ .id = "PCA9544", .driver_data = pca_9544 },
|
||||||
{ .id = "PCA9545", .driver_data = pca_9545 },
|
{ .id = "PCA9545", .driver_data = pca_9545 },
|
||||||
|
@ -218,6 +238,114 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
|
||||||
return pca954x_reg_write(muxc->parent, client, data->last_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 = 0;
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
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;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
|
|
||||||
|
data->irq_mask &= ~BIT(pos);
|
||||||
|
if (!data->irq_mask)
|
||||||
|
disable_irq(data->client->irq);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&data->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pca954x_irq_unmask(struct irq_data *idata)
|
||||||
|
{
|
||||||
|
struct pca954x *data = irq_data_get_irq_chip_data(idata);
|
||||||
|
unsigned int pos = idata->hwirq;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
|
|
||||||
|
if (!data->irq_mask)
|
||||||
|
enable_irq(data->client->irq);
|
||||||
|
data->irq_mask |= BIT(pos);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&data->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 pca954x_irq_setup(struct i2c_mux_core *muxc)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
spin_lock_init(&data->lock);
|
||||||
|
|
||||||
|
data->irq = irq_domain_add_linear(client->dev.of_node,
|
||||||
|
data->chip->nchans,
|
||||||
|
&irq_domain_simple_ops, data);
|
||||||
|
if (!data->irq)
|
||||||
|
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,
|
||||||
|
"pca954x", data);
|
||||||
|
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);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* I2C init/probing/exit functions
|
* I2C init/probing/exit functions
|
||||||
*/
|
*/
|
||||||
|
@ -282,6 +410,10 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
idle_disconnect_dt = of_node &&
|
idle_disconnect_dt = of_node &&
|
||||||
of_property_read_bool(of_node, "i2c-mux-idle-disconnect");
|
of_property_read_bool(of_node, "i2c-mux-idle-disconnect");
|
||||||
|
|
||||||
|
ret = pca954x_irq_setup(muxc);
|
||||||
|
if (ret)
|
||||||
|
goto fail_del_adapters;
|
||||||
|
|
||||||
/* Now create an adapter for each channel */
|
/* Now create an adapter for each channel */
|
||||||
for (num = 0; num < data->chip->nchans; num++) {
|
for (num = 0; num < data->chip->nchans; num++) {
|
||||||
bool idle_disconnect_pd = false;
|
bool idle_disconnect_pd = false;
|
||||||
|
@ -307,7 +439,7 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"failed to register multiplexed adapter"
|
"failed to register multiplexed adapter"
|
||||||
" %d as bus %d\n", num, force);
|
" %d as bus %d\n", num, force);
|
||||||
goto virt_reg_failed;
|
goto fail_del_adapters;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,7 +450,7 @@ static int pca954x_probe(struct i2c_client *client,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
virt_reg_failed:
|
fail_del_adapters:
|
||||||
i2c_mux_del_adapters(muxc);
|
i2c_mux_del_adapters(muxc);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -326,6 +458,16 @@ virt_reg_failed:
|
||||||
static int pca954x_remove(struct i2c_client *client)
|
static int pca954x_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct i2c_mux_core *muxc = i2c_get_clientdata(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);
|
i2c_mux_del_adapters(muxc);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue