|  | /* | 
|  | * Copyright (c) 2019 Intel Corporation | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT microchip_xec_i2c | 
|  |  | 
|  | #include <zephyr/drivers/clock_control.h> | 
|  | #include <zephyr/kernel.h> | 
|  | #include <soc.h> | 
|  | #include <errno.h> | 
|  | #include <zephyr/drivers/gpio.h> | 
|  | #include <zephyr/drivers/i2c.h> | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_REGISTER(i2c_mchp, CONFIG_I2C_LOG_LEVEL); | 
|  |  | 
|  | #define SPEED_100KHZ_BUS    0 | 
|  | #define SPEED_400KHZ_BUS    1 | 
|  | #define SPEED_1MHZ_BUS      2 | 
|  |  | 
|  | #define EC_OWN_I2C_ADDR		0x7F | 
|  | #define RESET_WAIT_US		20 | 
|  | #define BUS_IDLE_US_DFLT	5 | 
|  |  | 
|  | /* I2C timeout is 10 ms (WAIT_INTERVAL * WAIT_COUNT) */ | 
|  | #define WAIT_INTERVAL		50 | 
|  | #define WAIT_COUNT		200 | 
|  |  | 
|  | /* Line High Timeout is 2.5 ms (WAIT_LINE_HIGH_USEC * WAIT_LINE_HIGH_COUNT) */ | 
|  | #define WAIT_LINE_HIGH_USEC	25 | 
|  | #define WAIT_LINE_HIGH_COUNT	100 | 
|  |  | 
|  | /* I2C Read/Write bit pos */ | 
|  | #define I2C_READ_WRITE_POS  0 | 
|  |  | 
|  | struct xec_speed_cfg { | 
|  | uint32_t bus_clk; | 
|  | uint32_t data_timing; | 
|  | uint32_t start_hold_time; | 
|  | uint32_t config; | 
|  | uint32_t timeout_scale; | 
|  | }; | 
|  |  | 
|  | struct i2c_xec_config { | 
|  | uint32_t port_sel; | 
|  | uint32_t base_addr; | 
|  | uint8_t girq_id; | 
|  | uint8_t girq_bit; | 
|  | struct gpio_dt_spec sda_gpio; | 
|  | struct gpio_dt_spec scl_gpio; | 
|  | void (*irq_config_func)(void); | 
|  | }; | 
|  |  | 
|  | struct i2c_xec_data { | 
|  | uint32_t pending_stop; | 
|  | uint32_t error_seen; | 
|  | uint32_t timeout_seen; | 
|  | uint32_t previously_in_read; | 
|  | uint32_t speed_id; | 
|  | struct i2c_target_config *slave_cfg; | 
|  | bool slave_attached; | 
|  | bool slave_read; | 
|  | }; | 
|  |  | 
|  | /* Recommended programming values based on 16MHz | 
|  | * i2c_baud_clk_period/bus_clk_period - 2 = (low_period + hi_period) | 
|  | * bus_clk_reg (16MHz/100KHz -2) = 0x4F + 0x4F | 
|  | *             (16MHz/400KHz -2) = 0x0F + 0x17 | 
|  | *             (16MHz/1MHz -2) = 0x05 + 0x09 | 
|  | */ | 
|  | static const struct xec_speed_cfg xec_cfg_params[] = { | 
|  | [SPEED_100KHZ_BUS] = { | 
|  | .bus_clk            = 0x00004F4F, | 
|  | .data_timing        = 0x0C4D5006, | 
|  | .start_hold_time    = 0x0000004D, | 
|  | .config             = 0x01FC01ED, | 
|  | .timeout_scale      = 0x4B9CC2C7, | 
|  | }, | 
|  | [SPEED_400KHZ_BUS] = { | 
|  | .bus_clk            = 0x00000F17, | 
|  | .data_timing        = 0x040A0A06, | 
|  | .start_hold_time    = 0x0000000A, | 
|  | .config             = 0x01000050, | 
|  | .timeout_scale      = 0x159CC2C7, | 
|  | }, | 
|  | [SPEED_1MHZ_BUS] = { | 
|  | .bus_clk            = 0x00000509, | 
|  | .data_timing        = 0x04060601, | 
|  | .start_hold_time    = 0x00000006, | 
|  | .config             = 0x10000050, | 
|  | .timeout_scale      = 0x089CC2C7, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static void i2c_xec_reset_config(const struct device *dev) | 
|  | { | 
|  | const struct i2c_xec_config *config = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  |  | 
|  | uint32_t ba = config->base_addr; | 
|  |  | 
|  | /* Assert RESET and clr others */ | 
|  | MCHP_I2C_SMB_CFG(ba) = MCHP_I2C_SMB_CFG_RESET; | 
|  |  | 
|  | k_busy_wait(RESET_WAIT_US); | 
|  |  | 
|  | /* Bus reset */ | 
|  | MCHP_I2C_SMB_CFG(ba) = 0; | 
|  |  | 
|  | /* Write 0x80. i.e Assert PIN bit, ESO = 0 and Interrupts | 
|  | * disabled (ENI) | 
|  | */ | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = MCHP_I2C_SMB_CTRL_PIN; | 
|  |  | 
|  | /* Enable controller and I2C filters */ | 
|  | MCHP_I2C_SMB_CFG(ba) = MCHP_I2C_SMB_CFG_GC_EN | | 
|  | MCHP_I2C_SMB_CFG_ENAB | | 
|  | MCHP_I2C_SMB_CFG_FEN | | 
|  | (config->port_sel & | 
|  | MCHP_I2C_SMB_CFG_PORT_SEL_MASK); | 
|  |  | 
|  | /* Configure bus clock register, Data Timing register, | 
|  | * Repeated Start Hold Time register, | 
|  | * and Timeout Scaling register | 
|  | */ | 
|  | MCHP_I2C_SMB_BUS_CLK(ba) = xec_cfg_params[data->speed_id].bus_clk; | 
|  | MCHP_I2C_SMB_DATA_TM(ba) = xec_cfg_params[data->speed_id].data_timing; | 
|  | MCHP_I2C_SMB_RSHT(ba) = | 
|  | xec_cfg_params[data->speed_id].start_hold_time; | 
|  | MCHP_I2C_SMB_TMTSC(ba) = xec_cfg_params[data->speed_id].timeout_scale; | 
|  |  | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = MCHP_I2C_SMB_CTRL_PIN | | 
|  | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_ACK; | 
|  |  | 
|  | k_busy_wait(RESET_WAIT_US); | 
|  | } | 
|  |  | 
|  | static int xec_spin_yield(int *counter) | 
|  | { | 
|  | *counter = *counter + 1; | 
|  |  | 
|  | if (*counter > WAIT_COUNT) { | 
|  | return -ETIMEDOUT; | 
|  | } | 
|  |  | 
|  | k_busy_wait(WAIT_INTERVAL); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void cleanup_registers(uint32_t ba) | 
|  | { | 
|  | uint32_t cfg = MCHP_I2C_SMB_CFG(ba); | 
|  |  | 
|  | cfg |= MCHP_I2C_SMB_CFG_FLUSH_MXBUF_WO; | 
|  | MCHP_I2C_SMB_CFG(ba) = cfg; | 
|  | cfg &= ~MCHP_I2C_SMB_CFG_FLUSH_MXBUF_WO; | 
|  |  | 
|  | cfg |= MCHP_I2C_SMB_CFG_FLUSH_MRBUF_WO; | 
|  | MCHP_I2C_SMB_CFG(ba) = cfg; | 
|  | cfg &= ~MCHP_I2C_SMB_CFG_FLUSH_MRBUF_WO; | 
|  |  | 
|  | cfg |= MCHP_I2C_SMB_CFG_FLUSH_SXBUF_WO; | 
|  | MCHP_I2C_SMB_CFG(ba) = cfg; | 
|  | cfg &= ~MCHP_I2C_SMB_CFG_FLUSH_SXBUF_WO; | 
|  |  | 
|  | cfg |= MCHP_I2C_SMB_CFG_FLUSH_SRBUF_WO; | 
|  | MCHP_I2C_SMB_CFG(ba) = cfg; | 
|  | cfg &= ~MCHP_I2C_SMB_CFG_FLUSH_SRBUF_WO; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | static void restart_slave(uint32_t ba) | 
|  | { | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = MCHP_I2C_SMB_CTRL_PIN | | 
|  | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_ACK | | 
|  | MCHP_I2C_SMB_CTRL_ENI; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void recover_from_error(const struct device *dev) | 
|  | { | 
|  | const struct i2c_xec_config *config = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | uint32_t ba = config->base_addr; | 
|  |  | 
|  | cleanup_registers(ba); | 
|  | i2c_xec_reset_config(dev); | 
|  | } | 
|  |  | 
|  | static int wait_bus_free(const struct device *dev) | 
|  | { | 
|  | const struct i2c_xec_config *config = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | int ret; | 
|  | int counter = 0; | 
|  | uint32_t ba = config->base_addr; | 
|  |  | 
|  | while (!(MCHP_I2C_SMB_STS_RO(ba) & MCHP_I2C_SMB_STS_NBB)) { | 
|  | ret = xec_spin_yield(&counter); | 
|  |  | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Check for bus error */ | 
|  | if (MCHP_I2C_SMB_STS_RO(ba) & MCHP_I2C_SMB_STS_BER) { | 
|  | recover_from_error(dev); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Wait with timeout for I2C controller to finish transmit/receive of one | 
|  | * byte(address or data). | 
|  | * When transmit/receive operation is started the I2C PIN status is 1. Upon | 
|  | * normal completion I2C PIN status asserts(0). | 
|  | * We loop checking I2C status for the following events: | 
|  | * Bus Error: | 
|  | *      Reset controller and return -EBUSY | 
|  | * Lost Arbitration: | 
|  | *      Return -EPERM. We lost bus to another controller. No reset. | 
|  | * PIN == 0: I2C Status LRB is valid and contains ACK/NACK data on 9th clock. | 
|  | *      ACK return 0 (success) | 
|  | *      NACK Issue STOP, wait for bus minimum idle time, return -EIO. | 
|  | * Timeout: | 
|  | *      Reset controller and return -ETIMEDOUT | 
|  | * | 
|  | * NOTE: After generating a STOP the controller will not generate a START until | 
|  | * Bus Minimum Idle time has expired. | 
|  | */ | 
|  | static int wait_completion(const struct device *dev) | 
|  | { | 
|  | const struct i2c_xec_config *config = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | int ret; | 
|  | int counter = 0; | 
|  | uint32_t ba = config->base_addr; | 
|  |  | 
|  | while (1) { | 
|  | uint8_t status = MCHP_I2C_SMB_STS_RO(ba); | 
|  |  | 
|  | /* Is bus error ? */ | 
|  | if (status & MCHP_I2C_SMB_STS_BER) { | 
|  | recover_from_error(dev); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | /* Is Lost arbitration ? */ | 
|  | status = MCHP_I2C_SMB_STS_RO(ba); | 
|  | if (status & MCHP_I2C_SMB_STS_LAB) { | 
|  | recover_from_error(dev); | 
|  | return -EPERM; | 
|  | } | 
|  |  | 
|  | status = MCHP_I2C_SMB_STS_RO(ba); | 
|  | /* PIN -> 0 indicates I2C is done */ | 
|  | if (!(status & MCHP_I2C_SMB_STS_PIN)) { | 
|  | /* PIN == 0. LRB contains state of 9th bit */ | 
|  | if (status & MCHP_I2C_SMB_STS_LRB_AD0) { /* NACK? */ | 
|  | /* Send STOP */ | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = | 
|  | MCHP_I2C_SMB_CTRL_PIN | | 
|  | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_STO | | 
|  | MCHP_I2C_SMB_CTRL_ACK; | 
|  | k_busy_wait(BUS_IDLE_US_DFLT); | 
|  | return -EIO; | 
|  | } | 
|  | break; /* success: ACK */ | 
|  | } | 
|  |  | 
|  | ret = xec_spin_yield(&counter); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Call GPIO driver to read state of pins. | 
|  | * Return boolean true if both lines HIGH else return boolean false | 
|  | */ | 
|  | static bool check_lines_high(const struct device *dev) | 
|  | { | 
|  | const struct i2c_xec_config *config = | 
|  | (const struct i2c_xec_config *const)(dev->config); | 
|  | gpio_port_value_t sda = 0, scl = 0; | 
|  |  | 
|  | if (gpio_port_get_raw(config->sda_gpio.port, &sda)) { | 
|  | LOG_ERR("gpio_port_get_raw for %s SDA failed", dev->name); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* both pins could be on same GPIO group */ | 
|  | if (config->sda_gpio.port == config->scl_gpio.port) { | 
|  | scl = sda; | 
|  | } else { | 
|  | if (gpio_port_get_raw(config->scl_gpio.port, &scl)) { | 
|  | LOG_ERR("gpio_port_get_raw for %s SCL failed", | 
|  | dev->name); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return (sda & BIT(config->sda_gpio.pin)) && (scl & BIT(config->scl_gpio.pin)); | 
|  |  | 
|  | } | 
|  |  | 
|  | static int i2c_xec_configure(const struct device *dev, | 
|  | uint32_t dev_config_raw) | 
|  | { | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  |  | 
|  | if (!(dev_config_raw & I2C_MODE_CONTROLLER)) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | if (dev_config_raw & I2C_ADDR_10_BITS) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | switch (I2C_SPEED_GET(dev_config_raw)) { | 
|  | case I2C_SPEED_STANDARD: | 
|  | data->speed_id = SPEED_100KHZ_BUS; | 
|  | break; | 
|  | case I2C_SPEED_FAST: | 
|  | data->speed_id = SPEED_400KHZ_BUS; | 
|  | break; | 
|  | case I2C_SPEED_FAST_PLUS: | 
|  | data->speed_id = SPEED_1MHZ_BUS; | 
|  | break; | 
|  | default: | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | i2c_xec_reset_config(dev); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_xec_poll_write(const struct device *dev, struct i2c_msg msg, | 
|  | uint16_t addr) | 
|  | { | 
|  | const struct i2c_xec_config *config = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | uint32_t ba = config->base_addr; | 
|  | uint8_t i2c_timer = 0, byte; | 
|  | int ret; | 
|  |  | 
|  | if (data->timeout_seen == 1) { | 
|  | /* Wait to see if the slave has released the CLK */ | 
|  | ret = wait_completion(dev); | 
|  | if (ret) { | 
|  | data->timeout_seen = 1; | 
|  | LOG_ERR("%s: %s wait_completion failure %d\n", | 
|  | __func__, dev->name, ret); | 
|  | return ret; | 
|  | } | 
|  | data->timeout_seen = 0; | 
|  |  | 
|  | /* If we are here, it means the slave has finally released | 
|  | * the CLK. The master needs to end that transaction | 
|  | * gracefully by sending a STOP on the bus. | 
|  | */ | 
|  | LOG_DBG("%s: %s Force Stop", __func__, dev->name); | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = | 
|  | MCHP_I2C_SMB_CTRL_PIN | | 
|  | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_STO | | 
|  | MCHP_I2C_SMB_CTRL_ACK; | 
|  | k_busy_wait(BUS_IDLE_US_DFLT); | 
|  | data->pending_stop = 0; | 
|  |  | 
|  | /* If the timeout had occurred while the master was reading | 
|  | * something from the slave, that read needs to be completed | 
|  | * to clear the bus. | 
|  | */ | 
|  | if (data->previously_in_read == 1) { | 
|  | data->previously_in_read = 0; | 
|  | byte = MCHP_I2C_SMB_DATA(ba); | 
|  | } | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | if ((data->pending_stop == 0) || (data->error_seen == 1)) { | 
|  | /* Wait till clock and data lines are HIGH */ | 
|  | while (check_lines_high(dev) == false) { | 
|  | if (i2c_timer >= WAIT_LINE_HIGH_COUNT) { | 
|  | LOG_DBG("%s: %s not high", | 
|  | __func__, dev->name); | 
|  | data->error_seen = 1; | 
|  | return -EBUSY; | 
|  | } | 
|  | k_busy_wait(WAIT_LINE_HIGH_USEC); | 
|  | i2c_timer++; | 
|  | } | 
|  |  | 
|  | if (data->error_seen) { | 
|  | LOG_DBG("%s: Recovering %s previously in error", | 
|  | __func__, dev->name); | 
|  | data->error_seen = 0; | 
|  | recover_from_error(dev); | 
|  | } | 
|  |  | 
|  | /* Wait until bus is free */ | 
|  | ret = wait_bus_free(dev); | 
|  | if (ret) { | 
|  | data->error_seen = 1; | 
|  | LOG_DBG("%s: %s wait_bus_free failure %d", | 
|  | __func__, dev->name, ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Send slave address */ | 
|  | MCHP_I2C_SMB_DATA(ba) = (addr & ~BIT(0)); | 
|  |  | 
|  | /* Send start and ack bits */ | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = MCHP_I2C_SMB_CTRL_PIN | | 
|  | MCHP_I2C_SMB_CTRL_ESO | MCHP_I2C_SMB_CTRL_STA | | 
|  | MCHP_I2C_SMB_CTRL_ACK; | 
|  |  | 
|  | ret = wait_completion(dev); | 
|  | switch (ret) { | 
|  | case 0:	/* Success */ | 
|  | break; | 
|  |  | 
|  | case -EIO: | 
|  | LOG_WRN("%s: No Addr ACK from Slave 0x%x on %s", | 
|  | __func__, addr >> 1, dev->name); | 
|  | return ret; | 
|  |  | 
|  | default: | 
|  | data->error_seen = 1; | 
|  | LOG_ERR("%s: %s wait_comp error %d for addr send", | 
|  | __func__, dev->name, ret); | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Send bytes */ | 
|  | for (int i = 0U; i < msg.len; i++) { | 
|  | MCHP_I2C_SMB_DATA(ba) = msg.buf[i]; | 
|  | ret = wait_completion(dev); | 
|  |  | 
|  | switch (ret) { | 
|  | case 0:	/* Success */ | 
|  | break; | 
|  |  | 
|  | case -EIO: | 
|  | LOG_ERR("%s: No Data ACK from Slave 0x%x on %s", | 
|  | __func__, addr >> 1, dev->name); | 
|  | return ret; | 
|  |  | 
|  | case -ETIMEDOUT: | 
|  | data->timeout_seen = 1; | 
|  | LOG_ERR("%s: Clk stretch Timeout - Slave 0x%x on %s", | 
|  | __func__, addr >> 1, dev->name); | 
|  | return ret; | 
|  |  | 
|  | default: | 
|  | data->error_seen = 1; | 
|  | LOG_ERR("%s: %s wait_completion error %d for data send", | 
|  | __func__, dev->name, ret); | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Handle stop bit for last byte to write */ | 
|  | if (msg.flags & I2C_MSG_STOP) { | 
|  | /* Send stop and ack bits */ | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = | 
|  | MCHP_I2C_SMB_CTRL_PIN | | 
|  | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_STO | | 
|  | MCHP_I2C_SMB_CTRL_ACK; | 
|  | data->pending_stop = 0; | 
|  | } else { | 
|  | data->pending_stop = 1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_xec_poll_read(const struct device *dev, struct i2c_msg msg, | 
|  | uint16_t addr) | 
|  | { | 
|  | const struct i2c_xec_config *config = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | uint32_t ba = config->base_addr; | 
|  | uint8_t byte, ctrl, i2c_timer = 0; | 
|  | int ret; | 
|  |  | 
|  | if (data->timeout_seen == 1) { | 
|  | /* Wait to see if the slave has released the CLK */ | 
|  | ret = wait_completion(dev); | 
|  | if (ret) { | 
|  | data->timeout_seen = 1; | 
|  | LOG_ERR("%s: %s wait_completion failure %d\n", | 
|  | __func__, dev->name, ret); | 
|  | return ret; | 
|  | } | 
|  | data->timeout_seen = 0; | 
|  |  | 
|  | /* If we are here, it means the slave has finally released | 
|  | * the CLK. The master needs to end that transaction | 
|  | * gracefully by sending a STOP on the bus. | 
|  | */ | 
|  | LOG_DBG("%s: %s Force Stop", __func__, dev->name); | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = | 
|  | MCHP_I2C_SMB_CTRL_PIN | | 
|  | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_STO | | 
|  | MCHP_I2C_SMB_CTRL_ACK; | 
|  | k_busy_wait(BUS_IDLE_US_DFLT); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | if (!(msg.flags & I2C_MSG_RESTART) || (data->error_seen == 1)) { | 
|  | /* Wait till clock and data lines are HIGH */ | 
|  | while (check_lines_high(dev) == false) { | 
|  | if (i2c_timer >= WAIT_LINE_HIGH_COUNT) { | 
|  | LOG_DBG("%s: %s not high", | 
|  | __func__, dev->name); | 
|  | data->error_seen = 1; | 
|  | return -EBUSY; | 
|  | } | 
|  | k_busy_wait(WAIT_LINE_HIGH_USEC); | 
|  | i2c_timer++; | 
|  | } | 
|  |  | 
|  | if (data->error_seen) { | 
|  | LOG_DBG("%s: Recovering %s previously in error", | 
|  | __func__, dev->name); | 
|  | data->error_seen = 0; | 
|  | recover_from_error(dev); | 
|  | } | 
|  |  | 
|  | /* Wait until bus is free */ | 
|  | ret = wait_bus_free(dev); | 
|  | if (ret) { | 
|  | data->error_seen = 1; | 
|  | LOG_DBG("%s: %s wait_bus_free failure %d", | 
|  | __func__, dev->name, ret); | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* MCHP I2C spec recommends that for repeated start to write to control | 
|  | * register before writing to data register | 
|  | */ | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_STA | MCHP_I2C_SMB_CTRL_ACK; | 
|  |  | 
|  | /* Send slave address */ | 
|  | MCHP_I2C_SMB_DATA(ba) = (addr | BIT(0)); | 
|  |  | 
|  | ret = wait_completion(dev); | 
|  | switch (ret) { | 
|  | case 0:	/* Success */ | 
|  | break; | 
|  |  | 
|  | case -EIO: | 
|  | data->error_seen = 1; | 
|  | LOG_WRN("%s: No Addr ACK from Slave 0x%x on %s", | 
|  | __func__, addr >> 1, dev->name); | 
|  | return ret; | 
|  |  | 
|  | case -ETIMEDOUT: | 
|  | data->previously_in_read = 1; | 
|  | data->timeout_seen = 1; | 
|  | LOG_ERR("%s: Clk stretch Timeout - Slave 0x%x on %s", | 
|  | __func__, addr >> 1, dev->name); | 
|  | return ret; | 
|  |  | 
|  | default: | 
|  | data->error_seen = 1; | 
|  | LOG_ERR("%s: %s wait_completion error %d for address send", | 
|  | __func__, dev->name, ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | if (msg.len == 1) { | 
|  | /* Send NACK for last transaction */ | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = MCHP_I2C_SMB_CTRL_ESO; | 
|  | } | 
|  |  | 
|  | /* Read dummy byte */ | 
|  | byte = MCHP_I2C_SMB_DATA(ba); | 
|  |  | 
|  | for (int i = 0U; i < msg.len; i++) { | 
|  | ret = wait_completion(dev); | 
|  | switch (ret) { | 
|  | case 0:	/* Success */ | 
|  | break; | 
|  |  | 
|  | case -EIO: | 
|  | LOG_ERR("%s: No Data ACK from Slave 0x%x on %s", | 
|  | __func__, addr >> 1, dev->name); | 
|  | return ret; | 
|  |  | 
|  | case -ETIMEDOUT: | 
|  | data->previously_in_read = 1; | 
|  | data->timeout_seen = 1; | 
|  | LOG_ERR("%s: Clk stretch Timeout - Slave 0x%x on %s", | 
|  | __func__, addr >> 1, dev->name); | 
|  | return ret; | 
|  |  | 
|  | default: | 
|  | data->error_seen = 1; | 
|  | LOG_ERR("%s: %s wait_completion error %d for data send", | 
|  | __func__, dev->name, ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | if (i == (msg.len - 1)) { | 
|  | if (msg.flags & I2C_MSG_STOP) { | 
|  | /* Send stop and ack bits */ | 
|  | ctrl = (MCHP_I2C_SMB_CTRL_PIN | | 
|  | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_STO | | 
|  | MCHP_I2C_SMB_CTRL_ACK); | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = ctrl; | 
|  | data->pending_stop = 0; | 
|  | } | 
|  | } else if (i == (msg.len - 2)) { | 
|  | /* Send NACK for last transaction */ | 
|  | MCHP_I2C_SMB_CTRL_WO(ba) = MCHP_I2C_SMB_CTRL_ESO; | 
|  | } | 
|  | msg.buf[i] = MCHP_I2C_SMB_DATA(ba); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_xec_transfer(const struct device *dev, struct i2c_msg *msgs, | 
|  | uint8_t num_msgs, uint16_t addr) | 
|  | { | 
|  | int ret = 0; | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | struct i2c_xec_data *data = dev->data; | 
|  |  | 
|  | if (data->slave_attached) { | 
|  | LOG_ERR("%s Device is registered as slave", dev->name); | 
|  | return -EBUSY; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | addr <<= 1; | 
|  | for (int i = 0U; i < num_msgs; i++) { | 
|  | if ((msgs[i].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) { | 
|  | ret = i2c_xec_poll_write(dev, msgs[i], addr); | 
|  | if (ret) { | 
|  | LOG_ERR("%s Write error: %d", dev->name, ret); | 
|  | return ret; | 
|  | } | 
|  | } else { | 
|  | ret = i2c_xec_poll_read(dev, msgs[i], addr); | 
|  | if (ret) { | 
|  | LOG_ERR("%s Read error: %d", dev->name, ret); | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void i2c_xec_bus_isr(const struct device *dev) | 
|  | { | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | const struct i2c_xec_config *config = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = dev->data; | 
|  | const struct i2c_target_callbacks *slave_cb = data->slave_cfg->callbacks; | 
|  | uint32_t ba = config->base_addr; | 
|  |  | 
|  | uint32_t status; | 
|  | uint8_t val; | 
|  |  | 
|  | uint8_t dummy = 0U; | 
|  |  | 
|  | if (!data->slave_attached) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Get current status */ | 
|  | status = MCHP_I2C_SMB_STS_RO(ba); | 
|  |  | 
|  | /* Bus Error */ | 
|  | if (status & MCHP_I2C_SMB_STS_BER) { | 
|  | if (slave_cb->stop) { | 
|  | slave_cb->stop(data->slave_cfg); | 
|  | } | 
|  | restart_slave(ba); | 
|  | goto clear_iag; | 
|  | } | 
|  |  | 
|  | /* External stop */ | 
|  | if (status & MCHP_I2C_SMB_STS_EXT_STOP) { | 
|  | if (slave_cb->stop) { | 
|  | slave_cb->stop(data->slave_cfg); | 
|  | } | 
|  | dummy = MCHP_I2C_SMB_DATA(ba); | 
|  | restart_slave(ba); | 
|  | goto clear_iag; | 
|  | } | 
|  |  | 
|  | /* Address byte handling */ | 
|  | if (status & MCHP_I2C_SMB_STS_AAS) { | 
|  | uint8_t slv_data = MCHP_I2C_SMB_DATA(ba); | 
|  |  | 
|  | if (!(slv_data & BIT(I2C_READ_WRITE_POS))) { | 
|  | /* Slave receive  */ | 
|  | data->slave_read = false; | 
|  | if (slave_cb->write_requested) { | 
|  | slave_cb->write_requested(data->slave_cfg); | 
|  | } | 
|  | goto clear_iag; | 
|  | } else { | 
|  | /* Slave transmit */ | 
|  | data->slave_read = true; | 
|  | if (slave_cb->read_requested) { | 
|  | slave_cb->read_requested(data->slave_cfg, &val); | 
|  | } | 
|  | MCHP_I2C_SMB_DATA(ba) = val; | 
|  | goto clear_iag; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Slave transmit */ | 
|  | if (data->slave_read) { | 
|  | /* Master has Nacked, then just write a dummy byte */ | 
|  | if (MCHP_I2C_SMB_STS_RO(ba) & MCHP_I2C_SMB_STS_LRB_AD0) { | 
|  | MCHP_I2C_SMB_DATA(ba) = dummy; | 
|  | } else { | 
|  | if (slave_cb->read_processed) { | 
|  | slave_cb->read_processed(data->slave_cfg, &val); | 
|  | } | 
|  | MCHP_I2C_SMB_DATA(ba) = val; | 
|  | } | 
|  | } else { | 
|  | val = MCHP_I2C_SMB_DATA(ba); | 
|  | /* TODO NACK Master */ | 
|  | if (slave_cb->write_received) { | 
|  | slave_cb->write_received(data->slave_cfg, val); | 
|  | } | 
|  | } | 
|  |  | 
|  | clear_iag: | 
|  | MCHP_GIRQ_SRC(config->girq_id) = BIT(config->girq_bit); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | static int i2c_xec_slave_register(const struct device *dev, | 
|  | struct i2c_target_config *config) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = dev->config; | 
|  | struct i2c_xec_data *data = dev->data; | 
|  | uint32_t ba = cfg->base_addr; | 
|  | int ret; | 
|  | int counter = 0; | 
|  |  | 
|  | if (!config) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (data->slave_attached) { | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | /* Wait for any outstanding transactions to complete so that | 
|  | * the bus is free | 
|  | */ | 
|  | while (!(MCHP_I2C_SMB_STS_RO(ba) & MCHP_I2C_SMB_STS_NBB)) { | 
|  | ret = xec_spin_yield(&counter); | 
|  |  | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | data->slave_cfg = config; | 
|  |  | 
|  | /* Set own address */ | 
|  | MCHP_I2C_SMB_OWN_ADDR(ba) = data->slave_cfg->address; | 
|  | restart_slave(ba); | 
|  |  | 
|  | data->slave_attached = true; | 
|  |  | 
|  | /* Clear before enabling girq bit */ | 
|  | MCHP_GIRQ_SRC(cfg->girq_id) = BIT(cfg->girq_bit); | 
|  | MCHP_GIRQ_ENSET(cfg->girq_id) = BIT(cfg->girq_bit); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_xec_slave_unregister(const struct device *dev, | 
|  | struct i2c_target_config *config) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = dev->config; | 
|  | struct i2c_xec_data *data = dev->data; | 
|  |  | 
|  | if (!data->slave_attached) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | data->slave_attached = false; | 
|  |  | 
|  | MCHP_GIRQ_ENCLR(cfg->girq_id) = BIT(cfg->girq_bit); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static const struct i2c_driver_api i2c_xec_driver_api = { | 
|  | .configure = i2c_xec_configure, | 
|  | .transfer = i2c_xec_transfer, | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | .slave_register = i2c_xec_slave_register, | 
|  | .slave_unregister = i2c_xec_slave_unregister, | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | static int i2c_xec_init(const struct device *dev) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = dev->config; | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | int ret; | 
|  |  | 
|  | data->pending_stop = 0; | 
|  | data->slave_attached = false; | 
|  |  | 
|  | if (!device_is_ready(cfg->sda_gpio.port)) { | 
|  | LOG_ERR("%s GPIO device is not ready for SDA GPIO", dev->name); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | if (!device_is_ready(cfg->scl_gpio.port)) { | 
|  | LOG_ERR("%s GPIO device is not ready for SCL GPIO", dev->name); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | /* Default configuration */ | 
|  | ret = i2c_xec_configure(dev, | 
|  | I2C_MODE_CONTROLLER | | 
|  | I2C_SPEED_SET(I2C_SPEED_STANDARD)); | 
|  | if (ret) { | 
|  | LOG_ERR("%s configure failed %d", dev->name, ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | const struct i2c_xec_config *config = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  |  | 
|  | config->irq_config_func(); | 
|  | #endif | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #define I2C_XEC_DEVICE(n)						\ | 
|  | static void i2c_xec_irq_config_func_##n(void);			\ | 
|  | \ | 
|  | static struct i2c_xec_data i2c_xec_data_##n;			\ | 
|  | static const struct i2c_xec_config i2c_xec_config_##n = {	\ | 
|  | .base_addr =						\ | 
|  | DT_INST_REG_ADDR(n),				\ | 
|  | .port_sel = DT_INST_PROP(n, port_sel),			\ | 
|  | .girq_id = DT_INST_PROP(n, girq),			\ | 
|  | .girq_bit = DT_INST_PROP(n, girq_bit),			\ | 
|  | .sda_gpio = GPIO_DT_SPEC_INST_GET(n, sda_gpios),	\ | 
|  | .scl_gpio = GPIO_DT_SPEC_INST_GET(n, scl_gpios),	\ | 
|  | .irq_config_func = i2c_xec_irq_config_func_##n,		\ | 
|  | };								\ | 
|  | I2C_DEVICE_DT_INST_DEFINE(n, i2c_xec_init, NULL,	\ | 
|  | &i2c_xec_data_##n, &i2c_xec_config_##n,			\ | 
|  | POST_KERNEL, CONFIG_I2C_INIT_PRIORITY,			\ | 
|  | &i2c_xec_driver_api);					\ | 
|  | \ | 
|  | static void i2c_xec_irq_config_func_##n(void)			\ | 
|  | {                                                               \ | 
|  | IRQ_CONNECT(DT_INST_IRQN(n),				\ | 
|  | DT_INST_IRQ(n, priority),			\ | 
|  | i2c_xec_bus_isr,				\ | 
|  | DEVICE_DT_INST_GET(n), 0);			\ | 
|  | irq_enable(DT_INST_IRQN(n));				\ | 
|  | } | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(I2C_XEC_DEVICE) |