aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/chip.c1
-rw-r--r--core/i2c.c2
-rw-r--r--hw/lpc.c8
-rw-r--r--hw/p8-i2c.c114
-rw-r--r--hw/psi.c12
-rw-r--r--include/chip.h5
-rw-r--r--include/i2c.h4
-rw-r--r--include/lpc.h2
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);
};
}
diff --git a/core/i2c.c b/core/i2c.c
index 2eb77db..e0af9b2 100644
--- a/core/i2c.c
+++ b/core/i2c.c
@@ -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;
diff --git a/hw/lpc.c b/hw/lpc.c
index 975edd6..96d3e39 100644
--- a/hw/lpc.c
+++ b/hw/lpc.c
@@ -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);
}
diff --git a/hw/psi.c b/hw/psi.c
index de5c58d..1dba69a 100644
--- a/hw/psi.c
+++ b/hw/psi.c
@@ -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,