diff options
| author | Miguel Ojeda <ojeda@kernel.org> | 2026-08-16 15:32:33 +0200 |
|---|---|---|
| committer | Miguel Ojeda <ojeda@kernel.org> | 2026-08-23 21:27:17 +0200 |
| commit | 5febf432df1cfa5b25d99b54c32103fafbdd0eb9 (patch) | |
| tree | 93a1ed1d337bc53adf1e572c2586d347703b286e /drivers/i2c/algos | |
| download | linux-stable-5febf432df1cfa5b25d99b54c32103fafbdd0eb9.tar.gz linux-stable-5febf432df1cfa5b25d99b54c32103fafbdd0eb9.zip | |
kbuild: rust: keep Rust objects out of Clang LTO with inline helpersgrafted
Under `CONFIG_LTO_CLANG` + `CONFIG_RUST_INLINE_HELPERS`, one may hit
`objtool` errors such as:
vmlinux.o: error: objtool: _R..._3Gsp4boot+0xd6a:
can't find jump dest instruction at .text._R..._3Gsp4boot+0x1dfd
The reason is that in such builds, the Clang invocation that compiles
the combined Rust plus helpers bitcode emits LLVM bitcode (again) --
the final code generation happens in the linker's LTO step, which the
`-mllvm` trap options passed to Clang do not reach.
This, in turn, means that unreachable traps are missing, and the
impossible paths do not merely fallthrough to the next symbol, but past
the end of their own section, since LTO builds place each function in
its own section.
Thus filter `CC_FLAGS_LTO` out of the Clang invocation, so that it always
emits machine code directly, with the traps in place.
Assisted-by: LLM
Cc: Gary Guo <gary@garyguo.net>
Cc: Boqun Feng <boqun@kernel.org>
Cc: Alice Ryhl <aliceryhl@google.com>
Cc: Matthew Maurer <mmaurer@google.com>
Cc: Josh Poimboeuf <jpoimboe@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: stable@vger.kernel.org
Fixes: 3a2486cc1da5 ("kbuild: rust: provide an option to inline C helpers into Rust")
Acked-by: Gary Guo <gary@garyguo.net>
Link: https://patch.msgid.link/20260816133233.197500-2-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Diffstat (limited to 'drivers/i2c/algos')
| -rw-r--r-- | drivers/i2c/algos/Kconfig | 18 | ||||
| -rw-r--r-- | drivers/i2c/algos/Makefile | 10 | ||||
| -rw-r--r-- | drivers/i2c/algos/i2c-algo-bit.c | 689 | ||||
| -rw-r--r-- | drivers/i2c/algos/i2c-algo-pca.c | 561 | ||||
| -rw-r--r-- | drivers/i2c/algos/i2c-algo-pcf.c | 369 | ||||
| -rw-r--r-- | drivers/i2c/algos/i2c-algo-pcf.h | 65 |
6 files changed, 1712 insertions, 0 deletions
diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig new file mode 100644 index 000000000..aea523529 --- /dev/null +++ b/drivers/i2c/algos/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# I2C algorithm drivers configuration +# + +menu "I2C Algorithms" + visible if !I2C_HELPER_AUTO + +config I2C_ALGOBIT + tristate "I2C bit-banging interfaces" + +config I2C_ALGOPCF + tristate "I2C PCF 8584 interfaces" + +config I2C_ALGOPCA + tristate "I2C PCA 9564 interfaces" + +endmenu diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile new file mode 100644 index 000000000..9b319a30d --- /dev/null +++ b/drivers/i2c/algos/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the i2c algorithms +# + +obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o +obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o +obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o + +ccflags-$(CONFIG_I2C_DEBUG_ALGO) := -DDEBUG diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c new file mode 100644 index 000000000..d1d9a6c1a --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-bit.c @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * i2c-algo-bit.c: i2c driver algorithms for bit-shift adapters + * + * Copyright (C) 1995-2000 Simon G. Vogl + * + * 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> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/string_choices.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + + +/* ----- global defines ----------------------------------------------- */ + +#ifdef DEBUG +#define bit_dbg(level, dev, format, args...) \ + do { \ + if (i2c_debug >= level) \ + dev_dbg(dev, format, ##args); \ + } while (0) +#else +#define bit_dbg(level, dev, format, args...) \ + do {} while (0) +#endif /* DEBUG */ + +/* ----- global variables --------------------------------------------- */ + +static int bit_test; /* see if the line-setting functions work */ +module_param(bit_test, int, S_IRUGO); +MODULE_PARM_DESC(bit_test, "lines testing - 0 off; 1 report; 2 fail if stuck"); + +#ifdef DEBUG +static int i2c_debug = 1; +module_param(i2c_debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2 verbose; 3 very verbose"); +#endif + +/* --- setting states on the bus with the right timing: --------------- */ + +#define setsda(adap, val) adap->setsda(adap->data, val) +#define setscl(adap, val) adap->setscl(adap->data, val) +#define getsda(adap) adap->getsda(adap->data) +#define getscl(adap) adap->getscl(adap->data) + +static inline void sdalo(struct i2c_algo_bit_data *adap) +{ + setsda(adap, 0); + udelay((adap->udelay + 1) / 2); +} + +static inline void sdahi(struct i2c_algo_bit_data *adap) +{ + setsda(adap, 1); + udelay((adap->udelay + 1) / 2); +} + +static inline void scllo(struct i2c_algo_bit_data *adap) +{ + setscl(adap, 0); + udelay(adap->udelay / 2); +} + +/* + * Raise scl line, and do checking for delays. This is necessary for slower + * devices. + */ +static int sclhi(struct i2c_algo_bit_data *adap) +{ + unsigned long start; + + setscl(adap, 1); + + /* Not all adapters have scl sense line... */ + if (!adap->getscl) + goto done; + + start = jiffies; + while (!getscl(adap)) { + /* This hw knows how to read the clock line, so we wait + * until it actually gets high. This is safer as some + * chips may hold it low ("clock stretching") while they + * are processing data internally. + */ + if (time_after(jiffies, start + adap->timeout)) { + /* Test one last time, as we may have been preempted + * between last check and timeout test. + */ + if (getscl(adap)) + break; + return -ETIMEDOUT; + } + cpu_relax(); + } +#ifdef DEBUG + if (jiffies != start && i2c_debug >= 3) + pr_debug("i2c-algo-bit: needed %ld jiffies for SCL to go high\n", + jiffies - start); +#endif + +done: + udelay(adap->udelay); + return 0; +} + + +/* --- other auxiliary functions -------------------------------------- */ +static void i2c_start(struct i2c_algo_bit_data *adap) +{ + /* assert: scl, sda are high */ + setsda(adap, 0); + udelay(adap->udelay); + scllo(adap); +} + +static void i2c_repstart(struct i2c_algo_bit_data *adap) +{ + /* assert: scl is low */ + sdahi(adap); + sclhi(adap); + setsda(adap, 0); + udelay(adap->udelay); + scllo(adap); +} + + +static void i2c_stop(struct i2c_algo_bit_data *adap) +{ + /* assert: scl is low */ + sdalo(adap); + sclhi(adap); + setsda(adap, 1); + udelay(adap->udelay); +} + + + +/* send a byte without start cond., look for arbitration, + check ackn. from slave */ +/* returns: + * 1 if the device acknowledged + * 0 if the device did not ack + * -ETIMEDOUT if an error occurred (while raising the scl line) + */ +static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c) +{ + int i; + int sb; + int ack; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + for (i = 7; i >= 0; i--) { + sb = (c >> i) & 1; + setsda(adap, sb); + udelay((adap->udelay + 1) / 2); + if (sclhi(adap) < 0) { /* timed out */ + bit_dbg(1, &i2c_adap->dev, + "i2c_outb: 0x%02x, timeout at bit #%d\n", + (int)c, i); + return -ETIMEDOUT; + } + /* FIXME do arbitration here: + * if (sb && !getsda(adap)) -> ouch! Get out of here. + * + * Report a unique code, so higher level code can retry + * the whole (combined) message and *NOT* issue STOP. + */ + scllo(adap); + } + sdahi(adap); + if (sclhi(adap) < 0) { /* timeout */ + bit_dbg(1, &i2c_adap->dev, + "i2c_outb: 0x%02x, timeout at ack\n", (int)c); + return -ETIMEDOUT; + } + + /* read ack: SDA should be pulled down by slave, or it may + * NAK (usually to report problems with the data we wrote). + * Always report ACK if SDA is write-only. + */ + ack = !adap->getsda || !getsda(adap); /* ack: sda is pulled low -> success */ + bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c, + ack ? "A" : "NA"); + + scllo(adap); + return ack; + /* assert: scl is low (sda undef) */ +} + + +static int i2c_inb(struct i2c_adapter *i2c_adap) +{ + /* read byte via i2c port, without start/stop sequence */ + /* acknowledge is sent in i2c_read. */ + int i; + unsigned char indata = 0; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + sdahi(adap); + for (i = 0; i < 8; i++) { + if (sclhi(adap) < 0) { /* timeout */ + bit_dbg(1, &i2c_adap->dev, + "i2c_inb: timeout at bit #%d\n", + 7 - i); + return -ETIMEDOUT; + } + indata *= 2; + if (getsda(adap)) + indata |= 0x01; + setscl(adap, 0); + udelay(i == 7 ? adap->udelay / 2 : adap->udelay); + } + /* assert: scl is low */ + return indata; +} + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_adapter *i2c_adap) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + const char *name = i2c_adap->name; + int scl, sda, ret; + + if (adap->pre_xfer) { + ret = adap->pre_xfer(i2c_adap); + if (ret < 0) + return -ENODEV; + } + + if (adap->getsda == NULL) + pr_info("%s: SDA is write-only, testing not possible\n", name); + if (adap->getscl == NULL) + pr_info("%s: SCL is write-only, testing not possible\n", name); + + sda = adap->getsda ? getsda(adap) : 1; + scl = adap->getscl ? getscl(adap) : 1; + if (!scl || !sda) { + pr_warn("%s: bus seems to be busy (scl=%d, sda=%d)\n", name, scl, sda); + goto bailout; + } + + sdalo(adap); + if (adap->getsda && getsda(adap)) { + pr_warn("%s: SDA stuck high!\n", name); + goto bailout; + } + if (adap->getscl && !getscl(adap)) { + pr_warn("%s: SCL unexpected low while pulling SDA low!\n", name); + goto bailout; + } + + sdahi(adap); + if (adap->getsda && !getsda(adap)) { + pr_warn("%s: SDA stuck low!\n", name); + goto bailout; + } + if (adap->getscl && !getscl(adap)) { + pr_warn("%s: SCL unexpected low while pulling SDA high!\n", name); + goto bailout; + } + + scllo(adap); + if (adap->getscl && getscl(adap)) { + pr_warn("%s: SCL stuck high!\n", name); + goto bailout; + } + if (adap->getsda && !getsda(adap)) { + pr_warn("%s: SDA unexpected low while pulling SCL low!\n", name); + goto bailout; + } + + sclhi(adap); + if (adap->getscl && !getscl(adap)) { + pr_warn("%s: SCL stuck low!\n", name); + goto bailout; + } + if (adap->getsda && !getsda(adap)) { + pr_warn("%s: SDA unexpected low while pulling SCL high!\n", name); + goto bailout; + } + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + + pr_info("%s: Test OK\n", name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + + return -ENODEV; +} + +/* ----- Utility functions + */ + +/* try_address tries to contact a chip for a number of + * times before it gives up. + * return values: + * 1 chip answered + * 0 chip did not answer + * -x transmission error + */ +static int try_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + int i, ret = 0; + + for (i = 0; i <= retries; i++) { + ret = i2c_outb(i2c_adap, addr); + if (ret == 1 || i == retries) + break; + bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n"); + i2c_stop(adap); + udelay(adap->udelay); + yield(); + bit_dbg(3, &i2c_adap->dev, "emitting start condition\n"); + i2c_start(adap); + } + if (i && ret) + bit_dbg(1, &i2c_adap->dev, + "Used %d tries to %s client at 0x%02x: %s\n", i + 1, + addr & 1 ? "read from" : "write to", addr >> 1, + ret == 1 ? "success" : "failed, timeout?"); + return ret; +} + +static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +{ + const unsigned char *temp = msg->buf; + int count = msg->len; + unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; + int retval; + int wrcount = 0; + + while (count > 0) { + retval = i2c_outb(i2c_adap, *temp); + + /* OK/ACK; or ignored NAK */ + if ((retval > 0) || (nak_ok && (retval == 0))) { + count--; + temp++; + wrcount++; + + /* A slave NAKing the master means the slave didn't like + * something about the data it saw. For example, maybe + * the SMBus PEC was wrong. + */ + } else if (retval == 0) { + dev_err(&i2c_adap->dev, "sendbytes: NAK bailout.\n"); + return -EIO; + + /* Timeout; or (someday) lost arbitration + * + * FIXME Lost ARB implies retrying the transaction from + * the first message, after the "winning" master issues + * its STOP. As a rule, upper layer code has no reason + * to know or care about this ... it is *NOT* an error. + */ + } else { + dev_err(&i2c_adap->dev, "sendbytes: error %d\n", + retval); + return retval; + } + } + return wrcount; +} + +static int acknak(struct i2c_adapter *i2c_adap, int is_ack) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: sda is high */ + if (is_ack) /* send ack */ + setsda(adap, 0); + udelay((adap->udelay + 1) / 2); + if (sclhi(adap) < 0) { /* timeout */ + dev_err(&i2c_adap->dev, "readbytes: ack/nak timeout\n"); + return -ETIMEDOUT; + } + scllo(adap); + return 0; +} + +static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +{ + int inval; + int rdcount = 0; /* counts bytes read */ + unsigned char *temp = msg->buf; + int count = msg->len; + const unsigned flags = msg->flags; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + if (!adap->getsda) + return -EOPNOTSUPP; + + while (count > 0) { + inval = i2c_inb(i2c_adap); + if (inval >= 0) { + *temp = inval; + rdcount++; + } else { /* read timed out */ + break; + } + + temp++; + count--; + + /* Some SMBus transactions require that we receive the + transaction length as the first read byte. */ + if (rdcount == 1 && (flags & I2C_M_RECV_LEN)) { + if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) { + if (!(flags & I2C_M_NO_RD_ACK)) + acknak(i2c_adap, 0); + dev_err(&i2c_adap->dev, + "readbytes: invalid block length (%d)\n", + inval); + return -EPROTO; + } + /* The original count value accounts for the extra + bytes, that is, either 1 for a regular transaction, + or 2 for a PEC transaction. */ + count += inval; + msg->len += inval; + } + + bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n", + inval, + (flags & I2C_M_NO_RD_ACK) + ? "(no ack/nak)" + : (count ? "A" : "NA")); + + if (!(flags & I2C_M_NO_RD_ACK)) { + inval = acknak(i2c_adap, count); + if (inval < 0) + return inval; + } + } + return rdcount; +} + +/* doAddress initiates the transfer by generating the start condition (in + * try_address) and transmits the address in the necessary format to handle + * reads, writes as well as 10bit-addresses. + * returns: + * 0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set + * -x an error occurred (like: -ENXIO if the device did not answer, or + * -ETIMEDOUT, for example if the lines are stuck...) + */ +static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg) +{ + unsigned short flags = msg->flags; + unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + unsigned char addr; + int ret, retries; + + retries = nak_ok ? 0 : i2c_adap->retries; + + if (flags & I2C_M_TEN) { + /* a ten bit address */ + addr = 0xf0 | ((msg->addr >> 7) & 0x06); + bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr); + /* try extended address code...*/ + ret = try_address(i2c_adap, addr, retries); + if ((ret != 1) && !nak_ok) { + dev_err(&i2c_adap->dev, + "died at extended address code\n"); + return -ENXIO; + } + /* the remaining 8 bit address */ + ret = i2c_outb(i2c_adap, msg->addr & 0xff); + if ((ret != 1) && !nak_ok) { + /* the chip did not ack / xmission error occurred */ + dev_err(&i2c_adap->dev, "died at 2nd address code\n"); + return -ENXIO; + } + if (flags & I2C_M_RD) { + bit_dbg(3, &i2c_adap->dev, + "emitting repeated start condition\n"); + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(i2c_adap, addr, retries); + if ((ret != 1) && !nak_ok) { + dev_err(&i2c_adap->dev, + "died at repeated address code\n"); + return -EIO; + } + } + } else { /* normal 7bit address */ + addr = i2c_8bit_addr_from_msg(msg); + if (flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + ret = try_address(i2c_adap, addr, retries); + if ((ret != 1) && !nak_ok) + return -ENXIO; + } + + return 0; +} + +static int bit_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + int i, ret; + unsigned short nak_ok; + + if (adap->pre_xfer) { + ret = adap->pre_xfer(i2c_adap); + if (ret < 0) + return ret; + } + + bit_dbg(3, &i2c_adap->dev, "emitting start condition\n"); + i2c_start(adap); + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; + if (!(pmsg->flags & I2C_M_NOSTART)) { + if (i) { + if (msgs[i - 1].flags & I2C_M_STOP) { + bit_dbg(3, &i2c_adap->dev, + "emitting enforced stop/start condition\n"); + i2c_stop(adap); + i2c_start(adap); + } else { + bit_dbg(3, &i2c_adap->dev, + "emitting repeated start condition\n"); + i2c_repstart(adap); + } + } + ret = bit_doAddress(i2c_adap, pmsg); + if ((ret != 0) && !nak_ok) { + bit_dbg(1, &i2c_adap->dev, + "NAK from device addr 0x%02x msg #%d\n", + msgs[i].addr, i); + goto bailout; + } + } + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer*/ + ret = readbytes(i2c_adap, pmsg); + if (ret >= 1) + bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n", + ret, str_plural(ret)); + if (ret < pmsg->len) { + if (ret >= 0) + ret = -EIO; + goto bailout; + } + } else { + /* write bytes from buffer */ + ret = sendbytes(i2c_adap, pmsg); + if (ret >= 1) + bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n", + ret, str_plural(ret)); + if (ret < pmsg->len) { + if (ret >= 0) + ret = -EIO; + goto bailout; + } + } + } + ret = i; + +bailout: + bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n"); + i2c_stop(adap); + + if (adap->post_xfer) + adap->post_xfer(i2c_adap); + return ret; +} + +/* + * We print a warning when we are not flagged to support atomic transfers but + * will try anyhow. That's what the I2C core would do as well. Sadly, we can't + * modify the algorithm struct at probe time because this struct is exported + * 'const'. + */ +static int bit_xfer_atomic(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], + int num) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + if (!adap->can_do_atomic) + dev_warn(&i2c_adap->dev, "not flagged for atomic transfers\n"); + + return bit_xfer(i2c_adap, msgs, num); +} + +static u32 bit_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL_ALL | + I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; +} + + +/* -----exported algorithm data: ------------------------------------- */ + +const struct i2c_algorithm i2c_bit_algo = { + .xfer = bit_xfer, + .xfer_atomic = bit_xfer_atomic, + .functionality = bit_func, +}; +EXPORT_SYMBOL(i2c_bit_algo); + +static const struct i2c_adapter_quirks i2c_bit_quirk_no_clk_stretch = { + .flags = I2C_AQ_NO_CLK_STRETCH, +}; + +/* + * registering functions to load algorithms at runtime + */ +static int __i2c_bit_add_bus(struct i2c_adapter *adap, + int (*add_adapter)(struct i2c_adapter *)) +{ + struct i2c_algo_bit_data *bit_adap = adap->algo_data; + int ret; + + if (bit_test) { + ret = test_bus(adap); + if (bit_test >= 2 && ret < 0) + return -ENODEV; + } + + /* register new adapter to i2c module... */ + adap->algo = &i2c_bit_algo; + adap->retries = 3; + if (bit_adap->getscl == NULL) + adap->quirks = &i2c_bit_quirk_no_clk_stretch; + + /* + * We tried forcing SCL/SDA to an initial state here. But that caused a + * regression, sadly. Check Bugzilla #200045 for details. + */ + + ret = add_adapter(adap); + if (ret < 0) + return ret; + + if (bit_adap->getsda == NULL) + dev_warn(&adap->dev, "Not I2C compliant: can't read SDA\n"); + + if (bit_adap->getscl == NULL) + dev_warn(&adap->dev, "Not I2C compliant: can't read SCL\n"); + + if (bit_adap->getsda == NULL || bit_adap->getscl == NULL) + dev_warn(&adap->dev, "Bus may be unreliable\n"); + + return 0; +} + +int i2c_bit_add_bus(struct i2c_adapter *adap) +{ + return __i2c_bit_add_bus(adap, i2c_add_adapter); +} +EXPORT_SYMBOL(i2c_bit_add_bus); + +int i2c_bit_add_numbered_bus(struct i2c_adapter *adap) +{ + return __i2c_bit_add_bus(adap, i2c_add_numbered_adapter); +} +EXPORT_SYMBOL(i2c_bit_add_numbered_bus); + +MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>"); +MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c new file mode 100644 index 000000000..ee86df4cf --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pca.c @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * i2c-algo-pca.c i2c driver algorithms for PCA9564 adapters + * Copyright (C) 2004 Arcom Control Systems + * Copyright (C) 2008 Pengutronix + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-pca.h> + +#define DEB1(fmt, args...) do { if (i2c_debug >= 1) \ + printk(KERN_DEBUG fmt, ## args); } while (0) +#define DEB2(fmt, args...) do { if (i2c_debug >= 2) \ + printk(KERN_DEBUG fmt, ## args); } while (0) +#define DEB3(fmt, args...) do { if (i2c_debug >= 3) \ + printk(KERN_DEBUG fmt, ## args); } while (0) + +static int i2c_debug; + +#define pca_outw(adap, reg, val) adap->write_byte(adap->data, reg, val) +#define pca_inw(adap, reg) adap->read_byte(adap->data, reg) + +#define pca_status(adap) pca_inw(adap, I2C_PCA_STA) +#define pca_clock(adap) adap->i2c_clock +#define pca_set_con(adap, val) pca_outw(adap, I2C_PCA_CON, val) +#define pca_get_con(adap) pca_inw(adap, I2C_PCA_CON) +#define pca_wait(adap) adap->wait_for_completion_cb(adap->data) + +static void pca_reset(struct i2c_algo_pca_data *adap) +{ + if (adap->chip == I2C_PCA_CHIP_9665) { + /* Ignore the reset function from the module, + * we can use the parallel bus reset. + */ + pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IPRESET); + pca_outw(adap, I2C_PCA_IND, 0xA5); + pca_outw(adap, I2C_PCA_IND, 0x5A); + + /* + * After a reset we need to re-apply any configuration + * (calculated in pca_init) to get the bus in a working state. + */ + pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_IMODE); + pca_outw(adap, I2C_PCA_IND, adap->bus_settings.mode); + pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_ISCLL); + pca_outw(adap, I2C_PCA_IND, adap->bus_settings.tlow); + pca_outw(adap, I2C_PCA_INDPTR, I2C_PCA_ISCLH); + pca_outw(adap, I2C_PCA_IND, adap->bus_settings.thi); + + pca_set_con(adap, I2C_PCA_CON_ENSIO); + } else { + adap->reset_chip(adap->data); + pca_set_con(adap, I2C_PCA_CON_ENSIO | adap->bus_settings.clock_freq); + } +} + +/* + * Generate a start condition on the i2c bus. + * + * returns after the start condition has occurred + */ +static int pca_start(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== START\n"); + sta |= I2C_PCA_CON_STA; + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + return pca_wait(adap); +} + +/* + * Generate a repeated start condition on the i2c bus + * + * return after the repeated start condition has occurred + */ +static int pca_repeated_start(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== REPEATED START\n"); + sta |= I2C_PCA_CON_STA; + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + return pca_wait(adap); +} + +/* + * Generate a stop condition on the i2c bus + * + * returns after the stop condition has been generated + * + * STOPs do not generate an interrupt or set the SI flag, since the + * part returns the idle state (0xf8). Hence we don't need to + * pca_wait here. + */ +static void pca_stop(struct i2c_algo_pca_data *adap) +{ + int sta = pca_get_con(adap); + DEB2("=== STOP\n"); + sta |= I2C_PCA_CON_STO; + sta &= ~(I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); +} + +/* + * Send the slave address and R/W bit + * + * returns after the address has been sent + */ +static int pca_address(struct i2c_algo_pca_data *adap, + struct i2c_msg *msg) +{ + int sta = pca_get_con(adap); + int addr = i2c_8bit_addr_from_msg(msg); + + DEB2("=== SLAVE ADDRESS %#04x+%c=%#04x\n", + msg->addr, msg->flags & I2C_M_RD ? 'R' : 'W', addr); + + pca_outw(adap, I2C_PCA_DAT, addr); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + + return pca_wait(adap); +} + +/* + * Transmit a byte. + * + * Returns after the byte has been transmitted + */ +static int pca_tx_byte(struct i2c_algo_pca_data *adap, + __u8 b) +{ + int sta = pca_get_con(adap); + DEB2("=== WRITE %#04x\n", b); + pca_outw(adap, I2C_PCA_DAT, b); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI); + pca_set_con(adap, sta); + + return pca_wait(adap); +} + +/* + * Receive a byte + * + * returns immediately. + */ +static void pca_rx_byte(struct i2c_algo_pca_data *adap, + __u8 *b, int ack) +{ + *b = pca_inw(adap, I2C_PCA_DAT); + DEB2("=== READ %#04x %s\n", *b, ack ? "ACK" : "NACK"); +} + +/* + * Setup ACK or NACK for next received byte and wait for it to arrive. + * + * Returns after next byte has arrived. + */ +static int pca_rx_ack(struct i2c_algo_pca_data *adap, + int ack) +{ + int sta = pca_get_con(adap); + + sta &= ~(I2C_PCA_CON_STO|I2C_PCA_CON_STA|I2C_PCA_CON_SI|I2C_PCA_CON_AA); + + if (ack) + sta |= I2C_PCA_CON_AA; + + pca_set_con(adap, sta); + return pca_wait(adap); +} + +static int pca_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_pca_data *adap = i2c_adap->algo_data; + struct i2c_msg *msg = NULL; + int curmsg; + int numbytes = 0; + int state; + int ret; + int completed = 1; + unsigned long timeout = jiffies + i2c_adap->timeout; + + while ((state = pca_status(adap)) != 0xf8) { + if (time_before(jiffies, timeout)) { + msleep(10); + } else { + dev_dbg(&i2c_adap->dev, "bus is not idle. status is " + "%#04x\n", state); + return -EBUSY; + } + } + + DEB1("{{{ XFER %d messages\n", num); + + if (i2c_debug >= 2) { + for (curmsg = 0; curmsg < num; curmsg++) { + int addr, i; + msg = &msgs[curmsg]; + + addr = (0x7f & msg->addr) ; + + if (msg->flags & I2C_M_RD) + printk(KERN_INFO " [%02d] RD %d bytes from %#02x [%#02x, ...]\n", + curmsg, msg->len, addr, (addr << 1) | 1); + else { + printk(KERN_INFO " [%02d] WR %d bytes to %#02x [%#02x%s", + curmsg, msg->len, addr, addr << 1, + msg->len == 0 ? "" : ", "); + for (i = 0; i < msg->len; i++) + printk("%#04x%s", msg->buf[i], i == msg->len - 1 ? "" : ", "); + printk("]\n"); + } + } + } + + curmsg = 0; + ret = -EIO; + while (curmsg < num) { + state = pca_status(adap); + + DEB3("STATE is 0x%02x\n", state); + msg = &msgs[curmsg]; + + switch (state) { + case 0xf8: /* On reset or stop the bus is idle */ + completed = pca_start(adap); + break; + + case 0x08: /* A START condition has been transmitted */ + case 0x10: /* A repeated start condition has been transmitted */ + completed = pca_address(adap, msg); + break; + + case 0x18: /* SLA+W has been transmitted; ACK has been received */ + case 0x28: /* Data byte in I2CDAT has been transmitted; ACK has been received */ + if (numbytes < msg->len) { + completed = pca_tx_byte(adap, + msg->buf[numbytes]); + numbytes++; + break; + } + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + completed = pca_repeated_start(adap); + break; + + case 0x20: /* SLA+W has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after SLA+W\n"); + pca_stop(adap); + ret = -ENXIO; + goto out; + + case 0x40: /* SLA+R has been transmitted; ACK has been received */ + completed = pca_rx_ack(adap, msg->len > 1); + break; + + case 0x50: /* Data bytes has been received; ACK has been returned */ + if (numbytes < msg->len) { + pca_rx_byte(adap, &msg->buf[numbytes], 1); + numbytes++; + completed = pca_rx_ack(adap, + numbytes < msg->len - 1); + break; + } + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + completed = pca_repeated_start(adap); + break; + + case 0x48: /* SLA+R has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after SLA+R\n"); + pca_stop(adap); + ret = -ENXIO; + goto out; + + case 0x30: /* Data byte in I2CDAT has been transmitted; NOT ACK has been received */ + DEB2("NOT ACK received after data byte\n"); + pca_stop(adap); + goto out; + + case 0x38: /* Arbitration lost during SLA+W, SLA+R or data bytes */ + DEB2("Arbitration lost\n"); + /* + * The PCA9564 data sheet (2006-09-01) says "A + * START condition will be transmitted when the + * bus becomes free (STOP or SCL and SDA high)" + * when the STA bit is set (p. 11). + * + * In case this won't work, try pca_reset() + * instead. + */ + pca_start(adap); + goto out; + + case 0x58: /* Data byte has been received; NOT ACK has been returned */ + if (numbytes == msg->len - 1) { + pca_rx_byte(adap, &msg->buf[numbytes], 0); + curmsg++; numbytes = 0; + if (curmsg == num) + pca_stop(adap); + else + completed = pca_repeated_start(adap); + } else { + DEB2("NOT ACK sent after data byte received. " + "Not final byte. numbytes %d. len %d\n", + numbytes, msg->len); + pca_stop(adap); + goto out; + } + break; + case 0x70: /* Bus error - SDA stuck low */ + DEB2("BUS ERROR - SDA Stuck low\n"); + pca_reset(adap); + goto out; + case 0x78: /* Bus error - SCL stuck low (PCA9665) */ + case 0x90: /* Bus error - SCL stuck low (PCA9564) */ + DEB2("BUS ERROR - SCL Stuck low\n"); + pca_reset(adap); + goto out; + case 0x00: /* Bus error during master or slave mode due to illegal START or STOP condition */ + DEB2("BUS ERROR - Illegal START or STOP\n"); + pca_reset(adap); + goto out; + default: + dev_err(&i2c_adap->dev, "unhandled SIO state 0x%02x\n", state); + break; + } + + if (!completed) + goto out; + } + + ret = curmsg; + out: + DEB1("}}} transferred %d/%d messages. " + "status is %#04x. control is %#04x\n", + curmsg, num, pca_status(adap), + pca_get_con(adap)); + return ret; +} + +static u32 pca_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm pca_algo = { + .xfer = pca_xfer, + .functionality = pca_func, +}; + +static unsigned int pca_probe_chip(struct i2c_adapter *adap) +{ + struct i2c_algo_pca_data *pca_data = adap->algo_data; + /* The trick here is to check if there is an indirect register + * available. If there is one, we will read the value we first + * wrote on I2C_PCA_IADR. Otherwise, we will read the last value + * we wrote on I2C_PCA_ADR + */ + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IADR); + pca_outw(pca_data, I2C_PCA_IND, 0xAA); + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_ITO); + pca_outw(pca_data, I2C_PCA_IND, 0x00); + pca_outw(pca_data, I2C_PCA_INDPTR, I2C_PCA_IADR); + if (pca_inw(pca_data, I2C_PCA_IND) == 0xAA) { + printk(KERN_INFO "%s: PCA9665 detected.\n", adap->name); + pca_data->chip = I2C_PCA_CHIP_9665; + } else { + printk(KERN_INFO "%s: PCA9564 detected.\n", adap->name); + pca_data->chip = I2C_PCA_CHIP_9564; + } + return pca_data->chip; +} + +static int pca_init(struct i2c_adapter *adap) +{ + struct i2c_algo_pca_data *pca_data = adap->algo_data; + + adap->algo = &pca_algo; + + if (pca_probe_chip(adap) == I2C_PCA_CHIP_9564) { + static int freqs[] = {330, 288, 217, 146, 88, 59, 44, 36}; + int clock; + + if (pca_data->i2c_clock > 7) { + switch (pca_data->i2c_clock) { + case 330000: + pca_data->i2c_clock = I2C_PCA_CON_330kHz; + break; + case 288000: + pca_data->i2c_clock = I2C_PCA_CON_288kHz; + break; + case 217000: + pca_data->i2c_clock = I2C_PCA_CON_217kHz; + break; + case 146000: + pca_data->i2c_clock = I2C_PCA_CON_146kHz; + break; + case 88000: + pca_data->i2c_clock = I2C_PCA_CON_88kHz; + break; + case 59000: + pca_data->i2c_clock = I2C_PCA_CON_59kHz; + break; + case 44000: + pca_data->i2c_clock = I2C_PCA_CON_44kHz; + break; + case 36000: + pca_data->i2c_clock = I2C_PCA_CON_36kHz; + break; + default: + printk(KERN_WARNING + "%s: Invalid I2C clock speed selected." + " Using default 59kHz.\n", adap->name); + pca_data->i2c_clock = I2C_PCA_CON_59kHz; + } + } else { + printk(KERN_WARNING "%s: " + "Choosing the clock frequency based on " + "index is deprecated." + " Use the nominal frequency.\n", adap->name); + } + + clock = pca_clock(pca_data); + printk(KERN_INFO "%s: Clock frequency is %dkHz\n", + adap->name, freqs[clock]); + + /* Store settings as these will be needed when the PCA chip is reset */ + pca_data->bus_settings.clock_freq = clock; + + pca_reset(pca_data); + } else { + int clock; + int mode; + int tlow, thi; + /* Values can be found on PCA9665 datasheet section 7.3.2.6 */ + int min_tlow, min_thi; + /* These values are the maximum raise and fall values allowed + * by the I2C operation mode (Standard, Fast or Fast+) + * They are used (added) below to calculate the clock dividers + * of PCA9665. Note that they are slightly different of the + * real maximum, to allow the change on mode exactly on the + * maximum clock rate for each mode + */ + int raise_fall_time; + + if (pca_data->i2c_clock > 1265800) { + printk(KERN_WARNING "%s: I2C clock speed too high." + " Using 1265.8kHz.\n", adap->name); + pca_data->i2c_clock = 1265800; + } + + if (pca_data->i2c_clock < 60300) { + printk(KERN_WARNING "%s: I2C clock speed too low." + " Using 60.3kHz.\n", adap->name); + pca_data->i2c_clock = 60300; + } + + /* To avoid integer overflow, use clock/100 for calculations */ + clock = pca_clock(pca_data) / 100; + + if (pca_data->i2c_clock > I2C_MAX_FAST_MODE_PLUS_FREQ) { + mode = I2C_PCA_MODE_TURBO; + min_tlow = 14; + min_thi = 5; + raise_fall_time = 22; /* Raise 11e-8s, Fall 11e-8s */ + } else if (pca_data->i2c_clock > I2C_MAX_FAST_MODE_FREQ) { + mode = I2C_PCA_MODE_FASTP; + min_tlow = 17; + min_thi = 9; + raise_fall_time = 22; /* Raise 11e-8s, Fall 11e-8s */ + } else if (pca_data->i2c_clock > I2C_MAX_STANDARD_MODE_FREQ) { + mode = I2C_PCA_MODE_FAST; + min_tlow = 44; + min_thi = 20; + raise_fall_time = 58; /* Raise 29e-8s, Fall 29e-8s */ + } else { + mode = I2C_PCA_MODE_STD; + min_tlow = 157; + min_thi = 134; + raise_fall_time = 127; /* Raise 29e-8s, Fall 98e-8s */ + } + + /* The minimum clock that respects the thi/tlow = 134/157 is + * 64800 Hz. Below that, we have to fix the tlow to 255 and + * calculate the thi factor. + */ + if (clock < 648) { + tlow = 255; + thi = 1000000 - clock * raise_fall_time; + thi /= (I2C_PCA_OSC_PER * clock) - tlow; + } else { + tlow = (1000000 - clock * raise_fall_time) * min_tlow; + tlow /= I2C_PCA_OSC_PER * clock * (min_thi + min_tlow); + thi = tlow * min_thi / min_tlow; + } + + /* Store settings as these will be needed when the PCA chip is reset */ + pca_data->bus_settings.mode = mode; + pca_data->bus_settings.tlow = tlow; + pca_data->bus_settings.thi = thi; + + pca_reset(pca_data); + + printk(KERN_INFO + "%s: Clock frequency is %dHz\n", adap->name, clock * 100); + } + udelay(500); /* 500 us for oscillator to stabilise */ + + return 0; +} + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pca_add_bus(struct i2c_adapter *adap) +{ + int rval; + + rval = pca_init(adap); + if (rval) + return rval; + + return i2c_add_adapter(adap); +} +EXPORT_SYMBOL(i2c_pca_add_bus); + +int i2c_pca_add_numbered_bus(struct i2c_adapter *adap) +{ + int rval; + + rval = pca_init(adap); + if (rval) + return rval; + + return i2c_add_numbered_adapter(adap); +} +EXPORT_SYMBOL(i2c_pca_add_numbered_bus); + +MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>"); +MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>"); +MODULE_DESCRIPTION("I2C-Bus PCA9564/PCA9665 algorithm"); +MODULE_LICENSE("GPL"); + +module_param(i2c_debug, int, 0); diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c new file mode 100644 index 000000000..a87ecea7f --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pcf.c @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * i2c-algo-pcf.c i2c driver algorithms for PCF8584 adapters + * + * Copyright (C) 1995-1997 Simon G. Vogl + * 1998-2000 Hans Berglund + * + * With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and + * Frodo Looijaard <frodol@dds.nl>, and also from Martin Bailey + * <mbailey@littlefeet-inc.com> + * + * Partially rewriten by Oleg I. Vdovikin <vdovikin@jscc.ru> to handle multiple + * messages, proper stop/repstart signaling during receive, added detect code + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-pcf.h> +#include <linux/string_choices.h> +#include "i2c-algo-pcf.h" + + +#define DEF_TIMEOUT 16 + +/* setting states on the bus with the right timing: */ + +#define set_pcf(adap, ctl, val) adap->setpcf(adap->data, ctl, val) +#define get_pcf(adap, ctl) adap->getpcf(adap->data, ctl) +#define get_own(adap) adap->getown(adap->data) +#define get_clock(adap) adap->getclock(adap->data) +#define i2c_outb(adap, val) adap->setpcf(adap->data, 0, val) +#define i2c_inb(adap) adap->getpcf(adap->data, 0) + +/* other auxiliary functions */ + +static void i2c_start(struct i2c_algo_pcf_data *adap) +{ + set_pcf(adap, 1, I2C_PCF_START); +} + +static void i2c_repstart(struct i2c_algo_pcf_data *adap) +{ + set_pcf(adap, 1, I2C_PCF_REPSTART); +} + +static void i2c_stop(struct i2c_algo_pcf_data *adap) +{ + set_pcf(adap, 1, I2C_PCF_STOP); +} + +static void handle_lab(struct i2c_algo_pcf_data *adap, const int *status) +{ + /* + * Cleanup from LAB -- reset and enable ESO. + * This resets the PCF8584; since we've lost the bus, no + * further attempts should be made by callers to clean up + * (no i2c_stop() etc.) + */ + set_pcf(adap, 1, I2C_PCF_PIN); + set_pcf(adap, 1, I2C_PCF_ESO); + /* + * We pause for a time period sufficient for any running + * I2C transaction to complete -- the arbitration logic won't + * work properly until the next START is seen. + * It is assumed the bus driver or client has set a proper value. + * + * REVISIT: should probably use msleep instead of mdelay if we + * know we can sleep. + */ + if (adap->lab_mdelay) + mdelay(adap->lab_mdelay); + +} + +static int wait_for_bb(struct i2c_algo_pcf_data *adap) +{ + + int timeout = DEF_TIMEOUT; + int status; + + status = get_pcf(adap, 1); + + while (!(status & I2C_PCF_BB) && --timeout) { + udelay(100); /* wait for 100 us */ + status = get_pcf(adap, 1); + } + + if (timeout == 0) { + printk(KERN_ERR "Timeout waiting for Bus Busy\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) +{ + + int timeout = DEF_TIMEOUT; + + *status = get_pcf(adap, 1); + + while ((*status & I2C_PCF_PIN) && --timeout) { + adap->waitforpin(adap->data); + *status = get_pcf(adap, 1); + } + if (*status & I2C_PCF_LAB) { + handle_lab(adap, status); + return -EINTR; + } + + if (timeout == 0) + return -ETIMEDOUT; + + return 0; +} + +/* + * This should perform the 'PCF8584 initialization sequence' as described + * in the Philips IC12 data book (1995, Aug 29). + * There should be a 30 clock cycle wait after reset, I assume this + * has been fulfilled. + * There should be a delay at the end equal to the longest I2C message + * to synchronize the BB-bit (in multimaster systems). How long is + * this? I assume 1 second is always long enough. + * + * vdovikin: added detect code for PCF8584 + */ +static int pcf_init_8584(struct i2c_algo_pcf_data *adap) +{ + unsigned char temp; + + /* S1=0x80: S0 selected, serial interface off */ + set_pcf(adap, 1, I2C_PCF_PIN); + /* + * check to see S1 now used as R/W ctrl - + * PCF8584 does that when ESO is zero + */ + temp = get_pcf(adap, 1); + if ((temp & 0x7f) != 0) + return -ENXIO; /* definitely not PCF8584 */ + + /* load own address in S0, effective address is (own << 1) */ + i2c_outb(adap, get_own(adap)); + /* check it's really written */ + temp = i2c_inb(adap); + if (temp != get_own(adap)) + return -ENXIO; + + /* S1=0xA0, next byte in S2 */ + set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1); + /* check to see S2 now selected */ + temp = get_pcf(adap, 1); + if ((temp & 0x7f) != I2C_PCF_ES1) + return -ENXIO; + + /* load clock register S2 */ + i2c_outb(adap, get_clock(adap)); + /* check it's really written, the only 5 lowest bits does matter */ + temp = i2c_inb(adap); + if ((temp & 0x1f) != get_clock(adap)) + return -ENXIO; + + /* Enable serial interface, idle, S0 selected */ + set_pcf(adap, 1, I2C_PCF_IDLE); + + /* check to see PCF is really idled and we can access status register */ + temp = get_pcf(adap, 1); + if (temp != (I2C_PCF_PIN | I2C_PCF_BB)) + return -ENXIO; + + printk(KERN_DEBUG "i2c-algo-pcf.o: detected and initialized PCF8584.\n"); + + return 0; +} + +static int pcf_sendbytes(struct i2c_adapter *i2c_adap, const char *buf, + int count, int last) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wrcount, status, timeout; + + for (wrcount = 0; wrcount < count; ++wrcount) { + i2c_outb(adap, buf[wrcount]); + timeout = wait_for_pin(adap, &status); + if (timeout) { + if (timeout == -EINTR) + return -EINTR; /* arbitration lost */ + + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_write: error - timeout.\n"); + return -EREMOTEIO; /* got a better one ?? */ + } + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_write: error - no ack.\n"); + return -EREMOTEIO; /* got a better one ?? */ + } + } + if (last) + i2c_stop(adap); + else + i2c_repstart(adap); + + return wrcount; +} + +static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, + int count, int last) +{ + int i, status; + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wfp; + + /* increment number of bytes to read by one -- read dummy byte */ + for (i = 0; i <= count; i++) { + + wfp = wait_for_pin(adap, &status); + if (wfp) { + if (wfp == -EINTR) + return -EINTR; /* arbitration lost */ + + i2c_stop(adap); + dev_err(&i2c_adap->dev, "pcf_readbytes timed out.\n"); + return -1; + } + + if ((status & I2C_PCF_LRB) && (i != count)) { + i2c_stop(adap); + dev_err(&i2c_adap->dev, "i2c_read: i2c_inb, No ack.\n"); + return -1; + } + + if (i == count - 1) { + set_pcf(adap, 1, I2C_PCF_ESO); + } else if (i == count) { + if (last) + i2c_stop(adap); + else + i2c_repstart(adap); + } + + if (i) + buf[i - 1] = i2c_inb(adap); + else + i2c_inb(adap); /* dummy read */ + } + + return i - 1; +} + + +static void pcf_send_address(struct i2c_algo_pcf_data *adap, + struct i2c_msg *msg) +{ + unsigned char addr = i2c_8bit_addr_from_msg(msg); + + if (msg->flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + i2c_outb(adap, addr); +} + +static int pcf_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + struct i2c_msg *pmsg; + int i; + int timeout, status; + + if (adap->xfer_begin) + adap->xfer_begin(adap->data); + + /* Check for bus busy */ + timeout = wait_for_bb(adap); + if (timeout) { + i = -EIO; + goto out; + } + + for (i = 0; i < num; i++) { + int ret; + + pmsg = &msgs[i]; + pcf_send_address(adap, pmsg); + + /* Send START */ + if (i == 0) + i2c_start(adap); + + /* Wait for PIN (pending interrupt NOT) */ + timeout = wait_for_pin(adap, &status); + if (timeout) { + if (timeout == -EINTR) { + /* arbitration lost */ + i = -EINTR; + goto out; + } + i2c_stop(adap); + i = -EREMOTEIO; + goto out; + } + + /* Check LRB (last rcvd bit - slave ack) */ + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + i = -EREMOTEIO; + goto out; + } + + + if (pmsg->flags & I2C_M_RD) { + ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len, + (i + 1 == num)); + } else { + ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len, + (i + 1 == num)); + } + + if (ret < 0) + goto out; + } + +out: + if (adap->xfer_end) + adap->xfer_end(adap->data); + return i; +} + +static u32 pcf_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_PROTOCOL_MANGLING; +} + +/* exported algorithm data: */ +static const struct i2c_algorithm pcf_algo = { + .xfer = pcf_xfer, + .functionality = pcf_func, +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pcf_add_bus(struct i2c_adapter *adap) +{ + struct i2c_algo_pcf_data *pcf_adap = adap->algo_data; + int rval; + + /* register new adapter to i2c module... */ + adap->algo = &pcf_algo; + + rval = pcf_init_8584(pcf_adap); + if (rval) + return rval; + + rval = i2c_add_adapter(adap); + + return rval; +} +EXPORT_SYMBOL(i2c_pcf_add_bus); + +MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>"); +MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/algos/i2c-algo-pcf.h b/drivers/i2c/algos/i2c-algo-pcf.h new file mode 100644 index 000000000..2448ab192 --- /dev/null +++ b/drivers/i2c/algos/i2c-algo-pcf.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* -------------------------------------------------------------------- */ +/* i2c-pcf8584.h: PCF 8584 global defines */ +/* -------------------------------------------------------------------- */ +/* Copyright (C) 1996 Simon G. Vogl + 1999 Hans Berglund + + */ +/* -------------------------------------------------------------------- */ + +/* With some changes from Frodo Looijaard <frodol@dds.nl> */ + +#ifndef I2C_PCF8584_H +#define I2C_PCF8584_H 1 + +/* ----- Control register bits ---------------------------------------- */ +#define I2C_PCF_PIN 0x80 +#define I2C_PCF_ESO 0x40 +#define I2C_PCF_ES1 0x20 +#define I2C_PCF_ES2 0x10 +#define I2C_PCF_ENI 0x08 +#define I2C_PCF_STA 0x04 +#define I2C_PCF_STO 0x02 +#define I2C_PCF_ACK 0x01 + +#define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) +#define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK) + +/* ----- Status register bits ----------------------------------------- */ +/*#define I2C_PCF_PIN 0x80 as above*/ + +#define I2C_PCF_INI 0x40 /* 1 if not initialized */ +#define I2C_PCF_STS 0x20 +#define I2C_PCF_BER 0x10 +#define I2C_PCF_AD0 0x08 +#define I2C_PCF_LRB 0x08 +#define I2C_PCF_AAS 0x04 +#define I2C_PCF_LAB 0x02 +#define I2C_PCF_BB 0x01 + +/* ----- Chip clock frequencies --------------------------------------- */ +#define I2C_PCF_CLK3 0x00 +#define I2C_PCF_CLK443 0x10 +#define I2C_PCF_CLK6 0x14 +#define I2C_PCF_CLK 0x18 +#define I2C_PCF_CLK12 0x1c + +/* ----- transmission frequencies ------------------------------------- */ +#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ +#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ +#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ +#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ + + +/* ----- Access to internal registers according to ES1,ES2 ------------ */ +/* they are mapped to the data port ( a0 = 0 ) */ +/* available when ESO == 0 : */ + +#define I2C_PCF_OWNADR 0 +#define I2C_PCF_INTREG I2C_PCF_ES2 +#define I2C_PCF_CLKREG I2C_PCF_ES1 + +#endif /* I2C_PCF8584_H */ |
