diff options
-rw-r--r-- | hw/p8-i2c.c | 259 |
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); } } |