diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-30 13:01:02 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-30 13:01:02 -0700 |
commit | 592fa9532d4e4a7590ca383fd537eb4d53fa585a (patch) | |
tree | 48ead2d9a235f00fb2635613b1ed96e6b7d4bd4b /drivers | |
parent | efd8929b9eec1cde120abb36d76dd00ff6711023 (diff) | |
parent | a80f24945fcfdff31bdf04837145e56570741a67 (diff) |
Merge branch 'i2c/for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
- new drivers for Silicon Labs CP2615 and the HiSilicon I2C unit
- bigger refactoring for the MPC driver
- support for full software nodes - no need to work around with only
properties anymore
- we now have 'devm_i2c_add_adapter', too
- sub-system wide fixes for the RPM refcounting problem which often
caused a leak when an error was encountered during probe
- the rest is usual driver updates and improvements
* 'i2c/for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (77 commits)
i2c: mediatek: Use scl_int_delay_ns to compensate clock-stretching
i2c: mediatek: Fix wrong dma sync flag
i2c: mediatek: Fix send master code at more than 1MHz
i2c: sh7760: fix IRQ error path
i2c: i801: Add support for Intel Alder Lake PCH-M
i2c: core: Fix spacing error by checkpatch
i2c: s3c2410: simplify getting of_device_id match data
i2c: nomadik: Fix space errors
i2c: iop3xx: Fix coding style issues
i2c: amd8111: Fix coding style issues
i2c: mpc: Drop duplicate message from devm_platform_ioremap_resource()
i2c: mpc: Use device_get_match_data() helper
i2c: mpc: Remove CONFIG_PM_SLEEP ifdeffery
i2c: mpc: Use devm_clk_get_optional()
i2c: mpc: Update license and copyright
i2c: mpc: Interrupt driven transfer
i2c: sh7760: add IRQ check
i2c: rcar: add IRQ check
i2c: mlxbf: add IRQ check
i2c: jz4780: add IRQ check
...
Diffstat (limited to 'drivers')
43 files changed, 1916 insertions, 698 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 543a05ed0c82..281a65d9b44b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -645,6 +645,16 @@ config I2C_HIGHLANDER This driver can also be built as a module. If so, the module will be called i2c-highlander. +config I2C_HISI + tristate "HiSilicon I2C controller" + depends on ARM64 || COMPILE_TEST + help + Say Y here if you want to have Hisilicon I2C controller support + available on the Kunpeng Server. + + This driver can also be built as a module. If so, the module + will be called i2c-hisi. + config I2C_IBM_IIC tristate "IBM PPC 4xx on-chip I2C interface" depends on 4xx @@ -1199,6 +1209,16 @@ config I2C_DLN2 This driver can also be built as a module. If so, the module will be called i2c-dln2. +config I2C_CP2615 + tristate "Silicon Labs CP2615 USB sound card and I2C adapter" + depends on USB + help + If you say yes to this option, support will be included for Silicon + Labs CP2615's I2C interface. + + This driver can also be built as a module. If so, the module + will be called i2c-cp2615. + config I2C_PARPORT tristate "Parallel port adapter" depends on PARPORT diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 615f35e3e31f..69e9963615f6 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_I2C_EMEV2) += i2c-emev2.o obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o +obj-$(CONFIG_I2C_HISI) += i2c-hisi.o obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o obj-$(CONFIG_I2C_IMG) += i2c-img-scb.o @@ -123,6 +124,7 @@ obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o # External I2C/SMBus adapter drivers obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o +obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index 34862ad3423e..1ed7e945bb6d 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -186,9 +186,9 @@ static int amd_ec_write(struct amd_smbus *smbus, unsigned char address, #define AMD_SMB_PRTCL_PEC 0x80 -static s32 amd8111_access(struct i2c_adapter * adap, u16 addr, +static s32 amd8111_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, - union i2c_smbus_data * data) + union i2c_smbus_data *data) { struct amd_smbus *smbus = adap->algo_data; unsigned char protocol, len, pec, temp[2]; @@ -199,130 +199,130 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr, pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0; switch (size) { - case I2C_SMBUS_QUICK: - protocol |= AMD_SMB_PRTCL_QUICK; - read_write = I2C_SMBUS_WRITE; - break; - - case I2C_SMBUS_BYTE: - if (read_write == I2C_SMBUS_WRITE) { - status = amd_ec_write(smbus, AMD_SMB_CMD, - command); - if (status) - return status; - } - protocol |= AMD_SMB_PRTCL_BYTE; - break; - - case I2C_SMBUS_BYTE_DATA: - status = amd_ec_write(smbus, AMD_SMB_CMD, command); + case I2C_SMBUS_QUICK: + protocol |= AMD_SMB_PRTCL_QUICK; + read_write = I2C_SMBUS_WRITE; + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + status = amd_ec_write(smbus, AMD_SMB_CMD, + command); if (status) return status; - if (read_write == I2C_SMBUS_WRITE) { - status = amd_ec_write(smbus, AMD_SMB_DATA, - data->byte); - if (status) - return status; - } - protocol |= AMD_SMB_PRTCL_BYTE_DATA; - break; + } + protocol |= AMD_SMB_PRTCL_BYTE; + break; - case I2C_SMBUS_WORD_DATA: - status = amd_ec_write(smbus, AMD_SMB_CMD, command); + case I2C_SMBUS_BYTE_DATA: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) { + status = amd_ec_write(smbus, AMD_SMB_DATA, + data->byte); if (status) return status; - if (read_write == I2C_SMBUS_WRITE) { - status = amd_ec_write(smbus, AMD_SMB_DATA, - data->word & 0xff); - if (status) - return status; - status = amd_ec_write(smbus, AMD_SMB_DATA + 1, - data->word >> 8); - if (status) - return status; - } - protocol |= AMD_SMB_PRTCL_WORD_DATA | pec; - break; + } + protocol |= AMD_SMB_PRTCL_BYTE_DATA; + break; - case I2C_SMBUS_BLOCK_DATA: - status = amd_ec_write(smbus, AMD_SMB_CMD, command); - if (status) - return status; - if (read_write == I2C_SMBUS_WRITE) { - len = min_t(u8, data->block[0], - I2C_SMBUS_BLOCK_MAX); - status = amd_ec_write(smbus, AMD_SMB_BCNT, len); - if (status) - return status; - for (i = 0; i < len; i++) { - status = - amd_ec_write(smbus, AMD_SMB_DATA + i, - data->block[i + 1]); - if (status) - return status; - } - } - protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec; - break; - - case I2C_SMBUS_I2C_BLOCK_DATA: - len = min_t(u8, data->block[0], - I2C_SMBUS_BLOCK_MAX); - status = amd_ec_write(smbus, AMD_SMB_CMD, command); - if (status) - return status; - status = amd_ec_write(smbus, AMD_SMB_BCNT, len); - if (status) - return status; - if (read_write == I2C_SMBUS_WRITE) - for (i = 0; i < len; i++) { - status = - amd_ec_write(smbus, AMD_SMB_DATA + i, - data->block[i + 1]); - if (status) - return status; - } - protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA; - break; - - case I2C_SMBUS_PROC_CALL: - status = amd_ec_write(smbus, AMD_SMB_CMD, command); - if (status) - return status; + case I2C_SMBUS_WORD_DATA: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) { status = amd_ec_write(smbus, AMD_SMB_DATA, - data->word & 0xff); + data->word & 0xff); if (status) return status; status = amd_ec_write(smbus, AMD_SMB_DATA + 1, - data->word >> 8); + data->word >> 8); if (status) return status; - protocol = AMD_SMB_PRTCL_PROC_CALL | pec; - read_write = I2C_SMBUS_READ; - break; + } + protocol |= AMD_SMB_PRTCL_WORD_DATA | pec; + break; - case I2C_SMBUS_BLOCK_PROC_CALL: + case I2C_SMBUS_BLOCK_DATA: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) { len = min_t(u8, data->block[0], - I2C_SMBUS_BLOCK_MAX - 1); - status = amd_ec_write(smbus, AMD_SMB_CMD, command); - if (status) - return status; + I2C_SMBUS_BLOCK_MAX); status = amd_ec_write(smbus, AMD_SMB_BCNT, len); if (status) return status; for (i = 0; i < len; i++) { - status = amd_ec_write(smbus, AMD_SMB_DATA + i, - data->block[i + 1]); + status = + amd_ec_write(smbus, AMD_SMB_DATA + i, + data->block[i + 1]); if (status) return status; } - protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec; - read_write = I2C_SMBUS_READ; - break; + } + protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + len = min_t(u8, data->block[0], + I2C_SMBUS_BLOCK_MAX); + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_BCNT, len); + if (status) + return status; + if (read_write == I2C_SMBUS_WRITE) + for (i = 0; i < len; i++) { + status = + amd_ec_write(smbus, AMD_SMB_DATA + i, + data->block[i + 1]); + if (status) + return status; + } + protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA; + break; - default: - dev_warn(&adap->dev, "Unsupported transaction %d\n", size); - return -EOPNOTSUPP; + case I2C_SMBUS_PROC_CALL: + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_DATA, + data->word & 0xff); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_DATA + 1, + data->word >> 8); + if (status) + return status; + protocol = AMD_SMB_PRTCL_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; + break; + + case I2C_SMBUS_BLOCK_PROC_CALL: + len = min_t(u8, data->block[0], + I2C_SMBUS_BLOCK_MAX - 1); + status = amd_ec_write(smbus, AMD_SMB_CMD, command); + if (status) + return status; + status = amd_ec_write(smbus, AMD_SMB_BCNT, len); + if (status) + return status; + for (i = 0; i < len; i++) { + status = amd_ec_write(smbus, AMD_SMB_DATA + i, + data->block[i + 1]); + if (status) + return status; + } + protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; + break; + + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; } status = amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1); @@ -357,40 +357,40 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr, return 0; switch (size) { - case I2C_SMBUS_BYTE: - case I2C_SMBUS_BYTE_DATA: - status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte); - if (status) - return status; - break; + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte); + if (status) + return status; + break; - case I2C_SMBUS_WORD_DATA: - case I2C_SMBUS_PROC_CALL: - status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0); - if (status) - return status; - status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1); - if (status) - return status; - data->word = (temp[1] << 8) | temp[0]; - break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0); + if (status) + return status; + status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1); + if (status) + return status; + data->word = (temp[1] << 8) | temp[0]; + break; - case I2C_SMBUS_BLOCK_DATA: - case I2C_SMBUS_BLOCK_PROC_CALL: - status = amd_ec_read(smbus, AMD_SMB_BCNT, &len); + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + status = amd_ec_read(smbus, AMD_SMB_BCNT, &len); + if (status) + return status; + len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX); + fallthrough; + case I2C_SMBUS_I2C_BLOCK_DATA: + for (i = 0; i < len; i++) { + status = amd_ec_read(smbus, AMD_SMB_DATA + i, + data->block + i + 1); if (status) return status; - len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX); - fallthrough; - case I2C_SMBUS_I2C_BLOCK_DATA: - for (i = 0; i < len; i++) { - status = amd_ec_read(smbus, AMD_SMB_DATA + i, - data->block + i + 1); - if (status) - return status; - } - data->block[0] = len; - break; + } + data->block[0] = len; + break; } return 0; diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index ba766d24219e..490ee3962645 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -22,7 +22,6 @@ #include <linux/platform_device.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/version.h> #define N_DATA_REGS 8 diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index e4b7f2a951ad..c1bbc4caeb5c 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -789,7 +789,7 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, bool change_role = false; #endif - ret = pm_runtime_get_sync(id->dev); + ret = pm_runtime_resume_and_get(id->dev); if (ret < 0) return ret; @@ -911,7 +911,7 @@ static int cdns_reg_slave(struct i2c_client *slave) if (slave->flags & I2C_CLIENT_TEN) return -EAFNOSUPPORT; - ret = pm_runtime_get_sync(id->dev); + ret = pm_runtime_resume_and_get(id->dev); if (ret < 0) return ret; @@ -1200,7 +1200,10 @@ static int cdns_i2c_probe(struct platform_device *pdev) if (IS_ERR(id->membase)) return PTR_ERR(id->membase); - id->irq = platform_get_irq(pdev, 0); + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + id->irq = ret; id->adap.owner = THIS_MODULE; id->adap.dev.of_node = pdev->dev.of_node; diff --git a/drivers/i2c/busses/i2c-cht-wc.c b/drivers/i2c/busses/i2c-cht-wc.c index f80d79e973cd..08f491ea21ac 100644 --- a/drivers/i2c/busses/i2c-cht-wc.c +++ b/drivers/i2c/busses/i2c-cht-wc.c @@ -280,6 +280,10 @@ static const struct property_entry bq24190_props[] = { { } }; +static const struct software_node bq24190_node = { + .properties = bq24190_props, +}; + static struct regulator_consumer_supply fusb302_consumer = { .supply = "vbus", /* Must match fusb302 dev_name in intel_cht_int33fe.c */ @@ -308,7 +312,7 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev) .type = "bq24190", .addr = 0x6b, .dev_name = "bq24190", - .properties = bq24190_props, + .swnode = &bq24190_node, .platform_data = &bq24190_pdata, }; int ret, reg, irq; diff --git a/drivers/i2c/busses/i2c-cp2615.c b/drivers/i2c/busses/i2c-cp2615.c new file mode 100644 index 000000000000..78cfecd1ea76 --- /dev/null +++ b/drivers/i2c/busses/i2c-cp2615.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * i2c support for Silicon Labs' CP2615 Digital Audio Bridge + * + * (c) 2021, Bence Csókás <bence98@sch.bme.hu> + */ + +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/usb.h> + +/** CP2615 I/O Protocol implementation */ + +#define CP2615_VID 0x10c4 +#define CP2615_PID 0xeac1 + +#define IOP_EP_IN 0x82 +#define IOP_EP_OUT 0x02 +#define IOP_IFN 1 +#define IOP_ALTSETTING 2 + +#define MAX_IOP_SIZE 64 +#define MAX_IOP_PAYLOAD_SIZE (MAX_IOP_SIZE - 6) +#define MAX_I2C_SIZE (MAX_IOP_PAYLOAD_SIZE - 4) + +enum cp2615_iop_msg_type { + iop_GetAccessoryInfo = 0xD100, + iop_AccessoryInfo = 0xA100, + iop_GetPortConfiguration = 0xD203, + iop_PortConfiguration = 0xA203, + iop_DoI2cTransfer = 0xD400, + iop_I2cTransferResult = 0xA400, + iop_GetSerialState = 0xD501, + iop_SerialState = 0xA501 +}; + +struct __packed cp2615_iop_msg { + __be16 preamble, length, msg; + u8 data[MAX_IOP_PAYLOAD_SIZE]; +}; + +#define PART_ID_A01 0x1400 +#define PART_ID_A02 0x1500 + +struct __packed cp2615_iop_accessory_info { + __be16 part_id, option_id, proto_ver; +}; + +struct __packed cp2615_i2c_transfer { + u8 tag, i2caddr, read_len, write_len; + u8 data[MAX_I2C_SIZE]; +}; + +/* Possible values for struct cp2615_i2c_transfer_result.status */ +enum cp2615_i2c_status { + /* Writing to the internal EEPROM failed, because it is locked */ + CP2615_CFG_LOCKED = -6, + /* read_len or write_len out of range */ + CP2615_INVALID_PARAM = -4, + /* I2C slave did not ACK in time */ + CP2615_TIMEOUT, + /* I2C bus busy */ + CP2615_BUS_BUSY, + /* I2C bus error (ie. device NAK'd the request) */ + CP2615_BUS_ERROR, + CP2615_SUCCESS +}; + +struct __packed cp2615_i2c_transfer_result { + u8 tag, i2caddr; + s8 status; + u8 read_len; + u8 data[MAX_I2C_SIZE]; +}; + +static int cp2615_init_iop_msg(struct cp2615_iop_msg *ret, enum cp2615_iop_msg_type msg, + const void *data, size_t data_len) +{ + if (data_len > MAX_IOP_PAYLOAD_SIZE) + return -EFBIG; + + if (!ret) + return -EINVAL; + + ret->preamble = 0x2A2A; + ret->length = htons(data_len + 6); + ret->msg = htons(msg); + if (data && data_len) + memcpy(&ret->data, data, data_len); + return 0; +} + +static int cp2615_init_i2c_msg(struct cp2615_iop_msg *ret, const struct cp2615_i2c_transfer *data) +{ + return cp2615_init_iop_msg(ret, iop_DoI2cTransfer, data, 4 + data->write_len); +} + +/* Translates status codes to Linux errno's */ +static int cp2615_check_status(enum cp2615_i2c_status status) +{ + switch (status) { + case CP2615_SUCCESS: + return 0; + case CP2615_BUS_ERROR: + return -ENXIO; + case CP2615_BUS_BUSY: + return -EAGAIN; + case CP2615_TIMEOUT: + return -ETIMEDOUT; + case CP2615_INVALID_PARAM: + return -EINVAL; + case CP2615_CFG_LOCKED: + return -EPERM; + } + /* Unknown error code */ + return -EPROTO; +} + +/** Driver code */ + +static int +cp2615_i2c_send(struct usb_interface *usbif, struct cp2615_i2c_transfer *i2c_w) +{ + struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + struct usb_device *usbdev = interface_to_usbdev(usbif); + int res = cp2615_init_i2c_msg(msg, i2c_w); + + if (!res) + res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT), + msg, ntohs(msg->length), NULL, 0); + kfree(msg); + return res; +} + +static int +cp2615_i2c_recv(struct usb_interface *usbif, unsigned char tag, void *buf) +{ + struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + struct cp2615_i2c_transfer_result *i2c_r = (struct cp2615_i2c_transfer_result *)&msg->data; + struct usb_device *usbdev = interface_to_usbdev(usbif); + int res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN), + msg, sizeof(struct cp2615_iop_msg), NULL, 0); + + if (res < 0) { + kfree(msg); + return res; + } + + if (msg->msg != htons(iop_I2cTransferResult) || i2c_r->tag != tag) { + kfree(msg); + return -EIO; + } + + res = cp2615_check_status(i2c_r->status); + if (!res) + memcpy(buf, &i2c_r->data, i2c_r->read_len); + + kfree(msg); + return res; +} + +/* Checks if the IOP is functional by querying the part's ID */ +static int cp2615_check_iop(struct usb_interface *usbif) +{ + struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); + struct cp2615_iop_accessory_info *info = (struct cp2615_iop_accessory_info *)&msg->data; + struct usb_device *usbdev = interface_to_usbdev(usbif); + int res = cp2615_init_iop_msg(msg, iop_GetAccessoryInfo, NULL, 0); + + if (res) + goto out; + + res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT), + msg, ntohs(msg->length), NULL, 0); + if (res) + goto out; + + res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN), + msg, sizeof(struct cp2615_iop_msg), NULL, 0); + if (res) + goto out; + + if (msg->msg != htons(iop_AccessoryInfo)) { + res = -EIO; + goto out; + } + + switch (ntohs(info->part_id)) { + case PART_ID_A01: + dev_dbg(&usbif->dev, "Found A01 part. (WARNING: errata exists!)\n"); + break; + case PART_ID_A02: + dev_dbg(&usbif->dev, "Found good A02 part.\n"); + break; + default: + dev_warn(&usbif->dev, "Unknown part ID %04X\n", ntohs(info->part_id)); + } + +out: + kfree(msg); + return res; +} + +static int +cp2615_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct usb_interface *usbif = adap->algo_data; + int i = 0, ret = 0; + struct i2c_msg *msg; + struct cp2615_i2c_transfer i2c_w = {0}; + + dev_dbg(&usbif->dev, "Doing %d I2C transactions\n", num); + + for (; !ret && i < num; i++) { + msg = &msgs[i]; + + i2c_w.tag = 0xdd; + i2c_w.i2caddr = i2c_8bit_addr_from_msg(msg); + if (msg->flags & I2C_M_RD) { + i2c_w.read_len = msg->len; + i2c_w.write_len = 0; + } else { + i2c_w.read_len = 0; + i2c_w.write_len = msg->len; + memcpy(&i2c_w.data, msg->buf, i2c_w.write_len); + } + ret = cp2615_i2c_send(usbif, &i2c_w); + if (ret) + break; + ret = cp2615_i2c_recv(usbif, i2c_w.tag, msg->buf); + } + if (ret < 0) + return ret; + return i; +} + +static u32 +cp2615_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm cp2615_i2c_algo = { + .master_xfer = cp2615_i2c_master_xfer, + .functionality = cp2615_i2c_func, +}; + +/* + * This chip has some limitations: one is that the USB endpoint + * can only receive 64 bytes/transfer, that leaves 54 bytes for + * the I2C transfer. On top of that, EITHER read_len OR write_len + * may be zero, but not both. If both are non-zero, the adapter + * issues a write followed by a read. And the chip does not + * support repeated START between the write and read phases. + */ +static struct i2c_adapter_quirks cp2615_i2c_quirks = { + .max_write_len = MAX_I2C_SIZE, + .max_read_len = MAX_I2C_SIZE, + .flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN | I2C_AQ_NO_REP_START, + .max_comb_1st_msg_len = MAX_I2C_SIZE, + .max_comb_2nd_msg_len = MAX_I2C_SIZE +}; + +static void +cp2615_i2c_remove(struct usb_interface *usbif) +{ + struct i2c_adapter *adap = usb_get_intfdata(usbif); + + usb_set_intfdata(usbif, NULL); + i2c_del_adapter(adap); +} + +static int +cp2615_i2c_probe(struct usb_interface *usbif, const struct usb_device_id *id) +{ + int ret = 0; + struct i2c_adapter *adap; + struct usb_device *usbdev = interface_to_usbdev(usbif); + + ret = usb_set_interface(usbdev, IOP_IFN, IOP_ALTSETTING); + if (ret) + return ret; + + ret = cp2615_check_iop(usbif); + if (ret) + return ret; + + adap = devm_kzalloc(&usbif->dev, sizeof(struct i2c_adapter), GFP_KERNEL); + if (!adap) + return -ENOMEM; + + strncpy(adap->name, usbdev->serial, sizeof(adap->name) - 1); + adap->owner = THIS_MODULE; + adap->dev.parent = &usbif->dev; + adap->dev.of_node = usbif->dev.of_node; + adap->timeout = HZ; + adap->algo = &cp2615_i2c_algo; + adap->quirks = &cp2615_i2c_quirks; + adap->algo_data = usbif; + + ret = i2c_add_adapter(adap); + if (ret) + return ret; + + usb_set_intfdata(usbif, adap); + return 0; +} + +static const struct usb_device_id id_table[] = { + { USB_DEVICE_INTERFACE_NUMBER(CP2615_VID, CP2615_PID, IOP_IFN) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, id_table); + +static struct usb_driver cp2615_i2c_driver = { + .name = "i2c-cp2615", + .probe = cp2615_i2c_probe, + .disconnect = cp2615_i2c_remove, + .id_table = id_table, +}; + +module_usb_driver(cp2615_i2c_driver); + +MODULE_AUTHOR("Bence Csókás <bence98@sch.bme.hu>"); +MODULE_DESCRIPTION("CP2615 I2C bus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index 3c19aada4b30..fdc34d9e3702 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -150,6 +150,9 @@ int i2c_dw_init_regmap(struct dw_i2c_dev *dev) reg = readl(dev->base + DW_IC_COMP_TYPE); i2c_dw_release_lock(dev); + if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) + map_cfg.max_register = AMD_UCSI_INTR_REG; + if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) { map_cfg.reg_read = dw_reg_read_swab; map_cfg.reg_write = dw_reg_write_swab; diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 5392b82f68a4..6a53f75abf7c 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -295,8 +295,16 @@ struct dw_i2c_dev { #define MODEL_MSCC_OCELOT BIT(8) #define MODEL_BAIKAL_BT1 BIT(9) +#define MODEL_AMD_NAVI_GPU BIT(10) #define MODEL_MASK GENMASK(11, 8) +/* + * Enable UCSI interrupt by writing 0xd at register + * offset 0x474 specified in hardware specification. + */ +#define AMD_UCSI_INTR_REG 0x474 +#define AMD_UCSI_INTR_EN 0xd + int i2c_dw_init_regmap(struct dw_i2c_dev *dev); u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset); u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset); diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 873ef38eb1c8..13be1d678c39 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -23,6 +23,10 @@ #include "i2c-designware-core.h" +#define AMD_TIMEOUT_MIN_US 25 +#define AMD_TIMEOUT_MAX_US 250 +#define AMD_MASTERCFG_MASK GENMASK(15, 0) + static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev) { /* Configure Tx/Rx FIFO threshold levels */ @@ -35,10 +39,10 @@ static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev) static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) { - const char *mode_str, *fp_str = ""; u32 comp_param1; u32 sda_falling_time, scl_falling_time; struct i2c_timings *t = &dev->timings; + const char *fp_str = ""; u32 ic_clk; int ret; @@ -78,7 +82,7 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) * difference is the timing parameter values since the registers are * the same. */ - if (t->bus_freq_hz == 1000000) { + if (t->bus_freq_hz == I2C_MAX_FAST_MODE_PLUS_FREQ) { /* * Check are Fast Mode Plus parameters available. Calculate * SCL timing parameters for Fast Mode Plus if not set. @@ -154,22 +158,10 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) ret = i2c_dw_set_sda_hold(dev); if (ret) - goto out; - - switch (dev->master_cfg & DW_IC_CON_SPEED_MASK) { - case DW_IC_CON_SPEED_STD: - mode_str = "Standard Mode"; - break; - case DW_IC_CON_SPEED_HIGH: - mode_str = "High Speed Mode"; - break; - default: - mode_str = "Fast Mode"; - } - dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str); + return ret; -out: - return ret; + dev_dbg(dev->dev, "Bus speed: %s\n", i2c_freq_mode_string(t->bus_freq_hz)); + return 0; } /** @@ -260,6 +252,108 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_MASTER_MASK); } +static int i2c_dw_check_stopbit(struct dw_i2c_dev *dev) +{ + u32 val; + int ret; + + ret = regmap_read_poll_timeout(dev->map, DW_IC_INTR_STAT, val, + !(val & DW_IC_INTR_STOP_DET), + 1100, 20000); + if (ret) + dev_err(dev->dev, "i2c timeout error %d\n", ret); + + return ret; +} + +static int i2c_dw_status(struct dw_i2c_dev *dev) +{ + int status; + + status = i2c_dw_wait_bus_not_busy(dev); + if (status) + return status; + + return i2c_dw_check_stopbit(dev); +} + +/* + * Initiate and continue master read/write transaction with polling + * based transfer routine afterward write messages into the Tx buffer. + */ +static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs, int num_msgs) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + int msg_wrt_idx, msg_itr_lmt, buf_len, data_idx; + int cmd = 0, status; + u8 *tx_buf; + u32 val; + + /* + * In order to enable the interrupt for UCSI i.e. AMD NAVI GPU card, + * it is mandatory to set the right value in specific register + * (offset:0x474) as per the hardware IP specification. + */ + regmap_write(dev->map, AMD_UCSI_INTR_REG, AMD_UCSI_INTR_EN); + + dev->msgs = msgs; + dev->msgs_num = num_msgs; + i2c_dw_xfer_init(dev); + i2c_dw_disable_int(dev); + + /* Initiate messages read/write transaction */ + for (msg_wrt_idx = 0; msg_wrt_idx < num_msgs; msg_wrt_idx++) { + tx_buf = msgs[msg_wrt_idx].buf; + buf_len = msgs[msg_wrt_idx].len; + + if (!(msgs[msg_wrt_idx].flags & I2C_M_RD)) + regmap_write(dev->map, DW_IC_TX_TL, buf_len - 1); + /* + * Initiate the i2c read/write transaction of buffer length, + * and poll for bus busy status. For the last message transfer, + * update the command with stopbit enable. + */ + for (msg_itr_lmt = buf_len; msg_itr_lmt > 0; msg_itr_lmt--) { + if (msg_wrt_idx == num_msgs - 1 && msg_itr_lmt == 1) + cmd |= BIT(9); + + if (msgs[msg_wrt_idx].flags & I2C_M_RD) { + /* Due to hardware bug, need to write the same command twice. */ + regmap_write(dev->map, DW_IC_DATA_CMD, 0x100); + regmap_write(dev->map, DW_IC_DATA_CMD, 0x100 | cmd); + if (cmd) { + regmap_write(dev->map, DW_IC_TX_TL, 2 * (buf_len - 1)); + regmap_write(dev->map, DW_IC_RX_TL, 2 * (buf_len - 1)); + /* + * Need to check the stop bit. However, it cannot be + * detected from the registers so we check it always + * when read/write the last byte. + */ + status = i2c_dw_status(dev); + if (status) + return status; + + for (data_idx = 0; data_idx < buf_len; data_idx++) { + regmap_read(dev->map, DW_IC_DATA_CMD, &val); + tx_buf[data_idx] = val; + } + status = i2c_dw_check_stopbit(dev); + if (status) + return status; + } + } else { + regmap_write(dev->map, DW_IC_DATA_CMD, *tx_buf++ | cmd); + usleep_range(AMD_TIMEOUT_MIN_US, AMD_TIMEOUT_MAX_US); + } + } + status = i2c_dw_check_stopbit(dev); + if (status) + return status; + } + + return 0; +} + /* * Initiate (and continue) low level master read/write transaction. * This function is only called from i2c_dw_isr, and pumping i2c_msg @@ -463,6 +557,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) pm_runtime_get_sync(dev->dev); + /* + * Initiate I2C message transfer when AMD NAVI GPU card is enabled, + * As it is polling based transfer mechanism, which does not support + * interrupt based functionalities of existing DesignWare driver. + */ + if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) { + ret = amd_i2c_dw_xfer_quirk(adap, msgs, num); + goto done_nolock; + } + if (dev_WARN_ONCE(dev->dev, dev->suspended, "Transfer while suspended\n")) { ret = -ESHUTDOWN; goto done_nolock; @@ -739,6 +843,20 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev) return 0; } +static int amd_i2c_adap_quirk(struct dw_i2c_dev *dev) +{ + struct i2c_adapter *adap = &dev->adapter; + int ret; + + pm_runtime_get_noresume(dev->dev); + ret = i2c_add_numbered_adapter(adap); + if (ret) + dev_err(dev->dev, "Failed to add adapter: %d\n", ret); + pm_runtime_put_noidle(dev->dev); + + return ret; +} + int i2c_dw_probe_master(struct dw_i2c_dev *dev) { struct i2c_adapter *adap = &dev->adapter; @@ -775,6 +893,9 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) adap->dev.parent = dev->dev; i2c_set_adapdata(adap, dev); + if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) + return amd_i2c_adap_quirk(dev); + if (dev->flags & ACCESS_NO_IRQ_SUSPEND) { irq_flags = IRQF_NO_SUSPEND; } else { diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 55c83a7a24f3..0f409a4c2da0 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -26,6 +26,7 @@ #include "i2c-designware-core.h" #define DRIVER_NAME "i2c-designware-pci" +#define AMD_CLK_RATE_HZ 100000 enum dw_pci_ctl_id_t { medfield, @@ -34,6 +35,7 @@ enum dw_pci_ctl_id_t { cherrytrail, haswell, elkhartlake, + navi_amd, }; struct dw_scl_sda_cfg { @@ -78,11 +80,23 @@ static struct dw_scl_sda_cfg hsw_config = { .sda_hold = 0x9, }; +/* NAVI-AMD HCNT/LCNT/SDA hold time */ +static struct dw_scl_sda_cfg navi_amd_config = { + .ss_hcnt = 0x1ae, + .ss_lcnt = 0x23a, + .sda_hold = 0x9, +}; + static u32 mfld_get_clk_rate_khz(struct dw_i2c_dev *dev) { return 25000; } +static u32 navi_amd_get_clk_rate_khz(struct dw_i2c_dev *dev) +{ + return AMD_CLK_RATE_HZ; +} + static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c) { struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev); @@ -104,6 +118,35 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c) return -ENODEV; } + /* + * TODO find a better way how to deduplicate instantiation + * of USB PD slave device from nVidia GPU driver. + */ +static int navi_amd_register_client(struct dw_i2c_dev *dev) +{ + struct i2c_board_info info; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strscpy(info.type, "ccgx-ucsi", I2C_NAME_SIZE); + info.addr = 0x08; + info.irq = dev->irq; + + dev->slave = i2c_new_client_device(&dev->adapter, &info); + if (IS_ERR(dev->slave)) + return PTR_ERR(dev->slave); + + return 0; +} + +static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c) +{ + struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev); + + dev->flags |= MODEL_AMD_NAVI_GPU; + dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ; + return 0; +} + static int mrfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c) { /* @@ -155,6 +198,12 @@ static struct dw_pci_controller dw_pci_controllers[] = { .bus_num = -1, .get_clk_rate_khz = ehl_get_clk_rate_khz, }, + [navi_amd] = { + .bus_num = -1, + .scl_sda_cfg = &navi_amd_config, + .setup = navi_amd_setup, + .get_clk_rate_khz = navi_amd_get_clk_rate_khz, + }, }; #ifdef CONFIG_PM @@ -274,6 +323,14 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev, return r; } + if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) { + r = navi_amd_register_client(dev); + if (r) { + dev_err(dev->dev, "register client failed with %d\n", r); + return r; + } + } + pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); @@ -337,6 +394,10 @@ static const struct pci_device_id i2_designware_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x4bbe), elkhartlake }, { PCI_VDEVICE(INTEL, 0x4bbf), elkhartlake }, { PCI_VDEVICE(INTEL, 0x4bc0), elkhartlake }, + { PCI_VDEVICE(ATI, 0x7314), navi_amd }, + { PCI_VDEVICE(ATI, 0x73a4), navi_amd }, + { PCI_VDEVICE(ATI, 0x73e4), navi_amd }, + { PCI_VDEVICE(ATI, 0x73c4), navi_amd }, { 0,} }; MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids); diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c index a08554c1a570..bdff0e6345d9 100644 --- a/drivers/i2c/busses/i2c-emev2.c +++ b/drivers/i2c/busses/i2c-emev2.c @@ -395,7 +395,10 @@ static int em_i2c_probe(struct platform_device *pdev) em_i2c_reset(&priv->adap); - priv->irq = platform_get_irq(pdev, 0); + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err_clk; + priv->irq = ret; ret = devm_request_irq(&pdev->dev, priv->irq, em_i2c_irq_handler, 0, "em_i2c", priv); if (ret) diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c new file mode 100644 index 000000000000..acf394812061 --- /dev/null +++ b/drivers/i2c/busses/i2c-hisi.c @@ -0,0 +1,504 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HiSilicon I2C Controller Driver for Kunpeng SoC + * + * Copyright (c) 2021 HiSilicon Technologies Co., Ltd. + */ + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/completion.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/property.h> + +#define HISI_I2C_FRAME_CTRL 0x0000 +#define HISI_I2C_FRAME_CTRL_SPEED_MODE GENMASK(1, 0) +#define HISI_I2C_FRAME_CTRL_ADDR_TEN BIT(2) +#define HISI_I2C_SLV_ADDR 0x0004 +#define HISI_I2C_SLV_ADDR_VAL GENMASK(9, 0) +#define HISI_I2C_SLV_ADDR_GC_S_MODE BIT(10) +#define HISI_I2C_SLV_ADDR_GC_S_EN BIT(11) +#define HISI_I2C_CMD_TXDATA 0x0008 +#define HISI_I2C_CMD_TXDATA_DATA GENMASK(7, 0) +#define HISI_I2C_CMD_TXDATA_RW BIT(8) +#define HISI_I2C_CMD_TXDATA_P_EN BIT(9) +#define HISI_I2C_CMD_TXDATA_SR_EN BIT(10) +#define HISI_I2C_RXDATA 0x000c +#define HISI_I2C_RXDATA_DATA GENMASK(7, 0) +#define HISI_I2C_SS_SCL_HCNT 0x0010 +#define HISI_I2C_SS_SCL_LCNT 0x0014 +#define HISI_I2C_FS_SCL_HCNT 0x0018 +#define HISI_I2C_FS_SCL_LCNT 0x001c +#define HISI_I2C_HS_SCL_HCNT 0x0020 +#define HISI_I2C_HS_SCL_LCNT 0x0024 +#define HISI_I2C_FIFO_CTRL 0x0028 +#define HISI_I2C_FIFO_RX_CLR BIT(0) +#define HISI_I2C_FIFO_TX_CLR BIT(1) +#define HISI_I2C_FIFO_RX_AF_THRESH GENMASK(7, 2) +#define HISI_I2C_FIFO_TX_AE_THRESH GENMASK(13, 8) +#define HISI_I2C_FIFO_STATE 0x002c +#define HISI_I2C_FIFO_STATE_RX_RERR BIT(0) +#define HISI_I2C_FIFO_STATE_RX_WERR BIT(1) +#define HISI_I2C_FIFO_STATE_RX_EMPTY BIT(3) +#define HISI_I2C_FIFO_STATE_TX_RERR BIT(6) +#define HISI_I2C_FIFO_STATE_TX_WERR BIT(7) +#define HISI_I2C_FIFO_STATE_TX_FULL BIT(11) +#define HISI_I2C_SDA_HOLD 0x0030 +#define HISI_I2C_SDA_HOLD_TX GENMASK(15, 0) +#define HISI_I2C_SDA_HOLD_RX GENMASK(23, 16) +#define HISI_I2C_FS_SPK_LEN 0x0038 +#define HISI_I2C_FS_SPK_LEN_CNT GENMASK(7, 0) +#define HISI_I2C_HS_SPK_LEN 0x003c +#define HISI_I2C_HS_SPK_LEN_CNT GENMASK(7, 0) +#define HISI_I2C_INT_MSTAT 0x0044 +#define HISI_I2C_INT_CLR 0x0048 +#define HISI_I2C_INT_MASK 0x004C +#define HISI_I2C_TRANS_STATE 0x0050 +#define HISI_I2C_TRANS_ERR 0x0054 +#define HISI_I2C_VERSION 0x0058 + +#define HISI_I2C_INT_ALL GENMASK(4, 0) +#define HISI_I2C_INT_TRANS_CPLT BIT(0) +#define HISI_I2C_INT_TRANS_ERR BIT(1) +#define HISI_I2C_INT_FIFO_ERR BIT(2) +#define HISI_I2C_INT_RX_FULL BIT(3) +#define HISI_I2C_INT_TX_EMPTY BIT(4) +#define HISI_I2C_INT_ERR \ + (HISI_I2C_INT_TRANS_ERR | HISI_I2C_INT_FIFO_ERR) + +#define HISI_I2C_STD_SPEED_MODE 0 +#define HISI_I2C_FAST_SPEED_MODE 1 +#define HISI_I2C_HIGH_SPEED_MODE 2 + +#define HISI_I2C_TX_FIFO_DEPTH 64 +#define HISI_I2C_RX_FIFO_DEPTH 64 +#define HISI_I2C_TX_F_AE_THRESH 1 +#define HISI_I2C_RX_F_AF_THRESH 60 + +#define HZ_PER_KHZ 1000 + +#define NSEC_TO_CYCLES(ns, clk_rate_khz) \ + DIV_ROUND_UP_ULL((clk_rate_khz) * (ns), NSEC_PER_MSEC) + +struct hisi_i2c_controller { + struct i2c_adapter adapter; + void __iomem *iobase; + struct device *dev; + int irq; + + /* Intermediates for recording the transfer process */ + struct completion *completion; + struct i2c_msg *msgs; + int msg_num; + int msg_tx_idx; + int buf_tx_idx; + int msg_rx_idx; + int buf_rx_idx; + u16 tar_addr; + u32 xfer_err; + + /* I2C bus configuration */ + struct i2c_timings t; + u32 clk_rate_khz; + u32 spk_len; +}; + +static void hisi_i2c_enable_int(struct hisi_i2c_controller *ctlr, u32 mask) +{ + writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_MASK); +} + +static void hisi_i2c_disable_int(struct hisi_i2c_controller *ctlr, u32 mask) +{ + writel_relaxed((~mask) & HISI_I2C_INT_ALL, ctlr->iobase + HISI_I2C_INT_MASK); +} + +static void hisi_i2c_clear_int(struct hisi_i2c_controller *ctlr, u32 mask) +{ + writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_CLR); +} + +static void hisi_i2c_handle_errors(struct hisi_i2c_controller *ctlr) +{ + u32 int_err = ctlr->xfer_err, reg; + + if (int_err & HISI_I2C_INT_FIFO_ERR) { + reg = readl(ctlr->iobase + HISI_I2C_FIFO_STATE); + + if (reg & HISI_I2C_FIFO_STATE_RX_RERR) + dev_err(ctlr->dev, "rx fifo error read\n"); + + if (reg & HISI_I2C_FIFO_STATE_RX_WERR) + dev_err(ctlr->dev, "rx fifo error write\n"); + + if (reg & HISI_I2C_FIFO_STATE_TX_RERR) + dev_err(ctlr->dev, "tx fifo error read\n"); + + if (reg & HISI_I2C_FIFO_STATE_TX_WERR) + dev_err(ctlr->dev, "tx fifo error write\n"); + } +} + +static int hisi_i2c_start_xfer(struct hisi_i2c_controller *ctlr) +{ + struct i2c_msg *msg = ctlr->msgs; + u32 reg; + + reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL); + reg &= ~HISI_I2C_FRAME_CTRL_ADDR_TEN; + if (msg->flags & I2C_M_TEN) + reg |= HISI_I2C_FRAME_CTRL_ADDR_TEN; + writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL); + + reg = readl(ctlr->iobase + HISI_I2C_SLV_ADDR); + reg &= ~HISI_I2C_SLV_ADDR_VAL; + reg |= FIELD_PREP(HISI_I2C_SLV_ADDR_VAL, msg->addr); + writel(reg, ctlr->iobase + HISI_I2C_SLV_ADDR); + + reg = readl(ctlr->iobase + HISI_I2C_FIFO_CTRL); + reg |= HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR; + writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL); + reg &= ~(HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR); + writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL); + + hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL); + hisi_i2c_enable_int(ctlr, HISI_I2C_INT_ALL); + + return 0; +} + +static void hisi_i2c_reset_xfer(struct hisi_i2c_controller *ctlr) +{ + ctlr->msg_num = 0; + ctlr->xfer_err = 0; + ctlr->msg_tx_idx = 0; + ctlr->msg_rx_idx = 0; + ctlr->buf_tx_idx = 0; + ctlr->buf_rx_idx = 0; +} + +/* + * Initialize the transfer information and start the I2C bus transfer. + * We only configure the transfer and do some pre/post works here, and + * wait for the transfer done. The major transfer process is performed + * in the IRQ handler. + */ +static int hisi_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct hisi_i2c_controller *ctlr = i2c_get_adapdata(adap); + DECLARE_COMPLETION_ONSTACK(done); + int ret = num; + + hisi_i2c_reset_xfer(ctlr); + ctlr->completion = &done; + ctlr->msg_num = num; + ctlr->msgs = msgs; + + hisi_i2c_start_xfer(ctlr); + + if (!wait_for_completion_timeout(ctlr->completion, adap->timeout)) { + hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL); + synchronize_irq(ctlr->irq); + i2c_recover_bus(&ctlr->adapter); + dev_err(ctlr->dev, "bus transfer timeout\n"); + ret = -EIO; + } + + if (ctlr->xfer_err) { + hisi_i2c_handle_errors(ctlr); + ret = -EIO; + } + + hisi_i2c_reset_xfer(ctlr); + ctlr->completion = NULL; + + return ret; +} + +static u32 hisi_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm hisi_i2c_algo = { + .master_xfer = hisi_i2c_master_xfer, + .functionality = hisi_i2c_functionality, +}; + +static int hisi_i2c_read_rx_fifo(struct hisi_i2c_controller *ctlr) +{ + struct i2c_msg *cur_msg; + u32 fifo_state; + + while (ctlr->msg_rx_idx < ctlr->msg_num) { + cur_msg = ctlr->msgs + ctlr->msg_rx_idx; + + if (!(cur_msg->flags & I2C_M_RD)) { + ctlr->msg_rx_idx++; + continue; + } + + fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE); + while (!(fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY) && + ctlr->buf_rx_idx < cur_msg->len) { + cur_msg->buf[ctlr->buf_rx_idx++] = readl(ctlr->iobase + HISI_I2C_RXDATA); + fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE); + } + + if (ctlr->buf_rx_idx == cur_msg->len) { + ctlr->buf_rx_idx = 0; + ctlr->msg_rx_idx++; + } + + if (fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY) + break; + } + + return 0; +} + +static void hisi_i2c_xfer_msg(struct hisi_i2c_controller *ctlr) +{ + int max_write = HISI_I2C_TX_FIFO_DEPTH; + bool need_restart = false, last_msg; + struct i2c_msg *cur_msg; + u32 cmd, fifo_state; + + while (ctlr->msg_tx_idx < ctlr->msg_num) { + cur_msg = ctlr->msgs + ctlr->msg_tx_idx; + last_msg = (ctlr->msg_tx_idx == ctlr->msg_num - 1); + + /* Signal the SR bit when we start transferring a new message */ + if (ctlr->msg_tx_idx && !ctlr->buf_tx_idx) + need_restart = true; + + fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE); + while (!(fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) && + ctlr->buf_tx_idx < cur_msg->len && max_write) { + cmd = 0; + + if (need_restart) { + cmd |= HISI_I2C_CMD_TXDATA_SR_EN; + need_restart = false; + } + + /* Signal the STOP bit at the last frame of the last message */ + if (ctlr->buf_tx_idx == cur_msg->len - 1 && last_msg) + cmd |= HISI_I2C_CMD_TXDATA_P_EN; + + if (cur_msg->flags & I2C_M_RD) + cmd |= HISI_I2C_CMD_TXDATA_RW; + else + cmd |= FIELD_PREP(HISI_I2C_CMD_TXDATA_DATA, + cur_msg->buf[ctlr->buf_tx_idx]); + + writel(cmd, ctlr->iobase + HISI_I2C_CMD_TXDATA); + ctlr->buf_tx_idx++; + max_write--; + + fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE); + } + + /* Update the transfer index after per message transfer is done. */ + if (ctlr->buf_tx_idx == cur_msg->len) { + ctlr->buf_tx_idx = 0; + ctlr->msg_tx_idx++; + } + + if ((fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) || + max_write == 0) + break; + } +} + +static irqreturn_t hisi_i2c_irq(int irq, void *context) +{ + struct hisi_i2c_controller *ctlr = context; + u32 int_stat; + + int_stat = readl(ctlr->iobase + HISI_I2C_INT_MSTAT); + hisi_i2c_clear_int(ctlr, int_stat); + if (!(int_stat & HISI_I2C_INT_ALL)) + return IRQ_NONE; + + if (int_stat & HISI_I2C_INT_TX_EMPTY) + hisi_i2c_xfer_msg(ctlr); + + if (int_stat & HISI_I2C_INT_ERR) { + ctlr->xfer_err = int_stat; + goto out; + } + + /* Drain the rx fifo before finish the transfer */ + if (int_stat & (HISI_I2C_INT_TRANS_CPLT | HISI_I2C_INT_RX_FULL)) + hisi_i2c_read_rx_fifo(ctlr); + +out: + if (int_stat & HISI_I2C_INT_TRANS_CPLT || ctlr->xfer_err) { + hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL); + hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL); + complete(ctlr->completion); + } + + return IRQ_HANDLED; +} + +/* + * Helper function for calculating and configuring the HIGH and LOW + * periods of SCL clock. The caller will pass the ratio of the + * counts (divide / divisor) according to the target speed mode, + * and the target registers. + */ +static void hisi_i2c_set_scl(struct hisi_i2c_controller *ctlr, + u32 divide, u32 divisor, + u32 reg_hcnt, u32 reg_lcnt) +{ + u32 total_cnt, t_scl_hcnt, t_scl_lcnt, scl_fall_cnt, scl_rise_cnt; + u32 scl_hcnt, scl_lcnt; + + /* Total SCL clock cycles per speed period */ + total_cnt = DIV_ROUND_UP_ULL(ctlr->clk_rate_khz * HZ_PER_KHZ, ctlr->t.bus_freq_hz); + /* Total HIGH level SCL clock cycles including edges */ + t_scl_hcnt = DIV_ROUND_UP_ULL(total_cnt * divide, divisor); + /* Total LOW level SCL clock cycles including edges */ + t_scl_lcnt = total_cnt - t_scl_hcnt; + /* Fall edge SCL clock cycles */ + scl_fall_cnt = NSEC_TO_CYCLES(ctlr->t.scl_fall_ns, ctlr->clk_rate_khz); + /* Rise edge SCL clock cycles */ + scl_rise_cnt = NSEC_TO_CYCLES(ctlr->t.scl_rise_ns, ctlr->clk_rate_khz); + + /* Calculated HIGH and LOW periods of SCL clock */ + scl_hcnt = t_scl_hcnt - ctlr->spk_len - 7 - scl_fall_cnt; + scl_lcnt = t_scl_lcnt - 1 - scl_rise_cnt; + + writel(scl_hcnt, ctlr->iobase + reg_hcnt); + writel(scl_lcnt, ctlr->iobase + reg_lcnt); +} + +static void hisi_i2c_configure_bus(struct hisi_i2c_controller *ctlr) +{ + u32 reg, sda_hold_cnt, speed_mode; + + i2c_parse_fw_timings(ctlr->dev, &ctlr->t, true); + ctlr->spk_len = NSEC_TO_CYCLES(ctlr->t.digital_filter_width_ns, ctlr->clk_rate_khz); + + switch (ctlr->t.bus_freq_hz) { + case I2C_MAX_FAST_MODE_FREQ: + speed_mode = HISI_I2C_FAST_SPEED_MODE; + hisi_i2c_set_scl(ctlr, 26, 76, HISI_I2C_FS_SCL_HCNT, HISI_I2C_FS_SCL_LCNT); + break; + case I2C_MAX_HIGH_SPEED_MODE_FREQ: + speed_mode = HISI_I2C_HIGH_SPEED_MODE; + hisi_i2c_set_scl(ctlr, 6, 22, HISI_I2C_HS_SCL_HCNT, HISI_I2C_HS_SCL_LCNT); + break; + case I2C_MAX_STANDARD_MODE_FREQ: + default: + speed_mode = HISI_I2C_STD_SPEED_MODE; + + /* For default condition force the bus speed to standard mode. */ + ctlr->t.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ; + hisi_i2c_set_scl(ctlr, 40, 87, HISI_I2C_SS_SCL_HCNT, HISI_I2C_SS_SCL_LCNT); + break; + } + + reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL); + reg &= ~HISI_I2C_FRAME_CTRL_SPEED_MODE; + reg |= FIELD_PREP(HISI_I2C_FRAME_CTRL_SPEED_MODE, speed_mode); + writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL); + + sda_hold_cnt = NSEC_TO_CYCLES(ctlr->t.sda_hold_ns, ctlr->clk_rate_khz); + + reg = FIELD_PREP(HISI_I2C_SDA_HOLD_TX, sda_hold_cnt); + writel(reg, ctlr->iobase + HISI_I2C_SDA_HOLD); + + writel(ctlr->spk_len, ctlr->iobase + HISI_I2C_FS_SPK_LEN); + + reg = FIELD_PREP(HISI_I2C_FIFO_RX_AF_THRESH, HISI_I2C_RX_F_AF_THRESH); + reg |= FIELD_PREP(HISI_I2C_FIFO_TX_AE_THRESH, HISI_I2C_TX_F_AE_THRESH); + writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL); +} + +static int hisi_i2c_probe(struct platform_device *pdev) +{ + struct hisi_i2c_controller *ctlr; + struct device *dev = &pdev->dev; + struct i2c_adapter *adapter; + u64 clk_rate_hz; + u32 hw_version; + int ret; + + ctlr = devm_kzalloc(dev, sizeof(*ctlr), GFP_KERNEL); + if (!ctlr) + return -ENOMEM; + + ctlr->iobase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ctlr->iobase)) + return PTR_ERR(ctlr->iobase); + + ctlr->irq = platform_get_irq(pdev, 0); + if (ctlr->irq < 0) + return ctlr->irq; + + ctlr->dev = dev; + + hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL); + + ret = devm_request_irq(dev, ctlr->irq, hisi_i2c_irq, 0, "hisi-i2c", ctlr); + if (ret) { + dev_err(dev, "failed to request irq handler, ret = %d\n", ret); + return ret; + } + + ret = device_property_read_u64(dev, "clk_rate", &clk_rate_hz); + if (ret) { + dev_err(dev, "failed to get clock frequency, ret = %d\n", ret); + return ret; + } + + ctlr->clk_rate_khz = DIV_ROUND_UP_ULL(clk_rate_hz, HZ_PER_KHZ); + + hisi_i2c_configure_bus(ctlr); + + adapter = &ctlr->adapter; + snprintf(adapter->name, sizeof(adapter->name), + "HiSilicon I2C Controller %s", dev_name(dev)); + adapter->owner = THIS_MODULE; + adapter->algo = &hisi_i2c_algo; + adapter->dev.parent = dev; + i2c_set_adapdata(adapter, ctlr); + + ret = devm_i2c_add_adapter(dev, adapter); + if (ret) + return ret; + + hw_version = readl(ctlr->iobase + HISI_I2C_VERSION); + dev_info(ctlr->dev, "speed mode is %s. hw version 0x%x\n", + i2c_freq_mode_string(ctlr->t.bus_freq_hz), hw_version); + + return 0; +} + +static const struct acpi_device_id hisi_i2c_acpi_ids[] = { + { "HISI03D1", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hisi_i2c_acpi_ids); + +static struct platform_driver hisi_i2c_driver = { + .probe = hisi_i2c_probe, + .driver = { + .name = "hisi-i2c", + .acpi_match_table = hisi_i2c_acpi_ids, + }, +}; +module_platform_driver(hisi_i2c_driver); + +MODULE_AUTHOR("Yicong Yang <yangyicong@hisilicon.com>"); +MODULE_DESCRIPTION("HiSilicon I2C Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 4acee6f9e5a3..99d446763530 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -73,6 +73,7 @@ * Comet Lake-V (PCH) 0xa3a3 32 hard yes yes yes * Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes * Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes + * Alder Lake-M (PCH) 0x54a3 32 hard yes yes yes * * Features supported by this driver: * Software PEC no @@ -230,6 +231,7 @@ #define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23 #define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS 0x4da3 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS 0x51a3 +#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS 0x54a3 #define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS 0x7aa3 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 @@ -1087,6 +1089,7 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS) }, { 0, } }; @@ -1771,6 +1774,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) case PCI_DEVICE_ID_INTEL_EBG_SMBUS: case PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS: case PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS: + case PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS: priv->features |= FEATURE_BLOCK_PROC; priv->features |= FEATURE_I2C_BLOCK_READ; priv->features |= FEATURE_IRQ; diff --git a/drivers/i2c/busses/i2c-icy.c b/drivers/i2c/busses/i2c-icy.c index 66c9923fc766..c8c422e9dda4 100644 --- a/drivers/i2c/busses/i2c-icy.c +++ b/drivers/i2c/busses/i2c-icy.c @@ -54,7 +54,6 @@ struct icy_i2c { void __iomem *reg_s0; void __iomem *reg_s1; - struct fwnode_handle *ltc2990_fwnode; struct i2c_client *ltc2990_client; }; @@ -115,6 +114,10 @@ static const struct property_entry icy_ltc2990_props[] = { { } }; +static const struct software_node icy_ltc2990_node = { + .properties = icy_ltc2990_props, +}; + static int icy_probe(struct zorro_dev *z, const struct zorro_device_id *ent) { @@ -123,6 +126,7 @@ static int icy_probe(struct zorro_dev *z, struct fwnode_handle *new_fwnode; struct i2c_board_info ltc2990_info = { .type = "ltc2990", + .swnode = &icy_ltc2990_node, }; i2c = devm_kzalloc(&z->dev, sizeof(*i2c), GFP_KERNEL); @@ -174,26 +178,10 @@ static int icy_probe(struct zorro_dev *z, * * See property_entry above for in1, in2, temp3. */ - new_fwnode = fwnode_create_software_node(icy_ltc2990_props, NULL); - if (IS_ERR(new_fwnode)) { - dev_info(&z->dev, "Failed to create fwnode for LTC2990, error: %ld\n", - PTR_ERR(new_fwnode)); - } else { - /* - * Store the fwnode so we can destroy it on .remove(). - * Only store it on success, as fwnode_remove_software_node() - * is NULL safe, but not PTR_ERR safe. - */ - i2c->ltc2990_fwnode = new_fwnode; - ltc2990_info.fwnode = new_fwnode; - - i2c->ltc2990_client = - i2c_new_scanned_device(&i2c->adapter, - <c2990_info, - icy_ltc2990_addresses, - NULL); - } - + i2c->ltc2990_client = i2c_new_scanned_device(&i2c->adapter, + <c2990_info, + icy_ltc2990_addresses, + NULL); return 0; } @@ -202,8 +190,6 @@ static void icy_remove(struct zorro_dev *z) struct icy_i2c *i2c = dev_get_drvdata(&z->dev); i2c_unregister_device(i2c->ltc2990_client); - fwnode_remove_software_node(i2c->ltc2990_fwnode); - i2c_del_adapter(&i2c->adapter); } diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c index 98a89301ed2a..8e987945ed45 100644 --- a/drivers/i2c/busses/i2c-img-scb.c +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -1057,7 +1057,7 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, atomic = true; } - ret = pm_runtime_get_sync(adap->dev.parent); + ret = pm_runtime_resume_and_get(adap->dev.parent); if (ret < 0) return ret; @@ -1158,7 +1158,7 @@ static int img_i2c_init(struct img_i2c *i2c) u32 rev; int ret; - ret = pm_runtime_get_sync(i2c->adap.dev.parent); + ret = pm_runtime_resume_and_get(i2c->adap.dev.parent); if (ret < 0) return ret; diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index 9db6ccded5e9..8b9ba055c418 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -259,7 +259,7 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx) unsigned int temp; int ret; - ret = pm_runtime_get_sync(lpi2c_imx->adapter.dev.parent); + ret = pm_runtime_resume_and_get(lpi2c_imx->adapter.dev.parent); if (ret < 0) return ret; diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index b80fdc1f0092..dc5ca71906db 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -801,7 +801,7 @@ static int i2c_imx_reg_slave(struct i2c_client *client) i2c_imx->last_slave_event = I2C_SLAVE_STOP; /* Resume */ - ret = pm_runtime_get_sync(i2c_imx->adapter.dev.parent); + ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent); if (ret < 0) { dev_err(&i2c_imx->adapter.dev, "failed to resume i2c controller"); return ret; @@ -1253,7 +1253,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter); int result; - result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent); + result = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent); if (result < 0) return result; @@ -1496,7 +1496,7 @@ static int i2c_imx_remove(struct platform_device *pdev) struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev); int irq, ret; - ret = pm_runtime_get_sync(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) return ret; diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index 2f8b8050a223..cfecaf18ccbb 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -125,10 +125,12 @@ iop3xx_i2c_error(u32 sr) int rc = 0; if ((sr & IOP3XX_ISR_BERRD)) { - if ( !rc ) rc = -I2C_ERR_BERR; + if (!rc) + rc = -I2C_ERR_BERR; } if ((sr & IOP3XX_ISR_ALD)) { - if ( !rc ) rc = -I2C_ERR_ALD; + if (!rc) + rc = -I2C_ERR_ALD; } return rc; } @@ -151,12 +153,12 @@ iop3xx_i2c_get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap) * sleep until interrupted, then recover and analyse the SR * saved by handler */ -typedef int (* compare_func)(unsigned test, unsigned mask); +typedef int (*compare_func)(unsigned test, unsigned mask); /* returns 1 on correct comparison */ static int iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap, - unsigned flags, unsigned* status, + unsigned flags, unsigned *status, compare_func compare) { unsigned sr = 0; @@ -167,7 +169,7 @@ iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap, do { interrupted = wait_event_interruptible_timeout ( iop3xx_adap->waitq, - (done = compare( sr = iop3xx_i2c_get_srstat(iop3xx_adap) ,flags )), + (done = compare(sr = iop3xx_i2c_get_srstat(iop3xx_adap), flags)), 1 * HZ ); if ((rc = iop3xx_i2c_error(sr)) < 0) { @@ -177,7 +179,7 @@ iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap, *status = sr; return -ETIMEDOUT; } - } while(!done); + } while (!done); *status = sr; @@ -204,7 +206,7 @@ iop3xx_i2c_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) { return iop3xx_i2c_wait_event( iop3xx_adap, - IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD, + IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD, status, any_bits_set); } @@ -226,7 +228,7 @@ iop3xx_i2c_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status) static int iop3xx_i2c_send_target_addr(struct i2c_algo_iop3xx_data *iop3xx_adap, - struct i2c_msg* msg) + struct i2c_msg *msg) { unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); int status; @@ -273,7 +275,7 @@ iop3xx_i2c_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte, } static int -iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char* byte, +iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char *byte, int stop) { unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET); @@ -305,7 +307,7 @@ iop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, const char *buf, int count) int rc = 0; for (ii = 0; rc == 0 && ii != count; ++ii) - rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii==count-1); + rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii == count-1); return rc; } @@ -317,7 +319,7 @@ iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) int rc = 0; for (ii = 0; rc == 0 && ii != count; ++ii) - rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii==count-1); + rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii == count-1); return rc; } @@ -330,7 +332,7 @@ iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) * condition. */ static int -iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg) +iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg *pmsg) { struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data; int rc; @@ -369,7 +371,7 @@ iop3xx_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, iop3xx_i2c_transaction_cleanup(iop3xx_adap); - if(ret) + if (ret) return ret; return im; diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c index 55177eb21d7b..baa7319eee53 100644 --- a/drivers/i2c/busses/i2c-jz4780.c +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -825,7 +825,10 @@ static int jz4780_i2c_probe(struct platform_device *pdev) jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0x0); - i2c->irq = platform_get_irq(pdev, 0); + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err; + i2c->irq = ret; ret = devm_request_irq(&pdev->dev, i2c->irq, jz4780_i2c_irq, 0, dev_name(&pdev->dev), i2c); if (ret) diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c index 2fb0532d8a16..8716032f030a 100644 --- a/drivers/i2c/busses/i2c-mlxbf.c +++ b/drivers/i2c/busses/i2c-mlxbf.c @@ -172,12 +172,6 @@ #define MLXBF_I2C_SMBUS_THIGH_MAX_TBUF 0x14 #define MLXBF_I2C_SMBUS_SCL_LOW_TIMEOUT 0x18 -enum { - MLXBF_I2C_TIMING_100KHZ = 100000, - MLXBF_I2C_TIMING_400KHZ = 400000, - MLXBF_I2C_TIMING_1000KHZ = 1000000, -}; - /* * Defines SMBus operating frequency and core clock frequency. * According to ADB files, default values are compliant to 100KHz SMBus @@ -1202,7 +1196,7 @@ static int mlxbf_i2c_init_timings(struct platform_device *pdev, ret = device_property_read_u32(dev, "clock-frequency", &config_khz); if (ret < 0) - config_khz = MLXBF_I2C_TIMING_100KHZ; + config_khz = I2C_MAX_STANDARD_MODE_FREQ; switch (config_khz) { default: @@ -1210,15 +1204,15 @@ static int mlxbf_i2c_init_timings(struct platform_device *pdev, pr_warn("Illegal value %d: defaulting to 100 KHz\n", config_khz); fallthrough; - case MLXBF_I2C_TIMING_100KHZ: + case I2C_MAX_STANDARD_MODE_FREQ: config_idx = MLXBF_I2C_TIMING_CONFIG_100KHZ; break; - case MLXBF_I2C_TIMING_400KHZ: + case I2C_MAX_FAST_MODE_FREQ: config_idx = MLXBF_I2C_TIMING_CONFIG_400KHZ; break; - case MLXBF_I2C_TIMING_1000KHZ: + case I2C_MAX_FAST_MODE_PLUS_FREQ: config_idx = MLXBF_I2C_TIMING_CONFIG_1000KHZ; break; } @@ -2376,6 +2370,8 @@ static int mlxbf_i2c_probe(struct platform_device *pdev) mlxbf_i2c_init_slave(pdev, priv); irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; ret = devm_request_irq(dev, irq, mlxbf_smbus_irq, IRQF_ONESHOT | IRQF_SHARED | IRQF_PROBE_SHARED, dev_name(dev), priv); diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index d94f05c8b8b7..30d9e89a3db2 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -1,16 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * (C) Copyright 2003-2004 - * Humboldt Solutions Ltd, adrian@humboldt.co.uk. - * This is a combined i2c adapter and algorithm driver for the * MPC107/Tsi107 PowerPC northbridge and processors that include * the same I2C unit (8240, 8245, 85xx). * - * Release 0.8 - * - * 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. + * Copyright (C) 2003-2004 Humboldt Solutions Ltd, adrian@humboldt.co.uk + * Copyright (C) 2021 Allied Telesis Labs */ #include <linux/kernel.h> @@ -19,6 +14,7 @@ #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_platform.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/clk.h> @@ -58,18 +54,50 @@ #define CSR_MIF 0x02 #define CSR_RXAK 0x01 +enum mpc_i2c_action { + MPC_I2C_ACTION_START = 1, + MPC_I2C_ACTION_RESTART, + MPC_I2C_ACTION_READ_BEGIN, + MPC_I2C_ACTION_READ_BYTE, + MPC_I2C_ACTION_WRITE, + MPC_I2C_ACTION_STOP, + + __MPC_I2C_ACTION_CNT +}; + +static const char * const action_str[] = { + "invalid", + "start", + "restart", + "read begin", + "read", + "write", + "stop", +}; + +static_assert(ARRAY_SIZE(action_str) == __MPC_I2C_ACTION_CNT); + struct mpc_i2c { struct device *dev; void __iomem *base; u32 interrupt; - wait_queue_head_t queue; + wait_queue_head_t waitq; + spinlock_t lock; struct i2c_adapter adap; int irq; u32 real_clk; -#ifdef CONFIG_PM_SLEEP u8 fdr, dfsrr; -#endif struct clk *clk_per; + u32 cntl_bits; + enum mpc_i2c_action action; + struct i2c_msg *msgs; + int num_msgs; + int curr_msg; + u32 byte_posn; + u32 block; + int rc; + int expect_rxack; + }; struct mpc_i2c_divider { @@ -86,19 +114,6 @@ static inline void writeccr(struct mpc_i2c *i2c, u32 x) writeb(x, i2c->base + MPC_I2C_CR); } -static irqreturn_t mpc_i2c_isr(int irq, void *dev_id) -{ - struct mpc_i2c *i2c = dev_id; - if (readb(i2c->base + MPC_I2C_SR) & CSR_MIF) { - /* Read again to allow register to stabilise */ - i2c->interrupt = readb(i2c->base + MPC_I2C_SR); - writeb(0, i2c->base + MPC_I2C_SR); - wake_up(&i2c->queue); - return IRQ_HANDLED; - } - return IRQ_NONE; -} - /* Sometimes 9th clock pulse isn't generated, and slave doesn't release * the bus, because it wants to send ACK. * Following sequence of enabling/disabling and sending start/stop generates @@ -121,61 +136,6 @@ static void mpc_i2c_fixup(struct mpc_i2c *i2c) } } -static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing) -{ - unsigned long orig_jiffies = jiffies; - u32 cmd_err; - int result = 0; - - if (!i2c->irq) { - while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) { - schedule(); - if (time_after(jiffies, orig_jiffies + timeout)) { - dev_dbg(i2c->dev, "timeout\n"); - writeccr(i2c, 0); - result = -ETIMEDOUT; - break; - } - } - cmd_err = readb(i2c->base + MPC_I2C_SR); - writeb(0, i2c->base + MPC_I2C_SR); - } else { - /* Interrupt mode */ - result = wait_event_timeout(i2c->queue, - (i2c->interrupt & CSR_MIF), timeout); - - if (unlikely(!(i2c->interrupt & CSR_MIF))) { - dev_dbg(i2c->dev, "wait timeout\n"); - writeccr(i2c, 0); - result = -ETIMEDOUT; - } - - cmd_err = i2c->interrupt; - i2c->interrupt = 0; - } - - if (result < 0) - return result; - - if (!(cmd_err & CSR_MCF)) { - dev_dbg(i2c->dev, "unfinished\n"); - return -EIO; - } - - if (cmd_err & CSR_MAL) { - dev_dbg(i2c->dev, "MAL\n"); - return -EAGAIN; - } - - if (writing && (cmd_err & CSR_RXAK)) { - dev_dbg(i2c->dev, "No RXAK\n"); - /* generate stop */ - writeccr(i2c, CCR_MEN); - return -ENXIO; - } - return 0; -} - #if defined(CONFIG_PPC_MPC52xx) || defined(CONFIG_PPC_MPC512x) static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = { {20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23}, @@ -415,7 +375,7 @@ static int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, } *real_clk = fsl_get_sys_freq() / prescaler / div->divider; - return div ? (int)div->fdr : -EINVAL; + return (int)div->fdr; } static void mpc_i2c_setup_8xxx(struct device_node *node, @@ -450,168 +410,209 @@ static void mpc_i2c_setup_8xxx(struct device_node *node, } #endif /* CONFIG_FSL_SOC */ -static void mpc_i2c_start(struct mpc_i2c *i2c) +static void mpc_i2c_finish(struct mpc_i2c *i2c, int rc) { - /* Clear arbitration */ - writeb(0, i2c->base + MPC_I2C_SR); - /* Start with MEN */ - writeccr(i2c, CCR_MEN); + i2c->rc = rc; + i2c->block = 0; + i2c->cntl_bits = CCR_MEN; + writeccr(i2c, i2c->cntl_bits); + wake_up(&i2c->waitq); } -static void mpc_i2c_stop(struct mpc_i2c *i2c) +static void mpc_i2c_do_action(struct mpc_i2c *i2c) { - writeccr(i2c, CCR_MEN); -} + struct i2c_msg *msg = &i2c->msgs[i2c->curr_msg]; + int dir = 0; + int recv_len = 0; + u8 byte; + + dev_dbg(i2c->dev, "action = %s\n", action_str[i2c->action]); + + i2c->cntl_bits &= ~(CCR_RSTA | CCR_MTX | CCR_TXAK); + + if (msg->flags & I2C_M_RD) + dir = 1; + if (msg->flags & I2C_M_RECV_LEN) + recv_len = 1; + + switch (i2c->action) { + case MPC_I2C_ACTION_RESTART: + i2c->cntl_bits |= CCR_RSTA; + fallthrough; + + case MPC_I2C_ACTION_START: + i2c->cntl_bits |= CCR_MSTA | CCR_MTX; + writeccr(i2c, i2c->cntl_bits); + writeb((msg->addr << 1) | dir, i2c->base + MPC_I2C_DR); + i2c->expect_rxack = 1; + i2c->action = dir ? MPC_I2C_ACTION_READ_BEGIN : MPC_I2C_ACTION_WRITE; + break; + + case MPC_I2C_ACTION_READ_BEGIN: + if (msg->len) { + if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN)) + i2c->cntl_bits |= CCR_TXAK; + + writeccr(i2c, i2c->cntl_bits); + /* Dummy read */ + readb(i2c->base + MPC_I2C_DR); + } + i2c->action = MPC_I2C_ACTION_READ_BYTE; + break; + + case MPC_I2C_ACTION_READ_BYTE: + if (i2c->byte_posn || !recv_len) { + /* Generate Tx ACK on next to last byte */ + if (i2c->byte_posn == msg->len - 2) + i2c->cntl_bits |= CCR_TXAK; + /* Do not generate stop on last byte */ + if (i2c->byte_posn == msg->len - 1) + i2c->cntl_bits |= CCR_MTX; -static int mpc_write(struct mpc_i2c *i2c, int target, - const u8 *data, int length, int restart) -{ - int i, result; - unsigned timeout = i2c->adap.timeout; - u32 flags = restart ? CCR_RSTA : 0; + writeccr(i2c, i2c->cntl_bits); + } - /* Start as master */ - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags); - /* Write target byte */ - writeb((target << 1), i2c->base + MPC_I2C_DR); + byte = readb(i2c->base + MPC_I2C_DR); - result = i2c_wait(i2c, timeout, 1); - if (result < 0) - return result; + if (i2c->byte_posn == 0 && recv_len) { + if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX) { + mpc_i2c_finish(i2c, -EPROTO); + return; + } + msg->len += byte; + /* + * For block reads, generate Tx ACK here if data length + * is 1 byte (total length is 2 bytes). + */ + if (msg->len == 2) { + i2c->cntl_bits |= CCR_TXAK; + writeccr(i2c, i2c->cntl_bits); + } + } + + dev_dbg(i2c->dev, "%s %02x\n", action_str[i2c->action], byte); + msg->buf[i2c->byte_posn++] = byte; + break; + + case MPC_I2C_ACTION_WRITE: + dev_dbg(i2c->dev, "%s %02x\n", action_str[i2c->action], + msg->buf[i2c->byte_posn]); + writeb(msg->buf[i2c->byte_posn++], i2c->base + MPC_I2C_DR); + i2c->expect_rxack = 1; + break; - for (i = 0; i < length; i++) { - /* Write data byte */ - writeb(data[i], i2c->base + MPC_I2C_DR); + case MPC_I2C_ACTION_STOP: + mpc_i2c_finish(i2c, 0); + break; - result = i2c_wait(i2c, timeout, 1); - if (result < 0) - return result; + default: + WARN(1, "Unexpected action %d\n", i2c->action); + break; } - return 0; + if (msg->len == i2c->byte_posn) { + i2c->curr_msg++; + i2c->byte_posn = 0; + + if (i2c->curr_msg == i2c->num_msgs) { + i2c->action = MPC_I2C_ACTION_STOP; + /* + * We don't get another interrupt on read so + * finish the transfer now + */ + if (dir) + mpc_i2c_finish(i2c, 0); + } else { + i2c->action = MPC_I2C_ACTION_RESTART; + } + } } -static int mpc_read(struct mpc_i2c *i2c, int target, - u8 *data, int length, int restart, bool recv_len) +static void mpc_i2c_do_intr(struct mpc_i2c *i2c, u8 status) { - unsigned timeout = i2c->adap.timeout; - int i, result; - u32 flags = restart ? CCR_RSTA : 0; + spin_lock(&i2c->lock); - /* Switch to read - restart */ - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags); - /* Write target address byte - this time with the read flag set */ - writeb((target << 1) | 1, i2c->base + MPC_I2C_DR); + if (!(status & CSR_MCF)) { + dev_dbg(i2c->dev, "unfinished\n"); + mpc_i2c_finish(i2c, -EIO); + goto out; + } - result = i2c_wait(i2c, timeout, 1); - if (result < 0) - return result; + if (status & CSR_MAL) { + dev_dbg(i2c->dev, "arbitration lost\n"); + mpc_i2c_finish(i2c, -EAGAIN); + goto out; + } - if (length) { - if (length == 1 && !recv_len) - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK); - else - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA); - /* Dummy read */ - readb(i2c->base + MPC_I2C_DR); + if (i2c->expect_rxack && (status & CSR_RXAK)) { + dev_dbg(i2c->dev, "no Rx ACK\n"); + mpc_i2c_finish(i2c, -ENXIO); + goto out; } + i2c->expect_rxack = 0; - for (i = 0; i < length; i++) { - u8 byte; - - result = i2c_wait(i2c, timeout, 0); - if (result < 0) - return result; - - /* - * For block reads, we have to know the total length (1st byte) - * before we can determine if we are done. - */ - if (i || !recv_len) { - /* Generate txack on next to last byte */ - if (i == length - 2) - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA - | CCR_TXAK); - /* Do not generate stop on last byte */ - if (i == length - 1) - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA - | CCR_MTX); - } + mpc_i2c_do_action(i2c); - byte = readb(i2c->base + MPC_I2C_DR); +out: + spin_unlock(&i2c->lock); +} - /* - * Adjust length if first received byte is length. - * The length is 1 length byte plus actually data length - */ - if (i == 0 && recv_len) { - if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX) - return -EPROTO; - length += byte; - /* - * For block reads, generate txack here if data length - * is 1 byte (total length is 2 bytes). - */ - if (length == 2) - writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA - | CCR_TXAK); - } - data[i] = byte; +static irqreturn_t mpc_i2c_isr(int irq, void *dev_id) +{ + struct mpc_i2c *i2c = dev_id; + u8 status; + + status = readb(i2c->base + MPC_I2C_SR); + if (status & CSR_MIF) { + writeb(0, i2c->base + MPC_I2C_SR); + mpc_i2c_do_intr(i2c, status); + return IRQ_HANDLED; } + return IRQ_NONE; +} - return length; +static int mpc_i2c_wait_for_completion(struct mpc_i2c *i2c) +{ + long time_left; + + time_left = wait_event_timeout(i2c->waitq, !i2c->block, i2c->adap.timeout); + if (!time_left) + return -ETIMEDOUT; + if (time_left < 0) + return time_left; + + return 0; } -static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +static int mpc_i2c_execute_msg(struct mpc_i2c *i2c) { - struct i2c_msg *pmsg; - int i; - int ret = 0; - unsigned long orig_jiffies = jiffies; - struct mpc_i2c *i2c = i2c_get_adapdata(adap); + unsigned long orig_jiffies; + unsigned long flags; + int ret; - mpc_i2c_start(i2c); + spin_lock_irqsave(&i2c->lock, flags); - /* Allow bus up to 1s to become not busy */ - while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) { - if (signal_pending(current)) { - dev_dbg(i2c->dev, "Interrupted\n"); - writeccr(i2c, 0); - return -EINTR; - } - if (time_after(jiffies, orig_jiffies + HZ)) { - u8 status = readb(i2c->base + MPC_I2C_SR); + i2c->curr_msg = 0; + i2c->rc = 0; + i2c->byte_posn = 0; + i2c->block = 1; + i2c->action = MPC_I2C_ACTION_START; - dev_dbg(i2c->dev, "timeout\n"); - if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) { - writeb(status & ~CSR_MAL, - i2c->base + MPC_I2C_SR); - mpc_i2c_fixup(i2c); - } - return -EIO; - } - schedule(); - } + i2c->cntl_bits = CCR_MEN | CCR_MIEN; + writeb(0, i2c->base + MPC_I2C_SR); + writeccr(i2c, i2c->cntl_bits); + + mpc_i2c_do_action(i2c); + + spin_unlock_irqrestore(&i2c->lock, flags); + + ret = mpc_i2c_wait_for_completion(i2c); + if (ret) + i2c->rc = ret; + + if (i2c->rc == -EIO || i2c->rc == -EAGAIN || i2c->rc == -ETIMEDOUT) + i2c_recover_bus(&i2c->adap); - for (i = 0; ret >= 0 && i < num; i++) { - pmsg = &msgs[i]; - dev_dbg(i2c->dev, - "Doing %s %d bytes to 0x%02x - %d of %d messages\n", - pmsg->flags & I2C_M_RD ? "read" : "write", - pmsg->len, pmsg->addr, i + 1, num); - if (pmsg->flags & I2C_M_RD) { - bool recv_len = pmsg->flags & I2C_M_RECV_LEN; - - ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i, - recv_len); - if (recv_len && ret > 0) - pmsg->len = ret; - } else { - ret = - mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); - } - } - mpc_i2c_stop(i2c); /* Initiate STOP */ orig_jiffies = jiffies; /* Wait until STOP is seen, allow up to 1 s */ while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) { @@ -622,13 +623,41 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) { writeb(status & ~CSR_MAL, i2c->base + MPC_I2C_SR); - mpc_i2c_fixup(i2c); + i2c_recover_bus(&i2c->adap); } return -EIO; } cond_resched(); } - return (ret < 0) ? ret : num; + + return i2c->rc; +} + +static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + int rc, ret = num; + struct mpc_i2c *i2c = i2c_get_adapdata(adap); + int i; + + dev_dbg(i2c->dev, "num = %d\n", num); + for (i = 0; i < num; i++) + dev_dbg(i2c->dev, " addr = %02x, flags = %02x, len = %d, %*ph\n", + msgs[i].addr, msgs[i].flags, msgs[i].len, + msgs[i].flags & I2C_M_RD ? 0 : msgs[i].len, + msgs[i].buf); + + WARN_ON(i2c->msgs != NULL); + i2c->msgs = msgs; + i2c->num_msgs = num; + + rc = mpc_i2c_execute_msg(i2c); + if (rc < 0) + ret = rc; + + i2c->num_msgs = 0; + i2c->msgs = NULL; + + return ret; } static u32 mpc_functionality(struct i2c_adapter *adap) @@ -637,6 +666,15 @@ static u32 mpc_functionality(struct i2c_adapter *adap) | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL; } +static int fsl_i2c_bus_recovery(struct i2c_adapter *adap) +{ + struct mpc_i2c *i2c = i2c_get_adapdata(adap); + + mpc_i2c_fixup(i2c); + + return 0; +} + static const struct i2c_algorithm mpc_algo = { .master_xfer = mpc_xfer, .functionality = mpc_functionality, @@ -648,63 +686,61 @@ static struct i2c_adapter mpc_ops = { .timeout = HZ, }; -static const struct of_device_id mpc_i2c_of_match[]; +static struct i2c_bus_recovery_info fsl_i2c_recovery_info = { + .recover_bus = fsl_i2c_bus_recovery, +}; + static int fsl_i2c_probe(struct platform_device *op) { - const struct of_device_id *match; + const struct mpc_i2c_data *data; struct mpc_i2c *i2c; const u32 *prop; u32 clock = MPC_I2C_CLOCK_LEGACY; int result = 0; int plen; - struct resource res; struct clk *clk; int err; - match = of_match_device(mpc_i2c_of_match, &op->dev); - if (!match) - return -EINVAL; - - i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + i2c = devm_kzalloc(&op->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) return -ENOMEM; i2c->dev = &op->dev; /* for debug and error output */ - init_waitqueue_head(&i2c->queue); + init_waitqueue_head(&i2c->waitq); + spin_lock_init(&i2c->lock); - i2c->base = of_iomap(op->dev.of_node, 0); - if (!i2c->base) { - dev_err(i2c->dev, "failed to map controller\n"); - result = -ENOMEM; - goto fail_map; - } + i2c->base = devm_platform_ioremap_resource(op, 0); + if (IS_ERR(i2c->base)) + return PTR_ERR(i2c->base); - i2c->irq = irq_of_parse_and_map(op->dev.of_node, 0); - if (i2c->irq) { /* no i2c->irq implies polling */ - result = request_irq(i2c->irq, mpc_i2c_isr, - IRQF_SHARED, "i2c-mpc", i2c); - if (result < 0) { - dev_err(i2c->dev, "failed to attach interrupt\n"); - goto fail_request; - } + i2c->irq = platform_get_irq(op, 0); + if (i2c->irq < 0) + return i2c->irq; + + result = devm_request_irq(&op->dev, i2c->irq, mpc_i2c_isr, + IRQF_SHARED, "i2c-mpc", i2c); + if (result < 0) { + dev_err(i2c->dev, "failed to attach interrupt\n"); + return result; } /* * enable clock for the I2C peripheral (non fatal), * keep a reference upon successful allocation */ - clk = devm_clk_get(&op->dev, NULL); - if (!IS_ERR(clk)) { - err = clk_prepare_enable(clk); - if (err) { - dev_err(&op->dev, "failed to enable clock\n"); - goto fail_request; - } else { - i2c->clk_per = clk; - } + clk = devm_clk_get_optional(&op->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + err = clk_prepare_enable(clk); + if (err) { + dev_err(&op->dev, "failed to enable clock\n"); + return err; } + i2c->clk_per = clk; + if (of_property_read_bool(op->dev.of_node, "fsl,preserve-clocking")) { clock = MPC_I2C_CLOCK_PRESERVE; } else { @@ -714,8 +750,8 @@ static int fsl_i2c_probe(struct platform_device *op) clock = *prop; } - if (match->data) { - const struct mpc_i2c_data *data = match->data; + data = device_get_match_data(&op->dev); + if (data) { data->setup(op->dev.of_node, i2c, clock); } else { /* Backwards compatibility */ @@ -731,31 +767,25 @@ static int fsl_i2c_probe(struct platform_device *op) } dev_info(i2c->dev, "timeout %u us\n", mpc_ops.timeout * 1000000 / HZ); - platform_set_drvdata(op, i2c); - i2c->adap = mpc_ops; - of_address_to_resource(op->dev.of_node, 0, &res); scnprintf(i2c->adap.name, sizeof(i2c->adap.name), - "MPC adapter at 0x%llx", (unsigned long long)res.start); - i2c_set_adapdata(&i2c->adap, i2c); + "MPC adapter (%s)", of_node_full_name(op->dev.of_node)); i2c->adap.dev.parent = &op->dev; + i2c->adap.nr = op->id; i2c->adap.dev.of_node = of_node_get(op->dev.of_node); + i2c->adap.bus_recovery_info = &fsl_i2c_recovery_info; + platform_set_drvdata(op, i2c); + i2c_set_adapdata(&i2c->adap, i2c); - result = i2c_add_adapter(&i2c->adap); - if (result < 0) + result = i2c_add_numbered_adapter(&i2c->adap); + if (result) goto fail_add; - return result; + return 0; fail_add: - if (i2c->clk_per) - clk_disable_unprepare(i2c->clk_per); - free_irq(i2c->irq, i2c); - fail_request: - irq_dispose_mapping(i2c->irq); - iounmap(i2c->base); - fail_map: - kfree(i2c); + clk_disable_unprepare(i2c->clk_per); + return result; }; @@ -765,20 +795,12 @@ static int fsl_i2c_remove(struct platform_device *op) i2c_del_adapter(&i2c->adap); - if (i2c->clk_per) - clk_disable_unprepare(i2c->clk_per); + clk_disable_unprepare(i2c->clk_per); - if (i2c->irq) - free_irq(i2c->irq, i2c); - - irq_dispose_mapping(i2c->irq); - iounmap(i2c->base); - kfree(i2c); return 0; }; -#ifdef CONFIG_PM_SLEEP -static int mpc_i2c_suspend(struct device *dev) +static int __maybe_unused mpc_i2c_suspend(struct device *dev) { struct mpc_i2c *i2c = dev_get_drvdata(dev); @@ -788,7 +810,7 @@ static int mpc_i2c_suspend(struct device *dev) return 0; } -static int mpc_i2c_resume(struct device *dev) +static int __maybe_unused mpc_i2c_resume(struct device *dev) { struct mpc_i2c *i2c = dev_get_drvdata(dev); @@ -797,12 +819,7 @@ static int mpc_i2c_resume(struct device *dev) return 0; } - static SIMPLE_DEV_PM_OPS(mpc_i2c_pm_ops, mpc_i2c_suspend, mpc_i2c_resume); -#define MPC_I2C_PM_OPS (&mpc_i2c_pm_ops) -#else -#define MPC_I2C_PM_OPS NULL -#endif static const struct mpc_i2c_data mpc_i2c_data_512x = { .setup = mpc_i2c_setup_512x, @@ -845,7 +862,7 @@ static struct platform_driver mpc_i2c_driver = { .driver = { .name = DRV_NAME, .of_match_table = mpc_i2c_of_match, - .pm = MPC_I2C_PM_OPS, + .pm = &mpc_i2c_pm_ops, }, }; diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 2ffd2f354d0a..5ddfa4e56ee2 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -231,6 +231,7 @@ struct mtk_i2c { struct i2c_adapter adap; /* i2c host adapter */ struct device *dev; struct completion msg_complete; + struct i2c_timings timing_info; /* set in i2c probe */ void __iomem *base; /* i2c base addr */ @@ -479,7 +480,7 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c) { u16 control_reg; - if (i2c->dev_comp->dma_sync) { + if (i2c->dev_comp->apdma_sync) { writel(I2C_DMA_WARM_RST, i2c->pdmabase + OFFSET_RST); udelay(10); writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST); @@ -564,7 +565,7 @@ static const struct i2c_spec_values *mtk_i2c_get_spec(unsigned int speed) static int mtk_i2c_max_step_cnt(unsigned int target_speed) { - if (target_speed > I2C_MAX_FAST_MODE_FREQ) + if (target_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) return MAX_HS_STEP_CNT_DIV; else return MAX_STEP_CNT_DIV; @@ -607,7 +608,8 @@ static int mtk_i2c_check_ac_timing(struct mtk_i2c *i2c, else clk_ns = sample_ns / 2; - su_sta_cnt = DIV_ROUND_UP(spec->min_su_sta_ns, clk_ns); + su_sta_cnt = DIV_ROUND_UP(spec->min_su_sta_ns + + i2c->timing_info.scl_int_delay_ns, clk_ns); if (su_sta_cnt > max_sta_cnt) return -1; @@ -635,7 +637,7 @@ static int mtk_i2c_check_ac_timing(struct mtk_i2c *i2c, if (sda_min > sda_max) return -3; - if (check_speed > I2C_MAX_FAST_MODE_FREQ) { + if (check_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) { if (i2c->dev_comp->ltiming_adjust) { i2c->ac_timing.hs = I2C_TIME_DEFAULT_VALUE | (sample_cnt << 12) | (high_cnt << 8); @@ -850,7 +852,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs, control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) & ~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS); - if ((i2c->speed_hz > I2C_MAX_FAST_MODE_FREQ) || (left_num >= 1)) + if ((i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) || (left_num >= 1)) control_reg |= I2C_CONTROL_RS; if (i2c->op == I2C_MASTER_WRRD) @@ -1067,7 +1069,8 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, } } - if (i2c->auto_restart && num >= 2 && i2c->speed_hz > I2C_MAX_FAST_MODE_FREQ) + if (i2c->auto_restart && num >= 2 && + i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) /* ignore the first restart irq after the master code, * otherwise the first transfer will be discarded. */ @@ -1175,6 +1178,8 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c) i2c->use_push_pull = of_property_read_bool(np, "mediatek,use-push-pull"); + i2c_parse_fw_timings(i2c->dev, &i2c->timing_info, true); + return 0; } diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index a3363b20f168..dc77e1c4e80f 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -277,7 +277,7 @@ static int init_hw(struct nmk_i2c_dev *dev) goto exit; /* disable the controller */ - i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE); + i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE); disable_all_interrupts(dev); @@ -525,7 +525,7 @@ static int write_i2c(struct nmk_i2c_dev *dev, u16 flags) dev->virtbase + I2C_CR); /* enable the controller */ - i2c_set_bit(dev->virtbase + I2C_CR , I2C_CR_PE); + i2c_set_bit(dev->virtbase + I2C_CR, I2C_CR_PE); init_completion(&dev->xfer_complete); diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 6b20601ffb13..b5055a3cbd93 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -262,6 +262,10 @@ static const struct property_entry ccgx_props[] = { { } }; +static const struct software_node ccgx_node = { + .properties = ccgx_props, +}; + static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) { i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev, @@ -274,7 +278,7 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) sizeof(i2cd->gpu_ccgx_ucsi->type)); i2cd->gpu_ccgx_ucsi->addr = 0x8; i2cd->gpu_ccgx_ucsi->irq = irq; - i2cd->gpu_ccgx_ucsi->properties = ccgx_props; + i2cd->gpu_ccgx_ucsi->swnode = &ccgx_node; i2cd->ccgx_client = i2c_new_client_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); return PTR_ERR_OR_ZERO(i2cd->ccgx_client); } diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 12ac4212aded..d4f6c6d60683 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -1404,9 +1404,9 @@ omap_i2c_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(omap->dev, OMAP_I2C_PM_TIMEOUT); pm_runtime_use_autosuspend(omap->dev); - r = pm_runtime_get_sync(omap->dev); + r = pm_runtime_resume_and_get(omap->dev); if (r < 0) - goto err_free_mem; + goto err_disable_pm; /* * Read the Rev hi bit-[15:14] ie scheme this is 1 indicates ver2. @@ -1513,8 +1513,8 @@ err_unuse_clocks: omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0); pm_runtime_dont_use_autosuspend(omap->dev); pm_runtime_put_sync(omap->dev); +err_disable_pm: pm_runtime_disable(&pdev->dev); -err_free_mem: return r; } @@ -1525,7 +1525,7 @@ static int omap_i2c_remove(struct platform_device *pdev) int ret; i2c_del_adapter(&omap->adapter); - ret = pm_runtime_get_sync(&pdev->dev); + ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) return ret; diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 3e38e114948b..5241e6f414e9 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -76,11 +76,6 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap, * but I think the current API makes no sense and I don't want * any driver that I haven't verified for correctness to go * anywhere near a pmac i2c bus anyway ... - * - * I'm also not completely sure what kind of phases to do between - * the actual command and the data (what I am _supposed_ to do that - * is). For now, I assume writes are a single stream and reads have - * a repeat start/addr phase (but not stop in between) */ case I2C_SMBUS_BLOCK_DATA: buf = data->block; diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 1c259b5188de..c63d5545fc2a 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -569,9 +569,9 @@ static int cci_probe(struct platform_device *pdev) cci->master[idx].mode = I2C_MODE_STANDARD; ret = of_property_read_u32(child, "clock-frequency", &val); if (!ret) { - if (val == 400000) + if (val == I2C_MAX_FAST_MODE_FREQ) cci->master[idx].mode = I2C_MODE_FAST; - else if (val == 1000000) + else if (val == I2C_MAX_FAST_MODE_PLUS_FREQ) cci->master[idx].mode = I2C_MODE_FAST_PLUS; } diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 12f6d452c0f7..327c092a4130 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -141,6 +141,7 @@ struct rcar_i2c_priv { enum dma_data_direction dma_direction; struct reset_control *rstc; + bool atomic_xfer; int irq; struct i2c_client *host_notify_client; @@ -353,7 +354,9 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv) rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START); rcar_i2c_write(priv, ICMSR, 0); } - rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND); + + if (!priv->atomic_xfer) + rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND); } static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv) @@ -418,7 +421,7 @@ static bool rcar_i2c_dma(struct rcar_i2c_priv *priv) int len; /* Do various checks to see if DMA is feasible at all */ - if (IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN || + if (priv->atomic_xfer || IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN || !(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA)) return false; @@ -646,7 +649,8 @@ static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr) /* Nack */ if (msr & MNR) { /* HW automatically sends STOP after received NACK */ - rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP); + if (!priv->atomic_xfer) + rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP); priv->flags |= ID_NACK; goto out; } @@ -667,7 +671,8 @@ out: if (priv->flags & ID_DONE) { rcar_i2c_write(priv, ICMIER, 0); rcar_i2c_write(priv, ICMSR, 0); - wake_up(&priv->wait); + if (!priv->atomic_xfer) + wake_up(&priv->wait); } return IRQ_HANDLED; @@ -684,7 +689,8 @@ static irqreturn_t rcar_i2c_gen2_irq(int irq, void *ptr) /* Only handle interrupts that are currently enabled */ msr = rcar_i2c_read(priv, ICMSR); - msr &= rcar_i2c_read(priv, ICMIER); + if (!priv->atomic_xfer) + msr &= rcar_i2c_read(priv, ICMIER); return rcar_i2c_irq(irq, priv, msr); } @@ -696,7 +702,8 @@ static irqreturn_t rcar_i2c_gen3_irq(int irq, void *ptr) /* Only handle interrupts that are currently enabled */ msr = rcar_i2c_read(priv, ICMSR); - msr &= rcar_i2c_read(priv, ICMIER); + if (!priv->atomic_xfer) + msr &= rcar_i2c_read(priv, ICMIER); /* * Clear START or STOP immediately, except for REPSTART after read or @@ -804,6 +811,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap, int i, ret; long time_left; + priv->atomic_xfer = false; + pm_runtime_get_sync(dev); /* Check bus state before init otherwise bus busy info will be lost */ @@ -858,6 +867,68 @@ out: return ret; } +static int rcar_i2c_master_xfer_atomic(struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct rcar_i2c_priv *priv = i2c_get_adapdata(adap); + struct device *dev = rcar_i2c_priv_to_dev(priv); + unsigned long j; + bool time_left; + int ret; + + priv->atomic_xfer = true; + + pm_runtime_get_sync(dev); + + /* Check bus state before init otherwise bus busy info will be lost */ + ret = rcar_i2c_bus_barrier(priv); + if (ret < 0) + goto out; + + rcar_i2c_init(priv); + + /* init first message */ + priv->msg = msgs; + priv->msgs_left = num; + priv->flags = (priv->flags & ID_P_MASK) | ID_FIRST_MSG; + rcar_i2c_prepare_msg(priv); + + j = jiffies + num * adap->timeout; + do { + u32 msr = rcar_i2c_read(priv, ICMSR); + + msr &= (rcar_i2c_is_recv(priv) ? RCAR_IRQ_RECV : RCAR_IRQ_SEND) | RCAR_IRQ_STOP; + + if (msr) { + if (priv->devtype < I2C_RCAR_GEN3) + rcar_i2c_gen2_irq(0, priv); + else + rcar_i2c_gen3_irq(0, priv); + } + + time_left = time_before_eq(jiffies, j); + } while (!(priv->flags & ID_DONE) && time_left); + + if (!time_left) { + rcar_i2c_init(priv); + ret = -ETIMEDOUT; + } else if (priv->flags & ID_NACK) { + ret = -ENXIO; + } else if (priv->flags & ID_ARBLOST) { + ret = -EAGAIN; + } else { + ret = num - priv->msgs_left; /* The number of transfer */ + } +out: + pm_runtime_put(dev); + + if (ret < 0 && ret != -ENXIO) + dev_err(dev, "error %d : %x\n", ret, priv->flags); + + return ret; +} + static int rcar_reg_slave(struct i2c_client *slave) { struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter); @@ -922,6 +993,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap) static const struct i2c_algorithm rcar_i2c_algo = { .master_xfer = rcar_i2c_master_xfer, + .master_xfer_atomic = rcar_i2c_master_xfer_atomic, .functionality = rcar_i2c_func, .reg_slave = rcar_reg_slave, .unreg_slave = rcar_unreg_slave, @@ -1027,7 +1099,10 @@ static int rcar_i2c_probe(struct platform_device *pdev) if (of_property_read_bool(dev->of_node, "smbus")) priv->flags |= ID_P_HOST_NOTIFY; - priv->irq = platform_get_irq(pdev, 0); + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto out_pm_disable; + priv->irq = ret; ret = devm_request_irq(dev, priv->irq, irqhandler, irqflags, dev_name(dev), priv); if (ret < 0) { dev_err(dev, "cannot get irq %d\n", priv->irq); diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 62a903fbe912..ab928613afba 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/gpio/consumer.h> #include <linux/pinctrl/consumer.h> #include <linux/mfd/syscon.h> @@ -156,12 +157,8 @@ MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); */ static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev) { - if (pdev->dev.of_node) { - const struct of_device_id *match; - - match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node); - return (kernel_ulong_t)match->data; - } + if (pdev->dev.of_node) + return (kernel_ulong_t)of_device_get_match_data(&pdev->dev); return platform_get_device_id(pdev)->driver_data; } diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c index 1dc387392e74..6746aa46d96c 100644 --- a/drivers/i2c/busses/i2c-scmi.c +++ b/drivers/i2c/busses/i2c-scmi.c @@ -18,8 +18,6 @@ /* SMBUS HID definition as supported by Microsoft Windows */ #define ACPI_SMBUS_MS_HID "SMB0001" -ACPI_MODULE_NAME("smbus_cmi"); - struct smbus_methods_t { char *mt_info; char *mt_sbr; diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c index c2005c789d2b..319d1fa617c8 100644 --- a/drivers/i2c/busses/i2c-sh7760.c +++ b/drivers/i2c/busses/i2c-sh7760.c @@ -471,7 +471,10 @@ static int sh7760_i2c_probe(struct platform_device *pdev) goto out2; } - id->irq = platform_get_irq(pdev, 0); + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto out3; + id->irq = ret; id->adap.nr = pdev->id; id->adap.algo = &sh7760_i2c_algo; diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index 2917fecf6c80..4fe15cd78907 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -290,7 +290,7 @@ static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct sprd_i2c *i2c_dev = i2c_adap->algo_data; int im, ret; - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; @@ -576,7 +576,7 @@ static int sprd_i2c_remove(struct platform_device *pdev) struct sprd_i2c *i2c_dev = platform_get_drvdata(pdev); int ret; - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; @@ -640,6 +640,7 @@ static const struct of_device_id sprd_i2c_of_match[] = { { .compatible = "sprd,sc9860-i2c", }, {}, }; +MODULE_DEVICE_TABLE(of, sprd_i2c_of_match); static struct platform_driver sprd_i2c_driver = { .probe = sprd_i2c_probe, diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index c62c815b88eb..0138317ea600 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -164,7 +164,6 @@ enum { #define STM32F7_I2C_DNF_DEFAULT 0 #define STM32F7_I2C_DNF_MAX 15 -#define STM32F7_I2C_ANALOG_FILTER_ENABLE 1 #define STM32F7_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */ #define STM32F7_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */ @@ -223,8 +222,6 @@ struct stm32f7_i2c_spec { * @clock_src: I2C clock source frequency (Hz) * @rise_time: Rise time (ns) * @fall_time: Fall time (ns) - * @dnf: Digital filter coefficient (0-16) - * @analog_filter: Analog filter delay (On/Off) * @fmp_clr_offset: Fast Mode Plus clear register offset from set register */ struct stm32f7_i2c_setup { @@ -232,8 +229,6 @@ struct stm32f7_i2c_setup { u32 clock_src; u32 rise_time; u32 fall_time; - u8 dnf; - bool analog_filter; u32 fmp_clr_offset; }; @@ -312,6 +307,9 @@ struct stm32f7_i2c_msg { * @wakeup_src: boolean to know if the device is a wakeup source * @smbus_mode: states that the controller is configured in SMBus mode * @host_notify_client: SMBus host-notify client + * @analog_filter: boolean to indicate enabling of the analog filter + * @dnf_dt: value of digital filter requested via dt + * @dnf: value of digital filter to apply */ struct stm32f7_i2c_dev { struct i2c_adapter adap; @@ -340,6 +338,9 @@ struct stm32f7_i2c_dev { bool wakeup_src; bool smbus_mode; struct i2c_client *host_notify_client; + bool analog_filter; + u32 dnf_dt; + u32 dnf; }; /* @@ -385,15 +386,11 @@ static struct stm32f7_i2c_spec stm32f7_i2c_specs[] = { static const struct stm32f7_i2c_setup stm32f7_setup = { .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT, .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT, - .dnf = STM32F7_I2C_DNF_DEFAULT, - .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE, }; static const struct stm32f7_i2c_setup stm32mp15_setup = { .rise_time = STM32F7_I2C_RISE_TIME_DEFAULT, .fall_time = STM32F7_I2C_FALL_TIME_DEFAULT, - .dnf = STM32F7_I2C_DNF_DEFAULT, - .analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE, .fmp_clr_offset = 0x40, }; @@ -462,27 +459,28 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev, return -EINVAL; } - if (setup->dnf > STM32F7_I2C_DNF_MAX) { + i2c_dev->dnf = DIV_ROUND_CLOSEST(i2c_dev->dnf_dt, i2cclk); + if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) { dev_err(i2c_dev->dev, "DNF out of bound %d/%d\n", - setup->dnf, STM32F7_I2C_DNF_MAX); + i2c_dev->dnf * i2cclk, STM32F7_I2C_DNF_MAX * i2cclk); return -EINVAL; } /* Analog and Digital Filters */ af_delay_min = - (setup->analog_filter ? + (i2c_dev->analog_filter ? STM32F7_I2C_ANALOG_FILTER_DELAY_MIN : 0); af_delay_max = - (setup->analog_filter ? + (i2c_dev->analog_filter ? STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0); - dnf_delay = setup->dnf * i2cclk; + dnf_delay = i2c_dev->dnf * i2cclk; sdadel_min = specs->hddat_min + setup->fall_time - - af_delay_min - (setup->dnf + 3) * i2cclk; + af_delay_min - (i2c_dev->dnf + 3) * i2cclk; sdadel_max = specs->vddat_max - setup->rise_time - - af_delay_max - (setup->dnf + 4) * i2cclk; + af_delay_max - (i2c_dev->dnf + 4) * i2cclk; scldel_min = setup->rise_time + specs->sudat_min; @@ -648,6 +646,7 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, setup->speed_freq = t->bus_freq_hz; i2c_dev->setup.rise_time = t->scl_rise_ns; i2c_dev->setup.fall_time = t->scl_fall_ns; + i2c_dev->dnf_dt = t->digital_filter_width_ns; setup->clock_src = clk_get_rate(i2c_dev->clk); if (!setup->clock_src) { @@ -655,6 +654,9 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, return -EINVAL; } + if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter")) + i2c_dev->dnf_dt = STM32F7_I2C_DNF_DEFAULT; + do { ret = stm32f7_i2c_compute_timing(i2c_dev, setup, &i2c_dev->timing); @@ -676,12 +678,15 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev, return ret; } + i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node, + "i2c-analog-filter"); + dev_dbg(i2c_dev->dev, "I2C Speed(%i), Clk Source(%i)\n", setup->speed_freq, setup->clock_src); dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n", setup->rise_time, setup->fall_time); dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n", - (setup->analog_filter ? "On" : "Off"), setup->dnf); + (i2c_dev->analog_filter ? "On" : "Off"), i2c_dev->dnf); i2c_dev->bus_rate = setup->speed_freq; @@ -720,8 +725,8 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev) timing |= STM32F7_I2C_TIMINGR_SCLL(t->scll); writel_relaxed(timing, i2c_dev->base + STM32F7_I2C_TIMINGR); - /* Enable I2C */ - if (i2c_dev->setup.analog_filter) + /* Configure the Analog Filter */ + if (i2c_dev->analog_filter) stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_ANFOFF); else @@ -732,7 +737,7 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev) stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_DNF_MASK); stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1, - STM32F7_I2C_CR1_DNF(i2c_dev->setup.dnf)); + STM32F7_I2C_CR1_DNF(i2c_dev->dnf)); stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_PE); @@ -1597,7 +1602,8 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data) /* Bus error */ if (status & STM32F7_I2C_ISR_BERR) { - dev_err(dev, "<%s>: Bus error\n", __func__); + dev_err(dev, "<%s>: Bus error accessing addr 0x%x\n", + __func__, f7_msg->addr); writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR); stm32f7_i2c_release_bus(&i2c_dev->adap); f7_msg->result = -EIO; @@ -1605,13 +1611,15 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data) /* Arbitration loss */ if (status & STM32F7_I2C_ISR_ARLO) { - dev_dbg(dev, "<%s>: Arbitration loss\n", __func__); + dev_dbg(dev, "<%s>: Arbitration loss accessing addr 0x%x\n", + __func__, f7_msg->addr); writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR); f7_msg->result = -EAGAIN; } if (status & STM32F7_I2C_ISR_PECERR) { - dev_err(dev, "<%s>: PEC error in reception\n", __func__); + dev_err(dev, "<%s>: PEC error in reception accessing addr 0x%x\n", + __func__, f7_msg->addr); writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR); f7_msg->result = -EINVAL; } @@ -1652,7 +1660,7 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap, i2c_dev->msg_id = 0; f7_msg->smbus = false; - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; @@ -1698,7 +1706,7 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, f7_msg->read_write = read_write; f7_msg->smbus = true; - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; @@ -1799,7 +1807,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) if (ret) return ret; - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) return ret; @@ -1880,7 +1888,7 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave) WARN_ON(!i2c_dev->slave[id]); - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; @@ -2027,12 +2035,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) phy_addr = (dma_addr_t)res->start; irq_event = platform_get_irq(pdev, 0); - if (irq_event <= 0) { - if (irq_event != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to get IRQ event: %d\n", - irq_event); + if (irq_event <= 0) return irq_event ? : -ENOENT; - } irq_error = platform_get_irq(pdev, 1); if (irq_error <= 0) @@ -2267,13 +2271,12 @@ static int __maybe_unused stm32f7_i2c_runtime_resume(struct device *dev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev) +static int __maybe_unused stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev) { int ret; struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs; - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; @@ -2289,13 +2292,13 @@ static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev) return ret; } -static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev) +static int __maybe_unused stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev) { u32 cr1; int ret; struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs; - ret = pm_runtime_get_sync(i2c_dev->dev); + ret = pm_runtime_resume_and_get(i2c_dev->dev); if (ret < 0) return ret; @@ -2320,7 +2323,7 @@ static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev) return ret; } -static int stm32f7_i2c_suspend(struct device *dev) +static int __maybe_unused stm32f7_i2c_suspend(struct device *dev) { struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); int ret; @@ -2341,7 +2344,7 @@ static int stm32f7_i2c_suspend(struct device *dev) return 0; } -static int stm32f7_i2c_resume(struct device *dev) +static int __maybe_unused stm32f7_i2c_resume(struct device *dev) { struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); int ret; @@ -2361,7 +2364,6 @@ static int stm32f7_i2c_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops stm32f7_i2c_pm_ops = { SET_RUNTIME_PM_OPS(stm32f7_i2c_runtime_suspend, diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c index c0c7d01473f2..3680d608698b 100644 --- a/drivers/i2c/busses/i2c-tegra-bpmp.c +++ b/drivers/i2c/busses/i2c-tegra-bpmp.c @@ -38,49 +38,31 @@ struct tegra_bpmp_i2c { * firmware I2C driver to avoid any issues in future if Linux I2C flags are * changed. */ -static int tegra_bpmp_xlate_flags(u16 flags, u16 *out) +static void tegra_bpmp_xlate_flags(u16 flags, u16 *out) { - if (flags & I2C_M_TEN) { + if (flags & I2C_M_TEN) *out |= SERIALI2C_TEN; - flags &= ~I2C_M_TEN; - } - if (flags & I2C_M_RD) { + if (flags & I2C_M_RD) *out |= SERIALI2C_RD; - flags &= ~I2C_M_RD; - } - if (flags & I2C_M_STOP) { + if (flags & I2C_M_STOP) *out |= SERIALI2C_STOP; - flags &= ~I2C_M_STOP; - } - if (flags & I2C_M_NOSTART) { + if (flags & I2C_M_NOSTART) *out |= SERIALI2C_NOSTART; - flags &= ~I2C_M_NOSTART; - } - if (flags & I2C_M_REV_DIR_ADDR) { + if (flags & I2C_M_REV_DIR_ADDR) *out |= SERIALI2C_REV_DIR_ADDR; - flags &= ~I2C_M_REV_DIR_ADDR; - } - if (flags & I2C_M_IGNORE_NAK) { + if (flags & I2C_M_IGNORE_NAK) *out |= SERIALI2C_IGNORE_NAK; - flags &= ~I2C_M_IGNORE_NAK; - } - if (flags & I2C_M_NO_RD_ACK) { + if (flags & I2C_M_NO_RD_ACK) *out |= SERIALI2C_NO_RD_ACK; - flags &= ~I2C_M_NO_RD_ACK; - } - if (flags & I2C_M_RECV_LEN) { + if (flags & I2C_M_RECV_LEN) *out |= SERIALI2C_RECV_LEN; - flags &= ~I2C_M_RECV_LEN; - } - - return 0; } /** @@ -97,22 +79,19 @@ static int tegra_bpmp_xlate_flags(u16 flags, u16 *out) * * See deserialize_i2c documentation for the data format in the other direction. */ -static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c, +static void tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c, struct mrq_i2c_request *request, struct i2c_msg *msgs, unsigned int num) { char *buf = request->xfer.data_buf; unsigned int i, j, pos = 0; - int err; for (i = 0; i < num; i++) { struct i2c_msg *msg = &msgs[i]; u16 flags = 0; - err = tegra_bpmp_xlate_flags(msg->flags, &flags); - if (err < 0) - return err; + tegra_bpmp_xlate_flags(msg->flags, &flags); buf[pos++] = msg->addr & 0xff; buf[pos++] = (msg->addr & 0xff00) >> 8; @@ -128,8 +107,6 @@ static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c, } request->xfer.data_size = pos; - - return 0; } /** @@ -217,7 +194,32 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c, else err = tegra_bpmp_transfer(i2c->bpmp, &msg); - return err; + if (err < 0) { + dev_err(i2c->dev, "failed to transfer message: %d\n", err); + return err; + } + + if (msg.rx.ret != 0) { + if (msg.rx.ret == -BPMP_EAGAIN) { + dev_dbg(i2c->dev, "arbitration lost\n"); + return -EAGAIN; + } + + if (msg.rx.ret == -BPMP_ETIMEDOUT) { + dev_dbg(i2c->dev, "timeout\n"); + return -ETIMEDOUT; + } + + if (msg.rx.ret == -BPMP_ENXIO) { + dev_dbg(i2c->dev, "NAK\n"); + return -ENXIO; + } + + dev_err(i2c->dev, "transaction failed: %d\n", msg.rx.ret); + return -EIO; + } + + return 0; } static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter, @@ -238,12 +240,7 @@ static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter, memset(&request, 0, sizeof(request)); memset(&response, 0, sizeof(response)); - err = tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num); - if (err < 0) { - dev_err(i2c->dev, "failed to serialize message: %d\n", err); - return err; - } - + tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num); err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response, atomic); if (err < 0) { dev_err(i2c->dev, "failed to transfer message: %d\n", err); diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c index 63cbb9c7c1b0..bba08cbce6e1 100644 --- a/drivers/i2c/busses/i2c-xgene-slimpro.c +++ b/drivers/i2c/busses/i2c-xgene-slimpro.c @@ -19,7 +19,6 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/version.h> #define MAILBOX_OP_TIMEOUT 1000 /* Operation time out in ms */ #define MAILBOX_I2C_INDEX 0 diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 087b2951942e..2a8568b97c14 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -706,7 +706,7 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) dev_dbg(adap->dev.parent, "%s entry SR: 0x%x\n", __func__, xiic_getreg8(i2c, XIIC_SR_REG_OFFSET)); - err = pm_runtime_get_sync(i2c->dev); + err = pm_runtime_resume_and_get(i2c->dev); if (err < 0) return err; @@ -873,7 +873,7 @@ static int xiic_i2c_remove(struct platform_device *pdev) /* remove adapter & data */ i2c_del_adapter(&i2c->adap); - ret = pm_runtime_get_sync(i2c->dev); + ret = pm_runtime_resume_and_get(i2c->dev); if (ret < 0) return ret; diff --git a/drivers/i2c/i2c-boardinfo.c b/drivers/i2c/i2c-boardinfo.c index 8bc51d4e69df..4df8ad092df3 100644 --- a/drivers/i2c/i2c-boardinfo.c +++ b/drivers/i2c/i2c-boardinfo.c @@ -47,7 +47,6 @@ EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num); * * The board info passed can safely be __initdata, but be careful of embedded * pointers (for platform_data, functions, etc) since that won't be copied. - * Device properties are deep-copied though. */ int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) { @@ -72,16 +71,6 @@ int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsig devinfo->busnum = busnum; devinfo->board_info = *info; - if (info->properties) { - devinfo->board_info.properties = - property_entries_dup(info->properties); - if (IS_ERR(devinfo->board_info.properties)) { - status = PTR_ERR(devinfo->board_info.properties); - kfree(devinfo); - break; - } - } - if (info->resources) { devinfo->board_info.resources = kmemdup(info->resources, diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index f21362355973..5a97e4a02fa2 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -76,6 +76,27 @@ void i2c_transfer_trace_unreg(void) static_branch_dec(&i2c_trace_msg_key); } +const char *i2c_freq_mode_string(u32 bus_freq_hz) +{ + switch (bus_freq_hz) { + case I2C_MAX_STANDARD_MODE_FREQ: + return "Standard Mode (100 kHz)"; + case I2C_MAX_FAST_MODE_FREQ: + return "Fast Mode (400 kHz)"; + case I2C_MAX_FAST_MODE_PLUS_FREQ: + return "Fast Mode Plus (1.0 MHz)"; + case I2C_MAX_TURBO_MODE_FREQ: + return "Turbo Mode (1.4 MHz)"; + case I2C_MAX_HIGH_SPEED_MODE_FREQ: + return "High Speed Mode (3.4 MHz)"; + case I2C_MAX_ULTRA_FAST_MODE_FREQ: + return "Ultra Fast Mode (5.0 MHz)"; + default: + return "Unknown Mode"; + } +} +EXPORT_SYMBOL_GPL(i2c_freq_mode_string); + const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id, const struct i2c_client *client) { @@ -249,7 +270,7 @@ EXPORT_SYMBOL_GPL(i2c_generic_scl_recovery); int i2c_recover_bus(struct i2c_adapter *adap) { if (!adap->bus_recovery_info) - return -EOPNOTSUPP; + return -EBUSY; dev_dbg(&adap->dev, "Trying i2c bus recovery\n"); return adap->bus_recovery_info->recover_bus(adap); @@ -519,6 +540,13 @@ static int i2c_device_probe(struct device *dev) if (status) goto err_clear_wakeup_irq; + client->devres_group_id = devres_open_group(&client->dev, NULL, + GFP_KERNEL); + if (!client->devres_group_id) { + status = -ENOMEM; + goto err_detach_pm_domain; + } + /* * When there are no more users of probe(), * rename probe_new to probe. @@ -531,11 +559,21 @@ static int i2c_device_probe(struct device *dev) else status = -EINVAL; + /* + * Note that we are not closing the devres group opened above so + * even resources that were attached to the device after probe is + * run are released when i2c_device_remove() is executed. This is + * needed as some drivers would allocate additional resources, + * for example when updating firmware. + */ + if (status) - goto err_detach_pm_domain; + goto err_release_driver_resources; return 0; +err_release_driver_resources: + devres_release_group(&client->dev, client->devres_group_id); err_detach_pm_domain: dev_pm_domain_detach(&client->dev, true); err_clear_wakeup_irq: @@ -564,6 +602,8 @@ static int i2c_device_remove(struct device *dev) dev_warn(dev, "remove failed (%pe), will be ignored\n", ERR_PTR(status)); } + devres_release_group(&client->dev, client->devres_group_id); + dev_pm_domain_detach(&client->dev, true); dev_pm_clear_wake_irq(&client->dev); @@ -612,7 +652,7 @@ modalias_show(struct device *dev, struct device_attribute *attr, char *buf) if (len != -ENODEV) return len; - len = acpi_device_modalias(dev, buf, PAGE_SIZE -1); + len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); if (len != -ENODEV) return len; @@ -910,11 +950,11 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf i2c_dev_set_name(adap, client, info); - if (info->properties) { - status = device_add_properties(&client->dev, info->properties); + if (info->swnode) { + status = device_add_software_node(&client->dev, info->swnode); if (status) { dev_err(&adap->dev, - "Failed to add properties to client %s: %d\n", + "Failed to add software node to client %s: %d\n", client->name, status); goto out_err_put_of_node; } @@ -922,16 +962,15 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf status = device_register(&client->dev); if (status) - goto out_free_props; + goto out_remove_swnode; dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n", client->name, dev_name(&client->dev)); return client; -out_free_props: - if (info->properties) - device_remove_properties(&client->dev); +out_remove_swnode: + device_remove_software_node(&client->dev); out_err_put_of_node: of_node_put(info->of_node); out_err: @@ -961,6 +1000,7 @@ void i2c_unregister_device(struct i2c_client *client) if (ACPI_COMPANION(&client->dev)) acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev)); + device_remove_software_node(&client->dev); device_unregister(&client->dev); } EXPORT_SYMBOL_GPL(i2c_unregister_device); @@ -1017,15 +1057,9 @@ struct i2c_client *i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address } EXPORT_SYMBOL_GPL(i2c_new_dummy_device); -struct i2c_dummy_devres { - struct i2c_client *client; -}; - -static void devm_i2c_release_dummy(struct device *dev, void *res) +static void devm_i2c_release_dummy(void *client) { - struct i2c_dummy_devres *this = res; - - i2c_unregister_device(this->client); + i2c_unregister_device(client); } /** @@ -1042,20 +1076,16 @@ struct i2c_client *devm_i2c_new_dummy_device(struct device *dev, struct i2c_adapter *adapter, u16 address) { - struct i2c_dummy_devres *dr; struct i2c_client *client; - - dr = devres_alloc(devm_i2c_release_dummy, sizeof(*dr), GFP_KERNEL); - if (!dr) - return ERR_PTR(-ENOMEM); + int ret; client = i2c_new_dummy_device(adapter, address); - if (IS_ERR(client)) { - devres_free(dr); - } else { - dr->client = client; - devres_add(dev, dr); - } + if (IS_ERR(client)) + return client; + + ret = devm_add_action_or_reset(dev, devm_i2c_release_dummy, client); + if (ret) + return ERR_PTR(ret); return client; } @@ -1704,6 +1734,32 @@ void i2c_del_adapter(struct i2c_adapter *adap) } EXPORT_SYMBOL(i2c_del_adapter); +static void devm_i2c_del_adapter(void *adapter) +{ + i2c_del_adapter(adapter); +} + +/** + * devm_i2c_add_adapter - device-managed variant of i2c_add_adapter() + * @dev: managing device for adding this I2C adapter + * @adapter: the adapter to add + * Context: can sleep + * + * Add adapter with dynamic bus number, same with i2c_add_adapter() + * but the adapter will be auto deleted on driver detach. + */ +int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter) +{ + int ret; + + ret = i2c_add_adapter(adapter); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, devm_i2c_del_adapter, adapter); +} +EXPORT_SYMBOL_GPL(devm_i2c_add_adapter); + static void i2c_parse_timing(struct device *dev, char *prop_name, u32 *cur_val_p, u32 def_val, bool use_def) { diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 6ceb11cc4be1..6ef38a8ee95c 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -440,8 +440,13 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) sizeof(rdwr_arg))) return -EFAULT; - /* Put an arbitrary limit on the number of messages that can - * be sent at once */ + if (!rdwr_arg.msgs || rdwr_arg.nmsgs == 0) + return -EINVAL; + + /* + * Put an arbitrary limit on the number of messages that can + * be sent at once + */ if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS) return -EINVAL; diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 97381e2e03ba..2d0bc029619f 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1885,8 +1885,6 @@ static int elantech_create_smbus(struct psmouse *psmouse, }; unsigned int idx = 0; - smbus_board.properties = i2c_props; - i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-x", info->x_max + 1); i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-y", @@ -1918,6 +1916,10 @@ static int elantech_create_smbus(struct psmouse *psmouse, if (elantech_is_buttonpad(info)) i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,clickpad"); + smbus_board.fwnode = fwnode_create_software_node(i2c_props, NULL); + if (IS_ERR(smbus_board.fwnode)) + return PTR_ERR(smbus_board.fwnode); + return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false, leave_breadcrumbs); } diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 472a03daa869..4e14b4d6635d 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -52,12 +52,15 @@ struct i2c_peripheral { enum i2c_adapter_type type; u32 pci_devid; + const struct property_entry *properties; + struct i2c_client *client; }; struct acpi_peripheral { char hid[ACPI_ID_LEN]; - const struct property_entry *properties; + struct software_node swnode; + struct i2c_client *client; }; struct chromeos_laptop { @@ -68,7 +71,7 @@ struct chromeos_laptop { struct i2c_peripheral *i2c_peripherals; unsigned int num_i2c_peripherals; - const struct acpi_peripheral *acpi_peripherals; + struct acpi_peripheral *acpi_peripherals; unsigned int num_acpi_peripherals; }; @@ -161,7 +164,7 @@ static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter) static bool chromeos_laptop_adjust_client(struct i2c_client *client) { - const struct acpi_peripheral *acpi_dev; + struct acpi_peripheral *acpi_dev; struct acpi_device_id acpi_ids[2] = { }; int i; int error; @@ -175,8 +178,7 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client) memcpy(acpi_ids[0].id, acpi_dev->hid, ACPI_ID_LEN); if (acpi_match_device(acpi_ids, &client->dev)) { - error = device_add_properties(&client->dev, - acpi_dev->properties); + error = device_add_software_node(&client->dev, &acpi_dev->swnode); if (error) { dev_err(&client->dev, "failed to add properties: %d\n", @@ -184,6 +186,8 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client) break; } + acpi_dev->client = client; + return true; } } @@ -193,15 +197,28 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client) static void chromeos_laptop_detach_i2c_client(struct i2c_client *client) { + struct acpi_peripheral *acpi_dev; struct i2c_peripheral *i2c_dev; int i; - for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { - i2c_dev = &cros_laptop->i2c_peripherals[i]; + if (has_acpi_companion(&client->dev)) + for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) { + acpi_dev = &cros_laptop->acpi_peripherals[i]; - if (i2c_dev->client == client) - i2c_dev->client = NULL; - } + if (acpi_dev->client == client) { + acpi_dev->client = NULL; + return; + } + } + else + for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { + i2c_dev = &cros_laptop->i2c_peripherals[i]; + + if (i2c_dev->client == client) { + i2c_dev->client = NULL; + return; + } + } } static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb, @@ -302,28 +319,26 @@ static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = { .board_info = { I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), - .properties = - chromebook_atmel_touchscreen_props, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "touchscreen", .irqflags = IRQF_TRIGGER_FALLING, .type = I2C_ADAPTER_PANEL, .alt_addr = ATMEL_TS_I2C_BL_ADDR, + .properties = chromebook_atmel_touchscreen_props, }, /* Touchpad. */ { .board_info = { I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR), - .properties = - chromebook_pixel_trackpad_props, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "trackpad", .irqflags = IRQF_TRIGGER_FALLING, .type = I2C_ADAPTER_VGADDC, .alt_addr = ATMEL_TP_I2C_BL_ADDR, + .properties = chromebook_pixel_trackpad_props, }, /* Light Sensor. */ { @@ -414,8 +429,6 @@ static struct i2c_peripheral acer_c720_peripherals[] __initdata = { .board_info = { I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), - .properties = - chromebook_atmel_touchscreen_props, .flags = I2C_CLIENT_WAKE, }, .dmi_name = "touchscreen", @@ -423,6 +436,7 @@ static struct i2c_peripheral acer_c720_peripherals[] __initdata = { .type = I2C_ADAPTER_DESIGNWARE, .pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)), .alt_addr = ATMEL_TS_I2C_BL_ADDR, + .properties = chromebook_atmel_touchscreen_props, }, /* Touchpad. */ { @@ -498,12 +512,16 @@ static struct acpi_peripheral samus_peripherals[] __initdata = { /* Touchpad */ { .hid = "ATML0000", - .properties = samus_trackpad_props, + .swnode = { + .properties = samus_trackpad_props, + }, }, /* Touchsceen */ { .hid = "ATML0001", - .properties = chromebook_atmel_touchscreen_props, + .swnode = { + .properties = chromebook_atmel_touchscreen_props, + }, }, }; DECLARE_ACPI_CROS_LAPTOP(samus); @@ -512,12 +530,16 @@ static struct acpi_peripheral generic_atmel_peripherals[] __initdata = { /* Touchpad */ { .hid = "ATML0000", - .properties = chromebook_pixel_trackpad_props, + .swnode = { + .properties = chromebook_pixel_trackpad_props, + }, }, /* Touchsceen */ { .hid = "ATML0001", - .properties = chromebook_atmel_touchscreen_props, + .swnode = { + .properties = chromebook_atmel_touchscreen_props, + }, }, }; DECLARE_ACPI_CROS_LAPTOP(generic_atmel); @@ -743,12 +765,11 @@ chromeos_laptop_prepare_i2c_peripherals(struct chromeos_laptop *cros_laptop, if (error) goto err_out; - /* We need to deep-copy properties */ - if (info->properties) { - info->properties = - property_entries_dup(info->properties); - if (IS_ERR(info->properties)) { - error = PTR_ERR(info->properties); + /* Create primary fwnode for the device - copies everything */ + if (i2c_dev->properties) { + info->fwnode = fwnode_create_software_node(i2c_dev->properties, NULL); + if (IS_ERR(info->fwnode)) { + error = PTR_ERR(info->fwnode); goto err_out; } } @@ -760,8 +781,8 @@ err_out: while (--i >= 0) { i2c_dev = &cros_laptop->i2c_peripherals[i]; info = &i2c_dev->board_info; - if (info->properties) - property_entries_free(info->properties); + if (!IS_ERR_OR_NULL(info->fwnode)) + fwnode_remove_software_node(info->fwnode); } kfree(cros_laptop->i2c_peripherals); return error; @@ -801,11 +822,11 @@ chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop, *acpi_dev = *src_dev; /* We need to deep-copy properties */ - if (src_dev->properties) { - acpi_dev->properties = - property_entries_dup(src_dev->properties); - if (IS_ERR(acpi_dev->properties)) { - error = PTR_ERR(acpi_dev->properties); + if (src_dev->swnode.properties) { + acpi_dev->swnode.properties = + property_entries_dup(src_dev->swnode.properties); + if (IS_ERR(acpi_dev->swnode.properties)) { + error = PTR_ERR(acpi_dev->swnode.properties); goto err_out; } } @@ -821,8 +842,8 @@ chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop, err_out: while (--i >= 0) { acpi_dev = &acpi_peripherals[i]; - if (acpi_dev->properties) - property_entries_free(acpi_dev->properties); + if (!IS_ERR_OR_NULL(acpi_dev->swnode.properties)) + property_entries_free(acpi_dev->swnode.properties); } kfree(acpi_peripherals); @@ -833,21 +854,20 @@ static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop) { const struct acpi_peripheral *acpi_dev; struct i2c_peripheral *i2c_dev; - struct i2c_board_info *info; int i; for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) { i2c_dev = &cros_laptop->i2c_peripherals[i]; - info = &i2c_dev->board_info; - i2c_unregister_device(i2c_dev->client); - property_entries_free(info->properties); } for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) { acpi_dev = &cros_laptop->acpi_peripherals[i]; - property_entries_free(acpi_dev->properties); + if (acpi_dev->client) + device_remove_software_node(&acpi_dev->client->dev); + + property_entries_free(acpi_dev->swnode.properties); } kfree(cros_laptop->i2c_peripherals); diff --git a/drivers/platform/x86/intel_cht_int33fe_microb.c b/drivers/platform/x86/intel_cht_int33fe_microb.c index 20b11e0d9a75..673f41cd14b5 100644 --- a/drivers/platform/x86/intel_cht_int33fe_microb.c +++ b/drivers/platform/x86/intel_cht_int33fe_microb.c @@ -35,6 +35,10 @@ static const struct property_entry bq27xxx_props[] = { { } }; +static const struct software_node bq27xxx_node = { + .properties = bq27xxx_props, +}; + int cht_int33fe_microb_probe(struct cht_int33fe_data *data) { struct device *dev = data->dev; @@ -43,7 +47,7 @@ int cht_int33fe_microb_probe(struct cht_int33fe_data *data) memset(&board_info, 0, sizeof(board_info)); strscpy(board_info.type, "bq27542", ARRAY_SIZE(board_info.type)); board_info.dev_name = "bq27542"; - board_info.properties = bq27xxx_props; + board_info.swnode = &bq27xxx_node; data->battery_fg = i2c_acpi_new_device(dev, 1, &board_info); return PTR_ERR_OR_ZERO(data->battery_fg); |