From patchwork Fri Dec 2 02:29:26 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stewart Smith X-Patchwork-Id: 701787 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3tVJ5L1BXMz9t0J for ; Fri, 2 Dec 2016 13:29:42 +1100 (AEDT) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3tVJ5K6yBKzDw2r for ; Fri, 2 Dec 2016 13:29:41 +1100 (AEDT) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Received: from mx0a-001b2d01.pphosted.com (001b2d01.pphosted.com [148.163.156.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3tVJ5C1TyvzDvj3 for ; Fri, 2 Dec 2016 13:29:34 +1100 (AEDT) Received: from pps.filterd (m0098404.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.17/8.16.0.17) with SMTP id uB22TWI5055096 for ; Thu, 1 Dec 2016 21:29:33 -0500 Received: from e17.ny.us.ibm.com (e17.ny.us.ibm.com [129.33.205.207]) by mx0a-001b2d01.pphosted.com with ESMTP id 272sxr3vsq-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Thu, 01 Dec 2016 21:29:32 -0500 Received: from localhost by e17.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 1 Dec 2016 21:29:31 -0500 Received: from d01dlp02.pok.ibm.com (9.56.250.167) by e17.ny.us.ibm.com (146.89.104.204) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 1 Dec 2016 21:29:30 -0500 Received: from b01cxnp23034.gho.pok.ibm.com (b01cxnp23034.gho.pok.ibm.com [9.57.198.29]) by d01dlp02.pok.ibm.com (Postfix) with ESMTP id 0390F6E803C for ; Thu, 1 Dec 2016 21:29:04 -0500 (EST) Received: from b01ledav002.gho.pok.ibm.com (b01ledav002.gho.pok.ibm.com [9.57.199.107]) by b01cxnp23034.gho.pok.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id uB22TUY74784460; Fri, 2 Dec 2016 02:29:30 GMT Received: from b01ledav002.gho.pok.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 0231A12403D; Thu, 1 Dec 2016 21:29:30 -0500 (EST) Received: from birb.localdomain (unknown [9.192.255.123]) by b01ledav002.gho.pok.ibm.com (Postfix) with ESMTP id 69FBC124037; Thu, 1 Dec 2016 21:29:29 -0500 (EST) Received: from ka1.ozlabs.ibm.com (localhost.localdomain [127.0.0.1]) by birb.localdomain (Postfix) with ESMTP id C04FF229DB44; Fri, 2 Dec 2016 13:29:26 +1100 (AEDT) From: Stewart Smith To: skiboot@lists.ozlabs.org Date: Fri, 2 Dec 2016 13:29:26 +1100 X-Mailer: git-send-email 2.1.4 X-TM-AS-GCONF: 00 X-Content-Scanned: Fidelis XPS MAILER x-cbid: 16120202-0040-0000-0000-00000204C96E X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00006177; HX=3.00000240; KW=3.00000007; PH=3.00000004; SC=3.00000193; SDB=6.00788103; UDB=6.00381301; IPR=6.00565779; BA=6.00004935; NDR=6.00000001; ZLA=6.00000005; ZF=6.00000009; ZB=6.00000000; ZP=6.00000000; ZH=6.00000000; ZU=6.00000002; MB=3.00013510; XFM=3.00000011; UTC=2016-12-02 02:29:31 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 16120202-0041-0000-0000-000005F6D99C Message-Id: <1480645766-15080-1-git-send-email-stewart@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2016-12-02_02:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=1 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1609300000 definitions=main-1612020038 Subject: [Skiboot] [PATCH] p8-i2c reset things manually in some error conditions X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" It appears that our reset code wasn't entirely correct, and what we're meant to do is reset each port and wait for command complete. In the event where that fails, we can then bitbang things to recover to a state where at least the i2c engine isn't in a weird state. Practically, this means that "i2cdetect -y 10; i2cdetect -y 10" (where 10 is the bus where a TPM is attached, typically p8e1p2) doesn't hard lock the machine (things are still bad and you won't reboot successfully, but it's *better*). one downside to this patch is that we spend a *long* time in OPAL (tens of ms) when doing the reset. This is something that we really need to fix, as it's not at all nice. The full fix for this though will involve changing a decent chunk of the p8-i2c code, as we don't want to write *any* registers while doing this extended reset (while existing code checks status a bit later). Signed-off-by: Stewart Smith --- hw/p8-i2c.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 157 insertions(+), 22 deletions(-) diff --git a/hw/p8-i2c.c b/hw/p8-i2c.c index faaf6ede3373..221663596606 100644 --- a/hw/p8-i2c.c +++ b/hw/p8-i2c.c @@ -171,7 +171,11 @@ DEFINE_LOG_ENTRY(OPAL_RC_I2C_RESET, OPAL_INPUT_OUTPUT_ERR_EVT, OPAL_I2C, #define I2C_RESIDUAL_BACK_END PPC_BITMASK(16, 31) /* Port busy register */ -#define I2C_PORT_BUYS_REG 0xe +#define I2C_PORT_BUSY_REG 0xe +#define I2C_SET_S_SCL_REG 0xd +#define I2C_RESET_S_SCL_REG 0xf +#define I2C_SET_S_SDA_REG 0x10 +#define I2C_RESET_S_SDA_REG 0x11 enum p8_i2c_master_type { I2C_POWER8, @@ -207,6 +211,7 @@ struct p8_i2c_master { struct timer sensor_cache; uint8_t recovery_pass; struct list_node link; + struct list_head ports; }; struct p8_i2c_master_port { @@ -214,6 +219,7 @@ struct p8_i2c_master_port { struct p8_i2c_master *master; uint32_t port_num; uint32_t bit_rate_div; /* Divisor to set bus speed*/ + struct list_node link; }; struct p8_i2c_request { @@ -463,6 +469,153 @@ static void p8_i2c_translate_error(struct i2c_request *req, uint64_t status) req->result = OPAL_I2C_TIMEOUT; } +static void p8_i2c_force_reset(struct p8_i2c_master *master) +{ + struct p8_i2c_master_port *p; + uint64_t mode; + int rc; + + /* Reset the i2c engine */ + rc = xscom_write(master->chip_id, master->xscom_base + + I2C_RESET_I2C_REG, 0); + if (rc) { + log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed " + "to reset the i2c engine\n"); + return; + } + time_wait_us_nopoll(10); + /* Reset port busy */ + rc = xscom_write(master->chip_id, master->xscom_base + + I2C_PORT_BUSY_REG, 0x8000000000000000ULL); + if (rc) { + log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: Failed " + "to reset port busy on i2c engine\n"); + return; + } + time_wait_us_nopoll(10); + list_for_each(&master->ports, p, link) { + mode = 0; + mode = SETFIELD(I2C_MODE_PORT_NUM, mode, p->port_num); + mode = SETFIELD(I2C_MODE_BIT_RATE_DIV, mode, p->bit_rate_div); + mode |= I2C_MODE_DIAGNOSTIC; + rc = xscom_write(master->chip_id, + master->xscom_base + I2C_MODE_REG, + mode); + if (rc) + prlog(PR_ERR, "I2C: Failed to write the MODE_REG\n"); + + time_wait_us_nopoll(10); + rc = xscom_write(master->chip_id, + master->xscom_base + I2C_RESET_S_SCL_REG, + 0); + if (rc) + prlog(PR_ERR, "I2C: Failed to reset S_SCL\n"); + + time_wait_us_nopoll(10); + rc = xscom_write(master->chip_id, + master->xscom_base + I2C_SET_S_SCL_REG, + 0); + if (rc) + prlog(PR_ERR, "I2C: Failed to set S_SCL\n"); + + /* Manually reset */ + time_wait_us_nopoll(10); + rc = xscom_write(master->chip_id, + master->xscom_base + I2C_RESET_S_SCL_REG, + 0); + if (rc) + prlog(PR_ERR, "I2C: sendStop: fail reset S_SCL\n"); + + time_wait_us_nopoll(10); + rc = xscom_write(master->chip_id, + master->xscom_base + I2C_RESET_S_SDA_REG, + 0); + if (rc) + prlog(PR_ERR, "I2C: sendStop: fail reset S_SDA\n"); + + time_wait_us_nopoll(10); + rc = xscom_write(master->chip_id, + master->xscom_base + I2C_SET_S_SCL_REG, + 0); + if (rc) + prlog(PR_ERR, "I2C: sendStop: fail set S_SCL\n"); + + time_wait_us_nopoll(10); + rc = xscom_write(master->chip_id, + master->xscom_base + I2C_SET_S_SDA_REG, + 0); + if (rc) + prlog(PR_ERR, "I2C: sendStop: fail set 2 S_SDA\n"); + + mode ^= I2C_MODE_DIAGNOSTIC; + time_wait_us_nopoll(10); + rc = xscom_write(master->chip_id, + master->xscom_base + I2C_MODE_REG, + mode); + if (rc) + prlog(PR_ERR, "I2C: Failed to write the MODE_REG\n"); + } +} + +static int p8_i2c_reset_engine(struct p8_i2c_master *master) +{ + struct p8_i2c_master_port *p; + int reset_loops; + int rc; + uint64_t status; + + list_for_each(&master->ports, p, link) { + /* + * Reset each port by issuing a STOP command to slave. + * + * Reprogram the mode register with 'enhanced bit' set + */ + rc = p8_i2c_prog_mode(p, true); + if (rc) { + log_simple_error(&e_info(OPAL_RC_I2C_RESET), + "I2C: Failed to program the MODE_REG\n"); + return -1; + } + + /* Send an immediate stop */ + master->state = state_error; + rc = xscom_write(master->chip_id, master->xscom_base + + I2C_CMD_REG, I2C_CMD_WITH_STOP); + if (rc) { + log_simple_error(&e_info(OPAL_RC_I2C_RESET), + "I2C: Failed to issue immediate STOP\n"); + return -1; + } + + /* Wait for COMMAND COMPLETE */ + reset_loops = 0; + do { + rc = xscom_read(master->chip_id, + master->xscom_base + I2C_STAT_REG, + &status); + if (rc) { + log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), + "I2C: Failed to read the STAT_REG\n"); + return -1; + } + if (! (status & I2C_STAT_CMD_COMP)) { + time_wait_ms(10); + if (reset_loops++ == 5) { + prlog(PR_WARNING, "I2C: Retrying reset, with force!\n"); + p8_i2c_force_reset(master); + continue; + } + if (reset_loops == 10) { + log_simple_error(&e_info(OPAL_RC_I2C_TRANSFER), + "I2C: Failed to recover i2c engine\n"); + break; + } + } + } while (! (status & I2C_STAT_CMD_COMP)); + } + return 0; +} + static void p8_i2c_status_error(struct p8_i2c_master_port *port, struct i2c_request *req, uint64_t status) @@ -495,30 +648,10 @@ static void p8_i2c_status_error(struct p8_i2c_master_port *port, */ p8_i2c_complete_request(master, req, req->result); } else { - /* - * Reset the bus by issuing a STOP command to slave. - * - * Reprogram the mode register with 'enhanced bit' set - */ - rc = p8_i2c_prog_mode(port, true); - if (rc) { - log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: " - "Failed to program the MODE_REG\n"); + if (p8_i2c_reset_engine(master)) goto exit; - } - /* Enable the interrupt */ p8_i2c_enable_irqs(master); - - /* Send an immediate stop */ - master->state = state_error; - rc = xscom_write(master->chip_id, master->xscom_base + - I2C_CMD_REG, I2C_CMD_WITH_STOP); - if (rc) { - log_simple_error(&e_info(OPAL_RC_I2C_RESET), "I2C: " - "Failed to issue immediate STOP\n"); - goto exit; - } } return; @@ -1363,6 +1496,7 @@ static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type) master->fifo_size = GETFIELD(I2C_EXTD_STAT_FIFO_SIZE, ex_stat); list_head_init(&master->req_list); + list_head_init(&master->ports); /* Check if interrupt is usable */ master->irq_ok = p8_i2c_has_irqs(master); @@ -1422,6 +1556,7 @@ static void p8_i2c_init_one(struct dt_node *i2cm, enum p8_i2c_master_type type) port->bus.set_req_timeout = p8_i2c_set_request_timeout; port->bus.run_req = p8_i2c_run_request; i2c_add_bus(&port->bus); + list_add_tail(&master->ports, &port->link); /* Add OPAL properties to the bus node */ p8_i2c_add_bus_prop(port);