You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/11/02 09:10:14 UTC
[incubator-nuttx] branch master updated: mpfs: i2c: Add support for
adaptive I2C bus frequency
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new a16a9f8 mpfs: i2c: Add support for adaptive I2C bus frequency
a16a9f8 is described below
commit a16a9f80e2e9ad67573041a795cb7c29caec982c
Author: Jani Paalijarvi <ja...@unikie.com>
AuthorDate: Wed Oct 27 12:22:00 2021 +0300
mpfs: i2c: Add support for adaptive I2C bus frequency
Select the closest possible frequency which is smaller
than or equal to requested in I2C msg
---
arch/risc-v/src/mpfs/mpfs_i2c.c | 194 ++++++++++++++++++++++++------
boards/risc-v/mpfs/icicle/include/board.h | 1 +
2 files changed, 155 insertions(+), 40 deletions(-)
diff --git a/arch/risc-v/src/mpfs/mpfs_i2c.c b/arch/risc-v/src/mpfs/mpfs_i2c.c
index 83eea82..74886ff 100755
--- a/arch/risc-v/src/mpfs/mpfs_i2c.c
+++ b/arch/risc-v/src/mpfs/mpfs_i2c.c
@@ -89,9 +89,22 @@ typedef enum mpfs_i2c_clock_divider
MPFS_I2C_PCLK_DIV_960,
MPFS_I2C_PCLK_DIV_120,
MPFS_I2C_PCLK_DIV_60,
- MPFS_I2C_BCLK_DIV_8
+ MPFS_I2C_BCLK_DIV_8, /* FPGA generated BCLK */
+ MPFS_I2C_NUMBER_OF_DIVIDERS
} mpfs_i2c_clk_div_t;
+static const uint32_t mpfs_i2c_frequencies[MPFS_I2C_NUMBER_OF_DIVIDERS] =
+{
+ MPFS_MSS_APB_AHB_CLK / 256,
+ MPFS_MSS_APB_AHB_CLK / 224,
+ MPFS_MSS_APB_AHB_CLK / 192,
+ MPFS_MSS_APB_AHB_CLK / 160,
+ MPFS_MSS_APB_AHB_CLK / 960,
+ MPFS_MSS_APB_AHB_CLK / 120,
+ MPFS_MSS_APB_AHB_CLK / 60,
+ MPFS_FPGA_BCLK / 8
+};
+
static int mpfs_i2c_transfer(struct i2c_master_s *dev,
struct i2c_msg_s *msgs,
int count);
@@ -115,7 +128,7 @@ struct mpfs_i2c_priv_s
uintptr_t hw_base; /* I2C bus base address */
uint16_t plic_irq; /* Platform PLIC irq */
struct i2c_msg_s *msgv; /* Message list */
- mpfs_i2c_clk_div_t bus_divider; /* Bus divider */
+ uint32_t frequency; /* Current I2C frequency */
uint8_t msgid; /* Current message ID */
ssize_t bytes; /* Processed data bytes */
@@ -135,6 +148,8 @@ struct mpfs_i2c_priv_s
uint16_t rx_idx; /* Currently accessed index */
mpfs_i2c_status_t status; /* Bus driver status */
+
+ bool initialized; /* Bus initialization status */
};
#ifdef CONFIG_MPFS_I2C0
@@ -145,7 +160,7 @@ static struct mpfs_i2c_priv_s g_mpfs_i2c0_lo_priv =
.hw_base = MPFS_I2C0_LO_BASE,
.plic_irq = MPFS_IRQ_I2C0_MAIN,
.msgv = NULL,
- .bus_divider = MPFS_I2C_PCLK_DIV_256,
+ .frequency = 0,
.ser_address = 0x21,
.target_addr = 0,
.refs = 0,
@@ -153,7 +168,8 @@ static struct mpfs_i2c_priv_s g_mpfs_i2c0_lo_priv =
.tx_idx = 0,
.rx_size = 0,
.rx_idx = 0,
- .status = MPFS_I2C_SUCCESS
+ .status = MPFS_I2C_SUCCESS,
+ .initialized = false
};
#endif /* CONFIG_MPFS_I2C0 */
@@ -165,7 +181,7 @@ static struct mpfs_i2c_priv_s g_mpfs_i2c1_lo_priv =
.hw_base = MPFS_I2C1_LO_BASE,
.plic_irq = MPFS_IRQ_I2C1_MAIN,
.msgv = NULL,
- .bus_divider = MPFS_I2C_PCLK_DIV_256,
+ .frequency = 0,
.ser_address = 0x21,
.target_addr = 0,
.refs = 0,
@@ -173,10 +189,14 @@ static struct mpfs_i2c_priv_s g_mpfs_i2c1_lo_priv =
.tx_idx = 0,
.rx_size = 0,
.rx_idx = 0,
- .status = MPFS_I2C_SUCCESS
+ .status = MPFS_I2C_SUCCESS,
+ .initialized = false
};
#endif /* CONFIG_MPFS_I2C1 */
+static int mpfs_i2c_setfrequency(struct mpfs_i2c_priv_s *priv,
+ uint32_t frequency);
+
/****************************************************************************
* Private Functions
****************************************************************************/
@@ -224,56 +244,62 @@ static void mpfs_restore_interrupts(irqstate_t primask)
* Parameters:
* priv - Pointer to the internal driver state structure.
*
+ * Returned Value:
+ * Zero (OK) is returned on success. A negated errno value is returned on
+ * failure.
+ *
****************************************************************************/
-static void mpfs_i2c_init(struct mpfs_i2c_priv_s *priv)
+static int mpfs_i2c_init(struct mpfs_i2c_priv_s *priv)
{
- uint32_t clock_speed;
uint32_t primask;
- primask = mpfs_disable_interrupts();
-
- if (priv->id == 0)
+ if (!priv->initialized)
{
- modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0, SYSREG_SOFT_RESET_CR_I2C0);
+ primask = mpfs_disable_interrupts();
- modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, SYSREG_SOFT_RESET_CR_I2C0, 0);
+ if (priv->id == 0)
+ {
+ modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+ 0, SYSREG_SOFT_RESET_CR_I2C0);
- modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0,
- SYSREG_SUBBLK_CLOCK_CR_I2C0);
- }
- else
- {
- modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, 0, SYSREG_SOFT_RESET_CR_I2C1);
+ modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+ SYSREG_SOFT_RESET_CR_I2C0, 0);
- modifyreg32(MPFS_SYSREG_SOFT_RESET_CR, SYSREG_SOFT_RESET_CR_I2C1, 0);
+ modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR,
+ 0, SYSREG_SUBBLK_CLOCK_CR_I2C0);
+ }
+ else
+ {
+ modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+ 0, SYSREG_SOFT_RESET_CR_I2C1);
- modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0,
- SYSREG_SUBBLK_CLOCK_CR_I2C1);
- }
+ modifyreg32(MPFS_SYSREG_SOFT_RESET_CR,
+ SYSREG_SOFT_RESET_CR_I2C1, 0);
- clock_speed = priv->bus_divider;
+ modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR,
+ 0, SYSREG_SUBBLK_CLOCK_CR_I2C1);
+ }
- /* Update the clock divider */
+ /* Divider is zero after I2C reset */
- modifyreg32(MPFS_I2C_CTRL,
- MPFS_I2C_CTRL_CR2_MASK |
- MPFS_I2C_CTRL_CR1_MASK |
- MPFS_I2C_CTRL_CR0_MASK,
- (((clock_speed >> 2u) & 0x01u) << MPFS_I2C_CTRL_CR2) |
- (((clock_speed >> 1u) & 0x01u) << MPFS_I2C_CTRL_CR1) |
- (((clock_speed & 0x01u) << MPFS_I2C_CTRL_CR0)));
+ priv->frequency = mpfs_i2c_frequencies[0];
- /* This is our own address, not the target chip */
+ /* This is our own address, not the target chip */
- putreg32(priv->ser_address, MPFS_I2C_ADDR);
+ putreg32(priv->ser_address, MPFS_I2C_ADDR);
- /* Enable i2c bus */
+ /* Enable i2c bus */
- modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_ENS1_MASK,
- MPFS_I2C_CTRL_ENS1_MASK);
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_ENS1_MASK,
+ MPFS_I2C_CTRL_ENS1_MASK);
- mpfs_restore_interrupts(primask);
+ priv->initialized = true;
+
+ mpfs_restore_interrupts(primask);
+ }
+
+ return OK;
}
/****************************************************************************
@@ -291,6 +317,11 @@ static void mpfs_i2c_deinit(struct mpfs_i2c_priv_s *priv)
{
up_disable_irq(priv->plic_irq);
irq_detach(priv->plic_irq);
+
+ modifyreg32(MPFS_I2C_CTRL, MPFS_I2C_CTRL_ENS1_MASK,
+ ~MPFS_I2C_CTRL_ENS1_MASK);
+
+ priv->initialized = false;
}
/****************************************************************************
@@ -662,6 +693,12 @@ static int mpfs_i2c_transfer(struct i2c_master_s *dev,
}
}
+ ret = mpfs_i2c_setfrequency(priv, msgs[i].frequency);
+ if (ret != OK)
+ {
+ break;
+ }
+
i2cinfo("Sending message %" PRIu8 "...\n", priv->msgid);
mpfs_i2c_sendstart(priv);
@@ -713,11 +750,20 @@ static int mpfs_i2c_transfer(struct i2c_master_s *dev,
static int mpfs_i2c_reset(struct i2c_master_s *dev)
{
struct mpfs_i2c_priv_s *priv = (struct mpfs_i2c_priv_s *)dev;
+ int ret;
DEBUGASSERT(priv != NULL);
up_disable_irq(priv->plic_irq);
- mpfs_i2c_init(priv);
+
+ priv->initialized = false;
+
+ ret = mpfs_i2c_init(priv);
+ if (ret != OK)
+ {
+ up_enable_irq(priv->plic_irq);
+ return ret;
+ }
priv->tx_size = 0;
priv->tx_idx = 0;
@@ -731,6 +777,69 @@ static int mpfs_i2c_reset(struct i2c_master_s *dev)
#endif
/****************************************************************************
+ * Name: mpfs_i2c_setfrequency
+ *
+ * Description:
+ * Sets the closest possible frequency for the next transfers.
+ *
+ * Input Parameters:
+ * priv - Pointer to the internal driver state structure.
+ * frequency - Requested frequency
+ *
+ * Returned Value:
+ * Zero (OK) on success;
+ *
+ ****************************************************************************/
+
+static int mpfs_i2c_setfrequency(struct mpfs_i2c_priv_s *priv,
+ uint32_t frequency)
+{
+ uint32_t new_freq = 0;
+ uint32_t clock_div = 0;
+
+ if (priv->frequency != frequency)
+ {
+ /* Select the closest possible frequency
+ * which is smaller than or equal to requested
+ */
+
+ for (uint8_t i = 0; i < MPFS_I2C_NUMBER_OF_DIVIDERS; i++)
+ {
+ if (frequency >= mpfs_i2c_frequencies[i]
+ && mpfs_i2c_frequencies[i] > new_freq)
+ {
+ new_freq = mpfs_i2c_frequencies[i];
+ clock_div = i;
+ }
+ }
+
+ if (new_freq == 0)
+ {
+ i2cerr("ERROR: Requested frequency %" PRIu32 " for I2C bus %" PRIu8
+ " is not supported by hardware.\n", frequency, priv->id);
+ return -EINVAL;
+ }
+
+ i2cinfo("Changing I2Cbus %" PRIu8 " clock speed to %" PRIu32
+ " (div %" PRIx32 ")\n", priv->id, new_freq, clock_div);
+
+ /* Update the clock divider */
+
+ modifyreg32(MPFS_I2C_CTRL,
+ MPFS_I2C_CTRL_CR2_MASK |
+ MPFS_I2C_CTRL_CR1_MASK |
+ MPFS_I2C_CTRL_CR0_MASK,
+ (((clock_div >> 2u) & 0x01u) << MPFS_I2C_CTRL_CR2) |
+ (((clock_div >> 1u) & 0x01u) << MPFS_I2C_CTRL_CR1) |
+ (((clock_div & 0x01u) << MPFS_I2C_CTRL_CR0)));
+
+ priv->frequency = frequency;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
* Public Functions
****************************************************************************/
@@ -794,7 +903,12 @@ struct i2c_master_s *mpfs_i2cbus_initialize(int port)
}
mpfs_i2c_sem_init(priv);
- mpfs_i2c_init(priv);
+ ret = mpfs_i2c_init(priv);
+ if (ret != OK)
+ {
+ leave_critical_section(flags);
+ return NULL;
+ }
leave_critical_section(flags);
diff --git a/boards/risc-v/mpfs/icicle/include/board.h b/boards/risc-v/mpfs/icicle/include/board.h
index 680915d..a7ec4d2 100755
--- a/boards/risc-v/mpfs/icicle/include/board.h
+++ b/boards/risc-v/mpfs/icicle/include/board.h
@@ -45,6 +45,7 @@
#define MPFS_MSS_RTC_TOGGLE_CLK (1000000UL)
#define MPFS_MSS_AXI_CLK (300000000UL)
#define MPFS_MSS_APB_AHB_CLK (150000000UL)
+#define MPFS_FPGA_BCLK (3000000UL)
/* LED definitions **********************************************************/