| /* |
| * Copyright (c) 2021 IoT.bzh |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT renesas_rcar_i2c |
| |
| #include <errno.h> |
| #include <zephyr/device.h> |
| #include <zephyr/devicetree.h> |
| #include <zephyr/drivers/i2c.h> |
| #include <zephyr/drivers/clock_control.h> |
| #include <zephyr/drivers/clock_control/renesas_cpg_mssr.h> |
| |
| #include <zephyr/logging/log.h> |
| #include <zephyr/irq.h> |
| LOG_MODULE_REGISTER(i2c_rcar); |
| |
| #include "i2c-priv.h" |
| |
| typedef void (*init_func_t)(const struct device *dev); |
| |
| struct i2c_rcar_cfg { |
| uint32_t reg_addr; |
| init_func_t init_func; |
| const struct device *clock_dev; |
| struct rcar_cpg_clk mod_clk; |
| uint32_t bitrate; |
| }; |
| |
| struct i2c_rcar_data { |
| uint8_t status_mask; |
| struct k_sem int_sem; |
| }; |
| |
| /* Registers */ |
| #define RCAR_I2C_ICSCR 0x00 /* Slave Control Register */ |
| #define RCAR_I2C_ICMCR 0x04 /* Master Control Register */ |
| #define RCAR_I2C_ICSIER 0x10 /* Slave IRQ Enable */ |
| #define RCAR_I2C_ICMIER 0x14 /* Master IRQ Enable */ |
| #define RCAR_I2C_ICSSR 0x08 /* Slave Status */ |
| #define RCAR_I2C_ICMSR 0x0c /* Master Status */ |
| #define RCAR_I2C_ICCCR 0x18 /* Clock Control Register */ |
| #define RCAR_I2C_ICSAR 0x1c /* Slave Address Register */ |
| #define RCAR_I2C_ICMAR 0x20 /* Master Address Register */ |
| #define RCAR_I2C_ICRXD_ICTXD 0x24 /* Receive Transmit Data Register */ |
| #define RCAR_I2C_ICFBSCR 0x38 /* First Bit Setup Cycle (Gen3).*/ |
| #define RCAR_I2C_ICFBSCR_TCYC17 0x0f /* 17*Tcyc */ |
| |
| #define RCAR_I2C_ICMCR_MDBS BIT(7) /* Master Data Buffer Select */ |
| #define RCAR_I2C_ICMCR_FSCL BIT(6) /* Forced SCL */ |
| #define RCAR_I2C_ICMCR_FSDA BIT(5) /* Forced SDA */ |
| #define RCAR_I2C_ICMCR_OBPC BIT(4) /* Override Bus Pin Control */ |
| #define RCAR_I2C_ICMCR_MIE BIT(3) /* Master Interface Enable */ |
| #define RCAR_I2C_ICMCR_TSBE BIT(2) /* Start Byte Transmission Enable */ |
| #define RCAR_I2C_ICMCR_FSB BIT(1) /* Forced Stop onto the Bus */ |
| #define RCAR_I2C_ICMCR_ESG BIT(0) /* Enable Start Generation */ |
| #define RCAR_I2C_ICMCR_MASTER (RCAR_I2C_ICMCR_MDBS | RCAR_I2C_ICMCR_MIE) |
| |
| /* Bits to manage ICMIER and ICMSR registers */ |
| #define RCAR_I2C_MNR BIT(6) /* Master Nack Received */ |
| #define RCAR_I2C_MAL BIT(5) /* Master Arbitration lost */ |
| #define RCAR_I2C_MST BIT(4) /* Master Stop Transmitted */ |
| #define RCAR_I2C_MDE BIT(3) /* Master Data Empty */ |
| #define RCAR_I2C_MDT BIT(2) /* Master Data Transmitted */ |
| #define RCAR_I2C_MDR BIT(1) /* Master Data Received */ |
| #define RCAR_I2C_MAT BIT(0) /* Master Address Transmitted */ |
| |
| /* Recommended bitrate settings from official documentation */ |
| #define RCAR_I2C_ICCCR_CDF_100_KHZ 6 |
| #define RCAR_I2C_ICCCR_CDF_400_KHZ 6 |
| #define RCAR_I2C_ICCCR_SCGD_100_KHZ 21 |
| #define RCAR_I2C_ICCCR_SCGD_400_KHZ 3 |
| |
| #define MAX_WAIT_US 100 |
| |
| static uint32_t i2c_rcar_read(const struct i2c_rcar_cfg *config, |
| uint32_t offs) |
| { |
| return sys_read32(config->reg_addr + offs); |
| } |
| |
| static void i2c_rcar_write(const struct i2c_rcar_cfg *config, |
| uint32_t offs, uint32_t value) |
| { |
| sys_write32(value, config->reg_addr + offs); |
| } |
| |
| static void i2c_rcar_isr(const struct device *dev) |
| { |
| const struct i2c_rcar_cfg *config = dev->config; |
| struct i2c_rcar_data *data = dev->data; |
| |
| if (((i2c_rcar_read(config, RCAR_I2C_ICMSR)) & data->status_mask) == |
| data->status_mask) { |
| k_sem_give(&data->int_sem); |
| i2c_rcar_write(config, RCAR_I2C_ICMIER, 0); |
| } |
| } |
| |
| static int i2c_rcar_wait_for_state(const struct device *dev, uint8_t mask) |
| { |
| const struct i2c_rcar_cfg *config = dev->config; |
| struct i2c_rcar_data *data = dev->data; |
| |
| data->status_mask = mask; |
| |
| /* Reset interrupts semaphore */ |
| k_sem_reset(&data->int_sem); |
| |
| /* Enable interrupts */ |
| i2c_rcar_write(config, RCAR_I2C_ICMIER, mask); |
| |
| /* Wait for the interrupts */ |
| return k_sem_take(&data->int_sem, K_USEC(MAX_WAIT_US)); |
| } |
| |
| static int i2c_rcar_finish(const struct device *dev) |
| { |
| const struct i2c_rcar_cfg *config = dev->config; |
| int ret; |
| |
| /* Enable STOP generation */ |
| i2c_rcar_write(config, RCAR_I2C_ICMCR, RCAR_I2C_ICMCR_MASTER | RCAR_I2C_ICMCR_FSB); |
| i2c_rcar_write(config, RCAR_I2C_ICMSR, 0); |
| |
| /* Wait for STOP to be transmitted */ |
| ret = i2c_rcar_wait_for_state(dev, RCAR_I2C_MST); |
| i2c_rcar_write(config, RCAR_I2C_ICMSR, 0); |
| |
| /* Disable STOP generation */ |
| i2c_rcar_write(config, RCAR_I2C_ICMCR, RCAR_I2C_ICMCR_MASTER); |
| |
| return ret; |
| } |
| |
| static int i2c_rcar_set_addr(const struct device *dev, |
| uint8_t chip, uint8_t read) |
| { |
| const struct i2c_rcar_cfg *config = dev->config; |
| |
| /* Set slave address & transfer mode */ |
| i2c_rcar_write(config, RCAR_I2C_ICMAR, (chip << 1) | read); |
| /* Reset */ |
| i2c_rcar_write(config, RCAR_I2C_ICMCR, RCAR_I2C_ICMCR_MASTER | RCAR_I2C_ICMCR_ESG); |
| /* Clear Status */ |
| i2c_rcar_write(config, RCAR_I2C_ICMSR, 0); |
| |
| /* Wait for address & transfer mode to be transmitted */ |
| if (read != 0) { |
| return i2c_rcar_wait_for_state(dev, RCAR_I2C_MAT | RCAR_I2C_MDR); |
| } else { |
| return i2c_rcar_wait_for_state(dev, RCAR_I2C_MAT | RCAR_I2C_MDE); |
| } |
| } |
| |
| static int i2c_rcar_transfer_msg(const struct device *dev, struct i2c_msg *msg) |
| { |
| const struct i2c_rcar_cfg *config = dev->config; |
| uint32_t i, reg; |
| int ret = 0; |
| |
| if ((msg->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ) { |
| /* Reading as master */ |
| i2c_rcar_write(config, RCAR_I2C_ICMCR, RCAR_I2C_ICMCR_MASTER); |
| |
| for (i = 0; i < msg->len; i++) { |
| if (msg->len - 1 == i) { |
| i2c_rcar_write(config, RCAR_I2C_ICMCR, RCAR_I2C_ICMCR_MASTER | |
| RCAR_I2C_ICMCR_FSB); |
| } |
| |
| /* Start data reception */ |
| reg = i2c_rcar_read(config, RCAR_I2C_ICMSR); |
| reg &= ~RCAR_I2C_MDR; |
| i2c_rcar_write(config, RCAR_I2C_ICMSR, reg); |
| |
| /* Wait for data to be received */ |
| ret = i2c_rcar_wait_for_state(dev, RCAR_I2C_MDR); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| msg->buf[i] = i2c_rcar_read(config, RCAR_I2C_ICRXD_ICTXD) & 0xff; |
| } |
| } else { |
| /* Writing as master */ |
| for (i = 0; i < msg->len; i++) { |
| i2c_rcar_write(config, RCAR_I2C_ICRXD_ICTXD, msg->buf[i]); |
| |
| i2c_rcar_write(config, RCAR_I2C_ICMCR, RCAR_I2C_ICMCR_MASTER); |
| |
| /* Start data transmission */ |
| reg = i2c_rcar_read(config, RCAR_I2C_ICMSR); |
| reg &= ~RCAR_I2C_MDE; |
| i2c_rcar_write(config, RCAR_I2C_ICMSR, reg); |
| |
| /* Wait for all data to be transmitted */ |
| ret = i2c_rcar_wait_for_state(dev, RCAR_I2C_MDE); |
| if (ret != 0) { |
| return ret; |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| static int i2c_rcar_transfer(const struct device *dev, |
| struct i2c_msg *msgs, uint8_t num_msgs, |
| uint16_t addr) |
| { |
| const struct i2c_rcar_cfg *config = dev->config; |
| uint16_t timeout = 0; |
| int ret; |
| |
| if (!num_msgs) { |
| return 0; |
| } |
| |
| /* Wait for the bus to be available */ |
| while ((i2c_rcar_read(config, RCAR_I2C_ICMCR) & RCAR_I2C_ICMCR_FSDA) && (timeout < 10)) { |
| k_busy_wait(USEC_PER_MSEC); |
| timeout++; |
| } |
| if (timeout == 10) { |
| return -EIO; |
| } |
| |
| do { |
| /* We are not supporting 10-bit addressing */ |
| if ((msgs->flags & I2C_MSG_ADDR_10_BITS) == I2C_MSG_ADDR_10_BITS) { |
| return -ENOTSUP; |
| } |
| |
| /* Send slave address */ |
| if (i2c_rcar_set_addr(dev, addr, !!(msgs->flags & I2C_MSG_READ))) { |
| return -EIO; /* No ACK received */ |
| } |
| |
| /* Transfer data */ |
| if (msgs->len) { |
| ret = i2c_rcar_transfer_msg(dev, msgs); |
| if (ret != 0) { |
| return ret; |
| } |
| } |
| |
| /* Finish the transfer */ |
| if ((msgs->flags & I2C_MSG_STOP) == I2C_MSG_STOP) { |
| ret = i2c_rcar_finish(dev); |
| if (ret != 0) { |
| return ret; |
| } |
| } |
| |
| /* Next message */ |
| msgs++; |
| num_msgs--; |
| } while (num_msgs); |
| |
| /* Complete without error */ |
| return 0; |
| } |
| |
| static int i2c_rcar_configure(const struct device *dev, uint32_t dev_config) |
| { |
| const struct i2c_rcar_cfg *config = dev->config; |
| uint8_t cdf, scgd; |
| |
| /* We only support Master mode */ |
| if ((dev_config & I2C_MODE_CONTROLLER) != I2C_MODE_CONTROLLER) { |
| return -ENOTSUP; |
| } |
| |
| /* We are not supporting 10-bit addressing */ |
| if ((dev_config & I2C_ADDR_10_BITS) == I2C_ADDR_10_BITS) { |
| return -ENOTSUP; |
| } |
| |
| switch (I2C_SPEED_GET(dev_config)) { |
| case I2C_SPEED_STANDARD: |
| /* Use recommended value for 100 kHz bus */ |
| cdf = RCAR_I2C_ICCCR_CDF_100_KHZ; |
| scgd = RCAR_I2C_ICCCR_SCGD_100_KHZ; |
| break; |
| case I2C_SPEED_FAST: |
| /* Use recommended value for 400 kHz bus */ |
| cdf = RCAR_I2C_ICCCR_CDF_400_KHZ; |
| scgd = RCAR_I2C_ICCCR_SCGD_400_KHZ; |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| |
| /* Setting ICCCR to recommended value */ |
| i2c_rcar_write(config, RCAR_I2C_ICCCR, (scgd << 3) | cdf); |
| |
| /* Reset slave mode */ |
| i2c_rcar_write(config, RCAR_I2C_ICSIER, 0); |
| i2c_rcar_write(config, RCAR_I2C_ICSAR, 0); |
| i2c_rcar_write(config, RCAR_I2C_ICSCR, 0); |
| i2c_rcar_write(config, RCAR_I2C_ICSSR, 0); |
| |
| /* Reset master mode */ |
| i2c_rcar_write(config, RCAR_I2C_ICMIER, 0); |
| i2c_rcar_write(config, RCAR_I2C_ICMCR, 0); |
| i2c_rcar_write(config, RCAR_I2C_ICMSR, 0); |
| i2c_rcar_write(config, RCAR_I2C_ICMAR, 0); |
| |
| return 0; |
| } |
| |
| static int i2c_rcar_init(const struct device *dev) |
| { |
| const struct i2c_rcar_cfg *config = dev->config; |
| struct i2c_rcar_data *data = dev->data; |
| uint32_t bitrate_cfg; |
| int ret; |
| |
| k_sem_init(&data->int_sem, 0, 1); |
| |
| if (!device_is_ready(config->clock_dev)) { |
| return -ENODEV; |
| } |
| |
| ret = clock_control_on(config->clock_dev, |
| (clock_control_subsys_t)&config->mod_clk); |
| |
| if (ret != 0) { |
| return ret; |
| } |
| |
| bitrate_cfg = i2c_map_dt_bitrate(config->bitrate); |
| |
| ret = i2c_rcar_configure(dev, I2C_MODE_CONTROLLER | bitrate_cfg); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| config->init_func(dev); |
| |
| return 0; |
| } |
| |
| static const struct i2c_driver_api i2c_rcar_driver_api = { |
| .configure = i2c_rcar_configure, |
| .transfer = i2c_rcar_transfer, |
| #ifdef CONFIG_I2C_RTIO |
| .iodev_submit = i2c_iodev_submit_fallback, |
| #endif |
| }; |
| |
| /* Device Instantiation */ |
| #define I2C_RCAR_INIT(n) \ |
| static void i2c_rcar_##n##_init(const struct device *dev); \ |
| static const struct i2c_rcar_cfg i2c_rcar_cfg_##n = { \ |
| .reg_addr = DT_INST_REG_ADDR(n), \ |
| .init_func = i2c_rcar_##n##_init, \ |
| .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ |
| .bitrate = DT_INST_PROP(n, clock_frequency), \ |
| .mod_clk.module = \ |
| DT_INST_CLOCKS_CELL_BY_IDX(n, 0, module), \ |
| .mod_clk.domain = \ |
| DT_INST_CLOCKS_CELL_BY_IDX(n, 0, domain), \ |
| }; \ |
| \ |
| static struct i2c_rcar_data i2c_rcar_data_##n; \ |
| \ |
| I2C_DEVICE_DT_INST_DEFINE(n, \ |
| i2c_rcar_init, \ |
| NULL, \ |
| &i2c_rcar_data_##n, \ |
| &i2c_rcar_cfg_##n, \ |
| POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \ |
| &i2c_rcar_driver_api \ |
| ); \ |
| static void i2c_rcar_##n##_init(const struct device *dev) \ |
| { \ |
| IRQ_CONNECT(DT_INST_IRQN(n), \ |
| 0, \ |
| i2c_rcar_isr, \ |
| DEVICE_DT_INST_GET(n), 0); \ |
| \ |
| irq_enable(DT_INST_IRQN(n)); \ |
| } |
| |
| DT_INST_FOREACH_STATUS_OKAY(I2C_RCAR_INIT) |