[Skiboot] [PATCH] p8-i2c: occ: Add support for OCC to use I2C engines
Stewart Smith
stewart at linux.vnet.ibm.com
Wed Jun 7 14:10:10 AEST 2017
Oliver O'Halloran <oohall at gmail.com> writes:
> 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
Crap. Patch incoming.
--
Stewart Smith
OPAL Architect, IBM.
More information about the Skiboot
mailing list