|  | /* | 
|  | * Copyright (c) 2020 Nuvoton Technology Corporation. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT nuvoton_npcx_i2c_port | 
|  |  | 
|  | /** | 
|  | * @file | 
|  | * @brief Nuvoton NPCX smb/i2c port driver | 
|  | * | 
|  | * This file contains the driver of SMBus/I2C buses (ports) which provides | 
|  | * pin-muxing for each i2c io-pads. In order to support "SMBus Multi-Bus" | 
|  | * feature, please refer the diagram below, the driver also provides connection | 
|  | * between Zephyr i2c api functions and i2c controller driver which provides | 
|  | * full support for SMBus/I2C transactions. | 
|  | * | 
|  | *                           Port SEL | 
|  | *                             | | 
|  | *                           |\| | 
|  | *           SCL_N Port 0----| \     +--------------+ | 
|  | *           SDA_N Port 0----|  |----|   SMB/I2C    | | 
|  | *                           |  |----| Controller N | | 
|  | *           SCL_N Port 1----|  |    +--------------+ | 
|  | *           SDA_N Port 1----| / | 
|  | *                           |/ | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <zephyr/drivers/clock_control.h> | 
|  | #include <zephyr/drivers/i2c.h> | 
|  | #include <zephyr/drivers/pinctrl.h> | 
|  | #include <zephyr/dt-bindings/i2c/i2c.h> | 
|  | #include <soc.h> | 
|  |  | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_REGISTER(i2c_npcx_port, LOG_LEVEL_ERR); | 
|  |  | 
|  | #include "i2c_npcx_controller.h" | 
|  | #include "i2c-priv.h" | 
|  |  | 
|  | /* Device config */ | 
|  | struct i2c_npcx_port_config { | 
|  | uint32_t bitrate; | 
|  | uint8_t port; | 
|  | const struct device *i2c_ctrl; | 
|  | /* pinmux configuration */ | 
|  | const struct pinctrl_dev_config *pcfg; | 
|  | }; | 
|  |  | 
|  | /* I2C api functions */ | 
|  | static int i2c_npcx_port_configure(const struct device *dev, | 
|  | uint32_t dev_config) | 
|  | { | 
|  | const struct i2c_npcx_port_config *const config = dev->config; | 
|  |  | 
|  | if (config->i2c_ctrl == NULL) { | 
|  | LOG_ERR("Cannot find i2c controller on port%02x!", | 
|  | config->port); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | if (!(dev_config & I2C_MODE_CONTROLLER)) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | if (dev_config & I2C_ADDR_10_BITS) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | /* Configure i2c controller */ | 
|  | return npcx_i2c_ctrl_configure(config->i2c_ctrl, dev_config); | 
|  | } | 
|  |  | 
|  | static int i2c_npcx_port_get_config(const struct device *dev, uint32_t *dev_config) | 
|  | { | 
|  | const struct i2c_npcx_port_config *const config = dev->config; | 
|  | uint32_t speed; | 
|  | int ret; | 
|  |  | 
|  | if (config->i2c_ctrl == NULL) { | 
|  | LOG_ERR("Cannot find i2c controller on port%02x!", config->port); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | ret = npcx_i2c_ctrl_get_speed(config->i2c_ctrl, &speed); | 
|  | if (!ret) { | 
|  | *dev_config = (I2C_MODE_CONTROLLER | speed); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int i2c_npcx_port_transfer(const struct device *dev, | 
|  | struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr) | 
|  | { | 
|  | const struct i2c_npcx_port_config *const config = dev->config; | 
|  | int ret = 0; | 
|  | int idx_ctrl = (config->port & 0xF0) >> 4; | 
|  | int idx_port = (config->port & 0x0F); | 
|  |  | 
|  | if (config->i2c_ctrl == NULL) { | 
|  | LOG_ERR("Cannot find i2c controller on port%02x!", | 
|  | config->port); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | /* Lock mutex of i2c/smb controller */ | 
|  | npcx_i2c_ctrl_mutex_lock(config->i2c_ctrl); | 
|  |  | 
|  | /* Switch correct port for i2c controller first */ | 
|  | npcx_pinctrl_i2c_port_sel(idx_ctrl, idx_port); | 
|  |  | 
|  | /* Start transaction with i2c controller */ | 
|  | ret = npcx_i2c_ctrl_transfer(config->i2c_ctrl, msgs, num_msgs, addr, | 
|  | config->port); | 
|  |  | 
|  | /* Unlock mutex of i2c/smb controller */ | 
|  | npcx_i2c_ctrl_mutex_unlock(config->i2c_ctrl); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* I2C driver registration */ | 
|  | static int i2c_npcx_port_init(const struct device *dev) | 
|  | { | 
|  | const struct i2c_npcx_port_config *const config = dev->config; | 
|  | uint32_t i2c_config; | 
|  | int ret; | 
|  |  | 
|  | /* Configure pin-mux for I2C device */ | 
|  | ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); | 
|  | if (ret < 0) { | 
|  | LOG_ERR("I2C pinctrl setup failed (%d)", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Setup initial i2c configuration */ | 
|  | i2c_config = (I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate)); | 
|  | ret = i2c_npcx_port_configure(dev, i2c_config); | 
|  | if (ret != 0) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct i2c_driver_api i2c_port_npcx_driver_api = { | 
|  | .configure = i2c_npcx_port_configure, | 
|  | .get_config = i2c_npcx_port_get_config, | 
|  | .transfer = i2c_npcx_port_transfer, | 
|  | }; | 
|  |  | 
|  | /* I2C port init macro functions */ | 
|  | #define NPCX_I2C_PORT_INIT(inst)                                               \ | 
|  | PINCTRL_DT_INST_DEFINE(inst);					       \ | 
|  | \ | 
|  | static const struct i2c_npcx_port_config i2c_npcx_port_cfg_##inst = {  \ | 
|  | .port = DT_INST_PROP(inst, port),                              \ | 
|  | .bitrate = DT_INST_PROP(inst, clock_frequency),                \ | 
|  | .i2c_ctrl = DEVICE_DT_GET(DT_INST_PHANDLE(inst, controller)),  \ | 
|  | .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),                  \ | 
|  | };                                                                     \ | 
|  | \ | 
|  | DEVICE_DT_INST_DEFINE(inst,                                            \ | 
|  | i2c_npcx_port_init,                                \ | 
|  | NULL, NULL,                                        \ | 
|  | &i2c_npcx_port_cfg_##inst,                         \ | 
|  | PRE_KERNEL_1, CONFIG_I2C_INIT_PRIORITY,            \ | 
|  | &i2c_port_npcx_driver_api); | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(NPCX_I2C_PORT_INIT) |