forked from luck/tmp_suning_uos_patched
Merge branch 'i2c/for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - the I2C core gained helpers to assist drivers in handling their suspended state, and drivers were converted to use it - two new fault-injectors for stress-testing - bigger refactoring and feature improvements for the ocores, sh_mobile, and tegra drivers - platform_data removal for the at24 EEPROM driver - ... and various improvements and bugfixes all over the subsystem * 'i2c/for-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (69 commits) i2c: Allow recovery of the initial IRQ by an I2C client device. i2c: ocores: turn incomplete kdoc into a comment i2c: designware: Do not allow i2c_dw_xfer() calls while suspended i2c: tegra: Only display error messages if DMA setup fails i2c: gpio: fault-injector: add 'inject_panic' injector i2c: gpio: fault-injector: add 'lose_arbitration' injector i2c: tegra: remove multi-master support i2c: tegra: remove master fifo support on tegra186 i2c: tegra: change phrasing, "fallbacking" to "falling back" i2c: expand minor range when registering chrdev region i2c: aspeed: Add multi-master use case support i2c: core-smbus: don't trace smbus_reply data on errors i2c: ocores: Add support for bus clock via platform data i2c: ocores: Add support for IO mapper registers. i2c: ocores: checkpatch fixes i2c: ocores: add SPDX tag i2c: ocores: add polling interface i2c: ocores: do not handle IRQ if IF is not set i2c: ocores: stop transfer on timeout i2c: tegra: add i2c interface timing support ...
This commit is contained in:
commit
6c3f98fadd
|
@ -75,6 +75,8 @@ Optional properties:
|
|||
|
||||
- address-width: number of address bits (one of 8, 16).
|
||||
|
||||
- num-addresses: total number of i2c slave addresses this device takes
|
||||
|
||||
Example:
|
||||
|
||||
eeprom@52 {
|
||||
|
@ -82,4 +84,5 @@ eeprom@52 {
|
|||
reg = <0x52>;
|
||||
pagesize = <32>;
|
||||
wp-gpios = <&gpio1 3 0>;
|
||||
num-addresses = <8>;
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@ Required properties:
|
|||
"mediatek,mt6589-i2c": for MediaTek MT6589
|
||||
"mediatek,mt7622-i2c": for MediaTek MT7622
|
||||
"mediatek,mt7623-i2c", "mediatek,mt6577-i2c": for MediaTek MT7623
|
||||
"mediatek,mt7629-i2c", "mediatek,mt2712-i2c": for MediaTek MT7629
|
||||
"mediatek,mt8173-i2c": for MediaTek MT8173
|
||||
- reg: physical base address of the controller and dma base, length of memory
|
||||
mapped region.
|
||||
|
|
20
Documentation/devicetree/bindings/i2c/i2c-xscale.txt
Normal file
20
Documentation/devicetree/bindings/i2c/i2c-xscale.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
i2c Controller on XScale platforms such as IOP3xx and IXP4xx
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be one of
|
||||
"intel,iop3xx-i2c"
|
||||
"intel,ixp4xx-i2c";
|
||||
- reg
|
||||
- #address-cells = <1>;
|
||||
- #size-cells = <0>;
|
||||
|
||||
Optional properties:
|
||||
- Child nodes conforming to i2c bus binding
|
||||
|
||||
Example:
|
||||
|
||||
i2c@c8011000 {
|
||||
compatible = "intel,ixp4xx-i2c";
|
||||
reg = <0xc8011000 0x18>;
|
||||
interrupts = <33 IRQ_TYPE_LEVEL_LOW>;
|
||||
};
|
|
@ -112,6 +112,10 @@ EPROTO
|
|||
case is when the length of an SMBus block data response
|
||||
(from the SMBus slave) is outside the range 1-32 bytes.
|
||||
|
||||
ESHUTDOWN
|
||||
Returned when a transfer was requested using an adapter
|
||||
which is already suspended.
|
||||
|
||||
ETIMEDOUT
|
||||
This is returned by drivers when an operation took too much
|
||||
time, and was aborted before it completed.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
=========================
|
||||
Linux I2C fault injection
|
||||
=========================
|
||||
|
||||
|
@ -13,6 +14,9 @@ mounted at /sys/kernel/debug. There will be a separate subdirectory per GPIO
|
|||
driven I2C bus. Each subdirectory will contain files to trigger the fault
|
||||
injection. They will be described now along with their intended use-cases.
|
||||
|
||||
Wire states
|
||||
===========
|
||||
|
||||
"scl"
|
||||
-----
|
||||
|
||||
|
@ -34,10 +38,10 @@ I2C specification version 4, section 3.1.16) using the helpers of the Linux I2C
|
|||
core (see 'struct bus_recovery_info'). However, the bus recovery will not
|
||||
succeed because SDA is still pinned low until you manually release it again
|
||||
with "echo 1 > sda". A test with an automatic release can be done with the
|
||||
following class of fault injectors.
|
||||
"incomplete transfers" class of fault injectors.
|
||||
|
||||
Introduction to incomplete transfers
|
||||
------------------------------------
|
||||
Incomplete transfers
|
||||
====================
|
||||
|
||||
The following fault injectors create situations where SDA will be held low by a
|
||||
device. Bus recovery should be able to fix these situations. But please note:
|
||||
|
@ -79,3 +83,54 @@ This is why bus recovery (up to 9 clock pulses) must either check SDA or send
|
|||
additional STOP conditions to ensure the bus has been released. Otherwise
|
||||
random data will be written to a device!
|
||||
|
||||
Lost arbitration
|
||||
================
|
||||
|
||||
Here, we want to simulate the condition where the master under test loses the
|
||||
bus arbitration against another master in a multi-master setup.
|
||||
|
||||
"lose_arbitration"
|
||||
------------------
|
||||
|
||||
This file is write only and you need to write the duration of the arbitration
|
||||
intereference (in µs, maximum is 100ms). The calling process will then sleep
|
||||
and wait for the next bus clock. The process is interruptible, though.
|
||||
|
||||
Arbitration lost is achieved by waiting for SCL going down by the master under
|
||||
test and then pulling SDA low for some time. So, the I2C address sent out
|
||||
should be corrupted and that should be detected properly. That means that the
|
||||
address sent out should have a lot of '1' bits to be able to detect corruption.
|
||||
There doesn't need to be a device at this address because arbitration lost
|
||||
should be detected beforehand. Also note, that SCL going down is monitored
|
||||
using interrupts, so the interrupt latency might cause the first bits to be not
|
||||
corrupted. A good starting point for using this fault injector on an otherwise
|
||||
idle bus is:
|
||||
|
||||
# echo 200 > lose_arbitration &
|
||||
# i2cget -y <bus_to_test> 0x3f
|
||||
|
||||
Panic during transfer
|
||||
=====================
|
||||
|
||||
This fault injector will create a Kernel panic once the master under test
|
||||
started a transfer. This usually means that the state machine of the bus master
|
||||
driver will be ungracefully interrupted and the bus may end up in an unusual
|
||||
state. Use this to check if your shutdown/reboot/boot code can handle this
|
||||
scenario.
|
||||
|
||||
"inject_panic"
|
||||
--------------
|
||||
|
||||
This file is write only and you need to write the delay between the detected
|
||||
start of a transmission and the induced Kernel panic (in µs, maximum is 100ms).
|
||||
The calling process will then sleep and wait for the next bus clock. The
|
||||
process is interruptible, though.
|
||||
|
||||
Start of a transfer is detected by waiting for SCL going down by the master
|
||||
under test. A good starting point for using this fault injector is:
|
||||
|
||||
# echo 0 > inject_panic &
|
||||
# i2cget -y <bus_to_test> <some_address>
|
||||
|
||||
Note that there doesn't need to be a device listening to the address you are
|
||||
using. Results may vary depending on that, though.
|
||||
|
|
|
@ -2554,7 +2554,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
|
|||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/eeprom/at24.txt
|
||||
F: drivers/misc/eeprom/at24.c
|
||||
F: include/linux/platform_data/at24.h
|
||||
|
||||
ATA OVER ETHERNET (AOE) DRIVER
|
||||
M: "Ed L. Cashin" <ed.cashin@acm.org>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/clkdev.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
|
@ -25,7 +26,6 @@
|
|||
#include <linux/platform_data/keypad-omap.h>
|
||||
#include <linux/platform_data/lcd-mipid.h>
|
||||
#include <linux/platform_data/gpio-omap.h>
|
||||
#include <linux/platform_data/i2c-cbus-gpio.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/arch.h>
|
||||
|
@ -217,18 +217,19 @@ static inline void nokia770_mmc_init(void)
|
|||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_CBUS_GPIO)
|
||||
static struct i2c_cbus_platform_data nokia770_cbus_data = {
|
||||
.clk_gpio = OMAP_MPUIO(9),
|
||||
.dat_gpio = OMAP_MPUIO(10),
|
||||
.sel_gpio = OMAP_MPUIO(11),
|
||||
static struct gpiod_lookup_table nokia770_cbus_gpio_table = {
|
||||
.dev_id = "i2c-cbus-gpio.2",
|
||||
.table = {
|
||||
GPIO_LOOKUP_IDX("mpuio", 9, NULL, 0, 0), /* clk */
|
||||
GPIO_LOOKUP_IDX("mpuio", 10, NULL, 1, 0), /* dat */
|
||||
GPIO_LOOKUP_IDX("mpuio", 11, NULL, 2, 0), /* sel */
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device nokia770_cbus_device = {
|
||||
.name = "i2c-cbus-gpio",
|
||||
.id = 2,
|
||||
.dev = {
|
||||
.platform_data = &nokia770_cbus_data,
|
||||
},
|
||||
};
|
||||
|
||||
static struct i2c_board_info nokia770_i2c_board_info_2[] __initdata = {
|
||||
|
@ -257,6 +258,7 @@ static void __init nokia770_cbus_init(void)
|
|||
nokia770_i2c_board_info_2[1].irq = gpio_to_irq(tahvo_irq_gpio);
|
||||
i2c_register_board_info(2, nokia770_i2c_board_info_2,
|
||||
ARRAY_SIZE(nokia770_i2c_board_info_2));
|
||||
gpiod_add_lookup_table(&nokia770_cbus_gpio_table);
|
||||
platform_device_register(&nokia770_cbus_device);
|
||||
}
|
||||
#else /* CONFIG_I2C_CBUS_GPIO */
|
||||
|
|
|
@ -1,21 +1,12 @@
|
|||
/* -------------------------------------------------------------------------
|
||||
* i2c-algo-bit.c i2c driver algorithms for bit-shift adapters
|
||||
* -------------------------------------------------------------------------
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* i2c-algo-bit.c: i2c driver algorithms for bit-shift adapters
|
||||
*
|
||||
* Copyright (C) 1995-2000 Simon G. Vogl
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
|
||||
<kmalkki@cc.hut.fi> and Jean Delvare <jdelvare@suse.de> */
|
||||
*
|
||||
* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
|
||||
* <kmalkki@cc.hut.fi> and Jean Delvare <jdelvare@suse.de>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
|
|
@ -117,6 +117,7 @@
|
|||
|
||||
enum aspeed_i2c_master_state {
|
||||
ASPEED_I2C_MASTER_INACTIVE,
|
||||
ASPEED_I2C_MASTER_PENDING,
|
||||
ASPEED_I2C_MASTER_START,
|
||||
ASPEED_I2C_MASTER_TX_FIRST,
|
||||
ASPEED_I2C_MASTER_TX,
|
||||
|
@ -126,12 +127,13 @@ enum aspeed_i2c_master_state {
|
|||
};
|
||||
|
||||
enum aspeed_i2c_slave_state {
|
||||
ASPEED_I2C_SLAVE_STOP,
|
||||
ASPEED_I2C_SLAVE_INACTIVE,
|
||||
ASPEED_I2C_SLAVE_START,
|
||||
ASPEED_I2C_SLAVE_READ_REQUESTED,
|
||||
ASPEED_I2C_SLAVE_READ_PROCESSED,
|
||||
ASPEED_I2C_SLAVE_WRITE_REQUESTED,
|
||||
ASPEED_I2C_SLAVE_WRITE_RECEIVED,
|
||||
ASPEED_I2C_SLAVE_STOP,
|
||||
};
|
||||
|
||||
struct aspeed_i2c_bus {
|
||||
|
@ -156,6 +158,8 @@ struct aspeed_i2c_bus {
|
|||
int cmd_err;
|
||||
/* Protected only by i2c_lock_bus */
|
||||
int master_xfer_result;
|
||||
/* Multi-master */
|
||||
bool multi_master;
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
struct i2c_client *slave;
|
||||
enum aspeed_i2c_slave_state slave_state;
|
||||
|
@ -251,7 +255,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
|
|||
}
|
||||
|
||||
/* Slave is not currently active, irq was for someone else. */
|
||||
if (bus->slave_state == ASPEED_I2C_SLAVE_STOP)
|
||||
if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE)
|
||||
return irq_handled;
|
||||
|
||||
dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n",
|
||||
|
@ -277,16 +281,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
|
|||
irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP;
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
|
||||
}
|
||||
if (irq_status & ASPEED_I2CD_INTR_TX_NAK) {
|
||||
if (irq_status & ASPEED_I2CD_INTR_TX_NAK &&
|
||||
bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) {
|
||||
irq_handled |= ASPEED_I2CD_INTR_TX_NAK;
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
|
||||
}
|
||||
if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
|
||||
irq_handled |= ASPEED_I2CD_INTR_TX_ACK;
|
||||
|
||||
switch (bus->slave_state) {
|
||||
case ASPEED_I2C_SLAVE_READ_REQUESTED:
|
||||
if (irq_status & ASPEED_I2CD_INTR_TX_ACK)
|
||||
if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK))
|
||||
dev_err(bus->dev, "Unexpected ACK on read request.\n");
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED;
|
||||
i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value);
|
||||
|
@ -294,9 +297,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
|
|||
writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG);
|
||||
break;
|
||||
case ASPEED_I2C_SLAVE_READ_PROCESSED:
|
||||
if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK))
|
||||
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
|
||||
dev_err(bus->dev,
|
||||
"Expected ACK after processed read.\n");
|
||||
break;
|
||||
}
|
||||
irq_handled |= ASPEED_I2CD_INTR_TX_ACK;
|
||||
i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value);
|
||||
writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG);
|
||||
writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG);
|
||||
|
@ -310,10 +316,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
|
|||
break;
|
||||
case ASPEED_I2C_SLAVE_STOP:
|
||||
i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
|
||||
break;
|
||||
case ASPEED_I2C_SLAVE_START:
|
||||
/* Slave was just started. Waiting for the next event. */;
|
||||
break;
|
||||
default:
|
||||
dev_err(bus->dev, "unhandled slave_state: %d\n",
|
||||
dev_err(bus->dev, "unknown slave_state: %d\n",
|
||||
bus->slave_state);
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -329,6 +340,17 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus)
|
|||
u8 slave_addr = i2c_8bit_addr_from_msg(msg);
|
||||
|
||||
bus->master_state = ASPEED_I2C_MASTER_START;
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
/*
|
||||
* If it's requested in the middle of a slave session, set the master
|
||||
* state to 'pending' then H/W will continue handling this master
|
||||
* command when the bus comes back to the idle state.
|
||||
*/
|
||||
if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE)
|
||||
bus->master_state = ASPEED_I2C_MASTER_PENDING;
|
||||
#endif /* CONFIG_I2C_SLAVE */
|
||||
|
||||
bus->buf_index = 0;
|
||||
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
|
@ -384,10 +406,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
|
|||
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
|
||||
irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE;
|
||||
goto out_complete;
|
||||
} else {
|
||||
/* Master is not currently active, irq was for someone else. */
|
||||
if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE)
|
||||
goto out_no_complete;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -399,12 +417,33 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
|
|||
if (ret) {
|
||||
dev_dbg(bus->dev, "received error interrupt: 0x%08x\n",
|
||||
irq_status);
|
||||
bus->cmd_err = ret;
|
||||
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
|
||||
irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS);
|
||||
goto out_complete;
|
||||
if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) {
|
||||
bus->cmd_err = ret;
|
||||
bus->master_state = ASPEED_I2C_MASTER_INACTIVE;
|
||||
goto out_complete;
|
||||
}
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
/*
|
||||
* A pending master command will be started by H/W when the bus comes
|
||||
* back to idle state after completing a slave operation so change the
|
||||
* master state from 'pending' to 'start' at here if slave is inactive.
|
||||
*/
|
||||
if (bus->master_state == ASPEED_I2C_MASTER_PENDING) {
|
||||
if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE)
|
||||
goto out_no_complete;
|
||||
|
||||
bus->master_state = ASPEED_I2C_MASTER_START;
|
||||
}
|
||||
#endif /* CONFIG_I2C_SLAVE */
|
||||
|
||||
/* Master is not currently active, irq was for someone else. */
|
||||
if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE ||
|
||||
bus->master_state == ASPEED_I2C_MASTER_PENDING)
|
||||
goto out_no_complete;
|
||||
|
||||
/* We are in an invalid state; reset bus to a known state. */
|
||||
if (!bus->msgs) {
|
||||
dev_err(bus->dev, "bus in unknown state. irq_status: 0x%x\n",
|
||||
|
@ -423,6 +462,20 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
|
|||
* then update the state and handle the new state below.
|
||||
*/
|
||||
if (bus->master_state == ASPEED_I2C_MASTER_START) {
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
/*
|
||||
* If a peer master starts a xfer immediately after it queues a
|
||||
* master command, change its state to 'pending' then H/W will
|
||||
* continue the queued master xfer just after completing the
|
||||
* slave mode session.
|
||||
*/
|
||||
if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) {
|
||||
bus->master_state = ASPEED_I2C_MASTER_PENDING;
|
||||
dev_dbg(bus->dev,
|
||||
"master goes pending due to a slave start\n");
|
||||
goto out_no_complete;
|
||||
}
|
||||
#endif /* CONFIG_I2C_SLAVE */
|
||||
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) {
|
||||
if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) {
|
||||
bus->cmd_err = -ENXIO;
|
||||
|
@ -566,7 +619,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
|
|||
* interrupt bits. Each case needs to be handled using corresponding
|
||||
* handlers depending on the current state.
|
||||
*/
|
||||
if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) {
|
||||
if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE &&
|
||||
bus->master_state != ASPEED_I2C_MASTER_PENDING) {
|
||||
irq_handled = aspeed_i2c_master_irq(bus, irq_remaining);
|
||||
irq_remaining &= ~irq_handled;
|
||||
if (irq_remaining)
|
||||
|
@ -601,15 +655,16 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
|
|||
{
|
||||
struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap);
|
||||
unsigned long time_left, flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&bus->lock, flags);
|
||||
bus->cmd_err = 0;
|
||||
|
||||
/* If bus is busy, attempt recovery. We assume a single master
|
||||
* environment.
|
||||
*/
|
||||
if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) {
|
||||
/* If bus is busy in a single master environment, attempt recovery. */
|
||||
if (!bus->multi_master &&
|
||||
(readl(bus->base + ASPEED_I2C_CMD_REG) &
|
||||
ASPEED_I2CD_BUS_BUSY_STS)) {
|
||||
int ret;
|
||||
|
||||
spin_unlock_irqrestore(&bus->lock, flags);
|
||||
ret = aspeed_i2c_recover_bus(bus);
|
||||
if (ret)
|
||||
|
@ -629,10 +684,20 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
|
|||
time_left = wait_for_completion_timeout(&bus->cmd_complete,
|
||||
bus->adap.timeout);
|
||||
|
||||
if (time_left == 0)
|
||||
if (time_left == 0) {
|
||||
/*
|
||||
* If timed out and bus is still busy in a multi master
|
||||
* environment, attempt recovery at here.
|
||||
*/
|
||||
if (bus->multi_master &&
|
||||
(readl(bus->base + ASPEED_I2C_CMD_REG) &
|
||||
ASPEED_I2CD_BUS_BUSY_STS))
|
||||
aspeed_i2c_recover_bus(bus);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
else
|
||||
return bus->master_xfer_result;
|
||||
}
|
||||
|
||||
return bus->master_xfer_result;
|
||||
}
|
||||
|
||||
static u32 aspeed_i2c_functionality(struct i2c_adapter *adap)
|
||||
|
@ -672,7 +737,7 @@ static int aspeed_i2c_reg_slave(struct i2c_client *client)
|
|||
__aspeed_i2c_reg_slave(bus, client->addr);
|
||||
|
||||
bus->slave = client;
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_STOP;
|
||||
bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
|
||||
spin_unlock_irqrestore(&bus->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
@ -827,7 +892,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!of_property_read_bool(pdev->dev.of_node, "multi-master"))
|
||||
if (of_property_read_bool(pdev->dev.of_node, "multi-master"))
|
||||
bus->multi_master = true;
|
||||
else
|
||||
fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS;
|
||||
|
||||
/* Enable Master Mode */
|
||||
|
@ -930,7 +997,6 @@ static int aspeed_i2c_probe_bus(struct platform_device *pdev)
|
|||
init_completion(&bus->cmd_complete);
|
||||
bus->adap.owner = THIS_MODULE;
|
||||
bus->adap.retries = 0;
|
||||
bus->adap.timeout = 5 * HZ;
|
||||
bus->adap.algo = &aspeed_i2c_algo;
|
||||
bus->adap.dev.parent = &pdev->dev;
|
||||
bus->adap.dev.of_node = pdev->dev.of_node;
|
||||
|
|
|
@ -170,7 +170,6 @@ struct brcmstb_i2c_dev {
|
|||
struct bsc_regs *bsc_regmap;
|
||||
struct i2c_adapter adapter;
|
||||
struct completion done;
|
||||
bool is_suspended;
|
||||
u32 clk_freq_hz;
|
||||
int data_regsz;
|
||||
};
|
||||
|
@ -467,9 +466,6 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
|
|||
int xfersz = brcmstb_i2c_get_xfersz(dev);
|
||||
u32 cond, cond_per_msg;
|
||||
|
||||
if (dev->is_suspended)
|
||||
return -EBUSY;
|
||||
|
||||
/* Loop through all messages */
|
||||
for (i = 0; i < num; i++) {
|
||||
pmsg = &msgs[i];
|
||||
|
@ -689,10 +685,7 @@ static int brcmstb_i2c_suspend(struct device *dev)
|
|||
{
|
||||
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
||||
|
||||
i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||
i2c_dev->is_suspended = true;
|
||||
i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||
|
||||
i2c_mark_adapter_suspended(&i2c_dev->adapter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -700,10 +693,8 @@ static int brcmstb_i2c_resume(struct device *dev)
|
|||
{
|
||||
struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
||||
|
||||
i2c_lock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||
brcmstb_i2c_set_bsc_reg_defaults(i2c_dev);
|
||||
i2c_dev->is_suspended = false;
|
||||
i2c_unlock_bus(&i2c_dev->adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||
i2c_mark_adapter_resumed(&i2c_dev->adapter);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -18,16 +18,14 @@
|
|||
|
||||
#include <linux/io.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/i2c-cbus-gpio.h>
|
||||
|
||||
/*
|
||||
* Bit counts are derived from Nokia implementation. These should be checked
|
||||
|
@ -39,9 +37,9 @@
|
|||
struct cbus_host {
|
||||
spinlock_t lock; /* host lock */
|
||||
struct device *dev;
|
||||
int clk_gpio;
|
||||
int dat_gpio;
|
||||
int sel_gpio;
|
||||
struct gpio_desc *clk;
|
||||
struct gpio_desc *dat;
|
||||
struct gpio_desc *sel;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -51,9 +49,9 @@ struct cbus_host {
|
|||
*/
|
||||
static void cbus_send_bit(struct cbus_host *host, unsigned bit)
|
||||
{
|
||||
gpio_set_value(host->dat_gpio, bit ? 1 : 0);
|
||||
gpio_set_value(host->clk_gpio, 1);
|
||||
gpio_set_value(host->clk_gpio, 0);
|
||||
gpiod_set_value(host->dat, bit ? 1 : 0);
|
||||
gpiod_set_value(host->clk, 1);
|
||||
gpiod_set_value(host->clk, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,9 +76,9 @@ static int cbus_receive_bit(struct cbus_host *host)
|
|||
{
|
||||
int ret;
|
||||
|
||||
gpio_set_value(host->clk_gpio, 1);
|
||||
ret = gpio_get_value(host->dat_gpio);
|
||||
gpio_set_value(host->clk_gpio, 0);
|
||||
gpiod_set_value(host->clk, 1);
|
||||
ret = gpiod_get_value(host->dat);
|
||||
gpiod_set_value(host->clk, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -123,10 +121,10 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev,
|
|||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
/* Reset state and start of transfer, SEL stays down during transfer */
|
||||
gpio_set_value(host->sel_gpio, 0);
|
||||
gpiod_set_value(host->sel, 0);
|
||||
|
||||
/* Set the DAT pin to output */
|
||||
gpio_direction_output(host->dat_gpio, 1);
|
||||
gpiod_direction_output(host->dat, 1);
|
||||
|
||||
/* Send the device address */
|
||||
cbus_send_data(host, dev, CBUS_ADDR_BITS);
|
||||
|
@ -141,12 +139,12 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev,
|
|||
cbus_send_data(host, data, 16);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = gpio_direction_input(host->dat_gpio);
|
||||
ret = gpiod_direction_input(host->dat);
|
||||
if (ret) {
|
||||
dev_dbg(host->dev, "failed setting direction\n");
|
||||
goto out;
|
||||
}
|
||||
gpio_set_value(host->clk_gpio, 1);
|
||||
gpiod_set_value(host->clk, 1);
|
||||
|
||||
ret = cbus_receive_word(host);
|
||||
if (ret < 0) {
|
||||
|
@ -156,9 +154,9 @@ static int cbus_transfer(struct cbus_host *host, char rw, unsigned dev,
|
|||
}
|
||||
|
||||
/* Indicate end of transfer, SEL goes up until next transfer */
|
||||
gpio_set_value(host->sel_gpio, 1);
|
||||
gpio_set_value(host->clk_gpio, 1);
|
||||
gpio_set_value(host->clk_gpio, 0);
|
||||
gpiod_set_value(host->sel, 1);
|
||||
gpiod_set_value(host->clk, 1);
|
||||
gpiod_set_value(host->clk, 0);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
@ -214,7 +212,6 @@ static int cbus_i2c_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct i2c_adapter *adapter;
|
||||
struct cbus_host *chost;
|
||||
int ret;
|
||||
|
||||
adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter),
|
||||
GFP_KERNEL);
|
||||
|
@ -225,22 +222,20 @@ static int cbus_i2c_probe(struct platform_device *pdev)
|
|||
if (!chost)
|
||||
return -ENOMEM;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
struct device_node *dnode = pdev->dev.of_node;
|
||||
if (of_gpio_count(dnode) != 3)
|
||||
return -ENODEV;
|
||||
chost->clk_gpio = of_get_gpio(dnode, 0);
|
||||
chost->dat_gpio = of_get_gpio(dnode, 1);
|
||||
chost->sel_gpio = of_get_gpio(dnode, 2);
|
||||
} else if (dev_get_platdata(&pdev->dev)) {
|
||||
struct i2c_cbus_platform_data *pdata =
|
||||
dev_get_platdata(&pdev->dev);
|
||||
chost->clk_gpio = pdata->clk_gpio;
|
||||
chost->dat_gpio = pdata->dat_gpio;
|
||||
chost->sel_gpio = pdata->sel_gpio;
|
||||
} else {
|
||||
if (gpiod_count(&pdev->dev, NULL) != 3)
|
||||
return -ENODEV;
|
||||
}
|
||||
chost->clk = devm_gpiod_get_index(&pdev->dev, NULL, 0, GPIOD_OUT_LOW);
|
||||
if (IS_ERR(chost->clk))
|
||||
return PTR_ERR(chost->clk);
|
||||
chost->dat = devm_gpiod_get_index(&pdev->dev, NULL, 1, GPIOD_IN);
|
||||
if (IS_ERR(chost->dat))
|
||||
return PTR_ERR(chost->dat);
|
||||
chost->sel = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(chost->sel))
|
||||
return PTR_ERR(chost->sel);
|
||||
gpiod_set_consumer_name(chost->clk, "CBUS clk");
|
||||
gpiod_set_consumer_name(chost->dat, "CBUS dat");
|
||||
gpiod_set_consumer_name(chost->sel, "CBUS sel");
|
||||
|
||||
adapter->owner = THIS_MODULE;
|
||||
adapter->class = I2C_CLASS_HWMON;
|
||||
|
@ -254,21 +249,6 @@ static int cbus_i2c_probe(struct platform_device *pdev)
|
|||
spin_lock_init(&chost->lock);
|
||||
chost->dev = &pdev->dev;
|
||||
|
||||
ret = devm_gpio_request_one(&pdev->dev, chost->clk_gpio,
|
||||
GPIOF_OUT_INIT_LOW, "CBUS clk");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_gpio_request_one(&pdev->dev, chost->dat_gpio, GPIOF_IN,
|
||||
"CBUS data");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_gpio_request_one(&pdev->dev, chost->sel_gpio,
|
||||
GPIOF_OUT_INIT_HIGH, "CBUS sel");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2c_set_adapdata(adapter, chost);
|
||||
platform_set_drvdata(pdev, adapter);
|
||||
|
||||
|
|
|
@ -215,6 +215,7 @@
|
|||
* @disable_int: function to disable all interrupts
|
||||
* @init: function to initialize the I2C hardware
|
||||
* @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE
|
||||
* @suspended: set to true if the controller is suspended
|
||||
*
|
||||
* HCNT and LCNT parameters can be used if the platform knows more accurate
|
||||
* values than the one computed based only on the input clock frequency.
|
||||
|
@ -270,6 +271,7 @@ struct dw_i2c_dev {
|
|||
int (*set_sda_hold_time)(struct dw_i2c_dev *dev);
|
||||
int mode;
|
||||
struct i2c_bus_recovery_info rinfo;
|
||||
bool suspended;
|
||||
};
|
||||
|
||||
#define ACCESS_SWAP 0x00000001
|
||||
|
|
|
@ -426,6 +426,12 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
if (dev->suspended) {
|
||||
dev_err(dev->dev, "Error %s call while suspended\n", __func__);
|
||||
ret = -ESHUTDOWN;
|
||||
goto done_nolock;
|
||||
}
|
||||
|
||||
reinit_completion(&dev->cmd_complete);
|
||||
dev->msgs = msgs;
|
||||
dev->msgs_num = num;
|
||||
|
|
|
@ -176,6 +176,7 @@ static int i2c_dw_pci_suspend(struct device *dev)
|
|||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct dw_i2c_dev *i_dev = pci_get_drvdata(pdev);
|
||||
|
||||
i_dev->suspended = true;
|
||||
i_dev->disable(i_dev);
|
||||
|
||||
return 0;
|
||||
|
@ -185,8 +186,12 @@ static int i2c_dw_pci_resume(struct device *dev)
|
|||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct dw_i2c_dev *i_dev = pci_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
return i_dev->init(i_dev);
|
||||
ret = i_dev->init(i_dev);
|
||||
i_dev->suspended = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -454,6 +454,8 @@ static int dw_i2c_plat_suspend(struct device *dev)
|
|||
{
|
||||
struct dw_i2c_dev *i_dev = dev_get_drvdata(dev);
|
||||
|
||||
i_dev->suspended = true;
|
||||
|
||||
if (i_dev->shared_with_punit)
|
||||
return 0;
|
||||
|
||||
|
@ -471,6 +473,7 @@ static int dw_i2c_plat_resume(struct device *dev)
|
|||
i2c_dw_prepare_clk(i_dev, true);
|
||||
|
||||
i_dev->init(i_dev);
|
||||
i_dev->suspended = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -183,7 +183,6 @@ enum i2c_type_exynos {
|
|||
|
||||
struct exynos5_i2c {
|
||||
struct i2c_adapter adap;
|
||||
unsigned int suspended:1;
|
||||
|
||||
struct i2c_msg *msg;
|
||||
struct completion msg_complete;
|
||||
|
@ -715,11 +714,6 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
|
|||
struct exynos5_i2c *i2c = adap->algo_data;
|
||||
int i, ret;
|
||||
|
||||
if (i2c->suspended) {
|
||||
dev_err(i2c->dev, "HS-I2C is not initialized.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = clk_enable(i2c->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -847,8 +841,7 @@ static int exynos5_i2c_suspend_noirq(struct device *dev)
|
|||
{
|
||||
struct exynos5_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
||||
i2c->suspended = 1;
|
||||
|
||||
i2c_mark_adapter_suspended(&i2c->adap);
|
||||
clk_unprepare(i2c->clk);
|
||||
|
||||
return 0;
|
||||
|
@ -871,7 +864,7 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
|
|||
|
||||
exynos5_i2c_init(i2c);
|
||||
clk_disable(i2c->clk);
|
||||
i2c->suspended = 0;
|
||||
i2c_mark_adapter_resumed(&i2c->adap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -7,17 +7,19 @@
|
|||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/completion.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include <linux/platform_data/i2c-gpio.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_data/i2c-gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct i2c_gpio_private_data {
|
||||
struct gpio_desc *sda;
|
||||
|
@ -27,6 +29,9 @@ struct i2c_gpio_private_data {
|
|||
struct i2c_gpio_platform_data pdata;
|
||||
#ifdef CONFIG_I2C_GPIO_FAULT_INJECTOR
|
||||
struct dentry *debug_dir;
|
||||
/* these must be protected by bus lock */
|
||||
struct completion scl_irq_completion;
|
||||
u64 scl_irq_data;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -162,6 +167,96 @@ static int fops_incomplete_write_byte_set(void *data, u64 addr)
|
|||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_incomplete_write_byte, NULL, fops_incomplete_write_byte_set, "%llu\n");
|
||||
|
||||
static int i2c_gpio_fi_act_on_scl_irq(struct i2c_gpio_private_data *priv,
|
||||
irqreturn_t handler(int, void*))
|
||||
{
|
||||
int ret, irq = gpiod_to_irq(priv->scl);
|
||||
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
i2c_lock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
|
||||
ret = gpiod_direction_input(priv->scl);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
reinit_completion(&priv->scl_irq_completion);
|
||||
|
||||
ret = request_irq(irq, handler, IRQF_TRIGGER_FALLING,
|
||||
"i2c_gpio_fault_injector_scl_irq", priv);
|
||||
if (ret)
|
||||
goto output;
|
||||
|
||||
wait_for_completion_interruptible(&priv->scl_irq_completion);
|
||||
|
||||
free_irq(irq, priv);
|
||||
output:
|
||||
ret = gpiod_direction_output(priv->scl, 1) ?: ret;
|
||||
unlock:
|
||||
i2c_unlock_bus(&priv->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t lose_arbitration_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct i2c_gpio_private_data *priv = dev_id;
|
||||
|
||||
setsda(&priv->bit_data, 0);
|
||||
udelay(priv->scl_irq_data);
|
||||
setsda(&priv->bit_data, 1);
|
||||
|
||||
complete(&priv->scl_irq_completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int fops_lose_arbitration_set(void *data, u64 duration)
|
||||
{
|
||||
struct i2c_gpio_private_data *priv = data;
|
||||
|
||||
if (duration > 100 * 1000)
|
||||
return -EINVAL;
|
||||
|
||||
priv->scl_irq_data = duration;
|
||||
/*
|
||||
* Interrupt on falling SCL. This ensures that the master under test has
|
||||
* really started the transfer. Interrupt on falling SDA did only
|
||||
* exercise 'bus busy' detection on some HW but not 'arbitration lost'.
|
||||
* Note that the interrupt latency may cause the first bits to be
|
||||
* transmitted correctly.
|
||||
*/
|
||||
return i2c_gpio_fi_act_on_scl_irq(priv, lose_arbitration_irq);
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_lose_arbitration, NULL, fops_lose_arbitration_set, "%llu\n");
|
||||
|
||||
static irqreturn_t inject_panic_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct i2c_gpio_private_data *priv = dev_id;
|
||||
|
||||
udelay(priv->scl_irq_data);
|
||||
panic("I2C fault injector induced panic");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int fops_inject_panic_set(void *data, u64 duration)
|
||||
{
|
||||
struct i2c_gpio_private_data *priv = data;
|
||||
|
||||
if (duration > 100 * 1000)
|
||||
return -EINVAL;
|
||||
|
||||
priv->scl_irq_data = duration;
|
||||
/*
|
||||
* Interrupt on falling SCL. This ensures that the master under test has
|
||||
* really started the transfer.
|
||||
*/
|
||||
return i2c_gpio_fi_act_on_scl_irq(priv, inject_panic_irq);
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_inject_panic, NULL, fops_inject_panic_set, "%llu\n");
|
||||
|
||||
static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
|
||||
{
|
||||
struct i2c_gpio_private_data *priv = platform_get_drvdata(pdev);
|
||||
|
@ -181,12 +276,20 @@ static void i2c_gpio_fault_injector_init(struct platform_device *pdev)
|
|||
if (!priv->debug_dir)
|
||||
return;
|
||||
|
||||
debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl);
|
||||
debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda);
|
||||
init_completion(&priv->scl_irq_completion);
|
||||
|
||||
debugfs_create_file_unsafe("incomplete_address_phase", 0200, priv->debug_dir,
|
||||
priv, &fops_incomplete_addr_phase);
|
||||
debugfs_create_file_unsafe("incomplete_write_byte", 0200, priv->debug_dir,
|
||||
priv, &fops_incomplete_write_byte);
|
||||
if (priv->bit_data.getscl) {
|
||||
debugfs_create_file_unsafe("inject_panic", 0200, priv->debug_dir,
|
||||
priv, &fops_inject_panic);
|
||||
debugfs_create_file_unsafe("lose_arbitration", 0200, priv->debug_dir,
|
||||
priv, &fops_lose_arbitration);
|
||||
}
|
||||
debugfs_create_file_unsafe("scl", 0600, priv->debug_dir, priv, &fops_scl);
|
||||
debugfs_create_file_unsafe("sda", 0600, priv->debug_dir, priv, &fops_sda);
|
||||
}
|
||||
|
||||
static void i2c_gpio_fault_injector_exit(struct platform_device *pdev)
|
||||
|
@ -286,11 +389,11 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
/*
|
||||
* First get the GPIO pins; if it fails, we'll defer the probe.
|
||||
* If the SDA line is marked from platform data or device tree as
|
||||
* "open drain" it means something outside of our control is making
|
||||
* this line being handled as open drain, and we should just handle
|
||||
* it as any other output. Else we enforce open drain as this is
|
||||
* required for an I2C bus.
|
||||
* If the SCL/SDA lines are marked "open drain" by platform data or
|
||||
* device tree then this means that something outside of our control is
|
||||
* marking these lines to be handled as open drain, and we should just
|
||||
* handle them as we handle any other output. Else we enforce open
|
||||
* drain as this is required for an I2C bus.
|
||||
*/
|
||||
if (pdata->sda_is_open_drain)
|
||||
gflags = GPIOD_OUT_HIGH;
|
||||
|
@ -300,13 +403,6 @@ static int i2c_gpio_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(priv->sda))
|
||||
return PTR_ERR(priv->sda);
|
||||
|
||||
/*
|
||||
* If the SCL line is marked from platform data or device tree as
|
||||
* "open drain" it means something outside of our control is making
|
||||
* this line being handled as open drain, and we should just handle
|
||||
* it as any other output. Else we enforce open drain as this is
|
||||
* required for an I2C bus.
|
||||
*/
|
||||
if (pdata->scl_is_open_drain)
|
||||
gflags = GPIOD_OUT_HIGH;
|
||||
else
|
||||
|
|
|
@ -273,8 +273,8 @@ static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx,
|
|||
}
|
||||
|
||||
/* Functions for DMA support */
|
||||
static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
|
||||
dma_addr_t phy_addr)
|
||||
static int i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
|
||||
dma_addr_t phy_addr)
|
||||
{
|
||||
struct imx_i2c_dma *dma;
|
||||
struct dma_slave_config dma_sconfig;
|
||||
|
@ -283,11 +283,13 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
|
|||
|
||||
dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
|
||||
if (!dma)
|
||||
return;
|
||||
return -ENOMEM;
|
||||
|
||||
dma->chan_tx = dma_request_slave_channel(dev, "tx");
|
||||
if (!dma->chan_tx) {
|
||||
dev_dbg(dev, "can't request DMA tx channel\n");
|
||||
dma->chan_tx = dma_request_chan(dev, "tx");
|
||||
if (IS_ERR(dma->chan_tx)) {
|
||||
ret = PTR_ERR(dma->chan_tx);
|
||||
if (ret != -ENODEV && ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "can't request DMA tx channel (%d)\n", ret);
|
||||
goto fail_al;
|
||||
}
|
||||
|
||||
|
@ -298,13 +300,15 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
|
|||
dma_sconfig.direction = DMA_MEM_TO_DEV;
|
||||
ret = dmaengine_slave_config(dma->chan_tx, &dma_sconfig);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dev, "can't configure tx channel\n");
|
||||
dev_err(dev, "can't configure tx channel (%d)\n", ret);
|
||||
goto fail_tx;
|
||||
}
|
||||
|
||||
dma->chan_rx = dma_request_slave_channel(dev, "rx");
|
||||
if (!dma->chan_rx) {
|
||||
dev_dbg(dev, "can't request DMA rx channel\n");
|
||||
dma->chan_rx = dma_request_chan(dev, "rx");
|
||||
if (IS_ERR(dma->chan_rx)) {
|
||||
ret = PTR_ERR(dma->chan_rx);
|
||||
if (ret != -ENODEV && ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "can't request DMA rx channel (%d)\n", ret);
|
||||
goto fail_tx;
|
||||
}
|
||||
|
||||
|
@ -315,7 +319,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
|
|||
dma_sconfig.direction = DMA_DEV_TO_MEM;
|
||||
ret = dmaengine_slave_config(dma->chan_rx, &dma_sconfig);
|
||||
if (ret < 0) {
|
||||
dev_dbg(dev, "can't configure rx channel\n");
|
||||
dev_err(dev, "can't configure rx channel (%d)\n", ret);
|
||||
goto fail_rx;
|
||||
}
|
||||
|
||||
|
@ -324,7 +328,7 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
|
|||
dev_info(dev, "using %s (tx) and %s (rx) for DMA transfers\n",
|
||||
dma_chan_name(dma->chan_tx), dma_chan_name(dma->chan_rx));
|
||||
|
||||
return;
|
||||
return 0;
|
||||
|
||||
fail_rx:
|
||||
dma_release_channel(dma->chan_rx);
|
||||
|
@ -332,7 +336,8 @@ static void i2c_imx_dma_request(struct imx_i2c_struct *i2c_imx,
|
|||
dma_release_channel(dma->chan_tx);
|
||||
fail_al:
|
||||
devm_kfree(dev, dma);
|
||||
dev_info(dev, "can't use DMA, using PIO instead.\n");
|
||||
/* return successfully if there is no dma support */
|
||||
return ret == -ENODEV ? 0 : ret;
|
||||
}
|
||||
|
||||
static void i2c_imx_dma_callback(void *arg)
|
||||
|
@ -1160,11 +1165,13 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
|
||||
dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
|
||||
i2c_imx->adapter.name);
|
||||
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
|
||||
|
||||
/* Init DMA config if supported */
|
||||
i2c_imx_dma_request(i2c_imx, phy_addr);
|
||||
ret = i2c_imx_dma_request(i2c_imx, phy_addr);
|
||||
if (ret < 0)
|
||||
goto clk_notifier_unregister;
|
||||
|
||||
dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
|
||||
return 0; /* Return OK */
|
||||
|
||||
clk_notifier_unregister:
|
||||
|
|
|
@ -471,6 +471,7 @@ iop3xx_i2c_probe(struct platform_device *pdev)
|
|||
new_adapter->owner = THIS_MODULE;
|
||||
new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
|
||||
new_adapter->dev.parent = &pdev->dev;
|
||||
new_adapter->dev.of_node = pdev->dev.of_node;
|
||||
new_adapter->nr = pdev->id;
|
||||
|
||||
/*
|
||||
|
@ -508,12 +509,19 @@ iop3xx_i2c_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id i2c_iop3xx_match[] = {
|
||||
{ .compatible = "intel,iop3xx-i2c", },
|
||||
{ .compatible = "intel,ixp4xx-i2c", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, i2c_iop3xx_match);
|
||||
|
||||
static struct platform_driver iop3xx_i2c_driver = {
|
||||
.probe = iop3xx_i2c_probe,
|
||||
.remove = iop3xx_i2c_remove,
|
||||
.driver = {
|
||||
.name = "IOP3xx-I2C",
|
||||
.of_match_table = i2c_iop3xx_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -456,7 +456,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
|||
|
||||
control_reg = readw(i2c->base + OFFSET_CONTROL) &
|
||||
~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
|
||||
if ((i2c->speed_hz > 400000) || (left_num >= 1))
|
||||
if ((i2c->speed_hz > MAX_FS_MODE_SPEED) || (left_num >= 1))
|
||||
control_reg |= I2C_CONTROL_RS;
|
||||
|
||||
if (i2c->op == I2C_MASTER_WRRD)
|
||||
|
@ -465,7 +465,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
|||
writew(control_reg, i2c->base + OFFSET_CONTROL);
|
||||
|
||||
/* set start condition */
|
||||
if (i2c->speed_hz <= 100000)
|
||||
if (i2c->speed_hz <= I2C_DEFAULT_SPEED)
|
||||
writew(I2C_ST_START_CON, i2c->base + OFFSET_EXT_CONF);
|
||||
else
|
||||
writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF);
|
||||
|
@ -642,8 +642,6 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
completion_done(&i2c->msg_complete);
|
||||
|
||||
if (i2c->irq_stat & (I2C_HS_NACKERR | I2C_ACKERR)) {
|
||||
dev_dbg(i2c->dev, "addr: %x, transfer ACK error\n", msgs->addr);
|
||||
mtk_i2c_init_hw(i2c);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* i2c-ocores.c: I2C bus driver for OpenCores I2C controller
|
||||
* (https://opencores.org/project/i2c/overview)
|
||||
|
@ -6,13 +7,10 @@
|
|||
*
|
||||
* Support for the GRLIB port of the controller by
|
||||
* Andreas Larsson <andreas@gaisler.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -25,17 +23,28 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#define OCORES_FLAG_POLL BIT(0)
|
||||
|
||||
/*
|
||||
* 'process_lock' exists because ocores_process() and ocores_process_timeout()
|
||||
* can't run in parallel.
|
||||
*/
|
||||
struct ocores_i2c {
|
||||
void __iomem *base;
|
||||
int iobase;
|
||||
u32 reg_shift;
|
||||
u32 reg_io_width;
|
||||
unsigned long flags;
|
||||
wait_queue_head_t wait;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_msg *msg;
|
||||
int pos;
|
||||
int nmsgs;
|
||||
int state; /* see STATE_ */
|
||||
spinlock_t process_lock;
|
||||
struct clk *clk;
|
||||
int ip_clock_khz;
|
||||
int bus_clock_khz;
|
||||
|
@ -127,6 +136,16 @@ static inline u8 oc_getreg_32be(struct ocores_i2c *i2c, int reg)
|
|||
return ioread32be(i2c->base + (reg << i2c->reg_shift));
|
||||
}
|
||||
|
||||
static void oc_setreg_io_8(struct ocores_i2c *i2c, int reg, u8 value)
|
||||
{
|
||||
outb(value, i2c->iobase + reg);
|
||||
}
|
||||
|
||||
static inline u8 oc_getreg_io_8(struct ocores_i2c *i2c, int reg)
|
||||
{
|
||||
return inb(i2c->iobase + reg);
|
||||
}
|
||||
|
||||
static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value)
|
||||
{
|
||||
i2c->setreg(i2c, reg, value);
|
||||
|
@ -137,23 +156,29 @@ static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg)
|
|||
return i2c->getreg(i2c, reg);
|
||||
}
|
||||
|
||||
static void ocores_process(struct ocores_i2c *i2c)
|
||||
static void ocores_process(struct ocores_i2c *i2c, u8 stat)
|
||||
{
|
||||
struct i2c_msg *msg = i2c->msg;
|
||||
u8 stat = oc_getreg(i2c, OCI2C_STATUS);
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* If we spin here is because we are in timeout, so we are going
|
||||
* to be in STATE_ERROR. See ocores_process_timeout()
|
||||
*/
|
||||
spin_lock_irqsave(&i2c->process_lock, flags);
|
||||
|
||||
if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) {
|
||||
/* stop has been sent */
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
|
||||
wake_up(&i2c->wait);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* error? */
|
||||
if (stat & OCI2C_STAT_ARBLOST) {
|
||||
i2c->state = STATE_ERROR;
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) {
|
||||
|
@ -163,10 +188,11 @@ static void ocores_process(struct ocores_i2c *i2c)
|
|||
if (stat & OCI2C_STAT_NACK) {
|
||||
i2c->state = STATE_ERROR;
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA);
|
||||
}
|
||||
|
||||
/* end of msg? */
|
||||
if (i2c->pos == msg->len) {
|
||||
|
@ -183,15 +209,15 @@ static void ocores_process(struct ocores_i2c *i2c)
|
|||
i2c->state = STATE_START;
|
||||
|
||||
oc_setreg(i2c, OCI2C_DATA, addr);
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
|
||||
return;
|
||||
} else
|
||||
i2c->state = (msg->flags & I2C_M_RD)
|
||||
? STATE_READ : STATE_WRITE;
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
|
||||
goto out;
|
||||
}
|
||||
i2c->state = (msg->flags & I2C_M_RD)
|
||||
? STATE_READ : STATE_WRITE;
|
||||
} else {
|
||||
i2c->state = STATE_DONE;
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,20 +228,148 @@ static void ocores_process(struct ocores_i2c *i2c)
|
|||
oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]);
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&i2c->process_lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t ocores_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct ocores_i2c *i2c = dev_id;
|
||||
u8 stat = oc_getreg(i2c, OCI2C_STATUS);
|
||||
|
||||
ocores_process(i2c);
|
||||
if (!(stat & OCI2C_STAT_IF))
|
||||
return IRQ_NONE;
|
||||
|
||||
ocores_process(i2c, stat);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
/**
|
||||
* Process timeout event
|
||||
* @i2c: ocores I2C device instance
|
||||
*/
|
||||
static void ocores_process_timeout(struct ocores_i2c *i2c)
|
||||
{
|
||||
struct ocores_i2c *i2c = i2c_get_adapdata(adap);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&i2c->process_lock, flags);
|
||||
i2c->state = STATE_ERROR;
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
|
||||
spin_unlock_irqrestore(&i2c->process_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until something change in a given register
|
||||
* @i2c: ocores I2C device instance
|
||||
* @reg: register to query
|
||||
* @mask: bitmask to apply on register value
|
||||
* @val: expected result
|
||||
* @timeout: timeout in jiffies
|
||||
*
|
||||
* Timeout is necessary to avoid to stay here forever when the chip
|
||||
* does not answer correctly.
|
||||
*
|
||||
* Return: 0 on success, -ETIMEDOUT on timeout
|
||||
*/
|
||||
static int ocores_wait(struct ocores_i2c *i2c,
|
||||
int reg, u8 mask, u8 val,
|
||||
const unsigned long timeout)
|
||||
{
|
||||
unsigned long j;
|
||||
|
||||
j = jiffies + timeout;
|
||||
while (1) {
|
||||
u8 status = oc_getreg(i2c, reg);
|
||||
|
||||
if ((status & mask) == val)
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, j))
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until is possible to process some data
|
||||
* @i2c: ocores I2C device instance
|
||||
*
|
||||
* Used when the device is in polling mode (interrupts disabled).
|
||||
*
|
||||
* Return: 0 on success, -ETIMEDOUT on timeout
|
||||
*/
|
||||
static int ocores_poll_wait(struct ocores_i2c *i2c)
|
||||
{
|
||||
u8 mask;
|
||||
int err;
|
||||
|
||||
if (i2c->state == STATE_DONE || i2c->state == STATE_ERROR) {
|
||||
/* transfer is over */
|
||||
mask = OCI2C_STAT_BUSY;
|
||||
} else {
|
||||
/* on going transfer */
|
||||
mask = OCI2C_STAT_TIP;
|
||||
/*
|
||||
* We wait for the data to be transferred (8bit),
|
||||
* then we start polling on the ACK/NACK bit
|
||||
*/
|
||||
udelay((8 * 1000) / i2c->bus_clock_khz);
|
||||
}
|
||||
|
||||
/*
|
||||
* once we are here we expect to get the expected result immediately
|
||||
* so if after 1ms we timeout then something is broken.
|
||||
*/
|
||||
err = ocores_wait(i2c, OCI2C_STATUS, mask, 0, msecs_to_jiffies(1));
|
||||
if (err)
|
||||
dev_warn(i2c->adap.dev.parent,
|
||||
"%s: STATUS timeout, bit 0x%x did not clear in 1ms\n",
|
||||
__func__, mask);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* It handles an IRQ-less transfer
|
||||
* @i2c: ocores I2C device instance
|
||||
*
|
||||
* Even if IRQ are disabled, the I2C OpenCore IP behavior is exactly the same
|
||||
* (only that IRQ are not produced). This means that we can re-use entirely
|
||||
* ocores_isr(), we just add our polling code around it.
|
||||
*
|
||||
* It can run in atomic context
|
||||
*/
|
||||
static void ocores_process_polling(struct ocores_i2c *i2c)
|
||||
{
|
||||
while (1) {
|
||||
irqreturn_t ret;
|
||||
int err;
|
||||
|
||||
err = ocores_poll_wait(i2c);
|
||||
if (err) {
|
||||
i2c->state = STATE_ERROR;
|
||||
break; /* timeout */
|
||||
}
|
||||
|
||||
ret = ocores_isr(-1, i2c);
|
||||
if (ret == IRQ_NONE)
|
||||
break; /* all messages have been transferred */
|
||||
}
|
||||
}
|
||||
|
||||
static int ocores_xfer_core(struct ocores_i2c *i2c,
|
||||
struct i2c_msg *msgs, int num,
|
||||
bool polling)
|
||||
{
|
||||
int ret;
|
||||
u8 ctrl;
|
||||
|
||||
ctrl = oc_getreg(i2c, OCI2C_CONTROL);
|
||||
if (polling)
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~OCI2C_CTRL_IEN);
|
||||
else
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN);
|
||||
|
||||
i2c->msg = msgs;
|
||||
i2c->pos = 0;
|
||||
|
@ -225,11 +379,35 @@ static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|||
oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg));
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
|
||||
|
||||
if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
|
||||
(i2c->state == STATE_DONE), HZ))
|
||||
return (i2c->state == STATE_DONE) ? num : -EIO;
|
||||
else
|
||||
return -ETIMEDOUT;
|
||||
if (polling) {
|
||||
ocores_process_polling(i2c);
|
||||
} else {
|
||||
ret = wait_event_timeout(i2c->wait,
|
||||
(i2c->state == STATE_ERROR) ||
|
||||
(i2c->state == STATE_DONE), HZ);
|
||||
if (ret == 0) {
|
||||
ocores_process_timeout(i2c);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
return (i2c->state == STATE_DONE) ? num : -EIO;
|
||||
}
|
||||
|
||||
static int ocores_xfer_polling(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
return ocores_xfer_core(i2c_get_adapdata(adap), msgs, num, true);
|
||||
}
|
||||
|
||||
static int ocores_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct ocores_i2c *i2c = i2c_get_adapdata(adap);
|
||||
|
||||
if (i2c->flags & OCORES_FLAG_POLL)
|
||||
return ocores_xfer_polling(adap, msgs, num);
|
||||
return ocores_xfer_core(i2c, msgs, num, false);
|
||||
}
|
||||
|
||||
static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
|
||||
|
@ -239,7 +417,8 @@ static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
|
|||
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
|
||||
|
||||
/* make sure the device is disabled */
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
|
||||
ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN);
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl);
|
||||
|
||||
prescale = (i2c->ip_clock_khz / (5 * i2c->bus_clock_khz)) - 1;
|
||||
prescale = clamp(prescale, 0, 0xffff);
|
||||
|
@ -257,7 +436,7 @@ static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
|
|||
|
||||
/* Init the device */
|
||||
oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK);
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN);
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -294,13 +473,16 @@ static const struct of_device_id ocores_i2c_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, ocores_i2c_match);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/* Read and write functions for the GRLIB port of the controller. Registers are
|
||||
/*
|
||||
* Read and write functions for the GRLIB port of the controller. Registers are
|
||||
* 32-bit big endian and the PRELOW and PREHIGH registers are merged into one
|
||||
* register. The subsequent registers has their offset decreased accordingly. */
|
||||
* register. The subsequent registers have their offsets decreased accordingly.
|
||||
*/
|
||||
static u8 oc_getreg_grlib(struct ocores_i2c *i2c, int reg)
|
||||
{
|
||||
u32 rd;
|
||||
int rreg = reg;
|
||||
|
||||
if (reg != OCI2C_PRELOW)
|
||||
rreg--;
|
||||
rd = ioread32be(i2c->base + (rreg << i2c->reg_shift));
|
||||
|
@ -314,6 +496,7 @@ static void oc_setreg_grlib(struct ocores_i2c *i2c, int reg, u8 value)
|
|||
{
|
||||
u32 curr, wr;
|
||||
int rreg = reg;
|
||||
|
||||
if (reg != OCI2C_PRELOW)
|
||||
rreg--;
|
||||
if (reg == OCI2C_PRELOW || reg == OCI2C_PREHIGH) {
|
||||
|
@ -402,7 +585,7 @@ static int ocores_i2c_of_probe(struct platform_device *pdev,
|
|||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ocores_i2c_of_probe(pdev,i2c) -ENODEV
|
||||
#define ocores_i2c_of_probe(pdev, i2c) -ENODEV
|
||||
#endif
|
||||
|
||||
static int ocores_i2c_probe(struct platform_device *pdev)
|
||||
|
@ -414,25 +597,41 @@ static int ocores_i2c_probe(struct platform_device *pdev)
|
|||
int ret;
|
||||
int i;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
||||
if (!i2c)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&i2c->process_lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
i2c->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(i2c->base))
|
||||
return PTR_ERR(i2c->base);
|
||||
if (res) {
|
||||
i2c->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(i2c->base))
|
||||
return PTR_ERR(i2c->base);
|
||||
} else {
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
i2c->iobase = res->start;
|
||||
if (!devm_request_region(&pdev->dev, res->start,
|
||||
resource_size(res),
|
||||
pdev->name)) {
|
||||
dev_err(&pdev->dev, "Can't get I/O resource.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
i2c->setreg = oc_setreg_io_8;
|
||||
i2c->getreg = oc_getreg_io_8;
|
||||
}
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (pdata) {
|
||||
i2c->reg_shift = pdata->reg_shift;
|
||||
i2c->reg_io_width = pdata->reg_io_width;
|
||||
i2c->ip_clock_khz = pdata->clock_khz;
|
||||
i2c->bus_clock_khz = 100;
|
||||
if (pdata->bus_khz)
|
||||
i2c->bus_clock_khz = pdata->bus_khz;
|
||||
else
|
||||
i2c->bus_clock_khz = 100;
|
||||
} else {
|
||||
ret = ocores_i2c_of_probe(pdev, i2c);
|
||||
if (ret)
|
||||
|
@ -470,18 +669,29 @@ static int ocores_i2c_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
init_waitqueue_head(&i2c->wait);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq == -ENXIO) {
|
||||
i2c->flags |= OCORES_FLAG_POLL;
|
||||
} else {
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
if (!(i2c->flags & OCORES_FLAG_POLL)) {
|
||||
ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
|
||||
pdev->name, i2c);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot claim IRQ\n");
|
||||
goto err_clk;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ocores_init(&pdev->dev, i2c);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
init_waitqueue_head(&i2c->wait);
|
||||
ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
|
||||
pdev->name, i2c);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Cannot claim IRQ\n");
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
/* hook up driver to tree */
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
i2c->adap = ocores_adapter;
|
||||
|
@ -510,10 +720,11 @@ static int ocores_i2c_probe(struct platform_device *pdev)
|
|||
static int ocores_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ocores_i2c *i2c = platform_get_drvdata(pdev);
|
||||
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
|
||||
|
||||
/* disable i2c logic */
|
||||
oc_setreg(i2c, OCI2C_CONTROL, oc_getreg(i2c, OCI2C_CONTROL)
|
||||
& ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
|
||||
ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN);
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl);
|
||||
|
||||
/* remove adapter & data */
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
|
@ -531,7 +742,8 @@ static int ocores_i2c_suspend(struct device *dev)
|
|||
u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL);
|
||||
|
||||
/* make sure the device is disabled */
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN));
|
||||
ctrl &= ~(OCI2C_CTRL_EN | OCI2C_CTRL_IEN);
|
||||
oc_setreg(i2c, OCI2C_CONTROL, ctrl);
|
||||
|
||||
if (!IS_ERR(i2c->clk))
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
/*
|
||||
* Driver for the Renesas R-Car I2C unit
|
||||
*
|
||||
* Copyright (C) 2014-15 Wolfram Sang <wsa@sang-engineering.com>
|
||||
* Copyright (C) 2011-2015 Renesas Electronics Corporation
|
||||
* Copyright (C) 2014-19 Wolfram Sang <wsa@sang-engineering.com>
|
||||
* Copyright (C) 2011-2019 Renesas Electronics Corporation
|
||||
*
|
||||
* Copyright (C) 2012-14 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
|
@ -39,8 +39,8 @@
|
|||
#define ICSAR 0x1C /* slave address */
|
||||
#define ICMAR 0x20 /* master address */
|
||||
#define ICRXTX 0x24 /* data port */
|
||||
#define ICDMAER 0x3c /* DMA enable */
|
||||
#define ICFBSCR 0x38 /* first bit setup cycle */
|
||||
#define ICFBSCR 0x38 /* first bit setup cycle (Gen3) */
|
||||
#define ICDMAER 0x3c /* DMA enable (Gen3) */
|
||||
|
||||
/* ICSCR */
|
||||
#define SDBS (1 << 3) /* slave data buffer select */
|
||||
|
@ -83,7 +83,6 @@
|
|||
#define TMDMAE (1 << 0) /* DMA Master Transmitted Enable */
|
||||
|
||||
/* ICFBSCR */
|
||||
#define TCYC06 0x04 /* 6*Tcyc delay 1st bit between SDA and SCL */
|
||||
#define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */
|
||||
|
||||
|
||||
|
@ -212,6 +211,10 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
|
|||
rcar_i2c_write(priv, ICMSR, 0);
|
||||
/* start clock */
|
||||
rcar_i2c_write(priv, ICCCR, priv->icccr);
|
||||
|
||||
if (priv->devtype == I2C_RCAR_GEN3)
|
||||
rcar_i2c_write(priv, ICFBSCR, TCYC17);
|
||||
|
||||
}
|
||||
|
||||
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
|
||||
|
@ -355,9 +358,6 @@ static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
|
|||
rcar_i2c_prepare_msg(priv);
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupt functions
|
||||
*/
|
||||
static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE
|
||||
|
@ -366,9 +366,6 @@ static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
|
|||
/* Disable DMA Master Received/Transmitted */
|
||||
rcar_i2c_write(priv, ICDMAER, 0);
|
||||
|
||||
/* Reset default delay */
|
||||
rcar_i2c_write(priv, ICFBSCR, TCYC06);
|
||||
|
||||
dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg),
|
||||
sg_dma_len(&priv->sg), priv->dma_direction);
|
||||
|
||||
|
@ -464,9 +461,6 @@ static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Set delay for DMA operations */
|
||||
rcar_i2c_write(priv, ICFBSCR, TCYC17);
|
||||
|
||||
/* Enable DMA Master Received/Transmitted */
|
||||
if (read)
|
||||
rcar_i2c_write(priv, ICDMAER, RMDMAE);
|
||||
|
@ -1017,10 +1011,37 @@ static int rcar_i2c_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int rcar_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
i2c_mark_adapter_suspended(&priv->adap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
i2c_mark_adapter_resumed(&priv->adap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rcar_i2c_pm_ops = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rcar_i2c_suspend, rcar_i2c_resume)
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS (&rcar_i2c_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static struct platform_driver rcar_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "i2c-rcar",
|
||||
.of_match_table = rcar_i2c_dt_ids,
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
.probe = rcar_i2c_probe,
|
||||
.remove = rcar_i2c_remove,
|
||||
|
|
|
@ -104,7 +104,6 @@ enum s3c24xx_i2c_state {
|
|||
struct s3c24xx_i2c {
|
||||
wait_queue_head_t wait;
|
||||
kernel_ulong_t quirks;
|
||||
unsigned int suspended:1;
|
||||
|
||||
struct i2c_msg *msg;
|
||||
unsigned int msg_num;
|
||||
|
@ -703,9 +702,6 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
|
|||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
if (i2c->suspended)
|
||||
return -EIO;
|
||||
|
||||
ret = s3c24xx_i2c_set_master(i2c);
|
||||
if (ret != 0) {
|
||||
dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
|
||||
|
@ -1246,7 +1242,7 @@ static int s3c24xx_i2c_suspend_noirq(struct device *dev)
|
|||
{
|
||||
struct s3c24xx_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
||||
i2c->suspended = 1;
|
||||
i2c_mark_adapter_suspended(&i2c->adap);
|
||||
|
||||
if (!IS_ERR(i2c->sysreg))
|
||||
regmap_read(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, &i2c->sys_i2c_cfg);
|
||||
|
@ -1267,7 +1263,7 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev)
|
|||
return ret;
|
||||
s3c24xx_i2c_init(i2c);
|
||||
clk_disable(i2c->clk);
|
||||
i2c->suspended = 0;
|
||||
i2c_mark_adapter_resumed(&i2c->adap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
/*
|
||||
* SuperH Mobile I2C Controller
|
||||
*
|
||||
* Copyright (C) 2014 Wolfram Sang <wsa@sang-engineering.com>
|
||||
*
|
||||
* Copyright (C) 2014-19 Wolfram Sang <wsa@sang-engineering.com>
|
||||
* Copyright (C) 2008 Magnus Damm
|
||||
*
|
||||
* Portions of the code based on out-of-tree driver i2c-sh7343.c
|
||||
|
@ -303,13 +302,12 @@ static int sh_mobile_i2c_v2_init(struct sh_mobile_i2c_data *pd)
|
|||
return sh_mobile_i2c_check_timing(pd);
|
||||
}
|
||||
|
||||
static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
|
||||
enum sh_mobile_i2c_op op, unsigned char data)
|
||||
static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, enum sh_mobile_i2c_op op)
|
||||
{
|
||||
unsigned char ret = 0;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(pd->dev, "op %d, data in 0x%02x\n", op, data);
|
||||
dev_dbg(pd->dev, "op %d\n", op);
|
||||
|
||||
spin_lock_irqsave(&pd->lock, flags);
|
||||
|
||||
|
@ -317,12 +315,12 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
|
|||
case OP_START: /* issue start and trigger DTE interrupt */
|
||||
iic_wr(pd, ICCR, ICCR_ICE | ICCR_TRS | ICCR_BBSY);
|
||||
break;
|
||||
case OP_TX_FIRST: /* disable DTE interrupt and write data */
|
||||
case OP_TX_FIRST: /* disable DTE interrupt and write client address */
|
||||
iic_wr(pd, ICIC, ICIC_WAITE | ICIC_ALE | ICIC_TACKE);
|
||||
iic_wr(pd, ICDR, data);
|
||||
iic_wr(pd, ICDR, i2c_8bit_addr_from_msg(pd->msg));
|
||||
break;
|
||||
case OP_TX: /* write data */
|
||||
iic_wr(pd, ICDR, data);
|
||||
iic_wr(pd, ICDR, pd->msg->buf[pd->pos]);
|
||||
break;
|
||||
case OP_TX_STOP: /* issue a stop (or rep_start) */
|
||||
iic_wr(pd, ICCR, pd->send_stop ? ICCR_ICE | ICCR_TRS
|
||||
|
@ -353,34 +351,17 @@ static unsigned char i2c_op(struct sh_mobile_i2c_data *pd,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool sh_mobile_i2c_is_first_byte(struct sh_mobile_i2c_data *pd)
|
||||
{
|
||||
return pd->pos == -1;
|
||||
}
|
||||
|
||||
static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd,
|
||||
unsigned char *buf)
|
||||
{
|
||||
switch (pd->pos) {
|
||||
case -1:
|
||||
*buf = i2c_8bit_addr_from_msg(pd->msg);
|
||||
break;
|
||||
default:
|
||||
*buf = pd->msg->buf[pd->pos];
|
||||
}
|
||||
}
|
||||
|
||||
static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd)
|
||||
{
|
||||
unsigned char data;
|
||||
|
||||
if (pd->pos == pd->msg->len) {
|
||||
i2c_op(pd, OP_TX_STOP, 0);
|
||||
i2c_op(pd, OP_TX_STOP);
|
||||
return 1;
|
||||
}
|
||||
|
||||
sh_mobile_i2c_get_data(pd, &data);
|
||||
i2c_op(pd, sh_mobile_i2c_is_first_byte(pd) ? OP_TX_FIRST : OP_TX, data);
|
||||
if (pd->pos == -1)
|
||||
i2c_op(pd, OP_TX_FIRST);
|
||||
else
|
||||
i2c_op(pd, OP_TX);
|
||||
|
||||
pd->pos++;
|
||||
return 0;
|
||||
|
@ -391,45 +372,32 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd)
|
|||
unsigned char data;
|
||||
int real_pos;
|
||||
|
||||
do {
|
||||
if (pd->pos <= -1) {
|
||||
sh_mobile_i2c_get_data(pd, &data);
|
||||
/* switch from TX (address) to RX (data) adds two interrupts */
|
||||
real_pos = pd->pos - 2;
|
||||
|
||||
if (sh_mobile_i2c_is_first_byte(pd))
|
||||
i2c_op(pd, OP_TX_FIRST, data);
|
||||
else
|
||||
i2c_op(pd, OP_TX, data);
|
||||
break;
|
||||
if (pd->pos == -1) {
|
||||
i2c_op(pd, OP_TX_FIRST);
|
||||
} else if (pd->pos == 0) {
|
||||
i2c_op(pd, OP_TX_TO_RX);
|
||||
} else if (pd->pos == pd->msg->len) {
|
||||
if (pd->stop_after_dma) {
|
||||
/* Simulate PIO end condition after DMA transfer */
|
||||
i2c_op(pd, OP_RX_STOP);
|
||||
pd->pos++;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (pd->pos == 0) {
|
||||
i2c_op(pd, OP_TX_TO_RX, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
real_pos = pd->pos - 2;
|
||||
|
||||
if (pd->pos == pd->msg->len) {
|
||||
if (pd->stop_after_dma) {
|
||||
/* Simulate PIO end condition after DMA transfer */
|
||||
i2c_op(pd, OP_RX_STOP, 0);
|
||||
pd->pos++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (real_pos < 0) {
|
||||
i2c_op(pd, OP_RX_STOP, 0);
|
||||
break;
|
||||
}
|
||||
data = i2c_op(pd, OP_RX_STOP_DATA, 0);
|
||||
} else if (real_pos >= 0) {
|
||||
data = i2c_op(pd, OP_RX, 0);
|
||||
}
|
||||
|
||||
if (real_pos >= 0)
|
||||
pd->msg->buf[real_pos] = data;
|
||||
} while (0);
|
||||
if (real_pos < 0)
|
||||
i2c_op(pd, OP_RX_STOP);
|
||||
else
|
||||
data = i2c_op(pd, OP_RX_STOP_DATA);
|
||||
} else if (real_pos >= 0) {
|
||||
data = i2c_op(pd, OP_RX);
|
||||
}
|
||||
|
||||
if (real_pos >= 0)
|
||||
pd->msg->buf[real_pos] = data;
|
||||
done:
|
||||
pd->pos++;
|
||||
return pd->pos == (pd->msg->len + 2);
|
||||
}
|
||||
|
@ -698,7 +666,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter,
|
|||
start_ch(pd, msg, do_start);
|
||||
|
||||
if (do_start)
|
||||
i2c_op(pd, OP_START, 0);
|
||||
i2c_op(pd, OP_START);
|
||||
|
||||
/* The interrupt handler takes care of the rest... */
|
||||
timeout = wait_event_timeout(pd->wait,
|
||||
|
@ -749,8 +717,7 @@ static const struct i2c_adapter_quirks sh_mobile_i2c_quirks = {
|
|||
};
|
||||
|
||||
/*
|
||||
* r8a7740 chip has lasting errata on I2C I/O pad reset.
|
||||
* this is work-around for it.
|
||||
* r8a7740 has an errata regarding I2C I/O pad reset needing this workaround.
|
||||
*/
|
||||
static int sh_mobile_i2c_r8a7740_workaround(struct sh_mobile_i2c_data *pd)
|
||||
{
|
||||
|
@ -802,15 +769,15 @@ static const struct of_device_id sh_mobile_i2c_dt_ids[] = {
|
|||
{ .compatible = "renesas,iic-r8a7740", .data = &r8a7740_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a774c0", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7790", .data = &v2_freq_calc_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7791", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7792", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7793", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7794", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,rcar-gen2-iic", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7795", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,rcar-gen3-iic", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a77990", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7791", .data = &v2_freq_calc_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7792", .data = &v2_freq_calc_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7793", .data = &v2_freq_calc_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7794", .data = &v2_freq_calc_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a7795", .data = &v2_freq_calc_dt_config },
|
||||
{ .compatible = "renesas,iic-r8a77990", .data = &v2_freq_calc_dt_config },
|
||||
{ .compatible = "renesas,iic-sh73a0", .data = &fast_clock_dt_config },
|
||||
{ .compatible = "renesas,rcar-gen2-iic", .data = &v2_freq_calc_dt_config },
|
||||
{ .compatible = "renesas,rcar-gen3-iic", .data = &v2_freq_calc_dt_config },
|
||||
{ .compatible = "renesas,rmobile-iic", .data = &default_dt_config },
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -86,7 +86,6 @@ struct sprd_i2c {
|
|||
u32 count;
|
||||
int irq;
|
||||
int err;
|
||||
bool is_suspended;
|
||||
};
|
||||
|
||||
static void sprd_i2c_set_count(struct sprd_i2c *i2c_dev, u32 count)
|
||||
|
@ -284,9 +283,6 @@ static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap,
|
|||
struct sprd_i2c *i2c_dev = i2c_adap->algo_data;
|
||||
int im, ret;
|
||||
|
||||
if (i2c_dev->is_suspended)
|
||||
return -EBUSY;
|
||||
|
||||
ret = pm_runtime_get_sync(i2c_dev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -586,40 +582,34 @@ static int sprd_i2c_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused sprd_i2c_suspend_noirq(struct device *pdev)
|
||||
static int __maybe_unused sprd_i2c_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
|
||||
struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
|
||||
|
||||
i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
i2c_dev->is_suspended = true;
|
||||
i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
|
||||
return pm_runtime_force_suspend(pdev);
|
||||
i2c_mark_adapter_suspended(&i2c_dev->adap);
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused sprd_i2c_resume_noirq(struct device *pdev)
|
||||
static int __maybe_unused sprd_i2c_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
|
||||
struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
|
||||
|
||||
i2c_lock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
i2c_dev->is_suspended = false;
|
||||
i2c_unlock_bus(&i2c_dev->adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
|
||||
return pm_runtime_force_resume(pdev);
|
||||
i2c_mark_adapter_resumed(&i2c_dev->adap);
|
||||
return pm_runtime_force_resume(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused sprd_i2c_runtime_suspend(struct device *pdev)
|
||||
static int __maybe_unused sprd_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
|
||||
struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(i2c_dev->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused sprd_i2c_runtime_resume(struct device *pdev)
|
||||
static int __maybe_unused sprd_i2c_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sprd_i2c *i2c_dev = dev_get_drvdata(pdev);
|
||||
struct sprd_i2c *i2c_dev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(i2c_dev->clk);
|
||||
|
|
|
@ -144,8 +144,6 @@ struct synquacer_i2c {
|
|||
u32 timeout_ms;
|
||||
enum i2c_state state;
|
||||
struct i2c_adapter adapter;
|
||||
|
||||
bool is_suspended;
|
||||
};
|
||||
|
||||
static inline int is_lastmsg(struct synquacer_i2c *i2c)
|
||||
|
@ -316,9 +314,6 @@ static int synquacer_i2c_doxfer(struct synquacer_i2c *i2c,
|
|||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
if (i2c->is_suspended)
|
||||
return -EBUSY;
|
||||
|
||||
synquacer_i2c_hw_init(i2c);
|
||||
bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR);
|
||||
if (bsr & SYNQUACER_I2C_BSR_BB) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -66,7 +66,6 @@ struct zx2967_i2c {
|
|||
int msg_rd;
|
||||
u8 *cur_trans;
|
||||
u8 access_cnt;
|
||||
bool is_suspended;
|
||||
int error;
|
||||
};
|
||||
|
||||
|
@ -313,9 +312,6 @@ static int zx2967_i2c_xfer(struct i2c_adapter *adap,
|
|||
int ret;
|
||||
int i;
|
||||
|
||||
if (i2c->is_suspended)
|
||||
return -EBUSY;
|
||||
|
||||
zx2967_set_addr(i2c, msgs->addr);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
|
@ -470,7 +466,7 @@ static int __maybe_unused zx2967_i2c_suspend(struct device *dev)
|
|||
{
|
||||
struct zx2967_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
||||
i2c->is_suspended = true;
|
||||
i2c_mark_adapter_suspended(&i2c->adap);
|
||||
clk_disable_unprepare(i2c->clk);
|
||||
|
||||
return 0;
|
||||
|
@ -480,8 +476,8 @@ static int __maybe_unused zx2967_i2c_resume(struct device *dev)
|
|||
{
|
||||
struct zx2967_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
||||
i2c->is_suspended = false;
|
||||
clk_prepare_enable(i2c->clk);
|
||||
i2c_mark_adapter_resumed(&i2c->adap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -430,7 +430,7 @@ static int i2c_device_remove(struct device *dev)
|
|||
dev_pm_clear_wake_irq(&client->dev);
|
||||
device_init_wakeup(&client->dev, false);
|
||||
|
||||
client->irq = 0;
|
||||
client->irq = client->init_irq;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -741,10 +741,11 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
|
|||
client->flags = info->flags;
|
||||
client->addr = info->addr;
|
||||
|
||||
client->irq = info->irq;
|
||||
if (!client->irq)
|
||||
client->irq = i2c_dev_irq_from_resources(info->resources,
|
||||
client->init_irq = info->irq;
|
||||
if (!client->init_irq)
|
||||
client->init_irq = i2c_dev_irq_from_resources(info->resources,
|
||||
info->num_resources);
|
||||
client->irq = client->init_irq;
|
||||
|
||||
strlcpy(client->name, info->type, sizeof(client->name));
|
||||
|
||||
|
@ -1232,6 +1233,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
|||
if (!adap->lock_ops)
|
||||
adap->lock_ops = &i2c_adapter_lock_ops;
|
||||
|
||||
adap->locked_flags = 0;
|
||||
rt_mutex_init(&adap->bus_lock);
|
||||
rt_mutex_init(&adap->mux_lock);
|
||||
mutex_init(&adap->userspace_clients_lock);
|
||||
|
@ -1865,6 +1867,8 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|||
|
||||
if (WARN_ON(!msgs || num < 1))
|
||||
return -EINVAL;
|
||||
if (WARN_ON(test_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags)))
|
||||
return -ESHUTDOWN;
|
||||
|
||||
if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
|
||||
return -EOPNOTSUPP;
|
||||
|
|
|
@ -121,6 +121,17 @@ static int of_dev_node_match(struct device *dev, void *data)
|
|||
return dev->of_node == data;
|
||||
}
|
||||
|
||||
static int of_dev_or_parent_node_match(struct device *dev, void *data)
|
||||
{
|
||||
if (dev->of_node == data)
|
||||
return 1;
|
||||
|
||||
if (dev->parent)
|
||||
return dev->parent->of_node == data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* must call put_device() when done with returned i2c_client device */
|
||||
struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
|
||||
{
|
||||
|
@ -145,7 +156,8 @@ struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
|
|||
struct device *dev;
|
||||
struct i2c_adapter *adapter;
|
||||
|
||||
dev = bus_find_device(&i2c_bus_type, NULL, node, of_dev_node_match);
|
||||
dev = bus_find_device(&i2c_bus_type, NULL, node,
|
||||
of_dev_or_parent_node_match);
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
|
|
|
@ -585,7 +585,7 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
|||
trace:
|
||||
/* If enabled, the reply tracepoint is conditional on read_write. */
|
||||
trace_smbus_reply(adapter, addr, flags, read_write,
|
||||
command, protocol, data);
|
||||
command, protocol, data, res);
|
||||
trace_smbus_result(adapter, addr, flags, read_write,
|
||||
command, protocol, res);
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ struct i2c_dev {
|
|||
struct cdev cdev;
|
||||
};
|
||||
|
||||
#define I2C_MINORS MINORMASK
|
||||
#define I2C_MINORS (MINORMASK + 1)
|
||||
static LIST_HEAD(i2c_dev_list);
|
||||
static DEFINE_SPINLOCK(i2c_dev_list_lock);
|
||||
|
||||
|
|
|
@ -22,10 +22,24 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/platform_data/at24.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
/* Address pointer is 16 bit. */
|
||||
#define AT24_FLAG_ADDR16 BIT(7)
|
||||
/* sysfs-entry will be read-only. */
|
||||
#define AT24_FLAG_READONLY BIT(6)
|
||||
/* sysfs-entry will be world-readable. */
|
||||
#define AT24_FLAG_IRUGO BIT(5)
|
||||
/* Take always 8 addresses (24c00). */
|
||||
#define AT24_FLAG_TAKE8ADDR BIT(4)
|
||||
/* Factory-programmed serial number. */
|
||||
#define AT24_FLAG_SERIAL BIT(3)
|
||||
/* Factory-programmed mac address. */
|
||||
#define AT24_FLAG_MAC BIT(2)
|
||||
/* Does not auto-rollover reads to the next slave address. */
|
||||
#define AT24_FLAG_NO_RDROL BIT(1)
|
||||
|
||||
/*
|
||||
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
|
||||
* Differences between different vendor product lines (like Atmel AT24C or
|
||||
|
@ -107,10 +121,6 @@ module_param_named(write_timeout, at24_write_timeout, uint, 0);
|
|||
MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)");
|
||||
|
||||
struct at24_chip_data {
|
||||
/*
|
||||
* these fields mirror their equivalents in
|
||||
* struct at24_platform_data
|
||||
*/
|
||||
u32 byte_len;
|
||||
u8 flags;
|
||||
};
|
||||
|
@ -471,63 +481,11 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void at24_properties_to_pdata(struct device *dev,
|
||||
struct at24_platform_data *chip)
|
||||
{
|
||||
int err;
|
||||
u32 val;
|
||||
|
||||
if (device_property_present(dev, "read-only"))
|
||||
chip->flags |= AT24_FLAG_READONLY;
|
||||
if (device_property_present(dev, "no-read-rollover"))
|
||||
chip->flags |= AT24_FLAG_NO_RDROL;
|
||||
|
||||
err = device_property_read_u32(dev, "address-width", &val);
|
||||
if (!err) {
|
||||
switch (val) {
|
||||
case 8:
|
||||
if (chip->flags & AT24_FLAG_ADDR16)
|
||||
dev_warn(dev, "Override address width to be 8, while default is 16\n");
|
||||
chip->flags &= ~AT24_FLAG_ADDR16;
|
||||
break;
|
||||
case 16:
|
||||
chip->flags |= AT24_FLAG_ADDR16;
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "Bad \"address-width\" property: %u\n",
|
||||
val);
|
||||
}
|
||||
}
|
||||
|
||||
err = device_property_read_u32(dev, "size", &val);
|
||||
if (!err)
|
||||
chip->byte_len = val;
|
||||
|
||||
err = device_property_read_u32(dev, "pagesize", &val);
|
||||
if (!err) {
|
||||
chip->page_size = val;
|
||||
} else {
|
||||
/*
|
||||
* This is slow, but we can't know all eeproms, so we better
|
||||
* play safe. Specifying custom eeprom-types via platform_data
|
||||
* is recommended anyhow.
|
||||
*/
|
||||
chip->page_size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata)
|
||||
static const struct at24_chip_data *at24_get_chip_data(struct device *dev)
|
||||
{
|
||||
struct device_node *of_node = dev->of_node;
|
||||
const struct at24_chip_data *cdata;
|
||||
const struct i2c_device_id *id;
|
||||
struct at24_platform_data *pd;
|
||||
|
||||
pd = dev_get_platdata(dev);
|
||||
if (pd) {
|
||||
memcpy(pdata, pd, sizeof(*pdata));
|
||||
return 0;
|
||||
}
|
||||
|
||||
id = i2c_match_id(at24_ids, to_i2c_client(dev));
|
||||
|
||||
|
@ -544,13 +502,9 @@ static int at24_get_pdata(struct device *dev, struct at24_platform_data *pdata)
|
|||
cdata = acpi_device_get_match_data(dev);
|
||||
|
||||
if (!cdata)
|
||||
return -ENODEV;
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
pdata->byte_len = cdata->byte_len;
|
||||
pdata->flags = cdata->flags;
|
||||
at24_properties_to_pdata(dev, pdata);
|
||||
|
||||
return 0;
|
||||
return cdata;
|
||||
}
|
||||
|
||||
static void at24_remove_dummy_clients(struct at24_data *at24)
|
||||
|
@ -619,7 +573,8 @@ static int at24_probe(struct i2c_client *client)
|
|||
{
|
||||
struct regmap_config regmap_config = { };
|
||||
struct nvmem_config nvmem_config = { };
|
||||
struct at24_platform_data pdata = { };
|
||||
u32 byte_len, page_size, flags, addrw;
|
||||
const struct at24_chip_data *cdata;
|
||||
struct device *dev = &client->dev;
|
||||
bool i2c_fn_i2c, i2c_fn_block;
|
||||
unsigned int i, num_addresses;
|
||||
|
@ -634,35 +589,75 @@ static int at24_probe(struct i2c_client *client)
|
|||
i2c_fn_block = i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK);
|
||||
|
||||
err = at24_get_pdata(dev, &pdata);
|
||||
cdata = at24_get_chip_data(dev);
|
||||
if (IS_ERR(cdata))
|
||||
return PTR_ERR(cdata);
|
||||
|
||||
err = device_property_read_u32(dev, "pagesize", &page_size);
|
||||
if (err)
|
||||
return err;
|
||||
/*
|
||||
* This is slow, but we can't know all eeproms, so we better
|
||||
* play safe. Specifying custom eeprom-types via platform_data
|
||||
* is recommended anyhow.
|
||||
*/
|
||||
page_size = 1;
|
||||
|
||||
flags = cdata->flags;
|
||||
if (device_property_present(dev, "read-only"))
|
||||
flags |= AT24_FLAG_READONLY;
|
||||
if (device_property_present(dev, "no-read-rollover"))
|
||||
flags |= AT24_FLAG_NO_RDROL;
|
||||
|
||||
err = device_property_read_u32(dev, "address-width", &addrw);
|
||||
if (!err) {
|
||||
switch (addrw) {
|
||||
case 8:
|
||||
if (flags & AT24_FLAG_ADDR16)
|
||||
dev_warn(dev,
|
||||
"Override address width to be 8, while default is 16\n");
|
||||
flags &= ~AT24_FLAG_ADDR16;
|
||||
break;
|
||||
case 16:
|
||||
flags |= AT24_FLAG_ADDR16;
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "Bad \"address-width\" property: %u\n",
|
||||
addrw);
|
||||
}
|
||||
}
|
||||
|
||||
err = device_property_read_u32(dev, "size", &byte_len);
|
||||
if (err)
|
||||
byte_len = cdata->byte_len;
|
||||
|
||||
if (!i2c_fn_i2c && !i2c_fn_block)
|
||||
pdata.page_size = 1;
|
||||
page_size = 1;
|
||||
|
||||
if (!pdata.page_size) {
|
||||
if (!page_size) {
|
||||
dev_err(dev, "page_size must not be 0!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(pdata.page_size))
|
||||
if (!is_power_of_2(page_size))
|
||||
dev_warn(dev, "page_size looks suspicious (no power of 2)!\n");
|
||||
|
||||
if (pdata.flags & AT24_FLAG_TAKE8ADDR)
|
||||
num_addresses = 8;
|
||||
else
|
||||
num_addresses = DIV_ROUND_UP(pdata.byte_len,
|
||||
(pdata.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
|
||||
err = device_property_read_u32(dev, "num-addresses", &num_addresses);
|
||||
if (err) {
|
||||
if (flags & AT24_FLAG_TAKE8ADDR)
|
||||
num_addresses = 8;
|
||||
else
|
||||
num_addresses = DIV_ROUND_UP(byte_len,
|
||||
(flags & AT24_FLAG_ADDR16) ? 65536 : 256);
|
||||
}
|
||||
|
||||
if ((pdata.flags & AT24_FLAG_SERIAL) && (pdata.flags & AT24_FLAG_MAC)) {
|
||||
if ((flags & AT24_FLAG_SERIAL) && (flags & AT24_FLAG_MAC)) {
|
||||
dev_err(dev,
|
||||
"invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_config.val_bits = 8;
|
||||
regmap_config.reg_bits = (pdata.flags & AT24_FLAG_ADDR16) ? 16 : 8;
|
||||
regmap_config.reg_bits = (flags & AT24_FLAG_ADDR16) ? 16 : 8;
|
||||
regmap_config.disable_locking = true;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
|
@ -675,11 +670,11 @@ static int at24_probe(struct i2c_client *client)
|
|||
return -ENOMEM;
|
||||
|
||||
mutex_init(&at24->lock);
|
||||
at24->byte_len = pdata.byte_len;
|
||||
at24->page_size = pdata.page_size;
|
||||
at24->flags = pdata.flags;
|
||||
at24->byte_len = byte_len;
|
||||
at24->page_size = page_size;
|
||||
at24->flags = flags;
|
||||
at24->num_addresses = num_addresses;
|
||||
at24->offset_adj = at24_get_offset_adj(pdata.flags, pdata.byte_len);
|
||||
at24->offset_adj = at24_get_offset_adj(flags, byte_len);
|
||||
at24->client[0].client = client;
|
||||
at24->client[0].regmap = regmap;
|
||||
|
||||
|
@ -687,10 +682,10 @@ static int at24_probe(struct i2c_client *client)
|
|||
if (IS_ERR(at24->wp_gpio))
|
||||
return PTR_ERR(at24->wp_gpio);
|
||||
|
||||
writable = !(pdata.flags & AT24_FLAG_READONLY);
|
||||
writable = !(flags & AT24_FLAG_READONLY);
|
||||
if (writable) {
|
||||
at24->write_max = min_t(unsigned int,
|
||||
pdata.page_size, at24_io_limit);
|
||||
page_size, at24_io_limit);
|
||||
if (!i2c_fn_i2c && at24->write_max > I2C_SMBUS_BLOCK_MAX)
|
||||
at24->write_max = I2C_SMBUS_BLOCK_MAX;
|
||||
}
|
||||
|
@ -733,7 +728,7 @@ static int at24_probe(struct i2c_client *client)
|
|||
nvmem_config.priv = at24;
|
||||
nvmem_config.stride = 1;
|
||||
nvmem_config.word_size = 1;
|
||||
nvmem_config.size = pdata.byte_len;
|
||||
nvmem_config.size = byte_len;
|
||||
|
||||
at24->nvmem = devm_nvmem_register(dev, &nvmem_config);
|
||||
if (IS_ERR(at24->nvmem)) {
|
||||
|
@ -742,13 +737,9 @@ static int at24_probe(struct i2c_client *client)
|
|||
}
|
||||
|
||||
dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n",
|
||||
pdata.byte_len, client->name,
|
||||
byte_len, client->name,
|
||||
writable ? "writable" : "read-only", at24->write_max);
|
||||
|
||||
/* export data to kernel code */
|
||||
if (pdata.setup)
|
||||
pdata.setup(at24->nvmem, pdata.context);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clients:
|
||||
|
|
|
@ -1061,17 +1061,6 @@ static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ACPI) && IS_ENABLED(CONFIG_I2C)
|
||||
bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares,
|
||||
struct acpi_resource_i2c_serialbus **i2c);
|
||||
#else
|
||||
static inline bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares,
|
||||
struct acpi_resource_i2c_serialbus **i2c)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Device properties */
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
|
|
@ -1,30 +1,17 @@
|
|||
/* ------------------------------------------------------------------------- */
|
||||
/* i2c-algo-bit.h i2c driver algorithms for bit-shift adapters */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Copyright (C) 1995-99 Simon G. Vogl
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
MA 02110-1301 USA. */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
|
||||
Frodo Looijaard <frodol@dds.nl> */
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* i2c-algo-bit.h: i2c driver algorithms for bit-shift adapters
|
||||
*
|
||||
* Copyright (C) 1995-99 Simon G. Vogl
|
||||
* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
|
||||
* Frodo Looijaard <frodol@dds.nl>
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_I2C_ALGO_BIT_H
|
||||
#define _LINUX_I2C_ALGO_BIT_H
|
||||
|
||||
#include <linux/i2c.h>
|
||||
|
||||
/* --- Defines for bit-adapters --------------------------------------- */
|
||||
/*
|
||||
* This struct contains the hw-dependent functions of bit-style adapters to
|
||||
|
|
|
@ -333,6 +333,7 @@ struct i2c_client {
|
|||
char name[I2C_NAME_SIZE];
|
||||
struct i2c_adapter *adapter; /* the adapter we sit on */
|
||||
struct device dev; /* the device structure */
|
||||
int init_irq; /* irq set at initialization */
|
||||
int irq; /* irq issued by device */
|
||||
struct list_head detected;
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
|
@ -680,6 +681,8 @@ struct i2c_adapter {
|
|||
int timeout; /* in jiffies */
|
||||
int retries;
|
||||
struct device dev; /* the adapter device */
|
||||
unsigned long locked_flags; /* owned by the I2C core */
|
||||
#define I2C_ALF_IS_SUSPENDED 0
|
||||
|
||||
int nr;
|
||||
char name[48];
|
||||
|
@ -762,6 +765,38 @@ i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
|
|||
adapter->lock_ops->unlock_bus(adapter, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_mark_adapter_suspended - Report suspended state of the adapter to the core
|
||||
* @adap: Adapter to mark as suspended
|
||||
*
|
||||
* When using this helper to mark an adapter as suspended, the core will reject
|
||||
* further transfers to this adapter. The usage of this helper is optional but
|
||||
* recommended for devices having distinct handlers for system suspend and
|
||||
* runtime suspend. More complex devices are free to implement custom solutions
|
||||
* to reject transfers when suspended.
|
||||
*/
|
||||
static inline void i2c_mark_adapter_suspended(struct i2c_adapter *adap)
|
||||
{
|
||||
i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
set_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags);
|
||||
i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_mark_adapter_resumed - Report resumed state of the adapter to the core
|
||||
* @adap: Adapter to mark as resumed
|
||||
*
|
||||
* When using this helper to mark an adapter as resumed, the core will allow
|
||||
* further transfers to this adapter. See also further notes to
|
||||
* @i2c_mark_adapter_suspended().
|
||||
*/
|
||||
static inline void i2c_mark_adapter_resumed(struct i2c_adapter *adap)
|
||||
{
|
||||
i2c_lock_bus(adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
clear_bit(I2C_ALF_IS_SUSPENDED, &adap->locked_flags);
|
||||
i2c_unlock_bus(adap, I2C_LOCK_ROOT_ADAPTER);
|
||||
}
|
||||
|
||||
/*flags for the client struct: */
|
||||
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
|
||||
#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */
|
||||
|
@ -933,11 +968,21 @@ static inline int of_i2c_get_board_info(struct device *dev,
|
|||
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
struct acpi_resource;
|
||||
struct acpi_resource_i2c_serialbus;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares,
|
||||
struct acpi_resource_i2c_serialbus **i2c);
|
||||
u32 i2c_acpi_find_bus_speed(struct device *dev);
|
||||
struct i2c_client *i2c_acpi_new_device(struct device *dev, int index,
|
||||
struct i2c_board_info *info);
|
||||
#else
|
||||
static inline bool i2c_acpi_get_i2c_resource(struct acpi_resource *ares,
|
||||
struct acpi_resource_i2c_serialbus **i2c)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline u32 i2c_acpi_find_bus_speed(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* at24.h - platform_data for the at24 (generic eeprom) driver
|
||||
* (C) Copyright 2008 by Pengutronix
|
||||
* (C) Copyright 2012 by Wolfram Sang
|
||||
* same license as the driver
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AT24_H
|
||||
#define _LINUX_AT24_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/**
|
||||
* struct at24_platform_data - data to set up at24 (generic eeprom) driver
|
||||
* @byte_len: size of eeprom in byte
|
||||
* @page_size: number of byte which can be written in one go
|
||||
* @flags: tunable options, check AT24_FLAG_* defines
|
||||
* @setup: an optional callback invoked after eeprom is probed; enables kernel
|
||||
code to access eeprom via nvmem, see example
|
||||
* @context: optional parameter passed to setup()
|
||||
*
|
||||
* If you set up a custom eeprom type, please double-check the parameters.
|
||||
* Especially page_size needs extra care, as you risk data loss if your value
|
||||
* is bigger than what the chip actually supports!
|
||||
*
|
||||
* An example in pseudo code for a setup() callback:
|
||||
*
|
||||
* void get_mac_addr(struct nvmem_device *nvmem, void *context)
|
||||
* {
|
||||
* u8 *mac_addr = ethernet_pdata->mac_addr;
|
||||
* off_t offset = context;
|
||||
*
|
||||
* // Read MAC addr from EEPROM
|
||||
* if (nvmem_device_read(nvmem, offset, ETH_ALEN, mac_addr) == ETH_ALEN)
|
||||
* pr_info("Read MAC addr from EEPROM: %pM\n", mac_addr);
|
||||
* }
|
||||
*
|
||||
* This function pointer and context can now be set up in at24_platform_data.
|
||||
*/
|
||||
|
||||
struct at24_platform_data {
|
||||
u32 byte_len; /* size (sum of all addr) */
|
||||
u16 page_size; /* for writes */
|
||||
u8 flags;
|
||||
#define AT24_FLAG_ADDR16 BIT(7) /* address pointer is 16 bit */
|
||||
#define AT24_FLAG_READONLY BIT(6) /* sysfs-entry will be read-only */
|
||||
#define AT24_FLAG_IRUGO BIT(5) /* sysfs-entry will be world-readable */
|
||||
#define AT24_FLAG_TAKE8ADDR BIT(4) /* take always 8 addresses (24c00) */
|
||||
#define AT24_FLAG_SERIAL BIT(3) /* factory-programmed serial number */
|
||||
#define AT24_FLAG_MAC BIT(2) /* factory-programmed mac address */
|
||||
#define AT24_FLAG_NO_RDROL BIT(1) /* does not auto-rollover reads to */
|
||||
/* the next slave address */
|
||||
|
||||
void (*setup)(struct nvmem_device *nvmem, void *context);
|
||||
void *context;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_AT24_H */
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* i2c-cbus-gpio.h - CBUS I2C platform_data definition
|
||||
*
|
||||
* Copyright (C) 2004-2009 Nokia Corporation
|
||||
*
|
||||
* Written by Felipe Balbi and Aaro Koskinen.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General
|
||||
* Public License. See the file "COPYING" in the main directory of this
|
||||
* archive for more details.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_LINUX_I2C_CBUS_GPIO_H
|
||||
#define __INCLUDE_LINUX_I2C_CBUS_GPIO_H
|
||||
|
||||
struct i2c_cbus_platform_data {
|
||||
int dat_gpio;
|
||||
int clk_gpio;
|
||||
int sel_gpio;
|
||||
};
|
||||
|
||||
#endif /* __INCLUDE_LINUX_I2C_CBUS_GPIO_H */
|
|
@ -1,11 +1,8 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* i2c-ocores.h - definitions for the i2c-ocores interface
|
||||
*
|
||||
* Peter Korsgaard <peter@korsgaard.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public License
|
||||
* version 2. This program is licensed "as is" without any warranty of any
|
||||
* kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_I2C_OCORES_H
|
||||
|
@ -15,6 +12,7 @@ struct ocores_i2c_platform_data {
|
|||
u32 reg_shift; /* register offset shift value */
|
||||
u32 reg_io_width; /* register io read/write width */
|
||||
u32 clock_khz; /* input clock in kHz */
|
||||
u32 bus_khz; /* bus clock in kHz */
|
||||
bool big_endian; /* registers are big endian */
|
||||
u8 num_devices; /* number of devices in the devices list */
|
||||
struct i2c_board_info const *devices; /* devices connected to the bus */
|
||||
|
|
|
@ -138,9 +138,9 @@ TRACE_EVENT_CONDITION(smbus_reply,
|
|||
TP_PROTO(const struct i2c_adapter *adap,
|
||||
u16 addr, unsigned short flags,
|
||||
char read_write, u8 command, int protocol,
|
||||
const union i2c_smbus_data *data),
|
||||
TP_ARGS(adap, addr, flags, read_write, command, protocol, data),
|
||||
TP_CONDITION(read_write == I2C_SMBUS_READ),
|
||||
const union i2c_smbus_data *data, int res),
|
||||
TP_ARGS(adap, addr, flags, read_write, command, protocol, data, res),
|
||||
TP_CONDITION(res >= 0 && read_write == I2C_SMBUS_READ),
|
||||
TP_STRUCT__entry(
|
||||
__field(int, adapter_nr )
|
||||
__field(__u16, addr )
|
||||
|
|
Loading…
Reference in New Issue
Block a user