mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 02:32:09 -04:00
Merge branch 'i2c/sbs-manager' into i2c/for-4.15
This commit is contained in:
commit
f6d2953643
14 changed files with 675 additions and 148 deletions
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -189,38 +190,6 @@ static struct i2c_driver smbalert_driver = {
|
|||
.id_table = smbalert_ids,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
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;
|
||||
static void pca954x_cleanup(struct i2c_mux_core *muxc)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue