summaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorWolfram Sang <wsa@the-dreams.de>2017-11-01 23:49:36 +0100
committerWolfram Sang <wsa@the-dreams.de>2017-11-01 23:49:36 +0100
commitf6d2953643164525b22edcc09720c2cbf2e52d21 (patch)
treed4bd80a15eab6c4017215096e19bce6fb163e94f /drivers/i2c
parent4ee045f4e9b73c4635ceedbb1ee40e0b3ecbdbcc (diff)
parent4cf419a2b4c2e5bb0583e32e5fe079995c987c0f (diff)
downloadlinux-f6d2953643164525b22edcc09720c2cbf2e52d21.tar.gz
linux-f6d2953643164525b22edcc09720c2cbf2e52d21.tar.bz2
linux-f6d2953643164525b22edcc09720c2cbf2e52d21.zip
Merge branch 'i2c/sbs-manager' into i2c/for-4.15
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-parport-light.c1
-rw-r--r--drivers/i2c/busses/i2c-parport.c1
-rw-r--r--drivers/i2c/busses/i2c-thunderx-pcidrv.c6
-rw-r--r--drivers/i2c/i2c-core-base.c9
-rw-r--r--drivers/i2c/i2c-core-smbus.c55
-rw-r--r--drivers/i2c/i2c-smbus.c81
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c95
7 files changed, 121 insertions, 127 deletions
diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c
index faa8fb8f2b8f..fa41ff799533 100644
--- a/drivers/i2c/busses/i2c-parport-light.c
+++ b/drivers/i2c/busses/i2c-parport-light.c
@@ -123,7 +123,6 @@ static struct i2c_adapter parport_adapter = {
/* SMBus alert support */
static struct i2c_smbus_alert_setup alert_data = {
- .alert_edge_triggered = 1,
};
static struct i2c_client *ara;
static struct lineop parport_ctrl_irq = {
diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c
index a8e54df4aed6..319209a07353 100644
--- a/drivers/i2c/busses/i2c-parport.c
+++ b/drivers/i2c/busses/i2c-parport.c
@@ -237,7 +237,6 @@ static void i2c_parport_attach(struct parport *port)
/* Setup SMBus alert if supported */
if (adapter_parm[type].smbus_alert) {
- adapter->alert_data.alert_edge_triggered = 1;
adapter->ara = i2c_setup_smbus_alert(&adapter->adapter,
&adapter->alert_data);
if (adapter->ara)
diff --git a/drivers/i2c/busses/i2c-thunderx-pcidrv.c b/drivers/i2c/busses/i2c-thunderx-pcidrv.c
index df0976f4432a..1a7cad874756 100644
--- a/drivers/i2c/busses/i2c-thunderx-pcidrv.c
+++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c
@@ -118,8 +118,6 @@ static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk)
static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c,
struct device_node *node)
{
- u32 type;
-
if (!node)
return -EINVAL;
@@ -127,10 +125,6 @@ static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c,
if (!i2c->alert_data.irq)
return -EINVAL;
- type = irqd_get_trigger_type(irq_get_irq_data(i2c->alert_data.irq));
- i2c->alert_data.alert_edge_triggered =
- (type & IRQ_TYPE_LEVEL_MASK) ? 1 : 0;
-
i2c->ara = i2c_setup_smbus_alert(&i2c->adap, &i2c->alert_data);
if (!i2c->ara)
return -ENODEV;
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index db6558e5f657..7b08f3446bfc 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -29,6 +29,7 @@
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
+#include <linux/i2c-smbus.h>
#include <linux/idr.h>
#include <linux/init.h>
#include <linux/irqflags.h>
@@ -1269,6 +1270,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
goto out_list;
}
+ res = of_i2c_setup_smbus_alert(adap);
+ if (res)
+ goto out_reg;
+
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
pm_runtime_no_callbacks(&adap->dev);
@@ -1300,6 +1305,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
return 0;
+out_reg:
+ init_completion(&adap->dev_released);
+ device_unregister(&adap->dev);
+ wait_for_completion(&adap->dev_released);
out_list:
mutex_lock(&core_lock);
idr_remove(&i2c_adapter_idr, adap->nr);
diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c
index 10f00a82ec9d..4bb9927afd01 100644
--- a/drivers/i2c/i2c-core-smbus.c
+++ b/drivers/i2c/i2c-core-smbus.c
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/i2c-smbus.h>
#define CREATE_TRACE_POINTS
#include <trace/events/smbus.h>
@@ -592,3 +593,57 @@ s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
return i;
}
EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated);
+
+/**
+ * i2c_setup_smbus_alert - Setup SMBus alert support
+ * @adapter: the target adapter
+ * @setup: setup data for the SMBus alert handler
+ * Context: can sleep
+ *
+ * Setup handling of the SMBus alert protocol on a given I2C bus segment.
+ *
+ * Handling can be done either through our IRQ handler, or by the
+ * adapter (from its handler, periodic polling, or whatever).
+ *
+ * NOTE that if we manage the IRQ, we *MUST* know if it's level or
+ * edge triggered in order to hand it to the workqueue correctly.
+ * If triggering the alert seems to wedge the system, you probably
+ * should have said it's level triggered.
+ *
+ * This returns the ara client, which should be saved for later use with
+ * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or NULL
+ * to indicate an error.
+ */
+struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
+ struct i2c_smbus_alert_setup *setup)
+{
+ struct i2c_board_info ara_board_info = {
+ I2C_BOARD_INFO("smbus_alert", 0x0c),
+ .platform_data = setup,
+ };
+
+ return i2c_new_device(adapter, &ara_board_info);
+}
+EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert);
+
+#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
+int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter)
+{
+ struct i2c_client *client;
+ int irq;
+
+ irq = of_property_match_string(adapter->dev.of_node, "interrupt-names",
+ "smbus_alert");
+ if (irq == -EINVAL || irq == -ENODATA)
+ return 0;
+ else if (irq < 0)
+ return irq;
+
+ client = i2c_setup_smbus_alert(adapter, NULL);
+ if (!client)
+ return -ENODEV;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert);
+#endif
diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c
index f9271c713d20..5a1dd7f13bac 100644
--- a/drivers/i2c/i2c-smbus.c
+++ b/drivers/i2c/i2c-smbus.c
@@ -21,12 +21,11 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
struct i2c_smbus_alert {
- unsigned int alert_edge_triggered:1;
- int irq;
struct work_struct alert;
struct i2c_client *ara; /* Alert response address */
};
@@ -72,13 +71,12 @@ static int smbus_do_alert(struct device *dev, void *addrp)
* The alert IRQ handler needs to hand work off to a task which can issue
* SMBus calls, because those sleeping calls can't be made in IRQ context.
*/
-static void smbus_alert(struct work_struct *work)
+static irqreturn_t smbus_alert(int irq, void *d)
{
- struct i2c_smbus_alert *alert;
+ struct i2c_smbus_alert *alert = d;
struct i2c_client *ara;
unsigned short prev_addr = 0; /* Not a valid address */
- alert = container_of(work, struct i2c_smbus_alert, alert);
ara = alert->ara;
for (;;) {
@@ -115,21 +113,17 @@ static void smbus_alert(struct work_struct *work)
prev_addr = data.addr;
}
- /* We handled all alerts; re-enable level-triggered IRQs */
- if (!alert->alert_edge_triggered)
- enable_irq(alert->irq);
+ return IRQ_HANDLED;
}
-static irqreturn_t smbalert_irq(int irq, void *d)
+static void smbalert_work(struct work_struct *work)
{
- struct i2c_smbus_alert *alert = d;
+ struct i2c_smbus_alert *alert;
- /* Disable level-triggered IRQs until we handle them */
- if (!alert->alert_edge_triggered)
- disable_irq_nosync(irq);
+ alert = container_of(work, struct i2c_smbus_alert, alert);
+
+ smbus_alert(0, alert);
- schedule_work(&alert->alert);
- return IRQ_HANDLED;
}
/* Setup SMBALERT# infrastructure */
@@ -139,28 +133,35 @@ static int smbalert_probe(struct i2c_client *ara,
struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev);
struct i2c_smbus_alert *alert;
struct i2c_adapter *adapter = ara->adapter;
- int res;
+ int res, irq;
alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert),
GFP_KERNEL);
if (!alert)
return -ENOMEM;
- alert->alert_edge_triggered = setup->alert_edge_triggered;
- alert->irq = setup->irq;
- INIT_WORK(&alert->alert, smbus_alert);
+ if (setup) {
+ irq = setup->irq;
+ } else {
+ irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert");
+ if (irq <= 0)
+ return irq;
+ }
+
+ INIT_WORK(&alert->alert, smbalert_work);
alert->ara = ara;
- if (setup->irq > 0) {
- res = devm_request_irq(&ara->dev, setup->irq, smbalert_irq,
- 0, "smbus_alert", alert);
+ if (irq > 0) {
+ res = devm_request_threaded_irq(&ara->dev, irq,
+ NULL, smbus_alert,
+ IRQF_SHARED | IRQF_ONESHOT,
+ "smbus_alert", alert);
if (res)
return res;
}
i2c_set_clientdata(ara, alert);
- dev_info(&adapter->dev, "supports SMBALERT#, %s trigger\n",
- setup->alert_edge_triggered ? "edge" : "level");
+ dev_info(&adapter->dev, "supports SMBALERT#\n");
return 0;
}
@@ -190,38 +191,6 @@ static struct i2c_driver smbalert_driver = {
};
/**
- * i2c_setup_smbus_alert - Setup SMBus alert support
- * @adapter: the target adapter
- * @setup: setup data for the SMBus alert handler
- * Context: can sleep
- *
- * Setup handling of the SMBus alert protocol on a given I2C bus segment.
- *
- * Handling can be done either through our IRQ handler, or by the
- * adapter (from its handler, periodic polling, or whatever).
- *
- * NOTE that if we manage the IRQ, we *MUST* know if it's level or
- * edge triggered in order to hand it to the workqueue correctly.
- * If triggering the alert seems to wedge the system, you probably
- * should have said it's level triggered.
- *
- * This returns the ara client, which should be saved for later use with
- * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or NULL
- * to indicate an error.
- */
-struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
- struct i2c_smbus_alert_setup *setup)
-{
- struct i2c_board_info ara_board_info = {
- I2C_BOARD_INFO("smbus_alert", 0x0c),
- .platform_data = setup,
- };
-
- return i2c_new_device(adapter, &ara_board_info);
-}
-EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert);
-
-/**
* i2c_handle_smbus_alert - Handle an SMBus alert
* @ara: the ARA client on the relevant adapter
* Context: can't sleep
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index 7b992db38021..2ca068d8b92d 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -246,36 +246,6 @@ static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
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;
-
- raw_spin_lock_irqsave(&data->lock, flags);
-
- data->irq_mask &= ~BIT(pos);
- if (!data->irq_mask)
- disable_irq(data->client->irq);
-
- raw_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;
-
- raw_spin_lock_irqsave(&data->lock, flags);
-
- if (!data->irq_mask)
- enable_irq(data->client->irq);
- data->irq_mask |= BIT(pos);
-
- raw_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)
@@ -285,8 +255,6 @@ static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type)
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,
};
@@ -294,7 +262,7 @@ 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;
+ int c, irq;
if (!data->chip->has_irq || client->irq <= 0)
return 0;
@@ -309,29 +277,31 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
for (c = 0; c < data->chip->nchans; c++) {
irq = irq_create_mapping(data->irq, c);
+ if (!irq) {
+ dev_err(&client->dev, "failed irq create map\n");
+ return -EINVAL;
+ }
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;
+ return 0;
+}
- disable_irq(data->client->irq);
+static void pca954x_cleanup(struct i2c_mux_core *muxc)
+{
+ struct pca954x *data = i2c_mux_priv(muxc);
+ int c, 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);
+ 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);
}
- irq_domain_remove(data->irq);
-
- return err;
+ i2c_mux_del_adapters(muxc);
}
/*
@@ -391,7 +361,7 @@ static int pca954x_probe(struct i2c_client *client,
ret = pca954x_irq_setup(muxc);
if (ret)
- goto fail_del_adapters;
+ goto fail_cleanup;
/* Now create an adapter for each channel */
for (num = 0; num < data->chip->nchans; num++) {
@@ -414,7 +384,16 @@ static int pca954x_probe(struct i2c_client *client,
ret = i2c_mux_add_adapter(muxc, force, num, class);
if (ret)
- goto fail_del_adapters;
+ goto fail_cleanup;
+ }
+
+ if (data->irq) {
+ ret = devm_request_threaded_irq(&client->dev, data->client->irq,
+ NULL, pca954x_irq_handler,
+ IRQF_ONESHOT | IRQF_SHARED,
+ "pca954x", data);
+ if (ret)
+ goto fail_cleanup;
}
dev_info(&client->dev,
@@ -424,26 +403,16 @@ static int pca954x_probe(struct i2c_client *client,
return 0;
-fail_del_adapters:
- i2c_mux_del_adapters(muxc);
+fail_cleanup:
+ pca954x_cleanup(muxc);
return ret;
}
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);
+ pca954x_cleanup(muxc);
return 0;
}