|  | /* | 
|  | * Copyright (c) 2019 Intel Corporation | 
|  | * Copyright (c) 2021 Microchip Inc. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT microchip_xec_i2c_v2 | 
|  |  | 
|  | #include <zephyr/kernel.h> | 
|  | #include <soc.h> | 
|  | #include <errno.h> | 
|  | #include <zephyr/drivers/clock_control.h> | 
|  | #include <zephyr/drivers/gpio.h> | 
|  | #include <zephyr/drivers/i2c.h> | 
|  | #include <zephyr/drivers/interrupt_controller/intc_mchp_xec_ecia.h> | 
|  | #include <zephyr/drivers/pinctrl.h> | 
|  | #include <zephyr/sys/printk.h> | 
|  | #include <zephyr/sys/sys_io.h> | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_REGISTER(i2c_mchp, CONFIG_I2C_LOG_LEVEL); | 
|  |  | 
|  | #include "i2c-priv.h" | 
|  |  | 
|  | #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 | 
|  |  | 
|  | /* I2C timeout is  10 ms (WAIT_INTERVAL * WAIT_COUNT) */ | 
|  | #define WAIT_INTERVAL		50 | 
|  | #define WAIT_COUNT		200 | 
|  | #define STOP_WAIT_COUNT		500 | 
|  | #define PIN_CFG_WAIT		50 | 
|  |  | 
|  | /* I2C Read/Write bit pos */ | 
|  | #define I2C_READ_WRITE_POS	0 | 
|  |  | 
|  | /* I2C recover SCL low retries */ | 
|  | #define I2C_RECOVER_SCL_LOW_RETRIES	10 | 
|  | /* I2C recover SDA low retries */ | 
|  | #define I2C_RECOVER_SDA_LOW_RETRIES	3 | 
|  | /* I2C recovery bit bang delay */ | 
|  | #define I2C_RECOVER_BB_DELAY_US		5 | 
|  | /* I2C recovery SCL sample delay */ | 
|  | #define I2C_RECOVER_SCL_DELAY_US	50 | 
|  |  | 
|  | /* I2C SCL and SDA lines(signals) */ | 
|  | #define I2C_LINES_SCL_HI	BIT(SOC_I2C_SCL_POS) | 
|  | #define I2C_LINES_SDA_HI	BIT(SOC_I2C_SDA_POS) | 
|  | #define I2C_LINES_BOTH_HI	(I2C_LINES_SCL_HI | I2C_LINES_SDA_HI) | 
|  |  | 
|  | #define I2C_START		0U | 
|  | #define I2C_RPT_START		1U | 
|  |  | 
|  | #define I2C_ENI_DIS		0U | 
|  | #define I2C_ENI_EN		1U | 
|  |  | 
|  | #define I2C_WAIT_PIN_DEASSERT	0U | 
|  | #define I2C_WAIT_PIN_ASSERT	1U | 
|  |  | 
|  | #define I2C_XEC_CTRL_WR_DLY	8 | 
|  |  | 
|  | #define I2C_XEC_STATE_STOPPED	1U | 
|  | #define I2C_XEC_STATE_OPEN	2U | 
|  |  | 
|  | #define I2C_XEC_OK		0 | 
|  | #define I2C_XEC_ERR_LAB		1 | 
|  | #define I2C_XEC_ERR_BUS		2 | 
|  | #define I2C_XEC_ERR_TMOUT	3 | 
|  |  | 
|  | #define XEC_GPIO_CTRL_BASE	DT_REG_ADDR(DT_NODELABEL(gpio_000_036)) | 
|  |  | 
|  | struct xec_speed_cfg { | 
|  | uint32_t bus_clk; | 
|  | uint32_t data_timing; | 
|  | uint32_t start_hold_time; | 
|  | uint32_t idle_scale; | 
|  | uint32_t timeout_scale; | 
|  | }; | 
|  |  | 
|  | struct i2c_xec_config { | 
|  | uint32_t port_sel; | 
|  | uint32_t base_addr; | 
|  | uint32_t clock_freq; | 
|  | uint8_t girq; | 
|  | uint8_t girq_pos; | 
|  | uint8_t pcr_idx; | 
|  | uint8_t pcr_bitpos; | 
|  | const struct pinctrl_dev_config *pcfg; | 
|  | void (*irq_config_func)(void); | 
|  | }; | 
|  |  | 
|  | struct i2c_xec_data { | 
|  | uint8_t state; | 
|  | uint8_t read_discard; | 
|  | uint8_t speed_id; | 
|  | struct i2c_target_config *target_cfg; | 
|  | bool target_attached; | 
|  | bool target_read; | 
|  | uint32_t i2c_compl; | 
|  | uint8_t i2c_ctrl; | 
|  | uint8_t i2c_addr; | 
|  | uint8_t i2c_status; | 
|  | }; | 
|  |  | 
|  | /* 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, | 
|  | .idle_scale         = 0x01FC01ED, | 
|  | .timeout_scale      = 0x4B9CC2C7, | 
|  | }, | 
|  | [SPEED_400KHZ_BUS] = { | 
|  | .bus_clk            = 0x00000F17, | 
|  | .data_timing        = 0x040A0A06, | 
|  | .start_hold_time    = 0x0000000A, | 
|  | .idle_scale         = 0x01000050, | 
|  | .timeout_scale      = 0x159CC2C7, | 
|  | }, | 
|  | [SPEED_1MHZ_BUS] = { | 
|  | .bus_clk            = 0x00000509, | 
|  | .data_timing        = 0x04060601, | 
|  | .start_hold_time    = 0x00000006, | 
|  | .idle_scale         = 0x10000050, | 
|  | .timeout_scale      = 0x089CC2C7, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static void i2c_ctl_wr(const struct device *dev, uint8_t ctrl) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  |  | 
|  | data->i2c_ctrl = ctrl; | 
|  | regs->CTRLSTS = ctrl; | 
|  | for (int i = 0; i < I2C_XEC_CTRL_WR_DLY; i++) { | 
|  | regs->BLKID = ctrl; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int i2c_xec_reset_config(const struct device *dev); | 
|  |  | 
|  | static int wait_bus_free(const struct device *dev, uint32_t nwait) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  | uint32_t count = nwait; | 
|  | uint8_t sts = 0; | 
|  |  | 
|  | while (count--) { | 
|  | sts = regs->CTRLSTS; | 
|  | data->i2c_status = sts; | 
|  | if (sts & MCHP_I2C_SMB_STS_NBB) { | 
|  | break; /* bus is free */ | 
|  | } | 
|  | k_busy_wait(WAIT_INTERVAL); | 
|  | } | 
|  |  | 
|  | /* NBB -> 1 not busy can occur for STOP, BER, or LAB */ | 
|  | if (sts == (MCHP_I2C_SMB_STS_PIN | MCHP_I2C_SMB_STS_NBB)) { | 
|  | /* No service requested(PIN=1), NotBusy(NBB=1), and no errors */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (sts & MCHP_I2C_SMB_STS_BER) { | 
|  | return I2C_XEC_ERR_BUS; | 
|  | } | 
|  |  | 
|  | if (sts & MCHP_I2C_SMB_STS_LAB) { | 
|  | return I2C_XEC_ERR_LAB; | 
|  | } | 
|  |  | 
|  | return I2C_XEC_ERR_TMOUT; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * returns state of I2C SCL and SDA lines. | 
|  | * b[0] = SCL, b[1] = SDA | 
|  | * Call soc specific routine to read GPIO pad input. | 
|  | * Why? We can get the pins from our PINCTRL info but | 
|  | * we do not know which pin is I2C clock and which pin | 
|  | * is I2C data. There's no ordering in PINCTRL DT unless | 
|  | * we impose an order. | 
|  | */ | 
|  | static uint32_t get_lines(const struct device *dev) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  | uint8_t port = regs->CFG & MCHP_I2C_SMB_CFG_PORT_SEL_MASK; | 
|  | uint32_t lines = 0u; | 
|  |  | 
|  | soc_i2c_port_lines_get(port, &lines); | 
|  |  | 
|  | return lines; | 
|  | } | 
|  |  | 
|  | static int i2c_xec_reset_config(const struct device *dev) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  |  | 
|  | data->state = I2C_XEC_STATE_STOPPED; | 
|  | data->read_discard = 0; | 
|  |  | 
|  | /* Assert RESET and clr others */ | 
|  | regs->CFG = MCHP_I2C_SMB_CFG_RESET; | 
|  | k_busy_wait(RESET_WAIT_US); | 
|  | /* clear reset, set filter enable, select port */ | 
|  | regs->CFG = 0; | 
|  | regs->CFG = MCHP_I2C_SMB_CFG_FLUSH_SXBUF_WO | | 
|  | MCHP_I2C_SMB_CFG_FLUSH_SRBUF_WO | | 
|  | MCHP_I2C_SMB_CFG_FLUSH_MXBUF_WO | | 
|  | MCHP_I2C_SMB_CFG_FLUSH_MRBUF_WO; | 
|  |  | 
|  | mchp_xec_ecia_girq_src_clr(cfg->girq, cfg->girq_pos); | 
|  |  | 
|  | /* PIN=1 to clear all status except NBB and synchronize */ | 
|  | i2c_ctl_wr(dev, MCHP_I2C_SMB_CTRL_PIN); | 
|  |  | 
|  | /* | 
|  | * Controller implements two peripheral addresses for itself. | 
|  | * It always monitors whether an external controller issues START | 
|  | * plus target address. We should write valid peripheral addresses | 
|  | * that do not match any peripheral on the bus. | 
|  | * An alternative is to use the default 0 value which is the | 
|  | * general call address and disable the general call match | 
|  | * enable in the configuration register. | 
|  | */ | 
|  | regs->OWN_ADDR = EC_OWN_I2C_ADDR | (EC_OWN_I2C_ADDR << 8); | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | if (data->target_cfg) { | 
|  | regs->OWN_ADDR = data->target_cfg->address; | 
|  | } | 
|  | #endif | 
|  | /* Port number and filter enable MUST be written before enabling */ | 
|  | regs->CFG |= BIT(14); /* disable general call */ | 
|  | regs->CFG |= MCHP_I2C_SMB_CFG_FEN; | 
|  | regs->CFG |= (cfg->port_sel & MCHP_I2C_SMB_CFG_PORT_SEL_MASK); | 
|  |  | 
|  | /* | 
|  | * Before enabling the controller program the desired bus clock, | 
|  | * repeated start hold time, data timing, and timeout scaling | 
|  | * registers. | 
|  | */ | 
|  | regs->BUSCLK = xec_cfg_params[data->speed_id].bus_clk; | 
|  | regs->RSHTM = xec_cfg_params[data->speed_id].start_hold_time; | 
|  | regs->DATATM = xec_cfg_params[data->speed_id].data_timing; | 
|  | regs->TMOUTSC = xec_cfg_params[data->speed_id].timeout_scale; | 
|  | regs->IDLSC = xec_cfg_params[data->speed_id].idle_scale; | 
|  |  | 
|  | /* | 
|  | * PIN=1 clears all status except NBB | 
|  | * ESO=1 enables output drivers | 
|  | * ACK=1 enable ACK generation when data/address is clocked in. | 
|  | */ | 
|  | i2c_ctl_wr(dev, MCHP_I2C_SMB_CTRL_PIN | | 
|  | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_ACK); | 
|  |  | 
|  | /* Enable controller */ | 
|  | regs->CFG |= MCHP_I2C_SMB_CFG_ENAB; | 
|  | k_busy_wait(RESET_WAIT_US); | 
|  |  | 
|  | /* wait for NBB=1, BER, LAB, or timeout */ | 
|  | int rc = wait_bus_free(dev, WAIT_COUNT); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If SCL is low sample I2C_RECOVER_SCL_LOW_RETRIES times with a 5 us delay | 
|  | * between samples. If SCL remains low then return -EBUSY | 
|  | * If SCL is High and SDA is low then loop up to I2C_RECOVER_SDA_LOW_RETRIES | 
|  | * times driving the pins: | 
|  | * Drive SCL high | 
|  | * delay I2C_RECOVER_BB_DELAY_US | 
|  | * Generate 9 clock pulses on SCL checking SDA before each falling edge of SCL | 
|  | *   If SDA goes high exit clock loop else to all 9 clocks | 
|  | * Drive SDA low, delay 5 us, release SDA, delay 5 us | 
|  | * Both lines are high then exit SDA recovery loop | 
|  | * Both lines should not be driven | 
|  | * Check both lines: if any bad return error else return success | 
|  | * NOTE 1: Bit-bang mode uses a HW MUX to switch the lines away from the I2C | 
|  | * controller logic to BB logic. | 
|  | * NOTE 2: Bit-bang mode requires HW timeouts to be disabled. | 
|  | * NOTE 3: Bit-bang mode requires the controller's configuration enable bit | 
|  | * to be set. | 
|  | * NOTE 4: The controller must be reset after using bit-bang mode. | 
|  | */ | 
|  | static int i2c_xec_recover_bus(const struct device *dev) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  | int i, j, ret; | 
|  |  | 
|  | LOG_ERR("I2C attempt bus recovery\n"); | 
|  |  | 
|  | /* reset controller to a known state */ | 
|  | regs->CFG = MCHP_I2C_SMB_CFG_RESET; | 
|  | k_busy_wait(RESET_WAIT_US); | 
|  | regs->CFG = BIT(14) | MCHP_I2C_SMB_CFG_FEN | | 
|  | (cfg->port_sel & MCHP_I2C_SMB_CFG_PORT_SEL_MASK); | 
|  | regs->CFG |= MCHP_I2C_SMB_CFG_FLUSH_SXBUF_WO | | 
|  | MCHP_I2C_SMB_CFG_FLUSH_SRBUF_WO | | 
|  | MCHP_I2C_SMB_CFG_FLUSH_MXBUF_WO | | 
|  | MCHP_I2C_SMB_CFG_FLUSH_MRBUF_WO; | 
|  | regs->CTRLSTS = MCHP_I2C_SMB_CTRL_PIN; | 
|  | regs->BBCTRL = MCHP_I2C_SMB_BB_EN | MCHP_I2C_SMB_BB_CL | | 
|  | MCHP_I2C_SMB_BB_DAT; | 
|  | regs->CFG |= MCHP_I2C_SMB_CFG_ENAB; | 
|  |  | 
|  | if (!(regs->BBCTRL & MCHP_I2C_SMB_BB_CLKI_RO)) { | 
|  | for (i = 0;; i++) { | 
|  | if (i >= I2C_RECOVER_SCL_LOW_RETRIES) { | 
|  | ret = -EBUSY; | 
|  | goto recov_exit; | 
|  | } | 
|  | k_busy_wait(I2C_RECOVER_SCL_DELAY_US); | 
|  | if (regs->BBCTRL & MCHP_I2C_SMB_BB_CLKI_RO) { | 
|  | break; /* SCL went High */ | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (regs->BBCTRL & MCHP_I2C_SMB_BB_DATI_RO) { | 
|  | ret = 0; | 
|  | goto recov_exit; | 
|  | } | 
|  |  | 
|  | ret = -EBUSY; | 
|  | /* SDA recovery */ | 
|  | for (i = 0; i < I2C_RECOVER_SDA_LOW_RETRIES; i++) { | 
|  | /* SCL output mode and tri-stated */ | 
|  | regs->BBCTRL = MCHP_I2C_SMB_BB_EN | | 
|  | MCHP_I2C_SMB_BB_SCL_DIR_OUT | | 
|  | MCHP_I2C_SMB_BB_CL | | 
|  | MCHP_I2C_SMB_BB_DAT; | 
|  | k_busy_wait(I2C_RECOVER_BB_DELAY_US); | 
|  |  | 
|  | for (j = 0; j < 9; j++) { | 
|  | if (regs->BBCTRL & MCHP_I2C_SMB_BB_DATI_RO) { | 
|  | break; | 
|  | } | 
|  | /* drive SCL low */ | 
|  | regs->BBCTRL = MCHP_I2C_SMB_BB_EN | | 
|  | MCHP_I2C_SMB_BB_SCL_DIR_OUT | | 
|  | MCHP_I2C_SMB_BB_DAT; | 
|  | k_busy_wait(I2C_RECOVER_BB_DELAY_US); | 
|  | /* release SCL: pulled high by external pull-up */ | 
|  | regs->BBCTRL = MCHP_I2C_SMB_BB_EN | | 
|  | MCHP_I2C_SMB_BB_SCL_DIR_OUT | | 
|  | MCHP_I2C_SMB_BB_CL | | 
|  | MCHP_I2C_SMB_BB_DAT; | 
|  | k_busy_wait(I2C_RECOVER_BB_DELAY_US); | 
|  | } | 
|  |  | 
|  | /* SCL is High. Produce rising edge on SCL for STOP */ | 
|  | regs->BBCTRL = MCHP_I2C_SMB_BB_EN | MCHP_I2C_SMB_BB_CL | | 
|  | MCHP_I2C_SMB_BB_SDA_DIR_OUT; /* drive low */ | 
|  | k_busy_wait(I2C_RECOVER_BB_DELAY_US); | 
|  | regs->BBCTRL = MCHP_I2C_SMB_BB_EN | MCHP_I2C_SMB_BB_CL | | 
|  | MCHP_I2C_SMB_BB_DAT; /* release SCL */ | 
|  | k_busy_wait(I2C_RECOVER_BB_DELAY_US); | 
|  |  | 
|  | /* check if SCL and SDA are both high */ | 
|  | uint8_t bb = regs->BBCTRL & | 
|  | (MCHP_I2C_SMB_BB_CLKI_RO | MCHP_I2C_SMB_BB_DATI_RO); | 
|  |  | 
|  | if (bb == (MCHP_I2C_SMB_BB_CLKI_RO | MCHP_I2C_SMB_BB_DATI_RO)) { | 
|  | ret = 0; /* successful recovery */ | 
|  | goto recov_exit; | 
|  | } | 
|  | } | 
|  |  | 
|  | recov_exit: | 
|  | /* BB mode disable reconnects SCL and SDA to I2C logic. */ | 
|  | regs->BBCTRL = 0; | 
|  | regs->CTRLSTS = MCHP_I2C_SMB_CTRL_PIN; /* clear status */ | 
|  | i2c_xec_reset_config(dev); /* reset controller */ | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | /* | 
|  | * Restart I2C controller as target for ACK of address match. | 
|  | * Setting PIN clears all status in I2C.Status register except NBB. | 
|  | */ | 
|  | static void restart_target(const struct device *dev) | 
|  | { | 
|  | i2c_ctl_wr(dev, MCHP_I2C_SMB_CTRL_PIN | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_ACK | MCHP_I2C_SMB_CTRL_ENI); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Configure I2C controller acting as target to NACK the next received byte. | 
|  | * NOTE: Firmware must re-enable ACK generation before the start of the next | 
|  | * transaction otherwise the controller will NACK its target addresses. | 
|  | */ | 
|  | static void target_config_for_nack(const struct device *dev) | 
|  | { | 
|  | i2c_ctl_wr(dev, MCHP_I2C_SMB_CTRL_PIN | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_ENI); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static int wait_pin(const struct device *dev, bool pin_assert, uint32_t nwait) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  |  | 
|  | for (;;) { | 
|  | k_busy_wait(WAIT_INTERVAL); | 
|  |  | 
|  | data->i2c_compl = regs->COMPL; | 
|  | data->i2c_status = regs->CTRLSTS; | 
|  |  | 
|  | if (data->i2c_status & MCHP_I2C_SMB_STS_BER) { | 
|  | return I2C_XEC_ERR_BUS; | 
|  | } | 
|  |  | 
|  | if (data->i2c_status & MCHP_I2C_SMB_STS_LAB) { | 
|  | return I2C_XEC_ERR_LAB; | 
|  | } | 
|  |  | 
|  | if (!(data->i2c_status & MCHP_I2C_SMB_STS_PIN)) { | 
|  | if (pin_assert) { | 
|  | return 0; | 
|  | } | 
|  | } else if (!pin_assert) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (nwait) { | 
|  | --nwait; | 
|  | } else { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | return I2C_XEC_ERR_TMOUT; | 
|  | } | 
|  |  | 
|  | static int gen_start(const struct device *dev, uint8_t addr8, | 
|  | bool is_repeated) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  | uint8_t ctrl = MCHP_I2C_SMB_CTRL_ESO | MCHP_I2C_SMB_CTRL_STA | | 
|  | MCHP_I2C_SMB_CTRL_ACK; | 
|  |  | 
|  | data->i2c_addr = addr8; | 
|  |  | 
|  | if (is_repeated) { | 
|  | i2c_ctl_wr(dev, ctrl); | 
|  | regs->I2CDATA = addr8; | 
|  | } else { | 
|  | ctrl |= MCHP_I2C_SMB_CTRL_PIN; | 
|  | regs->I2CDATA = addr8; | 
|  | i2c_ctl_wr(dev, ctrl); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gen_stop(const struct device *dev) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  | uint8_t ctrl = MCHP_I2C_SMB_CTRL_PIN | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_STO | MCHP_I2C_SMB_CTRL_ACK; | 
|  |  | 
|  | data->i2c_ctrl = ctrl; | 
|  | regs->CTRLSTS = ctrl; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int do_stop(const struct device *dev, uint32_t nwait) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  | int ret; | 
|  |  | 
|  | data->state = I2C_XEC_STATE_STOPPED; | 
|  | data->read_discard = 0; | 
|  |  | 
|  | gen_stop(dev); | 
|  | ret = wait_bus_free(dev, nwait); | 
|  | if (ret) { | 
|  | uint32_t lines = get_lines(dev); | 
|  |  | 
|  | if (lines != I2C_LINES_BOTH_HI) { | 
|  | i2c_xec_recover_bus(dev); | 
|  | } else { | 
|  | ret = i2c_xec_reset_config(dev); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (ret == 0) { | 
|  | /* stop success: prepare for next transaction */ | 
|  | regs->CTRLSTS = MCHP_I2C_SMB_CTRL_PIN | MCHP_I2C_SMB_CTRL_ESO | | 
|  | MCHP_I2C_SMB_CTRL_ACK; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int do_start(const struct device *dev, uint8_t addr8, bool is_repeated) | 
|  | { | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | int ret; | 
|  |  | 
|  | gen_start(dev, addr8, is_repeated); | 
|  | ret = wait_pin(dev, I2C_WAIT_PIN_ASSERT, WAIT_COUNT); | 
|  | if (ret) { | 
|  | i2c_xec_reset_config(dev); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* PIN 1->0: check for NACK */ | 
|  | if (data->i2c_status & MCHP_I2C_SMB_STS_LRB_AD0) { | 
|  | gen_stop(dev); | 
|  | ret = wait_bus_free(dev, WAIT_COUNT); | 
|  | if (ret) { | 
|  | i2c_xec_reset_config(dev); | 
|  | } | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | int ret = i2c_xec_reset_config(dev); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* I2C Controller transmit: polling implementation */ | 
|  | static int ctrl_tx(const struct device *dev, struct i2c_msg *msg, uint16_t addr) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  | int ret = 0; | 
|  | uint8_t mflags = msg->flags; | 
|  | uint8_t addr8 = (uint8_t)((addr & 0x7FU) << 1); | 
|  |  | 
|  | if (data->state == I2C_XEC_STATE_STOPPED) { | 
|  | data->i2c_addr = addr8; | 
|  | /* Is bus free and controller ready? */ | 
|  | ret = wait_bus_free(dev, WAIT_COUNT); | 
|  | if (ret) { | 
|  | ret = i2c_xec_recover_bus(dev); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | ret = do_start(dev, addr8, I2C_START); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | data->state = I2C_XEC_STATE_OPEN; | 
|  |  | 
|  | } else if (mflags & I2C_MSG_RESTART) { | 
|  | data->i2c_addr = addr8; | 
|  | ret = do_start(dev, addr8, I2C_RPT_START); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (size_t n = 0; n < msg->len; n++) { | 
|  | regs->I2CDATA = msg->buf[n]; | 
|  | ret = wait_pin(dev, I2C_WAIT_PIN_ASSERT, WAIT_COUNT); | 
|  | if (ret) { | 
|  | i2c_xec_reset_config(dev); | 
|  | return ret; | 
|  | } | 
|  | if (data->i2c_status & MCHP_I2C_SMB_STS_LRB_AD0) { /* NACK? */ | 
|  | do_stop(dev, STOP_WAIT_COUNT); | 
|  | return -EIO; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (mflags & I2C_MSG_STOP) { | 
|  | ret = do_stop(dev, STOP_WAIT_COUNT); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * I2C Controller receive: polling implementation | 
|  | * Transmitting a target address with BIT[0] == 1 causes the controller | 
|  | * to enter controller-read mode where every read of I2CDATA generates | 
|  | * clocks for the next byte. When we generate START or Repeated-START | 
|  | * and transmit an address the address is also clocked in during | 
|  | * address transmission. The address must read and discarded. | 
|  | * Read of I2CDATA returns data currently in I2C read buffer, sets | 
|  | * I2CSTATUS.PIN = 1, and !!generates clocks for the next | 
|  | * byte!! | 
|  | * For this controller to NACK the last byte we must clear the | 
|  | * I2C CTRL register ACK bit BEFORE reading the next to last | 
|  | * byte. Before reading the last byte we configure I2C CTRL to generate a STOP | 
|  | * and then read the last byte from I2 DATA. | 
|  | * When controller is in STOP mode it will not generate clocks when I2CDATA is | 
|  | * read. UGLY HW DESIGN. | 
|  | * We will NOT attempt to follow this HW design for Controller read except | 
|  | * when all information is available: STOP message flag set AND number of | 
|  | * bytes to read including dummy is >= 2. General usage can result in the | 
|  | * controller not NACK'ing the last byte. | 
|  | */ | 
|  | static int ctrl_rx(const struct device *dev, struct i2c_msg *msg, uint16_t addr) | 
|  | { | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = | 
|  | (struct i2c_xec_data *const) (dev->data); | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  | int ret = 0; | 
|  | size_t data_len = msg->len; | 
|  | uint8_t mflags = msg->flags; | 
|  | uint8_t addr8 = (uint8_t)(((addr & 0x7FU) << 1) | BIT(0)); | 
|  | uint8_t temp = 0; | 
|  |  | 
|  | if (data->state == I2C_XEC_STATE_STOPPED) { | 
|  | data->i2c_addr = addr8; | 
|  | /* Is bus free and controller ready? */ | 
|  | ret = wait_bus_free(dev, WAIT_COUNT); | 
|  | if (ret) { | 
|  | i2c_xec_reset_config(dev); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | ret = do_start(dev, addr8, I2C_START); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | data->state = I2C_XEC_STATE_OPEN; | 
|  |  | 
|  | /* controller clocked address into I2CDATA */ | 
|  | data->read_discard = 1U; | 
|  |  | 
|  | } else if (mflags & I2C_MSG_RESTART) { | 
|  | data->i2c_addr = addr8; | 
|  | ret = do_start(dev, addr8, I2C_RPT_START); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* controller clocked address into I2CDATA */ | 
|  | data->read_discard = 1U; | 
|  | } | 
|  |  | 
|  | if (!data_len) { /* requested message length is 0 */ | 
|  | ret = 0; | 
|  | if (mflags & I2C_MSG_STOP) { | 
|  | data->state = I2C_XEC_STATE_STOPPED; | 
|  | data->read_discard = 0; | 
|  | ret = do_stop(dev, STOP_WAIT_COUNT); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | if (data->read_discard) { | 
|  | data_len++; | 
|  | } | 
|  |  | 
|  | uint8_t *p8 = &msg->buf[0]; | 
|  |  | 
|  | while (data_len) { | 
|  | if (mflags & I2C_MSG_STOP) { | 
|  | if (data_len == 2) { | 
|  | i2c_ctl_wr(dev, MCHP_I2C_SMB_CTRL_ESO); | 
|  | } else if (data_len == 1) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | temp = regs->I2CDATA; /* generates clocks */ | 
|  | if (data->read_discard) { | 
|  | data->read_discard = 0; | 
|  | } else { | 
|  | *p8++ = temp; | 
|  | } | 
|  | ret = wait_pin(dev, I2C_WAIT_PIN_ASSERT, WAIT_COUNT); | 
|  | if (ret) { | 
|  | i2c_xec_reset_config(dev); | 
|  | return ret; | 
|  | } | 
|  | data_len--; | 
|  | } | 
|  |  | 
|  | if (mflags & I2C_MSG_STOP) { | 
|  | data->state = I2C_XEC_STATE_STOPPED; | 
|  | data->read_discard = 0; | 
|  | ret = do_stop(dev, STOP_WAIT_COUNT); | 
|  | if (ret == 0) { | 
|  | *p8 = regs->I2CDATA; | 
|  | } | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int i2c_xec_transfer(const struct device *dev, struct i2c_msg *msgs, | 
|  | uint8_t num_msgs, uint16_t addr) | 
|  | { | 
|  | struct i2c_xec_data *data = dev->data; | 
|  | int ret = 0; | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | if (data->target_attached) { | 
|  | LOG_ERR("Device is registered as target"); | 
|  | return -EBUSY; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | for (uint8_t i = 0; i < num_msgs; i++) { | 
|  | struct i2c_msg *m = &msgs[i]; | 
|  |  | 
|  | if ((m->flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) { | 
|  | ret = ctrl_tx(dev, m, addr); | 
|  | } else { | 
|  | ret = ctrl_rx(dev, m, addr); | 
|  | } | 
|  | if (ret) { | 
|  | data->state = I2C_XEC_STATE_STOPPED; | 
|  | data->read_discard = 0; | 
|  | LOG_ERR("i2x_xfr: flags: %x error: %d", m->flags, ret); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void i2c_xec_bus_isr(const struct device *dev) | 
|  | { | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | const struct i2c_xec_config *cfg = | 
|  | (const struct i2c_xec_config *const) (dev->config); | 
|  | struct i2c_xec_data *data = dev->data; | 
|  | const struct i2c_target_callbacks *target_cb = | 
|  | data->target_cfg->callbacks; | 
|  | struct i2c_smb_regs *regs = (struct i2c_smb_regs *)cfg->base_addr; | 
|  | int ret; | 
|  | uint32_t status; | 
|  | uint32_t compl_status; | 
|  | uint8_t val; | 
|  | uint8_t dummy = 0U; | 
|  |  | 
|  | /* Get current status */ | 
|  | status = regs->CTRLSTS; | 
|  | compl_status = regs->COMPL & MCHP_I2C_SMB_CMPL_RW1C_MASK; | 
|  |  | 
|  | /* Idle interrupt enabled and active? */ | 
|  | if ((regs->CFG & MCHP_I2C_SMB_CFG_ENIDI) && | 
|  | (compl_status & MCHP_I2C_SMB_CMPL_IDLE_RWC)) { | 
|  | regs->CFG &= ~MCHP_I2C_SMB_CFG_ENIDI; | 
|  | if (status & MCHP_I2C_SMB_STS_NBB) { | 
|  | restart_target(dev); | 
|  | goto clear_iag; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!data->target_attached) { | 
|  | goto clear_iag; | 
|  | } | 
|  |  | 
|  | /* Bus Error */ | 
|  | if (status & MCHP_I2C_SMB_STS_BER) { | 
|  | if (target_cb->stop) { | 
|  | target_cb->stop(data->target_cfg); | 
|  | } | 
|  | restart_target(dev); | 
|  | goto clear_iag; | 
|  | } | 
|  |  | 
|  | /* External stop */ | 
|  | if (status & MCHP_I2C_SMB_STS_EXT_STOP) { | 
|  | if (target_cb->stop) { | 
|  | target_cb->stop(data->target_cfg); | 
|  | } | 
|  | restart_target(dev); | 
|  | goto clear_iag; | 
|  | } | 
|  |  | 
|  | /* Address byte handling */ | 
|  | if (status & MCHP_I2C_SMB_STS_AAS) { | 
|  | if (status & MCHP_I2C_SMB_STS_PIN) { | 
|  | goto clear_iag; | 
|  | } | 
|  |  | 
|  | uint8_t rx_data = regs->I2CDATA; | 
|  |  | 
|  | if (rx_data & BIT(I2C_READ_WRITE_POS)) { | 
|  | /* target transmitter mode */ | 
|  | data->target_read = true; | 
|  | val = dummy; | 
|  | if (target_cb->read_requested) { | 
|  | target_cb->read_requested( | 
|  | data->target_cfg, &val); | 
|  |  | 
|  | /* Application target transmit handler | 
|  | * does not have data to send. In | 
|  | * target transmit mode the external | 
|  | * Controller is ACK's data we send. | 
|  | * All we can do is keep sending dummy | 
|  | * data. We assume read_requested does | 
|  | * not modify the value pointed to by val | 
|  | * if it has not data(returns error). | 
|  | */ | 
|  | } | 
|  | /* | 
|  | * Writing I2CData causes this HW to release SCL | 
|  | * ending clock stretching. The external Controller | 
|  | * senses SCL released and begins generating clocks | 
|  | * and capturing data driven by this controller | 
|  | * on SDA. External Controller ACK's data until it | 
|  | * wants no more then it will NACK. | 
|  | */ | 
|  | regs->I2CDATA = val; | 
|  | goto clear_iag; /* Exit ISR */ | 
|  | } else { | 
|  | /* target receiver mode */ | 
|  | data->target_read = false; | 
|  | if (target_cb->write_requested) { | 
|  | ret = target_cb->write_requested( | 
|  | data->target_cfg); | 
|  | if (ret) { | 
|  | /* | 
|  | * Application handler can't accept | 
|  | * data. Configure HW to NACK next | 
|  | * data transmitted by external | 
|  | * Controller. | 
|  | * !!! TODO We must re-program our HW | 
|  | * for address ACK before next | 
|  | * transaction is begun !!! | 
|  | */ | 
|  | target_config_for_nack(dev); | 
|  | } | 
|  | } | 
|  | goto clear_iag; /* Exit ISR */ | 
|  | } | 
|  | } | 
|  |  | 
|  | if (data->target_read) { /* Target transmitter mode */ | 
|  |  | 
|  | /* Master has Nacked, then just write a dummy byte */ | 
|  | status = regs->CTRLSTS; | 
|  | if (status & MCHP_I2C_SMB_STS_LRB_AD0) { | 
|  |  | 
|  | /* | 
|  | * ISSUE: HW will not detect external STOP in | 
|  | * target transmit mode. Enable IDLE interrupt | 
|  | * to catch PIN 0 -> 1 and NBB 0 -> 1. | 
|  | */ | 
|  | regs->CFG |= MCHP_I2C_SMB_CFG_ENIDI; | 
|  |  | 
|  | /* | 
|  | * dummy write causes this controller's PIN status | 
|  | * to de-assert 0 -> 1. Data is not transmitted. | 
|  | * SCL is not driven low by this controller. | 
|  | */ | 
|  | regs->I2CDATA = dummy; | 
|  |  | 
|  | status = regs->CTRLSTS; | 
|  |  | 
|  | } else { | 
|  | val = dummy; | 
|  | if (target_cb->read_processed) { | 
|  | target_cb->read_processed( | 
|  | data->target_cfg, &val); | 
|  | } | 
|  | regs->I2CDATA = val; | 
|  | } | 
|  | } else { /* target receiver mode */ | 
|  | /* | 
|  | * Reading the I2CData register causes this target to release | 
|  | * SCL. The external Controller senses SCL released generates | 
|  | * clocks for transmitting the next data byte. | 
|  | * Reading I2C Data register causes PIN status 0 -> 1. | 
|  | */ | 
|  | val = regs->I2CDATA; | 
|  | if (target_cb->write_received) { | 
|  | /* | 
|  | * Call back returns error if we should NACK | 
|  | * next byte. | 
|  | */ | 
|  | ret = target_cb->write_received(data->target_cfg, val); | 
|  | if (ret) { | 
|  | /* | 
|  | * Configure HW to NACK next byte. It will not | 
|  | * generate clocks for another byte of data | 
|  | */ | 
|  | target_config_for_nack(dev); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | clear_iag: | 
|  | regs->COMPL = compl_status; | 
|  | mchp_xec_ecia_girq_src_clr(cfg->girq, cfg->girq_pos); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_I2C_TARGET | 
|  | static int i2c_xec_target_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; | 
|  | int ret; | 
|  |  | 
|  | if (!config) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | if (data->target_attached) { | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | /* Wait for any outstanding transactions to complete so that | 
|  | * the bus is free | 
|  | */ | 
|  | ret = wait_bus_free(dev, WAIT_COUNT); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | data->target_cfg = config; | 
|  |  | 
|  | ret = i2c_xec_reset_config(dev); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | restart_target(dev); | 
|  |  | 
|  | data->target_attached = true; | 
|  |  | 
|  | /* Clear before enabling girq bit */ | 
|  | mchp_xec_ecia_girq_src_clr(cfg->girq, cfg->girq_pos); | 
|  | mchp_xec_ecia_girq_src_en(cfg->girq, cfg->girq_pos); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int i2c_xec_target_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->target_attached) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | data->target_cfg = NULL; | 
|  | data->target_attached = false; | 
|  |  | 
|  | mchp_xec_ecia_girq_src_dis(cfg->girq, cfg->girq_pos); | 
|  |  | 
|  | 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 | 
|  | .target_register = i2c_xec_target_register, | 
|  | .target_unregister = i2c_xec_target_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; | 
|  | uint32_t bitrate_cfg; | 
|  |  | 
|  | data->state = I2C_XEC_STATE_STOPPED; | 
|  | data->target_cfg = NULL; | 
|  | data->target_attached = false; | 
|  |  | 
|  | ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); | 
|  | if (ret != 0) { | 
|  | LOG_ERR("XEC I2C pinctrl setup failed (%d)", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | bitrate_cfg = i2c_map_dt_bitrate(cfg->clock_freq); | 
|  | if (!bitrate_cfg) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* Default configuration */ | 
|  | ret = i2c_xec_configure(dev, I2C_MODE_CONTROLLER | bitrate_cfg); | 
|  | if (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)						\ | 
|  | \ | 
|  | PINCTRL_DT_INST_DEFINE(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),			\ | 
|  | .clock_freq = DT_INST_PROP(n, clock_frequency),		\ | 
|  | .girq = DT_INST_PROP_BY_IDX(n, girqs, 0),		\ | 
|  | .girq_pos = DT_INST_PROP_BY_IDX(n, girqs, 1),		\ | 
|  | .pcr_idx = DT_INST_PROP_BY_IDX(n, pcrs, 0),		\ | 
|  | .pcr_bitpos = DT_INST_PROP_BY_IDX(n, pcrs, 1),		\ | 
|  | .irq_config_func = i2c_xec_irq_config_func_##n,		\ | 
|  | .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(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) |