BIG
};
+enum i2c_scl_freq {
+ REG_VALUES_100KHZ = 0,
+ REG_VALUES_400KHZ = 1,
+ REG_VALUES_1MHZ = 2
+};
+
/**
* struct xiic_i2c - Internal representation of the XIIC I2C bus
* @dev: Pointer to device structure
* @prev_msg_tx: Previous message is Tx
* @quirks: To hold platform specific bug info
* @smbus_block_read: Flag to handle block read
+ * @input_clk: Input clock to I2C controller
+ * @i2c_clk: I2C SCL frequency
*/
struct xiic_i2c {
struct device *dev;
bool prev_msg_tx;
u32 quirks;
bool smbus_block_read;
+ unsigned long input_clk;
+ unsigned int i2c_clk;
};
struct xiic_version_data {
u32 quirks;
};
+/**
+ * struct timing_regs - AXI I2C timing registers that depend on I2C spec
+ * @tsusta: setup time for a repeated START condition
+ * @tsusto: setup time for a STOP condition
+ * @thdsta: hold time for a repeated START condition
+ * @tsudat: setup time for data
+ * @tbuf: bus free time between STOP and START
+ */
+struct timing_regs {
+ unsigned int tsusta;
+ unsigned int tsusto;
+ unsigned int thdsta;
+ unsigned int tsudat;
+ unsigned int tbuf;
+};
+
+/* Reg values in ns derived from I2C spec and AXI I2C PG for different frequencies */
+static const struct timing_regs timing_reg_values[] = {
+ { 5700, 5000, 4300, 550, 5000 }, /* Reg values for 100KHz */
+ { 900, 900, 900, 400, 1600 }, /* Reg values for 400KHz */
+ { 380, 380, 380, 170, 620 }, /* Reg values for 1MHz */
+};
+
#define XIIC_MSB_OFFSET 0
#define XIIC_REG_OFFSET (0x100 + XIIC_MSB_OFFSET)
#define XIIC_RFD_REG_OFFSET (0x20 + XIIC_REG_OFFSET) /* Rx FIFO Depth reg */
#define XIIC_GPO_REG_OFFSET (0x24 + XIIC_REG_OFFSET) /* Output Register */
+/*
+ * Timing register offsets from RegisterBase. These are used only for
+ * setting i2c clock frequency for the line.
+ */
+#define XIIC_TSUSTA_REG_OFFSET (0x28 + XIIC_REG_OFFSET) /* TSUSTA Register */
+#define XIIC_TSUSTO_REG_OFFSET (0x2C + XIIC_REG_OFFSET) /* TSUSTO Register */
+#define XIIC_THDSTA_REG_OFFSET (0x30 + XIIC_REG_OFFSET) /* THDSTA Register */
+#define XIIC_TSUDAT_REG_OFFSET (0x34 + XIIC_REG_OFFSET) /* TSUDAT Register */
+#define XIIC_TBUF_REG_OFFSET (0x38 + XIIC_REG_OFFSET) /* TBUF Register */
+#define XIIC_THIGH_REG_OFFSET (0x3C + XIIC_REG_OFFSET) /* THIGH Register */
+#define XIIC_TLOW_REG_OFFSET (0x40 + XIIC_REG_OFFSET) /* TLOW Register */
+#define XIIC_THDDAT_REG_OFFSET (0x44 + XIIC_REG_OFFSET) /* THDDAT Register */
+
/* Control Register masks */
#define XIIC_CR_ENABLE_DEVICE_MASK 0x01 /* Device enable = 1 */
#define XIIC_CR_TX_FIFO_RESET_MASK 0x02 /* Transmit FIFO reset=1 */
return 0;
}
+/**
+ * xiic_setclk - Sets the configured clock rate
+ * @i2c: Pointer to the xiic device structure
+ *
+ * The timing register values are calculated according to the input clock
+ * frequency and configured scl frequency. For details, please refer the
+ * AXI I2C PG and NXP I2C Spec.
+ * Supported frequencies are 100KHz, 400KHz and 1MHz.
+ *
+ * Return: 0 on success (Supported frequency selected or not configurable in SW)
+ * -EINVAL on failure (scl frequency not supported or THIGH is 0)
+ */
+static int xiic_setclk(struct xiic_i2c *i2c)
+{
+ unsigned int clk_in_mhz;
+ unsigned int index = 0;
+ u32 reg_val;
+
+ dev_dbg(i2c->adap.dev.parent,
+ "%s entry, i2c->input_clk: %ld, i2c->i2c_clk: %d\n",
+ __func__, i2c->input_clk, i2c->i2c_clk);
+
+ /* If not specified in DT, do not configure in SW. Rely only on Vivado design */
+ if (!i2c->i2c_clk || !i2c->input_clk)
+ return 0;
+
+ clk_in_mhz = DIV_ROUND_UP(i2c->input_clk, 1000000);
+
+ switch (i2c->i2c_clk) {
+ case I2C_MAX_FAST_MODE_PLUS_FREQ:
+ index = REG_VALUES_1MHZ;
+ break;
+ case I2C_MAX_FAST_MODE_FREQ:
+ index = REG_VALUES_400KHZ;
+ break;
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ index = REG_VALUES_100KHZ;
+ break;
+ default:
+ dev_warn(i2c->adap.dev.parent, "Unsupported scl frequency\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Value to be stored in a register is the number of clock cycles required
+ * for the time duration. So the time is divided by the input clock time
+ * period to get the number of clock cycles required. Refer Xilinx AXI I2C
+ * PG document and I2C specification for further details.
+ */
+
+ /* THIGH - Depends on SCL clock frequency(i2c_clk) as below */
+ reg_val = (DIV_ROUND_UP(i2c->input_clk, 2 * i2c->i2c_clk)) - 7;
+ if (reg_val == 0)
+ return -EINVAL;
+
+ xiic_setreg32(i2c, XIIC_THIGH_REG_OFFSET, reg_val - 1);
+
+ /* TLOW - Value same as THIGH */
+ xiic_setreg32(i2c, XIIC_TLOW_REG_OFFSET, reg_val - 1);
+
+ /* TSUSTA */
+ reg_val = (timing_reg_values[index].tsusta * clk_in_mhz) / 1000;
+ xiic_setreg32(i2c, XIIC_TSUSTA_REG_OFFSET, reg_val - 1);
+
+ /* TSUSTO */
+ reg_val = (timing_reg_values[index].tsusto * clk_in_mhz) / 1000;
+ xiic_setreg32(i2c, XIIC_TSUSTO_REG_OFFSET, reg_val - 1);
+
+ /* THDSTA */
+ reg_val = (timing_reg_values[index].thdsta * clk_in_mhz) / 1000;
+ xiic_setreg32(i2c, XIIC_THDSTA_REG_OFFSET, reg_val - 1);
+
+ /* TSUDAT */
+ reg_val = (timing_reg_values[index].tsudat * clk_in_mhz) / 1000;
+ xiic_setreg32(i2c, XIIC_TSUDAT_REG_OFFSET, reg_val - 1);
+
+ /* TBUF */
+ reg_val = (timing_reg_values[index].tbuf * clk_in_mhz) / 1000;
+ xiic_setreg32(i2c, XIIC_TBUF_REG_OFFSET, reg_val - 1);
+
+ /* THDDAT */
+ xiic_setreg32(i2c, XIIC_THDDAT_REG_OFFSET, 1);
+
+ return 0;
+}
+
static int xiic_reinit(struct xiic_i2c *i2c)
{
int ret;
xiic_setreg32(i2c, XIIC_RESETR_OFFSET, XIIC_RESET_MASK);
+ ret = xiic_setclk(i2c);
+ if (ret)
+ return ret;
+
/* Set receive Fifo depth to maximum (zero based). */
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, IIC_RX_FIFO_DEPTH - 1);
pm_runtime_use_autosuspend(i2c->dev);
pm_runtime_set_active(i2c->dev);
pm_runtime_enable(i2c->dev);
+
+ /* SCL frequency configuration */
+ i2c->input_clk = clk_get_rate(i2c->clk);
+ ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+ &i2c->i2c_clk);
+ /* If clock-frequency not specified in DT, do not configure in SW */
+ if (ret || i2c->i2c_clk > I2C_MAX_FAST_MODE_PLUS_FREQ)
+ i2c->i2c_clk = 0;
+
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
xiic_process, IRQF_ONESHOT,
pdev->name, i2c);
i2c_new_client_device(&i2c->adap, pdata->devices + i);
}
+ dev_dbg(&pdev->dev, "mmio %08lx irq %d scl clock frequency %d\n",
+ (unsigned long)res->start, irq, i2c->i2c_clk);
+
return 0;
err_clk_dis: