[Skiboot] [PATCH] p8-i2c: occ: Add support for OCC to use I2C engines

Joel Stanley joel at jms.id.au
Tue Jun 6 22:50:33 AEST 2017


On Tue, Jun 6, 2017 at 8:41 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

Out of interest, is this the same i2cm engines that are accessed by
the BMC over FSI?

If so, how does skiboot and/or the OCC mediate access with the BMC?

Cheers,

Joel

> + */
> +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;
> +
> +               /* 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
>
> _______________________________________________
> Skiboot mailing list
> Skiboot at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/skiboot


More information about the Skiboot mailing list