diff options
-rw-r--r-- | core/chip.c | 1 | ||||
-rw-r--r-- | core/i2c.c | 2 | ||||
-rw-r--r-- | hw/lpc.c | 8 | ||||
-rw-r--r-- | hw/p8-i2c.c | 114 | ||||
-rw-r--r-- | hw/psi.c | 12 | ||||
-rw-r--r-- | include/chip.h | 5 | ||||
-rw-r--r-- | include/i2c.h | 4 | ||||
-rw-r--r-- | include/lpc.h | 2 |
8 files changed, 68 insertions, 80 deletions
diff --git a/core/chip.c b/core/chip.c index e6eb81c..272f024 100644 --- a/core/chip.c +++ b/core/chip.c @@ -81,5 +81,6 @@ void init_chips(void) 0xffffffff); chip->pcid = dt_prop_get_u32_def(xn, "ibm,proc-chip-id", 0xffffffff); + list_head_init(&chip->i2cms); }; } @@ -20,7 +20,7 @@ #include <device.h> #include <opal-msg.h> -LIST_HEAD(i2c_bus_list); +static LIST_HEAD(i2c_bus_list); /* Used to assign OPAL IDs */ static uint32_t i2c_next_bus; @@ -20,7 +20,6 @@ #include <lock.h> #include <chip.h> #include <lpc.h> -#include <i2c.h> #include <timebase.h> #include <fsp-elog.h> @@ -455,12 +454,9 @@ bool lpc_present(void) return lpc_default_chip_id >= 0; } -void lpc_interrupt(void) +void lpc_interrupt(uint32_t chip_id __unused) { - /* i2c interrupts are routed through lpc */ - p8_i2c_interrupt(); - - /* Handle the lpc interrupt source */ + /* Handle the lpc interrupt source (errors etc...) TODO... */ } void lpc_init(void) diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c index e3f35b1..b1f7a28 100644 --- a/hw/p8-i2c.c +++ b/hw/p8-i2c.c @@ -166,8 +166,8 @@ struct p8_i2c_master { struct lock lock; /* Lock to guard the members */ - uint64_t poll_timer; /* Poll timer expiration */ - uint64_t poll_interval; /* Polling interval in usec */ + uint64_t poll_interval; /* Polling interval */ + uint64_t byte_timeout; /* Timeout per byte */ uint64_t xscom_base; /* xscom base of i2cm */ uint32_t bit_rate_div; /* Divisor to set bus speed*/ uint32_t fifo_size; /* Maximum size of FIFO */ @@ -185,6 +185,7 @@ struct p8_i2c_master { struct list_head req_list; /* Request queue head */ struct timer poller; struct timer timeout; + struct list_node link; }; struct p8_i2c_master_port { @@ -792,19 +793,14 @@ static int p8_i2c_start_request(struct p8_i2c_master *master, /* Enable the interrupts */ p8_i2c_enable_irqs(master); - /* If interrupts aren't working, start the poll timer. Also - * calculate the timeout differently + /* Run a poll timer for boot cases or non-working interrupts + * cases */ - now = mftb(); + now = schedule_timer(&master->poller, master->poll_interval); + + /* Calculate and start timeout */ tbytes = req->rw_len + req->offset_bytes + 2; - if (!master->irq_ok) { - schedule_timer_at(&master->poller, - now + master->poll_interval); - request->timeout = now + - tbytes * msecs_to_tb(I2C_TIMEOUT_POLL_MS); - } else - request->timeout = now + - tbytes * msecs_to_tb(I2C_TIMEOUT_IRQ_MS); + request->timeout = now + tbytes * master->byte_timeout; /* Start the timeout */ schedule_timer_at(&master->timeout, request->timeout); @@ -939,60 +935,42 @@ static void p8_i2c_timeout(struct timer *t __unused, void *data) static void p8_i2c_poll(struct timer *t __unused, void *data) { struct p8_i2c_master *master = data; - uint64_t now = mftb(); /* - * This is called when the interrupt isn't functional and - * will essentially just run the state machine from a timer - * - * There is some (mild) duplication with the OPAL poller but - * the latter is going to generally run very slowly so this - * isn't a problem. During boot they will both kick in but - * here too, this isn't a real problem. + * This is called when the interrupt isn't functional or + * generally from the opal pollers, so fast while booting + * and slowly when Linux is up. */ + + /* Lockless fast bailout */ + if (master->state == state_idle) + return; + lock(&master->lock); - master->poll_timer = now + master->poll_interval; p8_i2c_check_status(master); if (master->state != state_idle) - schedule_timer_at(&master->poller, - now + master->poll_interval); + schedule_timer(&master->poller, master->poll_interval); p8_i2c_check_work(master); unlock(&master->lock); } -static void p8_i2c_poll_each_master(bool interrupt) +void p8_i2c_interrupt(uint32_t chip_id) { + struct proc_chip *chip = get_chip(chip_id); struct p8_i2c_master *master = NULL; - struct p8_i2c_master_port *port; - struct i2c_bus *bus; - list_for_each(&i2c_bus_list, bus, link) { - uint64_t now = mftb(); + assert(chip); + list_for_each(&chip->i2cms, master, link) { - port = container_of(bus, struct p8_i2c_master_port, bus); - - /* Each master serves 1 or more ports, check for the first - * one found.. - */ - if (!master || master != port->common) - master = port->common; - else - continue; - - /* Lockless fast bailout for polling mode */ - if (!interrupt && master->state == state_idle && - list_empty(&master->req_list)) + /* Lockless fast bailout (shared interrupt) */ + if (master->state == state_idle) continue; lock(&master->lock); /* Run the state machine */ - if (interrupt || master->poll_timer == 0 || - tb_compare(now, master->poll_timer) == TB_AAFTERB || - tb_compare(now, master->poll_timer) == TB_AEQUALB) { - master->poll_timer = now + master->poll_interval; - p8_i2c_check_status(master); - } + p8_i2c_check_status(master); + /* Check for new work */ p8_i2c_check_work(master); @@ -1000,22 +978,13 @@ static void p8_i2c_poll_each_master(bool interrupt) } } -static void p8_i2c_opal_poll(void *data __unused) -{ - p8_i2c_poll_each_master(false); -} - -void p8_i2c_interrupt(void) -{ - p8_i2c_poll_each_master(true); -} - void p8_i2c_init(void) { struct p8_i2c_master_port *port; uint32_t bus_speed, lb_freq, count; struct dt_node *i2cm, *i2cm_port; struct p8_i2c_master *master; + struct proc_chip *chip; uint64_t ex_stat; static bool irq_printed; int rc; @@ -1035,7 +1004,10 @@ void p8_i2c_init(void) master->state = state_idle; master->chip_id = dt_get_chip_id(i2cm); 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); rc = xscom_read(master->chip_id, master->xscom_base + I2C_EXTD_STAT_REG, &ex_stat); @@ -1047,12 +1019,9 @@ void p8_i2c_init(void) master->fifo_size = GETFIELD(I2C_EXTD_STAT_FIFO_SIZE, ex_stat); list_head_init(&master->req_list); - master->poll_interval = p8_i2c_get_poll_interval(bus_speed); - master->poll_timer = 0; master->bit_rate_div = p8_i2c_get_bit_rate_divisor(lb_freq, bus_speed); /* Check if interrupt is usable */ - init_timer(&master->poller, p8_i2c_poll, master); master->irq_ok = p8_i2c_has_irqs(); if (!irq_printed) { irq_printed = true; @@ -1060,6 +1029,20 @@ void p8_i2c_init(void) master->irq_ok ? "" : "non-"); } + /* 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(bus_speed); + master->byte_timeout = master->irq_ok ? + msecs_to_tb(I2C_TIMEOUT_IRQ_MS) : + msecs_to_tb(I2C_TIMEOUT_POLL_MS); + /* Allocate ports driven by this master */ count = 0; dt_for_each_child(i2cm, i2cm_port) @@ -1072,6 +1055,9 @@ void p8_i2c_init(void) break; } + /* Add master to chip's list */ + list_add_tail(&chip->i2cms, &master->link); + dt_for_each_child(i2cm, i2cm_port) { port->port_num = dt_prop_get_u32(i2cm_port, "reg"); port->common = master; @@ -1083,10 +1069,4 @@ void p8_i2c_init(void) port++; } } - - /* Register the poller, one poller will cater all the masters, - * this is needed for when we operate without Linux running - * and thus no interrupts - */ - opal_add_poller(p8_i2c_opal_poll, NULL); } @@ -28,6 +28,7 @@ #include <xscom.h> #include <chip.h> #include <lpc.h> +#include <i2c.h> #include <timebase.h> #include <platform.h> @@ -280,8 +281,15 @@ static void handle_extra_interrupt(struct psi *psi) occ_interrupt(); if (val & PSIHB_IRQ_STAT_FSI) printf("PSI: FSI irq received\n"); - if (val & PSIHB_IRQ_STAT_LPC) - lpc_interrupt(); + if (val & PSIHB_IRQ_STAT_LPC) { + lpc_interrupt(psi->chip_id); + + /* + * i2c interrupts are ORed with the LPC ones on + * Murano DD2.1 and Venice DD2.0 + */ + p8_i2c_interrupt(psi->chip_id); + } if (val & PSIHB_IRQ_STAT_LOCAL_ERR) printf("PSI: ATTN irq received\n"); if (val & PSIHB_IRQ_STAT_HOST_ERR) { diff --git a/include/chip.h b/include/chip.h index 10623e6..fb2771e 100644 --- a/include/chip.h +++ b/include/chip.h @@ -20,6 +20,8 @@ #include <stdint.h> #include <lock.h> +#include <ccan/list/list.h> + /* * Note on chip IDs: * @@ -132,6 +134,9 @@ struct proc_chip { /* Used by hw/centaur.c */ struct centaur_chip *centaurs; + + /* Used by hw/p8-i2c.c */ + struct list_head i2cms; }; extern uint32_t pir_to_chip_id(uint32_t pir); diff --git a/include/i2c.h b/include/i2c.h index 2647bdc..1384697 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -47,8 +47,6 @@ struct i2c_request { void *user_data; /* Client data */ }; -extern struct list_head i2c_bus_list; - /* Generic i2c */ extern void i2c_add_bus(struct i2c_bus *bus); extern struct i2c_bus *i2c_find_bus_by_id(uint32_t opal_id); @@ -70,6 +68,6 @@ static inline int i2c_queue_req(struct i2c_request *req) /* P8 implementation details */ extern void p8_i2c_init(void); -extern void p8_i2c_interrupt(void); +extern void p8_i2c_interrupt(uint32_t chip_id); #endif /* __I2C_H */ diff --git a/include/lpc.h b/include/lpc.h index e4e24ea..632727f 100644 --- a/include/lpc.h +++ b/include/lpc.h @@ -28,7 +28,7 @@ extern void lpc_init(void); extern bool lpc_present(void); /* Handle the interrupt from LPC source */ -extern void lpc_interrupt(void); +extern void lpc_interrupt(uint32_t chip_id); /* Default bus accessors */ extern int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr, |