[Skiboot] [PATCH 01/13] p8/i2c: Add OPAL properties in the i2c bus nodes
Jeremy Kerr
jk at ozlabs.org
Fri Feb 27 20:11:06 AEDT 2015
From: Neelesh Gupta <neelegup at linux.vnet.ibm.com>
We are getting the i2c device tree data from the hostboot and it
has i2cm nodes compatible to multiple chips like p8 and centaur.
The patch further adds the OPAL properties to the i2c bus nodes
required by the kernel.
Signed-off-by: Neelesh Gupta <neelegup at linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh at kernel.crashing.org>
Signed-off-by: Jeremy Kerr <jk at ozlabs.org>
---
hw/p8-i2c.c | 259 ++++++++++++++++++++++++++++++++--------------------
1 file changed, 162 insertions(+), 97 deletions(-)
diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c
index 0641062..061bdaf 100644
--- a/hw/p8-i2c.c
+++ b/hw/p8-i2c.c
@@ -165,8 +165,15 @@ DEFINE_LOG_ENTRY(OPAL_RC_I2C_RESET, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C,
/* Port busy register */
#define I2C_PORT_BUYS_REG 0xe
+enum p8_i2c_master_type {
+ I2C_POWER8,
+ I2C_CENTAUR,
+ MAX_I2C_TYPE,
+};
+
struct p8_i2c_master {
struct lock lock; /* Lock to guard the members */
+ enum p8_i2c_master_type type; /* P8 vs. Centaur */
uint64_t poll_interval; /* Polling interval */
uint64_t byte_timeout; /* Timeout per byte */
uint64_t xscom_base; /* xscom base of i2cm */
@@ -1099,122 +1106,180 @@ void p8_i2c_interrupt(uint32_t chip_id)
}
}
-void p8_i2c_init(void)
+static const char *compat[] = {
+ "ibm,power8-i2cm",
+ "ibm,centaur-i2cm"
+};
+
+static void p8_i2c_add_bus_prop(struct p8_i2c_master_port *port)
+{
+ const struct dt_property *c, *p;
+ struct dt_node *np = port->bus.dt_node;
+ char name[32];
+
+ c = dt_find_property(np, "compatible");
+ p = dt_find_property(np, "ibm,port-name");
+
+ if (!c) {
+ if (port->master->type == I2C_POWER8)
+ dt_add_property_strings(np, "compatible",
+ "ibm,power8-i2c-port",
+ "ibm,opal-i2c");
+ else if (port->master->type == I2C_CENTAUR)
+ dt_add_property_strings(np, "compatible",
+ "ibm,centaur-i2c-port",
+ "ibm,opal-i2c");
+ }
+
+ if (!p) {
+ if (port->master->type == I2C_POWER8)
+ snprintf(name, sizeof(name), "p8_%08x_e%dp%d",
+ port->master->chip_id, port->master->engine_id,
+ port->port_num);
+ else if (port->master->type == I2C_CENTAUR)
+ snprintf(name, sizeof(name), "cen_%08x_e%dp%d",
+ port->master->chip_id, port->master->engine_id,
+ port->port_num);
+
+ dt_add_property_string(np, "ibm,port-name", name);
+ }
+}
+
+static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type)
{
struct p8_i2c_master_port *port;
uint32_t lb_freq, count, max_bus_speed;
- struct dt_node *i2cm, *i2cm_port;
+ struct dt_node *i2cm_port;
struct p8_i2c_master *master;
struct proc_chip *chip;
uint64_t ex_stat;
static bool irq_printed;
int rc;
- dt_for_each_compatible(dt_root, i2cm, "ibm,power8-i2cm") {
- master = zalloc(sizeof(*master));
- if (!master) {
- log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: "
- "Failed to allocate master structure\n");
- break;
- }
+ if (type == I2C_CENTAUR) {
+ /* Not supported yet ! No interrupt, possibly diff reg layout... */
+ return;
+ }
+
+ master = zalloc(sizeof(*master));
+ if (!master) {
+ log_simple_error(&e_info(OPAL_RC_I2C_INIT),
+ "I2C: Failed to allocate master "
+ "structure\n");
+ return;
+ }
+ master->type = type;
- /* Local bus speed in Hz */
- lb_freq = dt_prop_get_u32(i2cm, "clock-frequency");
+ /* Local bus speed in Hz */
+ lb_freq = dt_prop_get_u32(i2cm, "clock-frequency");
- /* Initialise the i2c master structure */
- master->state = state_idle;
- master->chip_id = dt_get_chip_id(i2cm);
- master->engine_id = dt_prop_get_u32(i2cm, "chip-engine#");
- master->xscom_base = dt_get_address(i2cm, 0, NULL);
- chip = get_chip(master->chip_id);
- assert(chip);
- init_timer(&master->timeout, p8_i2c_timeout, master);
- init_timer(&master->poller, p8_i2c_poll, master);
- init_timer(&master->recovery, p8_i2c_recover, master);
+ /* Initialise the i2c master structure */
+ master->state = state_idle;
+ master->chip_id = dt_get_chip_id(i2cm);
+ master->engine_id = dt_prop_get_u32(i2cm, "chip-engine#");
+ master->xscom_base = dt_get_address(i2cm, 0, NULL);
+ chip = get_chip(master->chip_id);
+ assert(chip);
+ init_timer(&master->timeout, p8_i2c_timeout, master);
+ init_timer(&master->poller, p8_i2c_poll, master);
+ init_timer(&master->recovery, p8_i2c_recover, master);
- prlog(PR_INFO, "I2C: Chip %08x Eng. %d\n",
- master->chip_id, master->engine_id);
+ prlog(PR_INFO, "I2C: Chip %08x Eng. %d\n",
+ master->chip_id, master->engine_id);
- rc = xscom_read(master->chip_id, master->xscom_base +
- I2C_EXTD_STAT_REG, &ex_stat);
- if (rc) {
- log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: "
- "Failed to read EXTD_STAT_REG\n");
- free(master);
- break;
- }
+ rc = xscom_read(master->chip_id, master->xscom_base +
+ I2C_EXTD_STAT_REG, &ex_stat);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: "
+ "Failed to read EXTD_STAT_REG\n");
+ free(master);
+ return;
+ }
- master->fifo_size = GETFIELD(I2C_EXTD_STAT_FIFO_SIZE, ex_stat);
- list_head_init(&master->req_list);
+ master->fifo_size = GETFIELD(I2C_EXTD_STAT_FIFO_SIZE, ex_stat);
+ list_head_init(&master->req_list);
- /* Check if interrupt is usable */
- master->irq_ok = p8_i2c_has_irqs();
- if (!irq_printed) {
- irq_printed = true;
- prlog(PR_INFO, "I2C: Interrupts %sfunctional\n",
- master->irq_ok ? "" : "non-");
- }
+ /* Check if interrupt is usable */
+ master->irq_ok = p8_i2c_has_irqs();
+ if (!irq_printed) {
+ irq_printed = true;
+ prlog(PR_INFO, "I2C: Interrupts %sfunctional\n",
+ master->irq_ok ? "" : "non-");
+ }
- /* Program the watermark register */
- rc = p8_i2c_prog_watermark(master);
- if (rc) {
- log_simple_error(&e_info(OPAL_RC_I2C_INIT), "I2C: "
- "Failed to program the WATERMARK_REG\n");
- free(master);
- break;
- }
+ /* Program the watermark register */
+ rc = p8_i2c_prog_watermark(master);
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_I2C_INIT),
+ "I2C: Failed to program the "
+ "WATERMARK_REG\n");
+ free(master);
+ return;
+ }
- /* Allocate ports driven by this master */
- count = 0;
- dt_for_each_child(i2cm, i2cm_port)
- count++;
+ /* Allocate ports driven by this master */
+ count = 0;
+ dt_for_each_child(i2cm, i2cm_port)
+ count++;
- port = zalloc(sizeof(*port) * count);
- if (!port) {
- log_simple_error(&e_info(OPAL_RC_I2C_INIT),
- "I2C: Insufficient memory\n");
- free(master);
- break;
- }
+ port = zalloc(sizeof(*port) * count);
+ if (!port) {
+ log_simple_error(&e_info(OPAL_RC_I2C_INIT),
+ "I2C: Insufficient memory\n");
+ free(master);
+ return;
+ }
- /* Add master to chip's list */
- list_add_tail(&chip->i2cms, &master->link);
- max_bus_speed = 0;
-
- dt_for_each_child(i2cm, i2cm_port) {
- uint32_t speed;
-
- port->port_num = dt_prop_get_u32(i2cm_port, "reg");
- port->master = master;
- speed = dt_prop_get_u32(i2cm_port, "bus-frequency");
- if (speed > max_bus_speed)
- max_bus_speed = speed;
- port->bit_rate_div =
- p8_i2c_get_bit_rate_divisor(lb_freq, speed);
- port->bus.dt_node = i2cm_port;
- port->bus.queue_req = p8_i2c_queue_request;
- port->bus.alloc_req = p8_i2c_alloc_request;
- port->bus.free_req = p8_i2c_free_request;
- i2c_add_bus(&port->bus);
- prlog(PR_INFO, " P%d: <%s> %d kHz\n",
- port->port_num,
- (char *)dt_prop_get(i2cm_port, "ibm,port-name"),
- speed/1000);
- port++;
- }
+ /* Add master to chip's list */
+ list_add_tail(&chip->i2cms, &master->link);
+ max_bus_speed = 0;
+
+ dt_for_each_child(i2cm, i2cm_port) {
+ uint32_t speed;
+
+ port->port_num = dt_prop_get_u32(i2cm_port, "reg");
+ port->master = master;
+ speed = dt_prop_get_u32(i2cm_port, "bus-frequency");
+ if (speed > max_bus_speed)
+ max_bus_speed = speed;
+ port->bit_rate_div =
+ p8_i2c_get_bit_rate_divisor(lb_freq, speed);
+ port->bus.dt_node = i2cm_port;
+ port->bus.queue_req = p8_i2c_queue_request;
+ port->bus.alloc_req = p8_i2c_alloc_request;
+ port->bus.free_req = p8_i2c_free_request;
+ i2c_add_bus(&port->bus);
+
+ /* Add OPAL properties to the bus node */
+ p8_i2c_add_bus_prop(port);
+ prlog(PR_INFO, " P%d: <%s> %d kHz\n",
+ port->port_num,
+ (char *)dt_prop_get(i2cm_port,
+ "ibm,port-name"), speed/1000);
+ port++;
+ }
- /* If we have no interrupt, calculate a poll interval, otherwise
- * just use a TIMER_POLL timer which will tick on OPAL pollers
- * only (which allows us to operate during boot before
- * interrupts are functional etc...
- */
- if (master->irq_ok)
- master->poll_interval = TIMER_POLL;
- else
- master->poll_interval =
- p8_i2c_get_poll_interval(max_bus_speed);
- master->byte_timeout = master->irq_ok ?
- msecs_to_tb(I2C_TIMEOUT_IRQ_MS) :
- msecs_to_tb(I2C_TIMEOUT_POLL_MS);
+ /* If we have no interrupt, calculate a poll interval,
+ * otherwise just use a TIMER_POLL timer which will tick
+ * on OPAL pollers only (which allows us to operate
+ * during boot before interrupts are functional etc...
+ */
+ if (master->irq_ok)
+ master->poll_interval = TIMER_POLL;
+ else
+ master->poll_interval = p8_i2c_get_poll_interval(max_bus_speed);
+ master->byte_timeout = master->irq_ok ?
+ msecs_to_tb(I2C_TIMEOUT_IRQ_MS) :
+ msecs_to_tb(I2C_TIMEOUT_POLL_MS);
+}
+
+void p8_i2c_init(void)
+{
+ struct dt_node *i2cm;
+ int i;
+
+ for (i = 0; i < MAX_I2C_TYPE; i++) {
+ dt_for_each_compatible(dt_root, i2cm, compat[i])
+ p8_i2c_init_one(i2cm, i);
}
}
More information about the Skiboot
mailing list