[Skiboot] [PATCH] p8-i2c: occ: Add support for OCC to use I2C engines
Oliver O'Halloran
oohall at gmail.com
Tue Jun 6 21:56:33 AEST 2017
On Tue, Jun 6, 2017 at 9:11 PM, Stewart Smith
<stewart at linux.vnet.ibm.com> wrote:
> From: Shilpasri G Bhat <shilpa.bhat at linux.vnet.ibm.com>
>
> This patch adds support to share the I2C engines with host and OCC.
> OCC uses I2C engines to read DIMM temperatures and to communicate with
> GPU. OCC Flag register is used for locking between host and OCC. Host
> requests for the bus by setting a bit in OCC Flag register. OCC sends
> an interrupt to indicate the change in ownership.
>
> Originally-from: Oliver O'Halloran <oohall at gmail.com>
> Signed-off-by: Shilpasri G Bhat <shilpa.bhat at linux.vnet.ibm.com>
> [stewart at linux.vnet.ibm.com: Pretty heavily rework logic, including
> fixing bus owner change and separating out occ lock from sensor cache]
> Signed-off-by: Stewart Smith <stewart at linux.vnet.ibm.com>
> ---
> hw/occ.c | 4 ++
> hw/p8-i2c.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> include/i2c.h | 3 ++
> 3 files changed, 154 insertions(+)
>
> diff --git a/hw/occ.c b/hw/occ.c
> index 4be4df43efa1..34ef9a74dddc 100644
> --- a/hw/occ.c
> +++ b/hw/occ.c
> @@ -27,6 +27,7 @@
> #include <opal-api.h>
> #include <opal-msg.h>
> #include <timer.h>
> +#include <i2c.h>
>
> /* OCC Communication Area for PStates */
>
> @@ -1397,6 +1398,9 @@ void occ_p9_interrupt(uint32_t chip_id)
> if (ireg & OCB_OCI_OCIMISC_IRQ_SHMEM)
> occ_throttle_poll(NULL);
>
> + if (ireg & OCB_OCI_OCIMISC_IRQ_I2C)
> + p9_i2c_bus_owner_change(chip_id);
> +
> /* We may have masked-out OCB_OCI_OCIMISC_IRQ in the previous
> * OCCMISC_AND write. Check if there are any new source bits set,
> * and trigger another interrupt if so.
> diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c
> index fefc1d4b250b..d55d08ee8c34 100644
> --- a/hw/p8-i2c.c
> +++ b/hw/p8-i2c.c
> @@ -200,6 +200,7 @@ struct p8_i2c_master {
> uint32_t bytes_sent;
> bool irq_ok; /* Interrupt working ? */
> bool occ_cache_dis; /* I have disabled the cache */
> + bool occ_lock_acquired; /* Acquired lock from OCC */
> enum request_state {
> state_idle,
> state_occache_dis,
> @@ -232,6 +233,8 @@ struct p8_i2c_request {
> uint64_t timeout;
> };
>
> +static int occ_i2c_unlock(struct p8_i2c_master *master);
> +
> static void p8_i2c_print_debug_info(struct p8_i2c_master_port *port,
> struct i2c_request *req, uint64_t end_time)
> {
> @@ -427,6 +430,10 @@ static void p8_i2c_complete_request(struct p8_i2c_master *master,
> schedule_timer(&master->sensor_cache,
> msecs_to_tb(SENSOR_CACHE_EN_DELAY));
>
> + /* If we're done with i2c master, allow OCC to use it */
> + if (master->occ_lock_acquired && list_empty(&master->req_list))
> + occ_i2c_unlock(master);
> +
> unlock(&master->lock);
> if (req->completion)
> req->completion(ret, req);
> @@ -1016,6 +1023,94 @@ static int p8_i2c_check_initial_status(struct p8_i2c_master_port *port)
> return 0;
> }
>
> +/*
> + * On POWER9, the I2C may also wish to use some of the i2cm engines,
> + * to do things like read sensor data. There's a couple of shared
> + * registers with the OCC to negotiate locking of the i2cm engines.
> + * See occ/src/occ_405/lock/lock.c
> + */
> +static bool occ_uses_master(struct p8_i2c_master *master)
> +{
> + /* OCC uses I2CM Engines 1,2 and 3, only on POWER9 */
> + if (master->type == I2C_POWER8 && proc_gen == proc_gen_p9)
> + return master->engine_id >= 1;
> +
> + return false;
> +}
> +
> +#define OCCFLG_BASE 0x00000000006C08A
> +#define OCCFLG_CLEAR 0x00000000006C08B
> +#define OCCFLG_SET 0x00000000006C08C
> +
> +static int occ_i2c_lock(struct p8_i2c_master *master)
> +{
> + u64 occflags, busflag;
> + int rc;
> +
> + if (!occ_uses_master(master))
> + return 0;
> +
> + if (master->occ_lock_acquired)
> + return 0;
> +
> + rc = xscom_read(master->chip_id, OCCFLG_BASE, &occflags);
> + if (rc) {
> + prerror("I2C: Failed to read OCC FLAG register\n");
> + return rc;
> + }
> +
> + assert(master->engine_id > 0);
> +
> + busflag = PPC_BIT(16 + (master->engine_id - 1) * 2);
> +
> + DBG("occflags = %llx (locks = %.6llx)\n", (u64)occflags,
> + GETFIELD(PPC_BITMASK(16, 22), occflags));
> +
> + rc = xscom_write(master->chip_id, OCCFLG_SET, busflag);
> + if (rc) {
> + prerror("I2C: Failed to write OCC FLAG register\n");
> + return rc;
> + }
> +
> + /* If the OCC also has this bus locked then wait for IRQ */
> + if (occflags & (busflag << 1))
> + return 1;
> +
> + master->occ_lock_acquired = true;
> +
> + return 0;
> +}
> +
> +static int occ_i2c_unlock(struct p8_i2c_master *master)
> +{
> + u64 busflag, occflags;
> + int rc;
> +
> + if (!occ_uses_master(master))
> + return 0;
> +
> + rc = xscom_read(master->chip_id, OCCFLG_BASE, &occflags);
> + if (rc) {
> + prerror("I2C: Failed to read OCC Flag register\n");
> + return rc;
> + }
> +
> + busflag = PPC_BIT(16 + (master->engine_id - 1) * 2);
> +
> + if (!(occflags & busflag)) {
> + prerror("I2C: busflag for %d already cleared (flags = %.16llx)",
> + master->engine_id, occflags);
> + }
> +
> + rc = xscom_write(master->chip_id, OCCFLG_CLEAR, busflag);
> + if (rc)
> + prerror("I2C: Failed to write OCC Flag register\n");
> +
> + master->occ_lock_acquired = false;
> +
> + return rc;
> +}
> +
> static int p8_i2c_start_request(struct p8_i2c_master *master,
> struct i2c_request *req)
> {
> @@ -1035,6 +1130,7 @@ static int p8_i2c_start_request(struct p8_i2c_master *master,
> if (master->type == I2C_CENTAUR && !master->occ_cache_dis) {
> DBG("Disabling OCC cache...\n");
> rc = centaur_disable_sensor_cache(master->chip_id);
> +
> if (rc < 0) {
> log_simple_error(&e_info(OPAL_RC_I2C_START_REQ),
> "I2C: Failed "
> @@ -1052,6 +1148,23 @@ static int p8_i2c_start_request(struct p8_i2c_master *master,
> }
> }
>
> + /*
> + * on P9 we need to set the "I2C master using bit" so we don't
> + * conflict with the OCC's use of the i2c master.
> + */
> + rc = occ_i2c_lock(master);
> + if (rc < 0) {
> + log_simple_error(&e_info(OPAL_RC_I2C_START_REQ),
> + "I2C: Failed to get I2CM lock from OCC\n");
> + return rc;
> + }
> + if (rc > 0) {
> + /* Wait for OCC IRQ */
> + master->state = state_occache_dis;
> + schedule_timer(&master->recovery, rc);
> + return 0;
> + }
> +
> /* Convert the offset if needed */
> if (req->offset_bytes) {
> int i;
> @@ -1163,6 +1276,36 @@ static void p8_i2c_check_work(struct p8_i2c_master *master)
> }
> }
>
> +/* OCC IRQ Handler for I2C Ownership Change*/
> +void p9_i2c_bus_owner_change(u32 chip_id)
> +{
> + struct proc_chip *chip = get_chip(chip_id);
> + struct p8_i2c_master *master = NULL;
> + int rc;
> +
> + assert(chip);
> + list_for_each(&chip->i2cms, master, link) {
> + if (master->state == state_idle ||
> + master->state != state_occache_dis)
> + continue;
> +
> + lock(&master->lock);
> +
> + /* Can we now lock this master? */
> + rc = occ_i2c_lock(master);
> + if (rc)
> + continue;
mate
> +
> + /* Run the state machine */
> + p8_i2c_check_status(master);
> +
> + /* Check for new work */
> + p8_i2c_check_work(master);
> +
> + unlock(&master->lock);
> + }
> +}
> +
> static int p8_i2c_queue_request(struct i2c_request *req)
> {
> struct i2c_bus *bus = req->bus;
> @@ -1322,6 +1465,9 @@ static void p8_i2c_recover(struct timer *t __unused, void *data,
> master->occ_cache_dis = false;
> }
>
> + if (master->occ_lock_acquired && list_empty(&master->req_list))
> + occ_i2c_unlock(master);
> +
> /* Re-check for new work */
> p8_i2c_check_work(master);
> unlock(&master->lock);
> @@ -1506,6 +1652,7 @@ static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type)
> "Failed to read EXTD_STAT_REG\n");
> if (master->type == I2C_CENTAUR)
> centaur_enable_sensor_cache(master->chip_id);
> +
> free(master);
> return;
> }
> diff --git a/include/i2c.h b/include/i2c.h
> index 2c4c6a1d4bd2..e5e8584b4073 100644
> --- a/include/i2c.h
> +++ b/include/i2c.h
> @@ -110,4 +110,7 @@ static inline int i2c_check_quirk(struct i2c_request *req, int *rc)
> extern void p8_i2c_init(void);
> extern void p8_i2c_interrupt(uint32_t chip_id);
>
> +/* P9 I2C Ownership Change OCC interrupt handler */
> +extern void p9_i2c_bus_owner_change(u32 chip_id);
> +
> #endif /* __I2C_H */
> --
> 2.9.4
>
More information about the Skiboot
mailing list