aboutsummaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-cadence.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-cadence.c')
-rw-r--r--drivers/i2c/busses/i2c-cadence.c117
1 files changed, 88 insertions, 29 deletions
diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
index b5d22e7282c2..3a4edf7e75f9 100644
--- a/drivers/i2c/busses/i2c-cadence.c
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/reset.h>
/* Register offsets for the I2C device. */
#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */
@@ -114,10 +115,10 @@
/* timeout for pm runtime autosuspend */
#define CNDS_I2C_PM_TIMEOUT 1000 /* ms */
-#define CDNS_I2C_FIFO_DEPTH 16
+#define CDNS_I2C_FIFO_DEPTH_DEFAULT 16
#define CDNS_I2C_MAX_TRANSFER_SIZE 255
/* Transfer size in multiples of data interrupt depth */
-#define CDNS_I2C_TRANSFER_SIZE (CDNS_I2C_MAX_TRANSFER_SIZE - 3)
+#define CDNS_I2C_TRANSFER_SIZE(max) ((max) - 3)
#define DRIVER_NAME "cdns-i2c"
@@ -178,12 +179,15 @@ enum cdns_i2c_slave_state {
* @bus_hold_flag: Flag used in repeated start for clearing HOLD bit
* @clk: Pointer to struct clk
* @clk_rate_change_nb: Notifier block for clock rate changes
+ * @reset: Reset control for the device
* @quirks: flag for broken hold bit usage in r1p10
* @ctrl_reg: Cached value of the control register.
* @ctrl_reg_diva_divb: value of fields DIV_A and DIV_B from CR register
* @slave: Registered slave instance.
* @dev_mode: I2C operating role(master/slave).
* @slave_state: I2C Slave state(idle/read/write).
+ * @fifo_depth: The depth of the transfer FIFO
+ * @transfer_size: The maximum number of bytes in one transfer
*/
struct cdns_i2c {
struct device *dev;
@@ -202,6 +206,7 @@ struct cdns_i2c {
unsigned int bus_hold_flag;
struct clk *clk;
struct notifier_block clk_rate_change_nb;
+ struct reset_control *reset;
u32 quirks;
u32 ctrl_reg;
struct i2c_bus_recovery_info rinfo;
@@ -211,6 +216,8 @@ struct cdns_i2c {
enum cdns_i2c_mode dev_mode;
enum cdns_i2c_slave_state slave_state;
#endif
+ u32 fifo_depth;
+ unsigned int transfer_size;
};
struct cdns_platform_data {
@@ -236,7 +243,7 @@ static void cdns_i2c_clear_bus_hold(struct cdns_i2c *id)
static inline bool cdns_is_holdquirk(struct cdns_i2c *id, bool hold_wrkaround)
{
return (hold_wrkaround &&
- (id->curr_recv_count == CDNS_I2C_FIFO_DEPTH + 1));
+ (id->curr_recv_count == id->fifo_depth + 1));
}
#if IS_ENABLED(CONFIG_I2C_SLAVE)
@@ -431,7 +438,7 @@ static irqreturn_t cdns_i2c_master_isr(void *ptr)
* if RX data left is less than or equal to
* FIFO DEPTH unless repeated start is selected
*/
- if (id->recv_count <= CDNS_I2C_FIFO_DEPTH &&
+ if (id->recv_count <= id->fifo_depth &&
!id->bus_hold_flag)
cdns_i2c_clear_bus_hold(id);
@@ -456,22 +463,22 @@ static irqreturn_t cdns_i2c_master_isr(void *ptr)
if (cdns_is_holdquirk(id, updatetx)) {
/* wait while fifo is full */
while (cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET) !=
- (id->curr_recv_count - CDNS_I2C_FIFO_DEPTH))
+ (id->curr_recv_count - id->fifo_depth))
;
/*
* Check number of bytes to be received against maximum
* transfer size and update register accordingly.
*/
- if (((int)(id->recv_count) - CDNS_I2C_FIFO_DEPTH) >
- CDNS_I2C_TRANSFER_SIZE) {
- cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
+ if (((int)(id->recv_count) - id->fifo_depth) >
+ id->transfer_size) {
+ cdns_i2c_writereg(id->transfer_size,
CDNS_I2C_XFER_SIZE_OFFSET);
- id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE +
- CDNS_I2C_FIFO_DEPTH;
+ id->curr_recv_count = id->transfer_size +
+ id->fifo_depth;
} else {
cdns_i2c_writereg(id->recv_count -
- CDNS_I2C_FIFO_DEPTH,
+ id->fifo_depth,
CDNS_I2C_XFER_SIZE_OFFSET);
id->curr_recv_count = id->recv_count;
}
@@ -494,7 +501,7 @@ static irqreturn_t cdns_i2c_master_isr(void *ptr)
* space available in FIFO and fill with that many bytes.
*/
if (id->send_count) {
- avail_bytes = CDNS_I2C_FIFO_DEPTH -
+ avail_bytes = id->fifo_depth -
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
if (id->send_count > avail_bytes)
bytes_to_send = avail_bytes;
@@ -588,7 +595,7 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
* Check for the message size against FIFO depth and set the
* 'hold bus' bit if it is greater than FIFO depth.
*/
- if (id->recv_count > CDNS_I2C_FIFO_DEPTH)
+ if (id->recv_count > id->fifo_depth)
ctrl_reg |= CDNS_I2C_CR_HOLD;
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
@@ -603,17 +610,17 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
* receive if it is less than transfer size and transfer size if
* it is more. Enable the interrupts.
*/
- if (id->recv_count > CDNS_I2C_TRANSFER_SIZE) {
- cdns_i2c_writereg(CDNS_I2C_TRANSFER_SIZE,
+ if (id->recv_count > id->transfer_size) {
+ cdns_i2c_writereg(id->transfer_size,
CDNS_I2C_XFER_SIZE_OFFSET);
- id->curr_recv_count = CDNS_I2C_TRANSFER_SIZE;
+ id->curr_recv_count = id->transfer_size;
} else {
cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
}
/* Determine hold_clear based on number of bytes to receive and hold flag */
- if (!id->bus_hold_flag && id->recv_count <= CDNS_I2C_FIFO_DEPTH) {
- if (cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & CDNS_I2C_CR_HOLD) {
+ if (!id->bus_hold_flag && id->recv_count <= id->fifo_depth) {
+ if (ctrl_reg & CDNS_I2C_CR_HOLD) {
hold_clear = true;
if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT)
irq_save = true;
@@ -624,7 +631,7 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
addr &= CDNS_I2C_ADDR_MASK;
if (hold_clear) {
- ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & ~CDNS_I2C_CR_HOLD;
+ ctrl_reg &= ~CDNS_I2C_CR_HOLD;
/*
* In case of Xilinx Zynq SOC, clear the HOLD bit before transfer size
* register reaches '0'. This is an IP bug which causes transfer size
@@ -673,7 +680,7 @@ static void cdns_i2c_msend(struct cdns_i2c *id)
* Check for the message size against FIFO depth and set the
* 'hold bus' bit if it is greater than FIFO depth.
*/
- if (id->send_count > CDNS_I2C_FIFO_DEPTH)
+ if (id->send_count > id->fifo_depth)
ctrl_reg |= CDNS_I2C_CR_HOLD;
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
@@ -686,7 +693,7 @@ static void cdns_i2c_msend(struct cdns_i2c *id)
* against the space available, and fill the FIFO accordingly.
* Enable the interrupts.
*/
- avail_bytes = CDNS_I2C_FIFO_DEPTH -
+ avail_bytes = id->fifo_depth -
cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
if (id->send_count > avail_bytes)
@@ -827,8 +834,10 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
#if IS_ENABLED(CONFIG_I2C_SLAVE)
/* Check i2c operating mode and switch if possible */
if (id->dev_mode == CDNS_I2C_MODE_SLAVE) {
- if (id->slave_state != CDNS_I2C_SLAVE_STATE_IDLE)
- return -EAGAIN;
+ if (id->slave_state != CDNS_I2C_SLAVE_STATE_IDLE) {
+ ret = -EAGAIN;
+ goto out;
+ }
/* Set mode to master */
cdns_i2c_set_mode(CDNS_I2C_MODE_MASTER, id);
@@ -1030,8 +1039,7 @@ static int cdns_i2c_calc_divs(unsigned long *f, unsigned long input_clk,
if (actual_fscl > fscl)
continue;
- current_error = ((actual_fscl > fscl) ? (actual_fscl - fscl) :
- (fscl - actual_fscl));
+ current_error = fscl - actual_fscl;
if (last_error > current_error) {
calc_div_a = div_a;
@@ -1227,6 +1235,37 @@ static const struct of_device_id cdns_i2c_of_match[] = {
MODULE_DEVICE_TABLE(of, cdns_i2c_of_match);
/**
+ * cdns_i2c_detect_transfer_size - Detect the maximum transfer size supported
+ * @id: Device private data structure
+ *
+ * Detect the maximum transfer size that is supported by this instance of the
+ * Cadence I2C controller.
+ */
+static void cdns_i2c_detect_transfer_size(struct cdns_i2c *id)
+{
+ u32 val;
+
+ /*
+ * Writing to the transfer size register is only possible if these two bits
+ * are set in the control register.
+ */
+ cdns_i2c_writereg(CDNS_I2C_CR_MS | CDNS_I2C_CR_RW, CDNS_I2C_CR_OFFSET);
+
+ /*
+ * The number of writable bits of the transfer size register can be between
+ * 4 and 8. This is a controlled through a synthesis parameter of the IP
+ * core and can vary from instance to instance. The unused MSBs always read
+ * back as 0. Writing 0xff and then reading the value back will report the
+ * maximum supported transfer size.
+ */
+ cdns_i2c_writereg(CDNS_I2C_MAX_TRANSFER_SIZE, CDNS_I2C_XFER_SIZE_OFFSET);
+ val = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
+ id->transfer_size = CDNS_I2C_TRANSFER_SIZE(val);
+ cdns_i2c_writereg(0, CDNS_I2C_XFER_SIZE_OFFSET);
+ cdns_i2c_writereg(0, CDNS_I2C_CR_OFFSET);
+}
+
+/**
* cdns_i2c_probe - Platform registration call
* @pdev: Handle to the platform device structure
*
@@ -1291,10 +1330,22 @@ static int cdns_i2c_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(id->clk),
"input clock not found.\n");
+ id->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
+ if (IS_ERR(id->reset))
+ return dev_err_probe(&pdev->dev, PTR_ERR(id->reset),
+ "Failed to request reset.\n");
+
ret = clk_prepare_enable(id->clk);
if (ret)
dev_err(&pdev->dev, "Unable to enable clock.\n");
+ ret = reset_control_deassert(id->reset);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret,
+ "Failed to de-assert reset.\n");
+ goto err_clk_dis;
+ }
+
pm_runtime_set_autosuspend_delay(id->dev, CNDS_I2C_PM_TIMEOUT);
pm_runtime_use_autosuspend(id->dev);
pm_runtime_set_active(id->dev);
@@ -1317,32 +1368,39 @@ static int cdns_i2c_probe(struct platform_device *pdev)
#endif
id->ctrl_reg = CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS;
+ id->fifo_depth = CDNS_I2C_FIFO_DEPTH_DEFAULT;
+ of_property_read_u32(pdev->dev.of_node, "fifo-depth", &id->fifo_depth);
+
+ cdns_i2c_detect_transfer_size(id);
+
ret = cdns_i2c_setclk(id->input_clk, id);
if (ret) {
dev_err(&pdev->dev, "invalid SCL clock: %u Hz\n", id->i2c_clk);
ret = -EINVAL;
- goto err_clk_dis;
+ goto err_clk_notifier_unregister;
}
ret = devm_request_irq(&pdev->dev, irq, cdns_i2c_isr, 0,
DRIVER_NAME, id);
if (ret) {
dev_err(&pdev->dev, "cannot get irq %d\n", irq);
- goto err_clk_dis;
+ goto err_clk_notifier_unregister;
}
cdns_i2c_init(id);
ret = i2c_add_adapter(&id->adap);
if (ret < 0)
- goto err_clk_dis;
+ goto err_clk_notifier_unregister;
dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",
id->i2c_clk / 1000, (unsigned long)r_mem->start, irq);
return 0;
-err_clk_dis:
+err_clk_notifier_unregister:
clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
+ reset_control_assert(id->reset);
+err_clk_dis:
clk_disable_unprepare(id->clk);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
@@ -1367,6 +1425,7 @@ static int cdns_i2c_remove(struct platform_device *pdev)
i2c_del_adapter(&id->adap);
clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
+ reset_control_assert(id->reset);
clk_disable_unprepare(id->clk);
return 0;