aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeelesh Gupta <neelegup@linux.vnet.ibm.com>2015-02-27 17:11:06 +0800
committerStewart Smith <stewart@linux.vnet.ibm.com>2015-03-04 13:06:10 +1100
commit7693f2c7b1bbd5dd63ffe9570bb6bc57d94a8482 (patch)
treeb27ca7d91161c886829446d9cb602ce8406a95c0
parentb54631ed4c45df5da61fb77b1af672389ddd8c3d (diff)
downloadskiboot-7693f2c7b1bbd5dd63ffe9570bb6bc57d94a8482.zip
skiboot-7693f2c7b1bbd5dd63ffe9570bb6bc57d94a8482.tar.gz
skiboot-7693f2c7b1bbd5dd63ffe9570bb6bc57d94a8482.tar.bz2
p8/i2c: Add OPAL properties in the i2c bus nodes
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@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Jeremy Kerr <jk@ozlabs.org> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
-rw-r--r--hw/p8-i2c.c259
1 files 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);
}
}