From 0f614d834bccd3537881c5d0933803b407ce3283 Mon Sep 17 00:00:00 2001 From: Jean-Michel Hautbois Date: Sun, 31 Jan 2016 16:33:00 +0100 Subject: i2c: Add generic support passing secondary devices addresses Some I2C devices have multiple addresses assigned, for example each address corresponding to a different internal register map page of the device. So far drivers which need support for this have handled this with a driver specific and non-generic implementation, e.g. passing the additional address via platform data. This patch provides a new helper function called i2c_new_secondary_device() which is intended to provide a generic way to get the secondary address as well as instantiate a struct i2c_client for the secondary address. The function expects a pointer to the primary i2c_client, a name for the secondary address and an optional default address. The name is used as a handle to specify which secondary address to get. The default address is used as a fallback in case no secondary address was explicitly specified. In case no secondary address and no default address were specified the function returns NULL. For now the function only supports look-up of the secondary address from devicetree, but it can be extended in the future to for example support board files and/or ACPI. Signed-off-by: Jean-Michel Hautbois Acked-by: Rob Herring Acked-by: Mika Westerberg Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index af11b658984d..952d2f0c02c5 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1145,6 +1145,47 @@ struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address) } EXPORT_SYMBOL_GPL(i2c_new_dummy); +/** + * i2c_new_secondary_device - Helper to get the instantiated secondary address + * and create the associated device + * @client: Handle to the primary client + * @name: Handle to specify which secondary address to get + * @default_addr: Used as a fallback if no secondary address was specified + * Context: can sleep + * + * I2C clients can be composed of multiple I2C slaves bound together in a single + * component. The I2C client driver then binds to the master I2C slave and needs + * to create I2C dummy clients to communicate with all the other slaves. + * + * This function creates and returns an I2C dummy client whose I2C address is + * retrieved from the platform firmware based on the given slave name. If no + * address is specified by the firmware default_addr is used. + * + * On DT-based platforms the address is retrieved from the "reg" property entry + * cell whose "reg-names" value matches the slave name. + * + * This returns the new i2c client, which should be saved for later use with + * i2c_unregister_device(); or NULL to indicate an error. + */ +struct i2c_client *i2c_new_secondary_device(struct i2c_client *client, + const char *name, + u16 default_addr) +{ + struct device_node *np = client->dev.of_node; + u32 addr = default_addr; + int i; + + if (np) { + i = of_property_match_string(np, "reg-names", name); + if (i >= 0) + of_property_read_u32_index(np, "reg", i, &addr); + } + + dev_dbg(&client->adapter->dev, "Address for %s : 0x%x\n", name, addr); + return i2c_new_dummy(client->adapter, addr); +} +EXPORT_SYMBOL_GPL(i2c_new_secondary_device); + /* ------------------------------------------------------------------------- */ /* I2C bus adapters -- one roots each I2C or SMBUS segment */ -- cgit v1.2.3 From a90bc5d9a77a4e0d5e65f69ef56eb40627f50e3e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 25 May 2016 09:37:02 +0200 Subject: i2c: i801: Drop needless bit-wise OR The interrupt handling code makes it look like several status values may be merged together before being processed, while this will never happen. Change from bit-wise OR to simple assignment to make it more obvious and avoid misunderstanding. Signed-off-by: Jean Delvare Reviewed-by: Mika Westerberg Reviewed-by: Daniel Kurtz Reviewed-by: Benjamin Tissoires Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-i801.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 4a60ad214747..b43696394d5b 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -547,7 +547,7 @@ static irqreturn_t i801_isr(int irq, void *dev_id) status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS; if (status) { outb_p(status, SMBHSTSTS(priv)); - priv->status |= status; + priv->status = status; wake_up(&priv->waitq); } -- cgit v1.2.3 From 27bfeb5a0619554d9734fb39e14f0e80fa7c342c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 5 Jun 2016 11:41:42 +0200 Subject: i2c: jz4780: drop superfluous init David reported that the length for memset was incorrect (element sizes were not taken into account). Then I saw that we are clearing kzalloced memory, so we can simply drop this code. Reported-by: David Binderman Reviewed-by: Axel Lin Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-jz4780.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c index ba14a863b451..cd9872594fe2 100644 --- a/drivers/i2c/busses/i2c-jz4780.c +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -791,10 +791,6 @@ static int jz4780_i2c_probe(struct platform_device *pdev) jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0x0); - i2c->cmd = 0; - memset(i2c->cmd_buf, 0, BUFSIZE); - memset(i2c->data_buf, 0, BUFSIZE); - i2c->irq = platform_get_irq(pdev, 0); ret = devm_request_irq(&pdev->dev, i2c->irq, jz4780_i2c_irq, 0, dev_name(&pdev->dev), i2c); -- cgit v1.2.3 From 33c77abcf4aa5e9679f702a2f979d44a470f6e6e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 2 Jun 2016 13:28:48 +0800 Subject: i2c: robotfuzz-osif: Constify osif_table osif_table is never modified, so declare it as const. Signed-off-by: Axel Lin Acked-by: Andrew Lunn Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-robotfuzz-osif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-robotfuzz-osif.c b/drivers/i2c/busses/i2c-robotfuzz-osif.c index ced9c6a308d1..89d8b41b6668 100644 --- a/drivers/i2c/busses/i2c-robotfuzz-osif.c +++ b/drivers/i2c/busses/i2c-robotfuzz-osif.c @@ -125,7 +125,7 @@ static struct i2c_algorithm osif_algorithm = { #define USB_OSIF_VENDOR_ID 0x1964 #define USB_OSIF_PRODUCT_ID 0x0001 -static struct usb_device_id osif_table[] = { +static const struct usb_device_id osif_table[] = { { USB_DEVICE(USB_OSIF_VENDOR_ID, USB_OSIF_PRODUCT_ID) }, { } }; -- cgit v1.2.3 From b4f210541fc319bd643ad9a4fdbfe2ce31be6cfc Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 9 Jun 2016 16:53:47 +0200 Subject: i2c: add a protocol parameter to the alert callback .alert() is meant to be generic, but there is currently no way for the device driver to know which protocol generated the alert. Add a parameter in .alert() to help the device driver to understand what is given in data. This patch is required to have the support of SMBus Host Notify protocol through .alert(). Tested-by: Andrew Duggan For hwmon: Acked-by: Guenter Roeck For IPMI: Acked-by: Corey Minyard Signed-off-by: Benjamin Tissoires Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-smbus.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index abb55d3e76f3..3b6765a4ebe9 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -56,7 +56,8 @@ static int smbus_do_alert(struct device *dev, void *addrp) if (client->dev.driver) { driver = to_i2c_driver(client->dev.driver); if (driver->alert) - driver->alert(client, data->flag); + driver->alert(client, I2C_PROTOCOL_SMBUS_ALERT, + data->flag); else dev_warn(&client->dev, "no driver alert()!\n"); } else -- cgit v1.2.3 From e456cd37bc28abe47dc65189df916ac0510ac1d4 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 9 Jun 2016 16:53:48 +0200 Subject: i2c: smbus: add SMBus Host Notify support SMBus Host Notify allows a slave device to act as a master on a bus to notify the host of an interrupt. On Intel chipsets, the functionality is directly implemented in the firmware. We just need to export a function to call .alert() on the proper device driver. i2c_handle_smbus_host_notify() behaves like i2c_handle_smbus_alert(). When called, it schedules a task that will be able to sleep to go through the list of devices attached to the adapter. The current implementation allows one Host Notification to be scheduled while an other is running. Tested-by: Andrew Duggan Signed-off-by: Benjamin Tissoires Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-smbus.c | 113 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 5 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index 3b6765a4ebe9..f574995b41c1 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -33,7 +33,8 @@ struct i2c_smbus_alert { struct alert_data { unsigned short addr; - u8 flag:1; + enum i2c_alert_protocol type; + unsigned int data; }; /* If this is the alerting device, notify its driver */ @@ -56,8 +57,7 @@ static int smbus_do_alert(struct device *dev, void *addrp) if (client->dev.driver) { driver = to_i2c_driver(client->dev.driver); if (driver->alert) - driver->alert(client, I2C_PROTOCOL_SMBUS_ALERT, - data->flag); + driver->alert(client, data->type, data->data); else dev_warn(&client->dev, "no driver alert()!\n"); } else @@ -97,8 +97,9 @@ static void smbus_alert(struct work_struct *work) if (status < 0) break; - data.flag = status & 1; + data.data = status & 1; data.addr = status >> 1; + data.type = I2C_PROTOCOL_SMBUS_ALERT; if (data.addr == prev_addr) { dev_warn(&ara->dev, "Duplicate SMBALERT# from dev " @@ -106,7 +107,7 @@ static void smbus_alert(struct work_struct *work) break; } dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n", - data.addr, data.flag); + data.addr, data.data); /* Notify driver for the device which issued the alert */ device_for_each_child(&ara->adapter->dev, &data, @@ -240,6 +241,108 @@ int i2c_handle_smbus_alert(struct i2c_client *ara) } EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert); +static void smbus_host_notify_work(struct work_struct *work) +{ + struct alert_data alert; + struct i2c_adapter *adapter; + unsigned long flags; + u16 payload; + u8 addr; + struct smbus_host_notify *data; + + data = container_of(work, struct smbus_host_notify, work); + + spin_lock_irqsave(&data->lock, flags); + payload = data->payload; + addr = data->addr; + adapter = data->adapter; + + /* clear the pending bit and release the spinlock */ + data->pending = false; + spin_unlock_irqrestore(&data->lock, flags); + + if (!adapter || !addr) + return; + + alert.type = I2C_PROTOCOL_SMBUS_HOST_NOTIFY; + alert.addr = addr; + alert.data = payload; + + device_for_each_child(&adapter->dev, &alert, smbus_do_alert); +} + +/** + * i2c_setup_smbus_host_notify - Allocate a new smbus_host_notify for the given + * I2C adapter. + * @adapter: the adapter we want to associate a Host Notify function + * + * Returns a struct smbus_host_notify pointer on success, and NULL on failure. + * The resulting smbus_host_notify must not be freed afterwards, it is a + * managed resource already. + */ +struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap) +{ + struct smbus_host_notify *host_notify; + + host_notify = devm_kzalloc(&adap->dev, sizeof(struct smbus_host_notify), + GFP_KERNEL); + if (!host_notify) + return NULL; + + host_notify->adapter = adap; + + spin_lock_init(&host_notify->lock); + INIT_WORK(&host_notify->work, smbus_host_notify_work); + + return host_notify; +} +EXPORT_SYMBOL_GPL(i2c_setup_smbus_host_notify); + +/** + * i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct + * I2C client. + * @host_notify: the struct host_notify attached to the relevant adapter + * @data: the Host Notify data which contains the payload and address of the + * client + * Context: can't sleep + * + * Helper function to be called from an I2C bus driver's interrupt + * handler. It will schedule the Host Notify work, in turn calling the + * corresponding I2C device driver's alert function. + * + * host_notify should be a valid pointer previously returned by + * i2c_setup_smbus_host_notify(). + */ +int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify, + unsigned short addr, unsigned int data) +{ + unsigned long flags; + struct i2c_adapter *adapter; + + if (!host_notify || !host_notify->adapter) + return -EINVAL; + + adapter = host_notify->adapter; + + spin_lock_irqsave(&host_notify->lock, flags); + + if (host_notify->pending) { + spin_unlock_irqrestore(&host_notify->lock, flags); + dev_warn(&adapter->dev, "Host Notify already scheduled.\n"); + return -EBUSY; + } + + host_notify->payload = data; + host_notify->addr = addr; + + /* Mark that there is a pending notification and release the lock */ + host_notify->pending = true; + spin_unlock_irqrestore(&host_notify->lock, flags); + + return schedule_work(&host_notify->work); +} +EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify); + module_i2c_driver(smbalert_driver); MODULE_AUTHOR("Jean Delvare "); -- cgit v1.2.3 From 0a6ad2f95f36bf1686c6598697d8afc7daca2a03 Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 16 May 2016 21:57:36 +0800 Subject: i2c: rk3x: add documentation to fields in "struct rk3x_i2c" Add kernel-doc documentation for the elements of the previously undocumented struct rk3x_i2c. Signed-off-by: David Wu Reviewed-by: Douglas Anderson Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rk3x.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 80bed02cd942..7e45d5101d32 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -90,6 +90,26 @@ struct rk3x_i2c_soc_data { int grf_offset; }; +/** + * struct rk3x_i2c - private data of the controller + * @adap: corresponding I2C adapter + * @dev: device for this controller + * @soc_data: related soc data struct + * @regs: virtual memory area + * @clk: clock of i2c bus + * @clk_rate_nb: i2c clk rate change notify + * @t: I2C known timing information + * @lock: spinlock for the i2c bus + * @wait: the waitqueue to wait for i2c transfer + * @busy: the condition for the event to wait for + * @msg: current i2c message + * @addr: addr of i2c slave device + * @mode: mode of i2c transfer + * @is_last_msg: flag determines whether it is the last msg in this transfer + * @state: state of i2c transfer + * @processed: byte length which has been send or received + * @error: error code for i2c transfer + */ struct rk3x_i2c { struct i2c_adapter adap; struct device *dev; @@ -116,7 +136,7 @@ struct rk3x_i2c { /* I2C state machine */ enum rk3x_i2c_state state; - unsigned int processed; /* sent/received bytes */ + unsigned int processed; int error; }; -- cgit v1.2.3 From e26747bf53b145dc4a64ad518915da8c9a40fef2 Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 16 May 2016 21:57:37 +0800 Subject: i2c: rk3x: use struct "rk3x_i2c_calced_timings" The "div_high" and "div_low" values are always used together. Group them into a structure to make it easier to pass them both around. This structure also provides a place for future calculated timings. Signed-off-by: David Wu Reviewed-by: Douglas Anderson Tested-by: Heiko Stuebner Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rk3x.c | 55 +++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 23 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 7e45d5101d32..1e2677ae1638 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -75,6 +75,16 @@ enum { #define WAIT_TIMEOUT 1000 /* ms */ #define DEFAULT_SCL_RATE (100 * 1000) /* Hz */ +/** + * struct rk3x_i2c_calced_timings: + * @div_low: Divider output for low + * @div_high: Divider output for high + */ +struct rk3x_i2c_calced_timings { + unsigned long div_low; + unsigned long div_high; +}; + enum rk3x_i2c_state { STATE_IDLE, STATE_START, @@ -454,9 +464,8 @@ out: * Calculate divider values for desired SCL frequency * * @clk_rate: I2C input clock rate - * @t: Known I2C timing information. - * @div_low: Divider output for low - * @div_high: Divider output for high + * @t: Known I2C timing information + * @t_calc: Caculated rk3x private timings that would be written into regs * * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case * a best-effort divider value is returned in divs. If the target rate is @@ -464,8 +473,7 @@ out: */ static int rk3x_i2c_calc_divs(unsigned long clk_rate, struct i2c_timings *t, - unsigned long *div_low, - unsigned long *div_high) + struct rk3x_i2c_calced_timings *t_calc) { unsigned long spec_min_low_ns, spec_min_high_ns; unsigned long spec_setup_start, spec_max_data_hold_ns; @@ -572,8 +580,8 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, * Time needed to meet hold requirements is important. * Just use that. */ - *div_low = min_low_div; - *div_high = min_high_div; + t_calc->div_low = min_low_div; + t_calc->div_high = min_high_div; } else { /* * We've got to distribute some time among the low and high @@ -602,25 +610,25 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, /* Give low the "ideal" and give high whatever extra is left */ extra_low_div = ideal_low_div - min_low_div; - *div_low = ideal_low_div; - *div_high = min_high_div + (extra_div - extra_low_div); + t_calc->div_low = ideal_low_div; + t_calc->div_high = min_high_div + (extra_div - extra_low_div); } /* * Adjust to the fact that the hardware has an implicit "+1". * NOTE: Above calculations always produce div_low > 0 and div_high > 0. */ - *div_low = *div_low - 1; - *div_high = *div_high - 1; + t_calc->div_low--; + t_calc->div_high--; /* Maximum divider supported by hw is 0xffff */ - if (*div_low > 0xffff) { - *div_low = 0xffff; + if (t_calc->div_low > 0xffff) { + t_calc->div_low = 0xffff; ret = -EINVAL; } - if (*div_high > 0xffff) { - *div_high = 0xffff; + if (t_calc->div_high > 0xffff) { + t_calc->div_high = 0xffff; ret = -EINVAL; } @@ -630,19 +638,21 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate) { struct i2c_timings *t = &i2c->t; - unsigned long div_low, div_high; + struct rk3x_i2c_calced_timings calc; u64 t_low_ns, t_high_ns; int ret; - ret = rk3x_i2c_calc_divs(clk_rate, t, &div_low, &div_high); + ret = rk3x_i2c_calc_divs(clk_rate, t, &calc); WARN_ONCE(ret != 0, "Could not reach SCL freq %u", t->bus_freq_hz); clk_enable(i2c->clk); - i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV); + i2c_writel(i2c, (calc.div_high << 16) | (calc.div_low & 0xffff), + REG_CLKDIV); clk_disable(i2c->clk); - t_low_ns = div_u64(((u64)div_low + 1) * 8 * 1000000000, clk_rate); - t_high_ns = div_u64(((u64)div_high + 1) * 8 * 1000000000, clk_rate); + t_low_ns = div_u64(((u64)calc.div_low + 1) * 8 * 1000000000, clk_rate); + t_high_ns = div_u64(((u64)calc.div_high + 1) * 8 * 1000000000, + clk_rate); dev_dbg(i2c->dev, "CLK %lukhz, Req %uns, Act low %lluns high %lluns\n", clk_rate / 1000, @@ -672,12 +682,11 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long { struct clk_notifier_data *ndata = data; struct rk3x_i2c *i2c = container_of(nb, struct rk3x_i2c, clk_rate_nb); - unsigned long div_low, div_high; + struct rk3x_i2c_calced_timings calc; switch (event) { case PRE_RATE_CHANGE: - if (rk3x_i2c_calc_divs(ndata->new_rate, &i2c->t, - &div_low, &div_high) != 0) + if (rk3x_i2c_calc_divs(ndata->new_rate, &i2c->t, &calc) != 0) return NOTIFY_STOP; /* scale up */ -- cgit v1.2.3 From 69eda367ac4240428b9d16ab0c81113753ea079a Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 16 May 2016 21:57:38 +0800 Subject: i2c: rk3x: Remove redundant rk3x_i2c_clean_ipd() rk3x_i2c_setup() gets called directly before rk3x_i2c_start(), and the last thing in setup was to clean the IPD, so no reason to do it at the beginning of start. Signed-off-by: David Wu Reviewed-by: Douglas Anderson Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rk3x.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 1e2677ae1638..9eeb4e5312f3 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -174,7 +174,6 @@ static void rk3x_i2c_start(struct rk3x_i2c *i2c) { u32 val; - rk3x_i2c_clean_ipd(i2c); i2c_writel(i2c, REG_INT_START, REG_IEN); /* enable adapter with correct mode, send START condition */ -- cgit v1.2.3 From bef358c4fe2efa7467297303cafe973748afda93 Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 16 May 2016 21:57:39 +0800 Subject: i2c: rk3x: Change SoC data to not use array Specifying the i2c SoC data in an array provides very little benefit and gets unwieldly / confusing as the array grows since the next bit of code needs to refer to elements in the array by their raw integral index. Let's just create a single 'static const' structure for each SoC so that we can refer to these structures by ID. Signed-off-by: David Wu Reviewed-by: Heiko Stuebner Suggested-by: Douglas Anderson Reviewed-by: Douglas Anderson Tested-by: Heiko Stuebner Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rk3x.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 9eeb4e5312f3..d7a871f9a52d 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -864,17 +864,39 @@ static const struct i2c_algorithm rk3x_i2c_algorithm = { .functionality = rk3x_i2c_func, }; -static struct rk3x_i2c_soc_data soc_data[3] = { - { .grf_offset = 0x154 }, /* rk3066 */ - { .grf_offset = 0x0a4 }, /* rk3188 */ - { .grf_offset = -1 }, /* no I2C switching needed */ +static const struct rk3x_i2c_soc_data rk3066_soc_data = { + .grf_offset = 0x154, +}; + +static const struct rk3x_i2c_soc_data rk3188_soc_data = { + .grf_offset = 0x0a4, +}; + +static const struct rk3x_i2c_soc_data rk3228_soc_data = { + .grf_offset = -1, +}; + +static const struct rk3x_i2c_soc_data rk3288_soc_data = { + .grf_offset = -1, }; static const struct of_device_id rk3x_i2c_match[] = { - { .compatible = "rockchip,rk3066-i2c", .data = (void *)&soc_data[0] }, - { .compatible = "rockchip,rk3188-i2c", .data = (void *)&soc_data[1] }, - { .compatible = "rockchip,rk3228-i2c", .data = (void *)&soc_data[2] }, - { .compatible = "rockchip,rk3288-i2c", .data = (void *)&soc_data[2] }, + { + .compatible = "rockchip,rk3066-i2c", + .data = (void *)&rk3066_soc_data + }, + { + .compatible = "rockchip,rk3188-i2c", + .data = (void *)&rk3188_soc_data + }, + { + .compatible = "rockchip,rk3228-i2c", + .data = (void *)&rk3228_soc_data + }, + { + .compatible = "rockchip,rk3288-i2c", + .data = (void *)&rk3288_soc_data + }, {}, }; MODULE_DEVICE_TABLE(of, rk3x_i2c_match); -- cgit v1.2.3 From b58fd3be40a21a88520d4a3682330abbbc021cc7 Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 16 May 2016 22:03:24 +0800 Subject: i2c: rk3x: Move spec timing data to "static const" structs The i2c timing specs are really just constant data. There's no reason to write code to init them, so move them out to structures. This not only is a cleaner solution but it will reduce code duplication when we introduce a new variant of rk3x_i2c_calc_divs() in a future patch. Signed-off-by: David Wu Suggested-by: Douglas Anderson Reviewed-by: Douglas Anderson Tested-by: Heiko Stuebner Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rk3x.c | 83 ++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 28 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index d7a871f9a52d..97917978ddd9 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -75,6 +75,34 @@ enum { #define WAIT_TIMEOUT 1000 /* ms */ #define DEFAULT_SCL_RATE (100 * 1000) /* Hz */ +/** + * struct i2c_spec_values: + * @min_low_ns: min LOW period of the SCL clock + * @min_high_ns: min HIGH period of the SCL cloc + * @min_setup_start_ns: min set-up time for a repeated START conditio + * @max_data_hold_ns: max data hold time + */ +struct i2c_spec_values { + unsigned long min_low_ns; + unsigned long min_high_ns; + unsigned long min_setup_start_ns; + unsigned long max_data_hold_ns; +}; + +static const struct i2c_spec_values standard_mode_spec = { + .min_low_ns = 4700, + .min_high_ns = 4000, + .min_setup_start_ns = 4700, + .max_data_hold_ns = 3450, +}; + +static const struct i2c_spec_values fast_mode_spec = { + .min_low_ns = 1300, + .min_high_ns = 600, + .min_setup_start_ns = 600, + .max_data_hold_ns = 900, +}; + /** * struct rk3x_i2c_calced_timings: * @div_low: Divider output for low @@ -459,6 +487,21 @@ out: return IRQ_HANDLED; } +/** + * Get timing values of I2C specification + * + * @speed: Desired SCL frequency + * + * Returns: Matched i2c spec values. + */ +static const struct i2c_spec_values *rk3x_i2c_get_spec(unsigned int speed) +{ + if (speed <= 100000) + return &standard_mode_spec; + else + return &fast_mode_spec; +} + /** * Calculate divider values for desired SCL frequency * @@ -474,10 +517,6 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, struct i2c_timings *t, struct rk3x_i2c_calced_timings *t_calc) { - unsigned long spec_min_low_ns, spec_min_high_ns; - unsigned long spec_setup_start, spec_max_data_hold_ns; - unsigned long data_hold_buffer_ns; - unsigned long min_low_ns, min_high_ns; unsigned long max_low_ns, min_total_ns; @@ -489,6 +528,8 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long min_div_for_hold, min_total_div; unsigned long extra_div, extra_low_div, ideal_low_div; + unsigned long data_hold_buffer_ns = 50; + const struct i2c_spec_values *spec; int ret = 0; /* Only support standard-mode and fast-mode */ @@ -511,22 +552,8 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, * This is because the i2c host on Rockchip holds the data line * for half the low time. */ - if (t->bus_freq_hz <= 100000) { - /* Standard-mode */ - spec_min_low_ns = 4700; - spec_setup_start = 4700; - spec_min_high_ns = 4000; - spec_max_data_hold_ns = 3450; - data_hold_buffer_ns = 50; - } else { - /* Fast-mode */ - spec_min_low_ns = 1300; - spec_setup_start = 600; - spec_min_high_ns = 600; - spec_max_data_hold_ns = 900; - data_hold_buffer_ns = 50; - } - min_high_ns = t->scl_rise_ns + spec_min_high_ns; + spec = rk3x_i2c_get_spec(t->bus_freq_hz); + min_high_ns = t->scl_rise_ns + spec->min_high_ns; /* * Timings for repeated start: @@ -536,14 +563,14 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, * We need to account for those rules in picking our "high" time so * we meet tSU;STA and tHD;STA times. */ - min_high_ns = max(min_high_ns, - DIV_ROUND_UP((t->scl_rise_ns + spec_setup_start) * 1000, 875)); - min_high_ns = max(min_high_ns, - DIV_ROUND_UP((t->scl_rise_ns + spec_setup_start + - t->sda_fall_ns + spec_min_high_ns), 2)); - - min_low_ns = t->scl_fall_ns + spec_min_low_ns; - max_low_ns = spec_max_data_hold_ns * 2 - data_hold_buffer_ns; + min_high_ns = max(min_high_ns, DIV_ROUND_UP( + (t->scl_rise_ns + spec->min_setup_start_ns) * 1000, 875)); + min_high_ns = max(min_high_ns, DIV_ROUND_UP( + (t->scl_rise_ns + spec->min_setup_start_ns + t->sda_fall_ns + + spec->min_high_ns), 2)); + + min_low_ns = t->scl_fall_ns + spec->min_low_ns; + max_low_ns = spec->max_data_hold_ns * 2 - data_hold_buffer_ns; min_total_ns = min_low_ns + min_high_ns; /* Adjust to avoid overflow */ -- cgit v1.2.3 From 7e086c3fc2df099f82371f320fef8d683f050be4 Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 16 May 2016 22:05:03 +0800 Subject: i2c: rk3x: add i2c support for rk3399 soc - new method to caculate i2c timings for rk3399: There was an timing issue about "repeated start" time at the I2C controller of version0, controller appears to drop SDA at .875x (7/8) programmed clk high. On version 1 of the controller, the rule(.875x) isn't enough to meet tSU;STA requirements on 100k's Standard-mode. To resolve this issue, sda_update_config, start_setup_config and stop_setup_config for I2C timing information are added, new rules are designed to calculate the timing information at new v1. - pclk and function clk are separated at rk3399 Signed-off-by: David Wu Tested-by: Caesar Wang Tested-by: Heiko Stuebner Reviewed-by: Douglas Anderson [wsa: fixed whitespace issue] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rk3x.c | 294 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 274 insertions(+), 20 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 97917978ddd9..e9ce6a33e8c4 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -58,6 +58,12 @@ enum { #define REG_CON_LASTACK BIT(5) /* 1: send NACK after last received byte */ #define REG_CON_ACTACK BIT(6) /* 1: stop if NACK is received */ +#define REG_CON_TUNING_MASK GENMASK(15, 8) + +#define REG_CON_SDA_CFG(cfg) ((cfg) << 8) +#define REG_CON_STA_CFG(cfg) ((cfg) << 12) +#define REG_CON_STO_CFG(cfg) ((cfg) << 14) + /* REG_MRXADDR bits */ #define REG_MRXADDR_VALID(x) BIT(24 + (x)) /* [x*8+7:x*8] of MRX[R]ADDR valid */ @@ -77,40 +83,62 @@ enum { /** * struct i2c_spec_values: + * @min_hold_start_ns: min hold time (repeated) START condition * @min_low_ns: min LOW period of the SCL clock * @min_high_ns: min HIGH period of the SCL cloc * @min_setup_start_ns: min set-up time for a repeated START conditio * @max_data_hold_ns: max data hold time + * @min_data_setup_ns: min data set-up time + * @min_setup_stop_ns: min set-up time for STOP condition + * @min_hold_buffer_ns: min bus free time between a STOP and + * START condition */ struct i2c_spec_values { + unsigned long min_hold_start_ns; unsigned long min_low_ns; unsigned long min_high_ns; unsigned long min_setup_start_ns; unsigned long max_data_hold_ns; + unsigned long min_data_setup_ns; + unsigned long min_setup_stop_ns; + unsigned long min_hold_buffer_ns; }; static const struct i2c_spec_values standard_mode_spec = { + .min_hold_start_ns = 4000, .min_low_ns = 4700, .min_high_ns = 4000, .min_setup_start_ns = 4700, .max_data_hold_ns = 3450, + .min_data_setup_ns = 250, + .min_setup_stop_ns = 4000, + .min_hold_buffer_ns = 4700, }; static const struct i2c_spec_values fast_mode_spec = { + .min_hold_start_ns = 600, .min_low_ns = 1300, .min_high_ns = 600, .min_setup_start_ns = 600, .max_data_hold_ns = 900, + .min_data_setup_ns = 100, + .min_setup_stop_ns = 600, + .min_hold_buffer_ns = 1300, }; /** * struct rk3x_i2c_calced_timings: * @div_low: Divider output for low * @div_high: Divider output for high + * @tuning: Used to adjust setup/hold data time, + * setup/hold start time and setup stop time for + * v1's calc_timings, the tuning should all be 0 + * for old hardware anyone using v0's calc_timings. */ struct rk3x_i2c_calced_timings { unsigned long div_low; unsigned long div_high; + unsigned int tuning; }; enum rk3x_i2c_state { @@ -123,9 +151,12 @@ enum rk3x_i2c_state { /** * @grf_offset: offset inside the grf regmap for setting the i2c type + * @calc_timings: Callback function for i2c timing information calculated */ struct rk3x_i2c_soc_data { int grf_offset; + int (*calc_timings)(unsigned long, struct i2c_timings *, + struct rk3x_i2c_calced_timings *); }; /** @@ -134,7 +165,8 @@ struct rk3x_i2c_soc_data { * @dev: device for this controller * @soc_data: related soc data struct * @regs: virtual memory area - * @clk: clock of i2c bus + * @clk: function clk for rk3399 or function & Bus clks for others + * @pclk: Bus clk for rk3399 * @clk_rate_nb: i2c clk rate change notify * @t: I2C known timing information * @lock: spinlock for the i2c bus @@ -156,6 +188,7 @@ struct rk3x_i2c { /* Hardware resources */ void __iomem *regs; struct clk *clk; + struct clk *pclk; struct notifier_block clk_rate_nb; /* Settings */ @@ -200,12 +233,12 @@ static inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c) */ static void rk3x_i2c_start(struct rk3x_i2c *i2c) { - u32 val; + u32 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; i2c_writel(i2c, REG_INT_START, REG_IEN); /* enable adapter with correct mode, send START condition */ - val = REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START; + val |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START; /* if we want to react to NACK, set ACTACK bit */ if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) @@ -246,7 +279,8 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error) * get the intended effect by resetting its internal state * and issuing an ordinary START. */ - i2c_writel(i2c, 0, REG_CON); + ctrl = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; + i2c_writel(i2c, ctrl, REG_CON); /* signal that we are finished with the current msg */ wake_up(&i2c->wait); @@ -513,9 +547,9 @@ static const struct i2c_spec_values *rk3x_i2c_get_spec(unsigned int speed) * a best-effort divider value is returned in divs. If the target rate is * too high, we silently use the highest possible rate. */ -static int rk3x_i2c_calc_divs(unsigned long clk_rate, - struct i2c_timings *t, - struct rk3x_i2c_calced_timings *t_calc) +static int rk3x_i2c_v0_calc_timings(unsigned long clk_rate, + struct i2c_timings *t, + struct rk3x_i2c_calced_timings *t_calc) { unsigned long min_low_ns, min_high_ns; unsigned long max_low_ns, min_total_ns; @@ -661,20 +695,191 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, return ret; } +/** + * Calculate timing values for desired SCL frequency + * + * @clk_rate: I2C input clock rate + * @t: Known I2C timing information + * @t_calc: Caculated rk3x private timings that would be written into regs + * + * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case + * a best-effort divider value is returned in divs. If the target rate is + * too high, we silently use the highest possible rate. + * The following formulas are v1's method to calculate timings. + * + * l = divl + 1; + * h = divh + 1; + * s = sda_update_config + 1; + * u = start_setup_config + 1; + * p = stop_setup_config + 1; + * T = Tclk_i2c; + * + * tHigh = 8 * h * T; + * tLow = 8 * l * T; + * + * tHD;sda = (l * s + 1) * T; + * tSU;sda = [(8 - s) * l + 1] * T; + * tI2C = 8 * (l + h) * T; + * + * tSU;sta = (8h * u + 1) * T; + * tHD;sta = [8h * (u + 1) - 1] * T; + * tSU;sto = (8h * p + 1) * T; + */ +static int rk3x_i2c_v1_calc_timings(unsigned long clk_rate, + struct i2c_timings *t, + struct rk3x_i2c_calced_timings *t_calc) +{ + unsigned long min_low_ns, min_high_ns, min_total_ns; + unsigned long min_setup_start_ns, min_setup_data_ns; + unsigned long min_setup_stop_ns, max_hold_data_ns; + + unsigned long clk_rate_khz, scl_rate_khz; + + unsigned long min_low_div, min_high_div; + + unsigned long min_div_for_hold, min_total_div; + unsigned long extra_div, extra_low_div; + unsigned long sda_update_cfg, stp_sta_cfg, stp_sto_cfg; + + const struct i2c_spec_values *spec; + int ret = 0; + + /* Support standard-mode and fast-mode */ + if (WARN_ON(t->bus_freq_hz > 400000)) + t->bus_freq_hz = 400000; + + /* prevent scl_rate_khz from becoming 0 */ + if (WARN_ON(t->bus_freq_hz < 1000)) + t->bus_freq_hz = 1000; + + /* + * min_low_ns: The minimum number of ns we need to hold low to + * meet I2C specification, should include fall time. + * min_high_ns: The minimum number of ns we need to hold high to + * meet I2C specification, should include rise time. + */ + spec = rk3x_i2c_get_spec(t->bus_freq_hz); + + /* calculate min-divh and min-divl */ + clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000); + scl_rate_khz = t->bus_freq_hz / 1000; + min_total_div = DIV_ROUND_UP(clk_rate_khz, scl_rate_khz * 8); + + min_high_ns = t->scl_rise_ns + spec->min_high_ns; + min_high_div = DIV_ROUND_UP(clk_rate_khz * min_high_ns, 8 * 1000000); + + min_low_ns = t->scl_fall_ns + spec->min_low_ns; + min_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns, 8 * 1000000); + + /* + * Final divh and divl must be greater than 0, otherwise the + * hardware would not output the i2c clk. + */ + min_high_div = (min_high_div < 1) ? 2 : min_high_div; + min_low_div = (min_low_div < 1) ? 2 : min_low_div; + + /* These are the min dividers needed for min hold times. */ + min_div_for_hold = (min_low_div + min_high_div); + min_total_ns = min_low_ns + min_high_ns; + + /* + * This is the maximum divider so we don't go over the maximum. + * We don't round up here (we round down) since this is a maximum. + */ + if (min_div_for_hold >= min_total_div) { + /* + * Time needed to meet hold requirements is important. + * Just use that. + */ + t_calc->div_low = min_low_div; + t_calc->div_high = min_high_div; + } else { + /* + * We've got to distribute some time among the low and high + * so we don't run too fast. + * We'll try to split things up by the scale of min_low_div and + * min_high_div, biasing slightly towards having a higher div + * for low (spend more time low). + */ + extra_div = min_total_div - min_div_for_hold; + extra_low_div = DIV_ROUND_UP(min_low_div * extra_div, + min_div_for_hold); + + t_calc->div_low = min_low_div + extra_low_div; + t_calc->div_high = min_high_div + (extra_div - extra_low_div); + } + + /* + * calculate sda data hold count by the rules, data_upd_st:3 + * is a appropriate value to reduce calculated times. + */ + for (sda_update_cfg = 3; sda_update_cfg > 0; sda_update_cfg--) { + max_hold_data_ns = DIV_ROUND_UP((sda_update_cfg + * (t_calc->div_low) + 1) + * 1000000, clk_rate_khz); + min_setup_data_ns = DIV_ROUND_UP(((8 - sda_update_cfg) + * (t_calc->div_low) + 1) + * 1000000, clk_rate_khz); + if ((max_hold_data_ns < spec->max_data_hold_ns) && + (min_setup_data_ns > spec->min_data_setup_ns)) + break; + } + + /* calculate setup start config */ + min_setup_start_ns = t->scl_rise_ns + spec->min_setup_start_ns; + stp_sta_cfg = DIV_ROUND_UP(clk_rate_khz * min_setup_start_ns + - 1000000, 8 * 1000000 * (t_calc->div_high)); + + /* calculate setup stop config */ + min_setup_stop_ns = t->scl_rise_ns + spec->min_setup_stop_ns; + stp_sto_cfg = DIV_ROUND_UP(clk_rate_khz * min_setup_stop_ns + - 1000000, 8 * 1000000 * (t_calc->div_high)); + + t_calc->tuning = REG_CON_SDA_CFG(--sda_update_cfg) | + REG_CON_STA_CFG(--stp_sta_cfg) | + REG_CON_STO_CFG(--stp_sto_cfg); + + t_calc->div_low--; + t_calc->div_high--; + + /* Maximum divider supported by hw is 0xffff */ + if (t_calc->div_low > 0xffff) { + t_calc->div_low = 0xffff; + ret = -EINVAL; + } + + if (t_calc->div_high > 0xffff) { + t_calc->div_high = 0xffff; + ret = -EINVAL; + } + + return ret; +} + static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate) { struct i2c_timings *t = &i2c->t; struct rk3x_i2c_calced_timings calc; u64 t_low_ns, t_high_ns; + unsigned long flags; + u32 val; int ret; - ret = rk3x_i2c_calc_divs(clk_rate, t, &calc); + ret = i2c->soc_data->calc_timings(clk_rate, t, &calc); WARN_ONCE(ret != 0, "Could not reach SCL freq %u", t->bus_freq_hz); - clk_enable(i2c->clk); + clk_enable(i2c->pclk); + + spin_lock_irqsave(&i2c->lock, flags); + val = i2c_readl(i2c, REG_CON); + val &= ~REG_CON_TUNING_MASK; + val |= calc.tuning; + i2c_writel(i2c, val, REG_CON); i2c_writel(i2c, (calc.div_high << 16) | (calc.div_low & 0xffff), REG_CLKDIV); - clk_disable(i2c->clk); + spin_unlock_irqrestore(&i2c->lock, flags); + + clk_disable(i2c->pclk); t_low_ns = div_u64(((u64)calc.div_low + 1) * 8 * 1000000000, clk_rate); t_high_ns = div_u64(((u64)calc.div_high + 1) * 8 * 1000000000, @@ -712,7 +917,13 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long switch (event) { case PRE_RATE_CHANGE: - if (rk3x_i2c_calc_divs(ndata->new_rate, &i2c->t, &calc) != 0) + /* + * Try the calculation (but don't store the result) ahead of + * time to see if we need to block the clock change. Timings + * shouldn't actually take effect until rk3x_i2c_adapt_div(). + */ + if (i2c->soc_data->calc_timings(ndata->new_rate, &i2c->t, + &calc) != 0) return NOTIFY_STOP; /* scale up */ @@ -822,12 +1033,14 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap, { struct rk3x_i2c *i2c = (struct rk3x_i2c *)adap->algo_data; unsigned long timeout, flags; + u32 val; int ret = 0; int i; spin_lock_irqsave(&i2c->lock, flags); clk_enable(i2c->clk); + clk_enable(i2c->pclk); i2c->is_last_msg = false; @@ -861,7 +1074,9 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap, /* Force a STOP condition without interrupt */ i2c_writel(i2c, 0, REG_IEN); - i2c_writel(i2c, REG_CON_EN | REG_CON_STOP, REG_CON); + val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; + val |= REG_CON_EN | REG_CON_STOP; + i2c_writel(i2c, val, REG_CON); i2c->state = STATE_IDLE; @@ -875,7 +1090,9 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap, } } + clk_disable(i2c->pclk); clk_disable(i2c->clk); + spin_unlock_irqrestore(&i2c->lock, flags); return ret < 0 ? ret : num; @@ -893,18 +1110,27 @@ static const struct i2c_algorithm rk3x_i2c_algorithm = { static const struct rk3x_i2c_soc_data rk3066_soc_data = { .grf_offset = 0x154, + .calc_timings = rk3x_i2c_v0_calc_timings, }; static const struct rk3x_i2c_soc_data rk3188_soc_data = { .grf_offset = 0x0a4, + .calc_timings = rk3x_i2c_v0_calc_timings, }; static const struct rk3x_i2c_soc_data rk3228_soc_data = { .grf_offset = -1, + .calc_timings = rk3x_i2c_v0_calc_timings, }; static const struct rk3x_i2c_soc_data rk3288_soc_data = { .grf_offset = -1, + .calc_timings = rk3x_i2c_v0_calc_timings, +}; + +static const struct rk3x_i2c_soc_data rk3399_soc_data = { + .grf_offset = -1, + .calc_timings = rk3x_i2c_v1_calc_timings, }; static const struct of_device_id rk3x_i2c_match[] = { @@ -924,6 +1150,10 @@ static const struct of_device_id rk3x_i2c_match[] = { .compatible = "rockchip,rk3288-i2c", .data = (void *)&rk3288_soc_data }, + { + .compatible = "rockchip,rk3399-i2c", + .data = (void *)&rk3399_soc_data + }, {}, }; MODULE_DEVICE_TABLE(of, rk3x_i2c_match); @@ -963,12 +1193,6 @@ static int rk3x_i2c_probe(struct platform_device *pdev) spin_lock_init(&i2c->lock); init_waitqueue_head(&i2c->wait); - i2c->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(i2c->clk)) { - dev_err(&pdev->dev, "cannot get clock\n"); - return PTR_ERR(i2c->clk); - } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); i2c->regs = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(i2c->regs)) @@ -1022,17 +1246,44 @@ static int rk3x_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, i2c); + if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) { + /* Only one clock to use for bus clock and peripheral clock */ + i2c->clk = devm_clk_get(&pdev->dev, NULL); + i2c->pclk = i2c->clk; + } else { + i2c->clk = devm_clk_get(&pdev->dev, "i2c"); + i2c->pclk = devm_clk_get(&pdev->dev, "pclk"); + } + + if (IS_ERR(i2c->clk)) { + ret = PTR_ERR(i2c->clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Can't get bus clk: %d\n", ret); + return ret; + } + if (IS_ERR(i2c->pclk)) { + ret = PTR_ERR(i2c->pclk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Can't get periph clk: %d\n", ret); + return ret; + } + ret = clk_prepare(i2c->clk); if (ret < 0) { - dev_err(&pdev->dev, "Could not prepare clock\n"); + dev_err(&pdev->dev, "Can't prepare bus clk: %d\n", ret); return ret; } + ret = clk_prepare(i2c->pclk); + if (ret < 0) { + dev_err(&pdev->dev, "Can't prepare periph clock: %d\n", ret); + goto err_clk; + } i2c->clk_rate_nb.notifier_call = rk3x_i2c_clk_notifier_cb; ret = clk_notifier_register(i2c->clk, &i2c->clk_rate_nb); if (ret != 0) { dev_err(&pdev->dev, "Unable to register clock notifier\n"); - goto err_clk; + goto err_pclk; } clk_rate = clk_get_rate(i2c->clk); @@ -1050,6 +1301,8 @@ static int rk3x_i2c_probe(struct platform_device *pdev) err_clk_notifier: clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb); +err_pclk: + clk_unprepare(i2c->pclk); err_clk: clk_unprepare(i2c->clk); return ret; @@ -1062,6 +1315,7 @@ static int rk3x_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&i2c->adap); clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb); + clk_unprepare(i2c->pclk); clk_unprepare(i2c->clk); return 0; -- cgit v1.2.3 From a02f3d081a79ec702b85b9c1a91edf770cf1eab2 Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 16 May 2016 22:06:29 +0800 Subject: i2c: rk3x: support fast-mode plus for rk3399 Implement fast mode plus that allows bus speeds of up to 1MHz. Signed-off-by: David Wu Reviewed-by: Douglas Anderson Tested-by: Caesar Wang Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rk3x.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index e9ce6a33e8c4..2bc8b01153d6 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -126,6 +126,17 @@ static const struct i2c_spec_values fast_mode_spec = { .min_hold_buffer_ns = 1300, }; +static const struct i2c_spec_values fast_mode_plus_spec = { + .min_hold_start_ns = 260, + .min_low_ns = 500, + .min_high_ns = 260, + .min_setup_start_ns = 260, + .max_data_hold_ns = 400, + .min_data_setup_ns = 50, + .min_setup_stop_ns = 260, + .min_hold_buffer_ns = 500, +}; + /** * struct rk3x_i2c_calced_timings: * @div_low: Divider output for low @@ -532,8 +543,10 @@ static const struct i2c_spec_values *rk3x_i2c_get_spec(unsigned int speed) { if (speed <= 100000) return &standard_mode_spec; - else + else if (speed <= 400000) return &fast_mode_spec; + else + return &fast_mode_plus_spec; } /** @@ -744,9 +757,9 @@ static int rk3x_i2c_v1_calc_timings(unsigned long clk_rate, const struct i2c_spec_values *spec; int ret = 0; - /* Support standard-mode and fast-mode */ - if (WARN_ON(t->bus_freq_hz > 400000)) - t->bus_freq_hz = 400000; + /* Support standard-mode, fast-mode and fast-mode plus */ + if (WARN_ON(t->bus_freq_hz > 1000000)) + t->bus_freq_hz = 1000000; /* prevent scl_rate_khz from becoming 0 */ if (WARN_ON(t->bus_freq_hz < 1000)) -- cgit v1.2.3 From 685983f4decc5fa5700a0ab083859a2fe59acf10 Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Fri, 10 Jun 2016 23:38:19 +0530 Subject: i2c: qup: Fix broken dma when CONFIG_DEBUG_SG is enabled With CONFIG_DEBUG_SG is enabled and when dma mode is used, below dump is seen, ------------[ cut here ]------------ kernel BUG at include/linux/scatterlist.h:140! Internal error: Oops - BUG: 0 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.4.0-00459-g9f087b9-dirty #7 Hardware name: Qualcomm Technologies, Inc. APQ 8016 SBC (DT) task: ffffffc036868000 ti: ffffffc036870000 task.ti: ffffffc036870000 PC is at qup_sg_set_buf.isra.13+0x138/0x154 LR is at qup_sg_set_buf.isra.13+0x50/0x154 pc : [] lr : [] pstate: 60000145 sp : ffffffc0368735c0 x29: ffffffc0368735c0 x28: ffffffc036873752 x27: ffffffc035233018 x26: ffffffc000c4e000 x25: 0000000000000000 x24: 0000000000000004 x23: 0000000000000000 x22: ffffffc035233668 x21: ffffff80004e3000 x20: ffffffc0352e0018 x19: 0000004000000000 x18: 0000000000000028 x17: 0000000000000004 x16: ffffffc0017a39c8 x15: 0000000000001cdf x14: ffffffc0019929d8 x13: ffffffc0352e0018 x12: 0000000000000000 x11: 0000000000000001 x10: 0000000000000001 x9 : ffffffc0012b2d70 x8 : ffffff80004e3000 x7 : 0000000000000018 x6 : 0000000030000000 x5 : ffffffc00199f018 x4 : ffffffc035233018 x3 : 0000000000000004 x2 : 00000000c0000000 x1 : 0000000000000003 x0 : 0000000000000000 Process swapper/0 (pid: 1, stack limit = 0xffffffc036870020) Stack: (0xffffffc0368735c0 to 0xffffffc036874000) sg_set_bug expects that the buf parameter passed in should be from lowmem and a valid pageframe. This is not true for pages from dma_alloc_coherent which can be carveouts, hence the check fails. Change allocation of sg buffers from dma_coherent memory to kzalloc to fix the issue. Note that now dma_map/unmap is used to make the kzalloc'ed buffers coherent before passing it to the dmaengine. Signed-off-by: Sricharan R Reviewed-by: Andy Gross Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-qup.c | 51 +++++++++++++------------------------------- 1 file changed, 15 insertions(+), 36 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index cc6439ab3f71..43adc2d8759b 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -585,8 +585,8 @@ static void qup_i2c_bam_cb(void *data) } static int qup_sg_set_buf(struct scatterlist *sg, void *buf, - struct qup_i2c_tag *tg, unsigned int buflen, - struct qup_i2c_dev *qup, int map, int dir) + unsigned int buflen, struct qup_i2c_dev *qup, + int dir) { int ret; @@ -595,9 +595,6 @@ static int qup_sg_set_buf(struct scatterlist *sg, void *buf, if (!ret) return -EINVAL; - if (!map) - sg_dma_address(sg) = tg->addr + ((u8 *)buf - tg->start); - return 0; } @@ -670,16 +667,15 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, /* scratch buf to read the start and len tags */ ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++], &qup->brx.tag.start[0], - &qup->brx.tag, - 2, qup, 0, 0); + 2, qup, DMA_FROM_DEVICE); if (ret) return ret; ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++], &msg->buf[limit * i], - NULL, tlen, qup, - 1, DMA_FROM_DEVICE); + tlen, qup, + DMA_FROM_DEVICE); if (ret) return ret; @@ -688,7 +684,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, } ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++], &qup->start_tag.start[off], - &qup->start_tag, len, qup, 0, 0); + len, qup, DMA_TO_DEVICE); if (ret) return ret; @@ -696,8 +692,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, /* scratch buf to read the BAM EOT and FLUSH tags */ ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++], &qup->brx.tag.start[0], - &qup->brx.tag, 2, - qup, 0, 0); + 2, qup, DMA_FROM_DEVICE); if (ret) return ret; } else { @@ -709,17 +704,15 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, len = qup_i2c_set_tags(tags, qup, msg, 1); ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++], - tags, - &qup->start_tag, len, - qup, 0, 0); + tags, len, + qup, DMA_TO_DEVICE); if (ret) return ret; tx_len += len; ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++], &msg->buf[limit * i], - NULL, tlen, qup, 1, - DMA_TO_DEVICE); + tlen, qup, DMA_TO_DEVICE); if (ret) return ret; i++; @@ -738,8 +731,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, QUP_BAM_FLUSH_STOP; ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++], &qup->btx.tag.start[0], - &qup->btx.tag, len, - qup, 0, 0); + len, qup, DMA_TO_DEVICE); if (ret) return ret; tx_nents += 1; @@ -1407,27 +1399,21 @@ static int qup_i2c_probe(struct platform_device *pdev) /* 2 tag bytes for each block + 5 for start, stop tags */ size = blocks * 2 + 5; - qup->dpool = dma_pool_create("qup_i2c-dma-pool", &pdev->dev, - size, 4, 0); - qup->start_tag.start = dma_pool_alloc(qup->dpool, GFP_KERNEL, - &qup->start_tag.addr); + qup->start_tag.start = devm_kzalloc(&pdev->dev, + size, GFP_KERNEL); if (!qup->start_tag.start) { ret = -ENOMEM; goto fail_dma; } - qup->brx.tag.start = dma_pool_alloc(qup->dpool, - GFP_KERNEL, - &qup->brx.tag.addr); + qup->brx.tag.start = devm_kzalloc(&pdev->dev, 2, GFP_KERNEL); if (!qup->brx.tag.start) { ret = -ENOMEM; goto fail_dma; } - qup->btx.tag.start = dma_pool_alloc(qup->dpool, - GFP_KERNEL, - &qup->btx.tag.addr); + qup->btx.tag.start = devm_kzalloc(&pdev->dev, 2, GFP_KERNEL); if (!qup->btx.tag.start) { ret = -ENOMEM; goto fail_dma; @@ -1566,13 +1552,6 @@ static int qup_i2c_remove(struct platform_device *pdev) struct qup_i2c_dev *qup = platform_get_drvdata(pdev); if (qup->is_dma) { - dma_pool_free(qup->dpool, qup->start_tag.start, - qup->start_tag.addr); - dma_pool_free(qup->dpool, qup->brx.tag.start, - qup->brx.tag.addr); - dma_pool_free(qup->dpool, qup->btx.tag.start, - qup->btx.tag.addr); - dma_pool_destroy(qup->dpool); dma_release_channel(qup->btx.dma); dma_release_channel(qup->brx.dma); } -- cgit v1.2.3 From fbf9921f8b35d9b241a1ee0008d310a3a5390273 Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Fri, 10 Jun 2016 23:38:21 +0530 Subject: i2c: qup: Fix error handling Among the bus errors reported from the QUP_MASTER_STATUS register only NACK is considered and transfer gets suspended, while other errors are ignored. Correct this and suspend the transfer for other errors as well. This avoids unnecessary 'timeouts' which happens when waiting for events that would never happen when there is already an error condition on the bus. Also the error handling procedure should be the same for both NACK and other bus errors in case of dma mode. So correct that as well. Signed-off-by: Sricharan R Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-qup.c | 76 ++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 35 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 43adc2d8759b..3e5fac88376d 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -310,6 +310,7 @@ static int qup_i2c_wait_ready(struct qup_i2c_dev *qup, int op, bool val, u32 opflags; u32 status; u32 shift = __ffs(op); + int ret = 0; len *= qup->one_byte_t; /* timeout after a wait of twice the max time */ @@ -321,18 +322,28 @@ static int qup_i2c_wait_ready(struct qup_i2c_dev *qup, int op, bool val, if (((opflags & op) >> shift) == val) { if ((op == QUP_OUT_NOT_EMPTY) && qup->is_last) { - if (!(status & I2C_STATUS_BUS_ACTIVE)) - return 0; + if (!(status & I2C_STATUS_BUS_ACTIVE)) { + ret = 0; + goto done; + } } else { - return 0; + ret = 0; + goto done; } } - if (time_after(jiffies, timeout)) - return -ETIMEDOUT; - + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + goto done; + } usleep_range(len, len * 2); } + +done: + if (qup->bus_err || qup->qup_err) + ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO; + + return ret; } static void qup_i2c_set_write_mode_v2(struct qup_i2c_dev *qup, @@ -793,39 +804,35 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg, } if (ret || qup->bus_err || qup->qup_err) { - if (qup->bus_err & QUP_I2C_NACK_FLAG) { - msg--; - dev_err(qup->dev, "NACK from %x\n", msg->addr); - ret = -EIO; + if (qup_i2c_change_state(qup, QUP_RUN_STATE)) { + dev_err(qup->dev, "change to run state timed out"); + goto desc_err; + } - if (qup_i2c_change_state(qup, QUP_RUN_STATE)) { - dev_err(qup->dev, "change to run state timed out"); - return ret; - } + if (rx_nents) + writel(QUP_BAM_INPUT_EOT, + qup->base + QUP_OUT_FIFO_BASE); - if (rx_nents) - writel(QUP_BAM_INPUT_EOT, - qup->base + QUP_OUT_FIFO_BASE); + writel(QUP_BAM_FLUSH_STOP, qup->base + QUP_OUT_FIFO_BASE); - writel(QUP_BAM_FLUSH_STOP, - qup->base + QUP_OUT_FIFO_BASE); + qup_i2c_flush(qup); - qup_i2c_flush(qup); + /* wait for remaining interrupts to occur */ + if (!wait_for_completion_timeout(&qup->xfer, HZ)) + dev_err(qup->dev, "flush timed out\n"); - /* wait for remaining interrupts to occur */ - if (!wait_for_completion_timeout(&qup->xfer, HZ)) - dev_err(qup->dev, "flush timed out\n"); + qup_i2c_rel_dma(qup); - qup_i2c_rel_dma(qup); - } + ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO; } +desc_err: dma_unmap_sg(qup->dev, qup->btx.sg, tx_nents, DMA_TO_DEVICE); if (rx_nents) dma_unmap_sg(qup->dev, qup->brx.sg, rx_nents, DMA_FROM_DEVICE); -desc_err: + return ret; } @@ -841,9 +848,6 @@ static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, if (ret) goto out; - qup->bus_err = 0; - qup->qup_err = 0; - writel(0, qup->base + QUP_MX_INPUT_CNT); writel(0, qup->base + QUP_MX_OUTPUT_CNT); @@ -881,12 +885,8 @@ static int qup_i2c_wait_for_complete(struct qup_i2c_dev *qup, ret = -ETIMEDOUT; } - if (qup->bus_err || qup->qup_err) { - if (qup->bus_err & QUP_I2C_NACK_FLAG) { - dev_err(qup->dev, "NACK from %x\n", msg->addr); - ret = -EIO; - } - } + if (qup->bus_err || qup->qup_err) + ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO; return ret; } @@ -1178,6 +1178,9 @@ static int qup_i2c_xfer(struct i2c_adapter *adap, if (ret < 0) goto out; + qup->bus_err = 0; + qup->qup_err = 0; + writel(1, qup->base + QUP_SW_RESET); ret = qup_i2c_poll_state(qup, QUP_RESET_STATE); if (ret) @@ -1227,6 +1230,9 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap, struct qup_i2c_dev *qup = i2c_get_adapdata(adap); int ret, len, idx = 0, use_dma = 0; + qup->bus_err = 0; + qup->qup_err = 0; + ret = pm_runtime_get_sync(qup->dev); if (ret < 0) goto out; -- cgit v1.2.3 From 0130944bc1d212ad4b33584424d56fe7c049eb65 Mon Sep 17 00:00:00 2001 From: Naveen Kaje Date: Thu, 5 May 2016 12:33:17 -0600 Subject: i2c: qup: use address helper function in read transfer qup_i2c_issue_read() derives the address from i2c_msg. This called in the read path when I2C_M_RD flag is set. Therefore, use the 8 bit address helper function. Signed-off-by: Naveen Kaje Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-qup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 3e5fac88376d..69849a95d479 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -1012,7 +1012,7 @@ static void qup_i2c_issue_read(struct qup_i2c_dev *qup, struct i2c_msg *msg) { u32 addr, len, val; - addr = (msg->addr << 1) | 1; + addr = i2c_8bit_addr_from_msg(msg); /* 0 is used to specify a length 256 (QUP_READ_LIMIT) */ len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len; -- cgit v1.2.3 From d0bcd8df9aea2bcdbfcb074d408bdc7136031bc5 Mon Sep 17 00:00:00 2001 From: Weifeng Voon Date: Fri, 17 Jun 2016 09:46:35 +0800 Subject: i2c: designware: Use transfer timeout from ioctl I2C_TIMEOUT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows applications to set the transfer timeout in 10ms increments via ioctl I2C_TIMEOUT. Signed-off-by: Weifeng Voon Acked-by: Jarkko Nikula Acked-by: Uwe Kleine-König Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index 99b54be6ba73..c6922b806fb7 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -663,7 +663,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) i2c_dw_xfer_init(dev); /* wait for tx to complete */ - if (!wait_for_completion_timeout(&dev->cmd_complete, HZ)) { + if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) { dev_err(dev->dev, "controller timed out\n"); /* i2c_dw_init implicitly disables the adapter */ i2c_dw_init(dev); -- cgit v1.2.3 From f41021bba68371b7ff4bcb0fc764473de1782c14 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 10 Dec 2015 13:48:44 +0200 Subject: i2c: designware: Allow build Baytrail semaphore support when IOSF_MBI=m I believe i2c-designware-baytrail.c doesn't have strict dependency that Intel SoC IOSF Sideband support must be always built-in in order to be able to compile support for Intel Baytrail I2C bus sharing HW semaphore. Redefine build dependencies so that CONFIG_IOSF_MBI=y is required only when CONFIG_I2C_DESIGNWARE_PLATFORM is built-in. Signed-off-by: Jarkko Nikula Acked-by: David Box Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index f167021b8c21..efa3d9bfe294 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -490,7 +490,9 @@ config I2C_DESIGNWARE_PCI config I2C_DESIGNWARE_BAYTRAIL bool "Intel Baytrail I2C semaphore support" - depends on I2C_DESIGNWARE_PLATFORM && IOSF_MBI=y && ACPI + depends on ACPI + depends on (I2C_DESIGNWARE_PLATFORM=m && IOSF_MBI) || \ + (I2C_DESIGNWARE_PLATFORM=y && IOSF_MBI=y) help This driver enables managed host access to the PMIC I2C bus on selec