Skip to content

Commit b1a2ae7

Browse files
committed
drivers: i2c: i2c_dw: adding optional feature support and improvements
Handling invalid 0 length i2c transfer and logging error if i2c transfer is initiated with length 0. Modified i2c shell to support i2c scan command only if I2C_ZERO_LEN_XFER_SUPPORTED Kconfig flag is enabled. Updated i2c shell for checking i2c-speed parameter in i2c speed shell command. Signed-off-by: samit singh sikarwar <[email protected]>
1 parent 80c2d19 commit b1a2ae7

File tree

5 files changed

+58
-23
lines changed

5 files changed

+58
-23
lines changed

drivers/i2c/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ config I2C_SHELL
2222
The I2C shell supports scanning, bus recovery, I2C read and write
2323
operations.
2424

25+
config I2C_ZERO_LEN_XFER_SUPPORTED
26+
bool "I2C transfer without data (data length 0)"
27+
default y
28+
help
29+
Enable this flag to support 0 length I2C transfers(quick commands).
30+
2531
config I2C_STATS
2632
bool "I2C device Stats"
2733
depends on STATS

drivers/i2c/i2c_dw.c

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
/* dw_i2c.c - I2C file for Design Ware */
1+
/* i2c_dw.c - I2C file for Design Ware */
22

33
/*
4-
* Copyright (c) 2015 Intel Corporation
54
* Copyright (c) 2022 Andrei-Edward Popa
5+
* Copyright (c) 2015-2023 Intel Corporation
66
*
77
* SPDX-License-Identifier: Apache-2.0
88
*/
@@ -351,7 +351,7 @@ static inline void i2c_dw_transfer_complete(const struct device *dev)
351351
}
352352

353353
#ifdef CONFIG_I2C_TARGET
354-
static inline uint8_t i2c_dw_read_byte_non_blocking(const struct device *dev);
354+
static inline int i2c_dw_read_byte_non_blocking(const struct device *dev, uint8_t *data);
355355
static inline void i2c_dw_write_byte_non_blocking(const struct device *dev, uint8_t data);
356356
static void i2c_dw_slave_read_clear_intr_bits(const struct device *dev);
357357
#endif
@@ -364,8 +364,8 @@ static void i2c_dw_isr(const struct device *port)
364364
int ret = 0;
365365
uint32_t reg_base = get_regs(port);
366366

367-
/* Cache ic_intr_stat for processing, so there is no need to read
368-
* the register multiple times.
367+
/* Cache I2C Interrupt Status Register(ic_intr_stat) for processing,
368+
* so there is no need to read the register multiple times.
369369
*/
370370
intr_stat.raw = read_intr_stat(reg_base);
371371

@@ -512,7 +512,7 @@ static int i2c_dw_setup(const struct device *dev, uint16_t slave_address)
512512
* Make sure to set both the master_mode and slave_disable_bit
513513
* to both 0 or both 1
514514
*/
515-
LOG_DBG("I2C: host configured as Master Device");
515+
LOG_DBG("I2C: host configured as Controller Device");
516516
ic_con.bits.master_mode = 1U;
517517
ic_con.bits.slave_disable = 1U;
518518
} else {
@@ -619,7 +619,7 @@ static int i2c_dw_transfer(const struct device *dev, struct i2c_msg *msgs, uint8
619619
{
620620
struct i2c_dw_dev_config *const dw = dev->data;
621621
struct i2c_msg *cur_msg = msgs;
622-
uint8_t msg_left = num_msgs;
622+
uint8_t msg_left;
623623
uint8_t pflags;
624624
int ret;
625625
uint32_t reg_base = get_regs(dev);
@@ -635,6 +635,21 @@ static int i2c_dw_transfer(const struct device *dev, struct i2c_msg *msgs, uint8
635635
return ret;
636636
}
637637

638+
/*
639+
* I2C transfer with 0 length are invalid but
640+
* SMbus quick commands uses 0 length transfer.
641+
* And in order to support SMbus quick commands, the driver
642+
* has to be enhanced.
643+
*/
644+
for (msg_left = 0; msg_left < num_msgs; msg_left++) {
645+
if (msgs[msg_left].len == 0) {
646+
LOG_ERR("Invalid transfer length 0 for msgs[%d]", msg_left);
647+
return -EINVAL;
648+
}
649+
}
650+
651+
msg_left = num_msgs;
652+
638653
/* First step, check if there is current activity */
639654
if (test_bit_status_activity(reg_base) || (dw->state & I2C_DW_BUSY)) {
640655
ret = -EBUSY;
@@ -741,14 +756,13 @@ static int i2c_dw_runtime_configure(const struct device *dev, uint32_t config)
741756
struct i2c_dw_dev_config *const dw = dev->data;
742757
const struct i2c_dw_rom_config *const rom = dev->config;
743758
uint32_t value = 0U;
744-
uint32_t rc = 0U;
759+
uint32_t ret = 0U;
745760
uint32_t reg_base = get_regs(dev);
746761

747-
dw->app_config = config;
748762

749763
/* Make sure we have a supported speed for the DesignWare model */
750764
/* and have setup the clock frequency and speed mode */
751-
switch (I2C_SPEED_GET(dw->app_config)) {
765+
switch (I2C_SPEED_GET(config)) {
752766
case I2C_SPEED_STANDARD:
753767
/* Following the directions on DW spec page 59, IC_SS_SCL_LCNT
754768
* must have register values larger than IC_FS_SPKLEN + 7
@@ -832,14 +846,17 @@ static int i2c_dw_runtime_configure(const struct device *dev, uint32_t config)
832846

833847
dw->hcnt = value;
834848
} else {
835-
rc = -EINVAL;
849+
ret = -EINVAL;
836850
}
837851
break;
838852
default:
839853
/* TODO change */
840-
rc = -EINVAL;
854+
ret = -EINVAL;
841855
}
842856

857+
if (ret == 0) {
858+
dw->app_config = config;
859+
}
843860
/*
844861
* Clear any interrupts currently waiting in the controller
845862
*/
@@ -852,26 +869,30 @@ static int i2c_dw_runtime_configure(const struct device *dev, uint32_t config)
852869
*/
853870
dw->app_config |= I2C_MODE_CONTROLLER;
854871

855-
return rc;
872+
return ret;
856873
}
857874

858875
#ifdef CONFIG_I2C_TARGET
859-
static inline uint8_t i2c_dw_read_byte_non_blocking(const struct device *dev)
876+
static inline int i2c_dw_read_byte_non_blocking(const struct device *dev, uint8_t *data)
860877
{
861878
uint32_t reg_base = get_regs(dev);
862879

863880
if (!test_bit_status_rfne(reg_base)) { /* Rx FIFO must not be empty */
881+
LOG_ERR("Rx FIFO is empty");
864882
return -EIO;
865883
}
866884

867-
return (uint8_t)read_cmd_data(reg_base);
885+
*data = (uint8_t)read_cmd_data(reg_base);
886+
887+
return 0;
868888
}
869889

870890
static inline void i2c_dw_write_byte_non_blocking(const struct device *dev, uint8_t data)
871891
{
872892
uint32_t reg_base = get_regs(dev);
873893

874894
if (!test_bit_status_tfnt(reg_base)) { /* Tx FIFO must not be full */
895+
LOG_ERR("Tx FIFO is full");
875896
return;
876897
}
877898

@@ -925,7 +946,7 @@ static int i2c_dw_set_slave_mode(const struct device *dev, uint8_t addr)
925946
write_tx_tl(0, reg_base);
926947
write_rx_tl(0, reg_base);
927948

928-
LOG_DBG("I2C: Host registered as Slave Device");
949+
LOG_DBG("I2C: Host registered as Target Device");
929950

930951
return 0;
931952
}
@@ -947,13 +968,11 @@ static int i2c_dw_slave_register(const struct device *dev, struct i2c_target_con
947968

948969
static int i2c_dw_slave_unregister(const struct device *dev, struct i2c_target_config *cfg)
949970
{
950-
struct i2c_dw_dev_config *const dw = dev->data;
951-
int ret;
971+
struct i2c_dw_dev_config * const dw = dev->data;
952972

953973
dw->state = I2C_DW_STATE_READY;
954-
ret = i2c_dw_set_master_mode(dev);
955974

956-
return ret;
975+
return (int)i2c_dw_set_master_mode(dev);
957976
}
958977

959978
static void i2c_dw_slave_read_clear_intr_bits(const struct device *dev)

drivers/i2c/i2c_dw.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
/* dw_i2c.h - header for Design Ware I2C operations */
1+
/* i2c_dw.h - header for Design Ware I2C operations */
22

33
/*
4-
* Copyright (c) 2015 Intel Corporation
4+
* Copyright (c) 2015-2023 Intel Corporation
55
*
66
* SPDX-License-Identifier: Apache-2.0
77
*/

drivers/i2c/i2c_shell.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ static int get_bytes_count_for_hex(char *arg)
3333
return MIN(MAX_BYTES_FOR_REGISTER_INDEX, length);
3434
}
3535

36+
#if CONFIG_I2C_ZERO_LEN_XFER_SUPPORTED
3637
/*
3738
* This sends I2C messages without any data (i.e. stop condition after
3839
* sending just the address). If there is an ACK for the address, it
@@ -94,6 +95,7 @@ static int cmd_i2c_scan(const struct shell *shell_ctx,
9495

9596
return 0;
9697
}
98+
#endif
9799

98100
/* i2c recover <device> */
99101
static int cmd_i2c_recover(const struct shell *shell_ctx,
@@ -318,6 +320,12 @@ static int cmd_i2c_speed(const struct shell *shell_ctx, size_t argc, char **argv
318320
}
319321

320322
speed = strtol(argv[ARGV_DEV + 1], NULL, 10);
323+
324+
if (speed > I2C_SPEED_DT) {
325+
shell_error(shell_ctx, "Invalid speed parameter");
326+
return -EINVAL;
327+
}
328+
321329
ret = i2c_get_config(dev, &dev_config);
322330
if (ret == 0) {
323331
dev_config &= ~I2C_SPEED_MASK;
@@ -349,10 +357,12 @@ static void device_name_get(size_t idx, struct shell_static_entry *entry)
349357
SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
350358

351359
SHELL_STATIC_SUBCMD_SET_CREATE(sub_i2c_cmds,
360+
#if CONFIG_I2C_ZERO_LEN_XFER_SUPPORTED
352361
SHELL_CMD_ARG(scan, &dsub_device_name,
353362
"Scan I2C devices\n"
354363
"Usage: scan <device>",
355364
cmd_i2c_scan, 2, 0),
365+
#endif
356366
SHELL_CMD_ARG(recover, &dsub_device_name,
357367
"Recover I2C bus\n"
358368
"Usage: recover <device>",

dts/bindings/i2c/snps,designware-i2c.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Synopsys DesignWare I2C node
55

66
compatible: "snps,designware-i2c"
77

8-
include: [i2c-controller.yaml, pinctrl-device.yaml, pcie-device.yaml]
8+
include: [i2c-controller.yaml, reset-device.yaml, pinctrl-device.yaml, pcie-device.yaml]
99

1010
properties:
1111
interrupts:

0 commit comments

Comments
 (0)