struct i2c_control *control;
struct i2c_ctlr *regs;
int is_dvc; /* DVC type, rather than I2C */
+ int is_scs; /* single clock source (T114+) */
int inited; /* bus is inited */
};
* 16 to get the right frequency.
*/
clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH,
- i2c_bus->speed * 2 * 8);
+ i2c_bus->speed * 2 * 8);
+
+ if (i2c_bus->is_scs) {
+ /*
+ * T114 I2C went to a single clock source for standard/fast and
+ * HS clock speeds. The new clock rate setting calculation is:
+ * SCL = CLK_SOURCE.I2C /
+ * (CLK_MULT_STD_FAST_MODE * (I2C_CLK_DIV_STD_FAST_MODE+1) *
+ * I2C FREQUENCY DIVISOR) as per the T114 TRM (sec 30.3.1).
+ *
+ * NOTE: We do this here, after the initial clock/pll start,
+ * because if we read the clk_div reg before the controller
+ * is running, we hang, and we need it for the new calc.
+ */
+ int clk_div_stdfst_mode = readl(&i2c_bus->regs->clk_div) >> 16;
+ debug("%s: CLK_DIV_STD_FAST_MODE setting = %d\n", __func__,
+ clk_div_stdfst_mode);
+
+ clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH,
+ CLK_MULT_STD_FAST_MODE * (clk_div_stdfst_mode + 1) *
+ i2c_bus->speed * 2);
+ }
/* Reset I2C controller. */
i2c_reset_controller(i2c_bus);
return error;
}
-static int tegra_i2c_write_data(u32 addr, u8 *data, u32 len)
+static int tegra_i2c_write_data(struct i2c_bus *bus, u32 addr, u8 *data,
+ u32 len)
{
int error;
struct i2c_trans_info trans_info;
trans_info.num_bytes = len;
trans_info.is_10bit_address = 0;
- error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info);
+ error = send_recv_packets(bus, &trans_info);
if (error)
debug("tegra_i2c_write_data: Error (%d) !!!\n", error);
return error;
}
-static int tegra_i2c_read_data(u32 addr, u8 *data, u32 len)
+static int tegra_i2c_read_data(struct i2c_bus *bus, u32 addr, u8 *data,
+ u32 len)
{
int error;
struct i2c_trans_info trans_info;
trans_info.num_bytes = len;
trans_info.is_10bit_address = 0;
- error = send_recv_packets(&i2c_controllers[i2c_bus_num], &trans_info);
+ error = send_recv_packets(bus, &trans_info);
if (error)
debug("tegra_i2c_read_data: Error (%d) !!!\n", error);
#error "Please enable device tree support to use this driver"
#endif
+/**
+ * Check that a bus number is valid and return a pointer to it
+ *
+ * @param bus_num Bus number to check / return
+ * @return pointer to bus, if valid, else NULL
+ */
+static struct i2c_bus *tegra_i2c_get_bus(unsigned int bus_num)
+{
+ struct i2c_bus *bus;
+
+ if (bus_num >= TEGRA_I2C_NUM_CONTROLLERS) {
+ debug("%s: Invalid bus number %u\n", __func__, bus_num);
+ return NULL;
+ }
+ bus = &i2c_controllers[bus_num];
+ if (!bus->inited) {
+ debug("%s: Bus %u not available\n", __func__, bus_num);
+ return NULL;
+ }
+
+ return bus;
+}
+
unsigned int i2c_get_bus_speed(void)
{
- return i2c_controllers[i2c_bus_num].speed;
+ struct i2c_bus *bus;
+
+ bus = tegra_i2c_get_bus(i2c_bus_num);
+ if (!bus)
+ return 0;
+ return bus->speed;
}
int i2c_set_bus_speed(unsigned int speed)
{
- struct i2c_bus *i2c_bus;
+ struct i2c_bus *bus;
- i2c_bus = &i2c_controllers[i2c_bus_num];
- i2c_bus->speed = speed;
- i2c_init_controller(i2c_bus);
+ bus = tegra_i2c_get_bus(i2c_bus_num);
+ if (!bus)
+ return 0;
+ bus->speed = speed;
+ i2c_init_controller(bus);
return 0;
}
* @param node_list list of nodes to process (any <=0 are ignored)
* @param count number of nodes to process
* @param is_dvc 1 if these are DVC ports, 0 if standard I2C
+ * @param is_scs 1 if this HW uses a single clock source (T114+)
* @return 0 if ok, -1 on error
*/
static int process_nodes(const void *blob, int node_list[], int count,
- int is_dvc)
+ int is_dvc, int is_scs)
{
struct i2c_bus *i2c_bus;
int i;
return -1;
}
+ i2c_bus->is_scs = is_scs;
+
i2c_bus->is_dvc = is_dvc;
if (is_dvc) {
i2c_bus->control =
const void *blob = gd->fdt_blob;
int count;
- /* First get the normal i2c ports */
+ /* First check for newer (T114+) I2C ports */
+ count = fdtdec_find_aliases_for_id(blob, "i2c",
+ COMPAT_NVIDIA_TEGRA114_I2C, node_list,
+ TEGRA_I2C_NUM_CONTROLLERS);
+ if (process_nodes(blob, node_list, count, 0, 1))
+ return;
+
+ /* Now get the older (T20/T30) normal I2C ports */
count = fdtdec_find_aliases_for_id(blob, "i2c",
COMPAT_NVIDIA_TEGRA20_I2C, node_list,
TEGRA_I2C_NUM_CONTROLLERS);
- if (process_nodes(blob, node_list, count, 0))
+ if (process_nodes(blob, node_list, count, 0, 0))
return;
/* Now look for dvc ports */
count = fdtdec_add_aliases_for_id(blob, "i2c",
COMPAT_NVIDIA_TEGRA20_DVC, node_list,
TEGRA_I2C_NUM_CONTROLLERS);
- if (process_nodes(blob, node_list, count, 1))
+ if (process_nodes(blob, node_list, count, 1, 0))
return;
}
}
/* i2c write version without the register address */
-int i2c_write_data(uchar chip, uchar *buffer, int len)
+int i2c_write_data(struct i2c_bus *bus, uchar chip, uchar *buffer, int len)
{
int rc;
debug("\n");
/* Shift 7-bit address over for lower-level i2c functions */
- rc = tegra_i2c_write_data(chip << 1, buffer, len);
+ rc = tegra_i2c_write_data(bus, chip << 1, buffer, len);
if (rc)
debug("i2c_write_data(): rc=%d\n", rc);
}
/* i2c read version without the register address */
-int i2c_read_data(uchar chip, uchar *buffer, int len)
+int i2c_read_data(struct i2c_bus *bus, uchar chip, uchar *buffer, int len)
{
int rc;
debug("inside i2c_read_data():\n");
/* Shift 7-bit address over for lower-level i2c functions */
- rc = tegra_i2c_read_data(chip << 1, buffer, len);
+ rc = tegra_i2c_read_data(bus, chip << 1, buffer, len);
if (rc) {
debug("i2c_read_data(): rc=%d\n", rc);
return rc;
/* Probe to see if a chip is present. */
int i2c_probe(uchar chip)
{
+ struct i2c_bus *bus;
int rc;
uchar reg;
debug("i2c_probe: addr=0x%x\n", chip);
+ bus = tegra_i2c_get_bus(i2c_get_bus_num());
+ if (!bus)
+ return 1;
reg = 0;
- rc = i2c_write_data(chip, ®, 1);
+ rc = i2c_write_data(bus, chip, ®, 1);
if (rc) {
debug("Error probing 0x%x.\n", chip);
return 1;
/* Read bytes */
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
+ struct i2c_bus *bus;
uint offset;
int i;
debug("i2c_read: chip=0x%x, addr=0x%x, len=0x%x\n",
chip, addr, len);
+ bus = tegra_i2c_get_bus(i2c_bus_num);
+ if (!bus)
+ return 1;
if (!i2c_addr_ok(addr, alen)) {
debug("i2c_read: Bad address %x.%d.\n", addr, alen);
return 1;
data[alen - i - 1] =
(addr + offset) >> (8 * i);
}
- if (i2c_write_data(chip, data, alen)) {
+ if (i2c_write_data(bus, chip, data, alen)) {
debug("i2c_read: error sending (0x%x)\n",
addr);
return 1;
}
}
- if (i2c_read_data(chip, buffer + offset, 1)) {
+ if (i2c_read_data(bus, chip, buffer + offset, 1)) {
debug("i2c_read: error reading (0x%x)\n", addr);
return 1;
}
/* Write bytes */
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
+ struct i2c_bus *bus;
uint offset;
int i;
debug("i2c_write: chip=0x%x, addr=0x%x, len=0x%x\n",
chip, addr, len);
+ bus = tegra_i2c_get_bus(i2c_bus_num);
+ if (!bus)
+ return 1;
if (!i2c_addr_ok(addr, alen)) {
debug("i2c_write: Bad address %x.%d.\n", addr, alen);
return 1;
for (i = 0; i < alen; i++)
data[alen - i - 1] = (addr + offset) >> (8 * i);
data[alen] = buffer[offset];
- if (i2c_write_data(chip, data, alen + 1)) {
+ if (i2c_write_data(bus, chip, data, alen + 1)) {
debug("i2c_write: error sending (0x%x)\n", addr);
return 1;
}