From nobody Tue Nov 26 14:47:11 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=linux.ibm.com ARC-Seal: i=1; a=rsa-sha256; t=1707322259; cv=none; d=zohomail.com; s=zohoarc; b=N1AoyPQgxQOVMEbOOLl5erItQStPLKKOu5wW+3BVtk4EMzS9j9/r1wOIT6+4c1gSh/BG8CXp3dCGkWRco5TPbKUFMz6qpKXFClCIy4BnyiVGNgi3hFwoYVjqulVoh3CE3XGM7L/dQ8dCx/VJzhIynSt5S236eoP7O44laWvOnyo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1707322259; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=K7iBYniCofC4gpovL8UgQezjR8NEML3PDA1/A+RJhsQ=; b=eC+/ffBrXIVG1p77AKyY1ywnBknnMOch9uvpOGCC3OLBa6GNFj2mmoFRSIGgXOzW0rJNf7yZ1wvvSSCLax/aBnwdgnzZl31a1jJJWi8aCTaGYG938622ZFHAmBt7ZN4BLqu2WXCLFAoO6sAwvqxF48WVnK9JN2L7OSFK0r5PsS8= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1707322259132197.34736951634295; Wed, 7 Feb 2024 08:10:59 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rXkUH-0003r2-0p; Wed, 07 Feb 2024 11:09:25 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rXkU9-0003fm-GK; Wed, 07 Feb 2024 11:09:18 -0500 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rXkU3-0000ls-Fh; Wed, 07 Feb 2024 11:09:16 -0500 Received: from pps.filterd (m0353728.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 417G2K2K030680; Wed, 7 Feb 2024 16:09:02 GMT Received: from pps.reinject (localhost [127.0.0.1]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 3w4d1dr8ng-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 07 Feb 2024 16:09:00 +0000 Received: from m0353728.ppops.net (m0353728.ppops.net [127.0.0.1]) by pps.reinject (8.17.1.5/8.17.1.5) with ESMTP id 417G59jM012297; Wed, 7 Feb 2024 16:09:00 GMT Received: from ppma11.dal12v.mail.ibm.com (db.9e.1632.ip4.static.sl-reverse.com [50.22.158.219]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 3w4d1dr8mh-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 07 Feb 2024 16:09:00 +0000 Received: from pps.filterd (ppma11.dal12v.mail.ibm.com [127.0.0.1]) by ppma11.dal12v.mail.ibm.com (8.17.1.19/8.17.1.19) with ESMTP id 417FS9pw016195; Wed, 7 Feb 2024 16:08:59 GMT Received: from smtprelay03.fra02v.mail.ibm.com ([9.218.2.224]) by ppma11.dal12v.mail.ibm.com (PPS) with ESMTPS id 3w22h2680t-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 07 Feb 2024 16:08:59 +0000 Received: from smtpav01.fra02v.mail.ibm.com (smtpav01.fra02v.mail.ibm.com [10.20.54.100]) by smtprelay03.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 417G8t6I1835748 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Wed, 7 Feb 2024 16:08:55 GMT Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id B413F20043; Wed, 7 Feb 2024 16:08:55 +0000 (GMT) Received: from smtpav01.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id D177520040; Wed, 7 Feb 2024 16:08:53 +0000 (GMT) Received: from gfwr515.rchland.ibm.com (unknown [9.10.239.103]) by smtpav01.fra02v.mail.ibm.com (Postfix) with ESMTP; Wed, 7 Feb 2024 16:08:53 +0000 (GMT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding; s=pp1; bh=K7iBYniCofC4gpovL8UgQezjR8NEML3PDA1/A+RJhsQ=; b=bM0VZa/j7HQHSUeBTlOazNelprG66DM2Z/G5PVkOtP7p2SEOEJSGBVBe5hVn28UFVwdz z3zGjF6FJ2kWswF9/JYFNpv+GX94nAVBHkZhFzZtFzRhNxRl/GDLSE6jOONHc6c5e4lj wyjCIWYlAMSy3jwbF7CV+/XKhUx8R2IDMU0M/C2eqy2eMj0JPpWFipAtpFnS7fDOEmWU JbCfsr9iui7KACm79greo7D0ULIzKSjG4dxIKthJF5WftqAtJpFKKBBe6OCM5Maw8gaP zcgHbBIrzBOLfI5cVRL1fvDRJglaFWyiTHf2/1UsKeqi7kkhGQ0grjazerMKESXTo8kM dg== From: Chalapathi V To: qemu-devel@nongnu.org Cc: qemu-ppc@nongnu.org, fbarrat@linux.ibm.com, npiggin@gmail.com, clg@kaod.org, calebs@us.ibm.com, chalapathi.v@ibm.com, chalapathi.v@linux.ibm.com, saif.abrar@linux.vnet.ibm.com Subject: [PATCH v1 3/5] hw/ppc: SPI controller model - sequencer and shifter Date: Wed, 7 Feb 2024 10:08:31 -0600 Message-Id: <20240207160833.3437779-4-chalapathi.v@linux.ibm.com> X-Mailer: git-send-email 2.31.1 In-Reply-To: <20240207160833.3437779-1-chalapathi.v@linux.ibm.com> References: <20240207160833.3437779-1-chalapathi.v@linux.ibm.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-TM-AS-GCONF: 00 X-Proofpoint-GUID: hws7pUlBopXTKtKJAgxFGo2VuL4Jod7J X-Proofpoint-ORIG-GUID: VGZe_ToihdwV--j5aQeXG8e33mEOzNPn X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.1011,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2024-02-07_06,2024-02-07_01,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 phishscore=0 bulkscore=0 adultscore=0 lowpriorityscore=0 mlxscore=0 spamscore=0 suspectscore=0 malwarescore=0 mlxlogscore=999 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2311290000 definitions=main-2402070119 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=148.163.156.1; envelope-from=chalapathi.v@linux.ibm.com; helo=mx0a-001b2d01.pphosted.com X-Spam_score_int: -19 X-Spam_score: -2.0 X-Spam_bar: -- X-Spam_report: (-2.0 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @ibm.com) X-ZM-MESSAGEID: 1707322260396100001 Content-Type: text/plain; charset="utf-8" In this commit SPI shift engine and sequencer logic is implemented. Shift engine performs serialization and de-serialization according to the control by the sequencer and according to the setup defined in the configuration registers. Sequencer implements the main control logic and FSM to handle data transmit and data receive control of the shift engine. Signed-off-by: Chalapathi V --- include/hw/ppc/pnv_spi_controller.h | 58 ++ hw/ppc/pnv_spi_controller.c | 1274 ++++++++++++++++++++++++++- 2 files changed, 1331 insertions(+), 1 deletion(-) diff --git a/include/hw/ppc/pnv_spi_controller.h b/include/hw/ppc/pnv_spi_c= ontroller.h index 8afaabdd1b..8160c35f5c 100644 --- a/include/hw/ppc/pnv_spi_controller.h +++ b/include/hw/ppc/pnv_spi_controller.h @@ -8,6 +8,14 @@ * This model Supports a connection to a single SPI responder. * Introduced for P10 to provide access to SPI seeproms, TPM, flash device * and an ADC controller. + * + * All SPI function control is mapped into the SPI register space to enable + * full control by firmware. + * + * SPI Controller has sequencer and shift engine. The SPI shift engine + * performs serialization and de-serialization according to the control by + * the sequencer and according to the setup defined in the configuration + * registers and the SPI sequencer implements the main control logic. */ =20 #ifndef PPC_PNV_SPI_CONTROLLER_H @@ -20,6 +28,7 @@ #define SPI_CONTROLLER_REG_SIZE 8 =20 typedef struct SpiBus SpiBus; +typedef struct xfer_buffer xfer_buffer; =20 typedef struct PnvSpiController { DeviceState parent; @@ -28,6 +37,39 @@ typedef struct PnvSpiController { MemoryRegion xscom_spic_regs; /* SPI controller object number */ uint32_t spic_num; + uint8_t responder_select; + /* To verify if shift_n1 happens prior to shift_n2 */ + bool shift_n1_done; + /* + * Internal flags for the first and last indicators for the SPI + * interface methods + */ + uint8_t first; + uint8_t last; + /* Loop counter for branch operation opcode Ex/Fx */ + uint8_t loop_counter_1; + uint8_t loop_counter_2; + /* N1/N2_bits specifies the size of the N1/N2 segment of a frame in bi= ts.*/ + uint8_t N1_bits; + uint8_t N2_bits; + /* Number of bytes in a payload for the N1/N2 frame segment.*/ + uint8_t N1_bytes; + uint8_t N2_bytes; + /* Number of N1/N2 bytes marked for transmit */ + uint8_t N1_tx; + uint8_t N2_tx; + /* Number of N1/N2 bytes marked for receive */ + uint8_t N1_rx; + uint8_t N2_rx; + /* + * Setting this attribute to true will cause the engine to reverse the + * bit order of each byte it appends to a payload before sending the + * payload to a device. There may be cases where an end device expects + * a reversed order, like in the case of the Nuvoton TPM device. The + * order of bytes in the payload is not reversed, only the order of the + * 8 bits in each payload byte. + */ + bool reverse_bits; =20 /* SPI Controller registers */ uint64_t error_reg; @@ -40,4 +82,20 @@ typedef struct PnvSpiController { uint8_t sequencer_operation_reg[SPI_CONTROLLER_REG_SIZE]; uint64_t status_reg; } PnvSpiController; + +void log_all_N_counts(PnvSpiController *spi_controller); +void spi_response(PnvSpiController *spi_controller, int bits, + xfer_buffer *rsp_payload); +void operation_sequencer(PnvSpiController *spi_controller); +bool operation_shiftn1(PnvSpiController *spi_controller, uint8_t opcode, + xfer_buffer **payload, bool send_n1_alone); +bool operation_shiftn2(PnvSpiController *spi_controller, uint8_t opcode, + xfer_buffer **payload); +bool does_rdr_match(PnvSpiController *spi_controller); +uint8_t get_from_offset(PnvSpiController *spi_controller, uint8_t offset); +void shift_byte_in(PnvSpiController *spi_controller, uint8_t byte); +void calculate_N1(PnvSpiController *spi_controller, uint8_t opcode); +void calculate_N2(PnvSpiController *spi_controller, uint8_t opcode); +void do_reset(PnvSpiController *spi_controller); +uint8_t reverse_bits8(uint8_t x); #endif /* PPC_PNV_SPI_CONTROLLER_H */ diff --git a/hw/ppc/pnv_spi_controller.c b/hw/ppc/pnv_spi_controller.c index 0f2bc25e82..ef48af5d03 100644 --- a/hw/ppc/pnv_spi_controller.c +++ b/hw/ppc/pnv_spi_controller.c @@ -9,7 +9,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/qdev-properties.h" -#include "hw/ppc/pnv.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/pnv_spi_controller.h" #include "hw/ppc/pnv_spi_responder.h" @@ -155,6 +154,12 @@ static uint64_t pnv_spi_controller_read(void *opaque, = hwaddr addr, val)); sc->status_reg =3D SETFIELD(STATUS_REG_RDR_FULL, sc->status_reg, 0= ); SPI_DEBUG(qemu_log("RDR being read, RDR_full set to 0\n")); + if (GETFIELD(STATUS_REG_SHIFTER_FSM, sc->status_reg) =3D=3D FSM_WA= IT) { + /* call $operation_sequencer(); */ + SPI_DEBUG(qemu_log("RDR being read while shifter is waiting, " + "starting sequencer\n")); + operation_sequencer(sc); + } break; case SEQUENCER_OPERATION_REG: for (int i =3D 0; i < SPI_CONTROLLER_REG_SIZE; i++) { @@ -227,6 +232,9 @@ static void pnv_spi_controller_write(void *opaque, hwad= dr addr, sc->status_reg =3D SETFIELD(STATUS_REG_TDR_UNDERRUN, sc->status_re= g, 0); SPI_DEBUG(qemu_log("TDR being written, TDR_underrun set to 0\n")); SPI_DEBUG(qemu_log("TDR being written, starting sequencer\n")); + /* call $operation_sequencer(); */ + operation_sequencer(sc); + break; case RECEIVE_DATA_REG: sc->receive_data_reg =3D val; @@ -264,8 +272,1272 @@ static const MemoryRegionOps pnv_spi_controller_xsco= m_ops =3D { .endianness =3D DEVICE_BIG_ENDIAN, }; =20 +uint8_t reverse_bits8(uint8_t x) +{ + x =3D (x << 4) | (x >> 4); + x =3D ((x & 0x33) << 2) | ((x & 0xcc) >> 2); + x =3D ((x & 0x55) << 1) | ((x & 0xaa) >> 1); + return x; +} + +bool does_rdr_match(PnvSpiController *sc) +{ + /* + * The mask bits that are 0 are compared and the + * bits that are 1 are ignored. + */ + uint16_t rdr_match_mask =3D GETFIELD(MEMORY_MAPPING_REG_RDR_MATCH_MASK, + sc->memory_mapping_reg); + uint16_t rdr_match_val =3D GETFIELD(MEMORY_MAPPING_REG_RDR_MATCH_VAL, + sc->memory_mapping_reg); + if ((~rdr_match_mask & rdr_match_val) =3D=3D ((~rdr_match_mask) & + GETFIELD(PPC_BITMASK(48, 63), sc->receive_data_reg))) { + SPI_DEBUG(qemu_log("RDR match successful, match=3D0x%4.4x, " + "mask=3D0x%4.4x, RDR[48:63]=3D0x%4.4llx\n= ", + rdr_match_val, rdr_match_mask, + GETFIELD(PPC_BITMASK(48, 63), + sc->receive_data_reg))); + return true; + } else { + SPI_DEBUG(qemu_log("RDR match failed, match=3D0x%4.4x, mask=3D0x%4= .4x, " + "RDR[48:63]=3D0x%4.4llx\n", rdr_match_val, rdr_match_mask, + GETFIELD(PPC_BITMASK(48, 63), sc->receive_data_reg))); + return false; + } +} + +uint8_t get_from_offset(PnvSpiController *sc, uint8_t offset) +{ + uint8_t byte; + /* + * Offset is an index between 0 and SPI_CONTROLLER_REG_SIZE - 1 + * Check the offset before using it. + */ + if (offset < SPI_CONTROLLER_REG_SIZE) { + byte =3D GETFIELD(PPC_BITMASK(offset * 8 , (offset * 8) + 7), + sc->transmit_data_reg); + } else { + /* + * Log an error and return a 0xFF since we have to assign something + * to byte before returning. + */ + qemu_log_mask(LOG_GUEST_ERROR, "Invalid offset =3D %d used to get = byte " + "from TDR\n", offset); + byte =3D 0xff; + } + return byte; +} + +void shift_byte_in(PnvSpiController *sc, uint8_t byte) +{ + sc->receive_data_reg =3D (sc->receive_data_reg << 8) | byte; + SPI_DEBUG(qemu_log("0x%2.2x shifted in, RDR now =3D 0x%16.16lx\n", byt= e, + sc->receive_data_reg)); +} + +void spi_response(PnvSpiController *sc, int bits, xfer_buffer *rsp_payload) +{ + uint8_t *read_buf =3D NULL; + /* + * Processing here must handle: + * - Which bytes in the payload we should move to the RDR + * - Explicit mode counter configuration settings + * - RDR full and RDR overrun status + */ + + /* + * First check that the response payload is the exact same + * number of bytes as the request payload was + */ + if (rsp_payload->len !=3D (sc->N1_bytes + sc->N2_bytes)) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid response payload size in " + "bytes, expected %d, got %d\n", + (sc->N1_bytes + sc->N2_bytes), rsp_payload->len); + } else { + SPI_DEBUG(qemu_log("SPI response received, payload len =3D %d\n", + rsp_payload->len)); + log_all_N_counts(sc); + /* + * Adding an ECC count let's us know when we have found a payload = byte + * that was shifted in but cannot be loaded into RDR. Bits 29-30 + * equal to either 0b00 or 0b10 indicate that we are taking in data + * with ECC and either applying the ECC or discarding it. + */ + uint8_t ecc_count =3D 0; + uint8_t ecc_control =3D GETFIELD(CLOCK_CONFIG_REG_ECC_CONTROL, + sc->clock_config_reset_control); + uint8_t ecc_control_1_2 =3D GETFIELD(PPC_BITMASK8(1, 2), ecc_contr= ol); + if (ecc_control_1_2 =3D=3D 0 || ecc_control_1_2 =3D=3D 2) { + ecc_count =3D 1; + } + /* + * Use the N1_rx and N2_rx counts to control shifting data from the + * payload into the RDR. Keep an overall count of the number of b= ytes + * shifted into RDR so we can discard every 9th byte when ECC is + * enabled. + */ + uint8_t shift_in_count =3D 0; + /* Handle the N1 portion of the frame first */ + if (sc->N1_rx !=3D 0) { + uint8_t n1_count =3D 0; + while (n1_count < sc->N1_bytes) { + shift_in_count++; + xfer_buffer_read_ptr(rsp_payload, &read_buf, n1_count, 1); + if ((ecc_count !=3D 0) && + (shift_in_count =3D=3D (SPI_CONTROLLER_REG_SIZE + ecc_= count))) { + SPI_DEBUG(qemu_log("Discarding rx N1 ECC byte =3D 0x%2= .2x at " + "payload index =3D %d\n", read_buf[0], n1_count= )); + shift_in_count =3D 0; + } else { + uint8_t n1_byte =3D 0x00; + n1_byte =3D read_buf[0]; + SPI_DEBUG(qemu_log("Extracting rx n1_byte =3D 0x%2.2x = from " + "payload at index =3D %d\n", n1_byte, n1_count= )); + if (sc->reverse_bits) { + SPI_DEBUG(qemu_log("Reversing bit order of rx " + "n1_byte\n")); + n1_byte =3D reverse_bits8(n1_byte); + } + SPI_DEBUG(qemu_log("Shifting rx N1 byte =3D 0x%2.2x in= to " + "RDR\n", n1_byte)); + shift_byte_in(sc, n1_byte); + } + n1_count++; + } /* end of while */ + } + /* Handle the N2 portion of the frame */ + if (sc->N2_rx !=3D 0) { + uint8_t n2_count =3D 0; + while (n2_count < sc->N2_bytes) { + shift_in_count++; + xfer_buffer_read_ptr(rsp_payload, &read_buf, + (sc->N1_bytes + n2_count), 1); + if ((ecc_count !=3D 0) && + (shift_in_count =3D=3D (SPI_CONTROLLER_REG_SIZE + ecc_= count))) { + SPI_DEBUG(qemu_log("Discarding rx N1 ECC byte =3D 0x%2= .2x at " + "payload index =3D %d\n", read_buf[0], + (sc->N1_bytes + n2_count))); + shift_in_count =3D 0; + } else { + /* + * The code handles shifting data from the payload rec= eived + * from the responder into the responder's RDR. Since= this + * is an N2 frame segment it is safe to assume that th= ere + * was a preceding N1 segment which was combined with = an N2 + * segment to create a single frame. The response data= will + * then have N1_bytes of data in the payload represent= ing a + * responder response to the N1 section of the frame. = If N2 + * is set to receive the shifting for N2 data begins a= fter + * the N1 bytes regardless of whether or not N1 was ma= rked + * for transmit or receive. + */ + uint8_t n2_byte =3D 0x00; + n2_byte =3D read_buf[0]; + SPI_DEBUG(qemu_log("Extracting rx n2_byte =3D 0x%2.2x = from " + "payload at index =3D %d\n", n2_byte, + (sc->N1_bytes + n2_count))); + if (sc->reverse_bits) { + SPI_DEBUG(qemu_log("Reversing bit order of rx " + "n2_byte\n")); + n2_byte =3D reverse_bits8(n2_byte); + } + SPI_DEBUG(qemu_log("Shifting rx N2 byte =3D 0x%2.2x in= to " + "RDR\n", n2_byte)); + shift_byte_in(sc, n2_byte); + } + n2_count++; + } + } + if ((sc->N1_rx + sc->N2_rx) > 0) { + /* + * Data was received so handle RDR status. + * It is easier to handle RDR_full and RDR_overrun status here + * since the RDR register's shift_byte_in method is called + * multiple times in a row. Controlling RDR status is done here + * instead of in the RDR scoped methods for that reason. + */ + if (GETFIELD(STATUS_REG_RDR_FULL, sc->status_reg) =3D=3D 1) { + /* + * Data was shifted into the RDR before having been read + * causing previous data to have been overrun. + */ + sc->status_reg =3D SETFIELD(STATUS_REG_RDR_OVERRUN, + sc->status_reg, 1); + } else { + /* + * Set status to indicate that the received data register = is + * full. This flag is only cleared once the RDR is unloade= d. + */ + sc->status_reg =3D SETFIELD(STATUS_REG_RDR_FULL, + sc->status_reg, 1); + SPI_DEBUG(qemu_log("RDR_full set to 1\n")); + } + } + } /* end of else */ +} /* end of spi_response() */ + +void log_all_N_counts(PnvSpiController *sc) +{ + SPI_DEBUG(qemu_log("N1_bits =3D %d, N1_bytes =3D %d, N1_tx =3D %d, N1_= rx =3D %d, " + "N2_bits =3D %d, N2_bytes =3D %d, N2_tx =3D %d, N2_rx =3D %d\n", + sc->N1_bits, sc->N1_bytes, sc->N1_tx, sc->N1_rx, sc->N2_bits, + sc->N2_bytes, sc->N2_tx, sc->N2_rx)); +} + +void operation_sequencer(PnvSpiController *sc) +{ + /* + * Loop through each sequencer operation ID and perform the requested + * operations. + * Flag for indicating if we should send the N1 frame or wait to combi= ne + * it with a preceding N2 frame. + */ + bool send_n1_alone =3D true; + /* Flag to stop the sequencer */ + bool stop =3D false; + + /* + * xfer_buffer for containing the payload of the SPI frame. + * This is a static because there are cases where a sequence has to st= op + * and wait for the target application to unload the RDR. If this occ= urs + * during a sequence where N1 is not sent alone and instead combined w= ith + * N2 since the N1 tx length + the N2 tx length is less than the size = of + * the TDR. + */ + static xfer_buffer *payload; + if (payload =3D=3D NULL) { + payload =3D xfer_buffer_new(); + } + /* + * Clear the sequencer FSM error bit - general_SPI_status[3] + * before starting a sequence. + */ + sc->status_reg =3D SETFIELD(PPC_BIT(35), sc->status_reg, 0); + /* + * If the FSM is idle set the sequencer index to 0 + * (new/restarted sequence) + */ + if (GETFIELD(STATUS_REG_SEQUENCER_FSM, sc->status_reg) =3D=3D + SEQ_STATE_IDLE) { + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, 0); + } + /* + * There are only 8 possible operation IDs to iterate through though + * some operations may cause more than one frame to be sequenced. + */ + while (GETFIELD(STATUS_REG_SEQUENCER_INDEX, sc->status_reg) < 8) { + uint8_t opcode =3D 0; + uint8_t masked_opcode =3D 0; + + opcode =3D sc->sequencer_operation_reg[GETFIELD( + STATUS_REG_SEQUENCER_INDEX, sc->status_reg)]; + /* Set sequencer state to decode */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, sc->status_r= eg, + SEQ_STATE_DECODE); + /* + * Only the upper nibble of the operation ID is needed to know what + * kind of operation is requested. + */ + masked_opcode =3D opcode & 0xF0; + switch (masked_opcode) { + /* + * Increment the operation index in each case instead of just + * once at the end in case an operation like the branch + * operation needs to change the index. + */ + case SEQ_OP_STOP: + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_EXECUTE); + /* A stop operation in any position stops the sequencer */ + SPI_DEBUG(qemu_log("Sequencer STOP at index =3D 0x%llx, sequen= cer " + "idling\n", GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg))); + stop =3D true; + sc->status_reg =3D SETFIELD(STATUS_REG_SHIFTER_FSM, sc->status= _reg, + FSM_IDLE); + sc->loop_counter_1 =3D 0; + sc->loop_counter_2 =3D 0; + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_IDLE); + break; + + case SEQ_OP_SELECT_SLAVE: + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_EXECUTE); + SPI_DEBUG(qemu_log("Sequencer SELECT_SLAVE at index =3D 0x%llx= \n", + GETFIELD(STATUS_REG_SEQUENCER_INDEX, sc->status_reg))); + /* + * This device currently only supports a single responder + * connection at position 0. De-selecting a responder is fine + * and expected at the end of a sequence but selecting any + * responder other than 0 should cause an error. + */ + sc->responder_select =3D opcode & 0x0F; + if (sc->responder_select =3D=3D 0) { + if (spi_disconnect_controller(sc->spi_bus)) { + SPI_DEBUG(qemu_log("Slave (present) de-selected, " + "shifter done\n")); + } else { + SPI_DEBUG(qemu_log("Slave (not-present) de-selected " + "(no-op), shifter done\n")); + } + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, + (GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1)); + sc->status_reg =3D SETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg, FSM_DONE); + } else if (sc->responder_select !=3D 1) { + qemu_log_mask(LOG_GUEST_ERROR, "Slave selection other than= 1 " + "not supported, select =3D 0x%x\n", + sc->responder_select); + SPI_DEBUG(qemu_log("Sequencer stop requested due to invali= d " + "responder select at index =3D 0x%= llx, " + "shifter idling\n", GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg))); + sc->status_reg =3D SETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg, FSM_IDLE); + stop =3D true; + } else { + /* + * Only allow an FSM_START state when a responder is + * selected + */ + sc->status_reg =3D SETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg, FSM_START); + if (spi_connect_controller(sc->spi_bus, NULL)) { + SPI_DEBUG(qemu_log("Slave 0x%x (present) selected, " + "shifter starting\n", + sc->responder_select)); + } else { + SPI_DEBUG(qemu_log("Slave 0x%x (not-present) selected " + "(no-op), shifter starting\n", + sc->responder_select)); + } + sc->first =3D 1; + sc->last =3D 0; + /* + * A Shift_N2 operation is only valid after a Shift_N1. We + * will track the occurrence of a Shift_N1 to enforce this + * requirement in the most generic way possible by assuming + * that the rule applies once a valid responder select has + * occurred. + */ + sc->shift_n1_done =3D false; + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, + SEQ_STATE_INDEX_INCREMENT); + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, + (GETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1)); + } + break; + + case SEQ_OP_SHIFT_N1: + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_EXECUTE); + SPI_DEBUG(qemu_log("Sequencer SHIFT_N1 at index =3D 0x%llx\n", + GETFIELD(STATUS_REG_SEQUENCER_INDEX, sc->status_reg))); + /* + * Only allow a shift_n1 when the state is not IDLE or DONE. + * In either of those two cases the sequencer is not in a prop= er + * state to perform shift operations because the sequencer has: + * - processed a responder deselect (DONE) + * - processed a stop opcode (IDLE) + * - encountered an error (IDLE) + */ + if ((GETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg) =3D=3D FSM_IDLE) || + (GETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg) =3D=3D FSM_DONE)) { + qemu_log_mask(LOG_GUEST_ERROR, "Shift_N1 not allowed in " + "shifter state =3D 0x%llx", GETFIELD( + STATUS_REG_SHIFTER_FSM, sc->status_reg)); + /* + * Set sequencer FSM error bit 3 (general_SPI_status[3]) + * in status reg. + */ + sc->status_reg =3D SETFIELD(PPC_BIT(35), sc->status_reg, 1= ); + SPI_DEBUG(qemu_log("Sequencer stop requested due to invali= d " + "shifter state at index =3D 0x%llx\n", GETFIELD( + STATUS_REG_SEQUENCER_INDEX, sc->status_reg))); + stop =3D true; + } else { + /* + * Look for the special case where there is a shift_n1 set= for + * transmit and it is followed by a shift_n2 set for trans= mit + * AND the combined transmit length of the two operations = is + * less than or equal to the size of the TDR register. In = this + * case we want to use both this current shift_n1 opcode a= nd the + * following shift_n2 opcode to assemble the frame for + * transmission to the responder without requiring a refil= l of + * the TDR between the two operations. + */ + if ((sc->sequencer_operation_reg[GETFIELD( + STATUS_REG_SEQUENCER_INDEX, sc->status_reg) + 1] & 0x= F0) + =3D=3D SEQ_OP_SHIFT_N2) { + SPI_DEBUG(qemu_log("Not sending N1 alone\n")); + send_n1_alone =3D false; + } + /* + * If the next opcode is 0x10, which deselects the SPI dev= ice + * then this is the last shift + */ + if (sc->sequencer_operation_reg[GETFIELD( + STATUS_REG_SEQUENCER_INDEX, sc->status_reg) + 1] =3D= =3D + SEQ_OP_SELECT_SLAVE) { + sc->last =3D 1; + } + sc->status_reg =3D SETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg, FSM_SHIFT_N1); + stop =3D operation_shiftn1(sc, opcode, &payload, send_n1_a= lone); + if (stop) { + /* + * The operation code says to stop, this can occur if: + * (1) RDR is full and the N1 shift is set for receive + * (2) TDR was empty at the time of the N1 shift so we= need + * to wait for data. + * (3) Neither 1 nor 2 are occurring and we aren't sen= ding + * N1 alone and N2 counter reload is set (bit 0 of the= N2 + * counter reload field). In this case TDR_underrun w= ill + * will be set and the Payload has been loaded so it is + * ok to advance the sequencer. + */ + if (GETFIELD(STATUS_REG_TDR_UNDERRUN, sc->status_reg))= { + SPI_DEBUG(qemu_log("Sequencer stop requested due t= o N2 " + "counter reload active.\n")); + sc->shift_n1_done =3D true; + sc->status_reg =3D SETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg, + FSM_SHIFT_N2); + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_I= NDEX, + sc->status_reg, + (GETFIELD( + STATUS_REG_SEQUENCER_IND= EX, + sc->status_reg) + 1)); + SPI_DEBUG(qemu_log("Set new sequencer index to =3D= " + "0x%llx\n", GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg))); + } else { + /* + * This is case (1) or (2) so the sequencer needs = to + * wait and NOT go to the next sequence yet. + */ + sc->status_reg =3D SETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg, FSM_WAIT= ); + } + } else { + /* Ok to move on to the next index */ + sc->shift_n1_done =3D true; + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_INDEX_INCREMENT); + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, + (GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1)); + } + } + break; + + case SEQ_OP_SHIFT_N2: + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_EXECUTE); + SPI_DEBUG(qemu_log("Sequencer SHIFT_N2 at index =3D %lld\n", + GETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg))); + if (!sc->shift_n1_done) { + qemu_log_mask(LOG_GUEST_ERROR, "Shift_N2 is not allowed if= a " + "Shift_N1 is not done, shifter state =3D 0x%= llx", + GETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg)); + /* + * In case the sequencer actually stops if an N2 shift is + * requested before any N1 shift is done. Set sequencer FSM + * error bit 3 (general_SPI_status[3]) in status reg. + */ + sc->status_reg =3D SETFIELD(PPC_BIT(35), sc->status_reg, 1= ); + SPI_DEBUG(qemu_log("Sequencer stop requested due to shift_= n2 " + "w/no shift_n1 done at index =3D 0x%ll= x\n", + GETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg))); + stop =3D true; + } else { + /* + * If the next opcode is 0x10, which deselects the SPI dev= ice + * then this is the last shift + */ + if (sc->sequencer_operation_reg[GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1] =3D=3D SEQ_OP_SELECT_SLAVE) { + sc->last =3D 1; + } + /* Ok to do a Shift_N2 */ + sc->status_reg =3D SETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg, FSM_SHIFT_N2); + stop =3D operation_shiftn2(sc, opcode, &payload); + /* + * If the operation code says to stop set the shifter stat= e to + * wait and stop + */ + if (stop) { + sc->status_reg =3D SETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg, FSM_WAIT); + } else { + /* Ok to move on to the next index */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, + SEQ_STATE_INDEX_INCREMENT); + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, + (GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1)); + } + } + break; + + case SEQ_OP_BRANCH_IFNEQ_RDR: + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_EXECUTE); + SPI_DEBUG(qemu_log("Sequencer BRANCH_IFNEQ_RDR at " + "index =3D 0x%llx\n", GETFIELD( + STATUS_REG_SEQUENCER_INDEX, sc->status_reg)= )); + /* + * The memory mapping register RDR match value is compared aga= inst + * the 16 rightmost bytes of the RDR (potentially with masking= ). + * Since this comparison is performed against the contents of = the + * RDR then a receive must have previously occurred otherwise + * there is no data to compare and the operation cannot be + * completed and will stop the sequencer until RDR full is set= to + * 1. + */ + if (GETFIELD(STATUS_REG_RDR_FULL, sc->status_reg) =3D=3D 1) { + bool rdr_matched =3D false; + rdr_matched =3D does_rdr_match(sc); + if (rdr_matched) { + SPI_DEBUG(qemu_log("Proceed to next sequencer index " + "(increment on RDR match)\n")); + /* A match occurred, increment the sequencer index. */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, + SEQ_STATE_INDEX_INCREMENT); + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, + (GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1)); + } else { + SPI_DEBUG(qemu_log("Proceed to sequencer index=3D0x%x " + "(branch on RDR match fail)\n", (opcode & 0x7))); + /* + * Branch the sequencer to the index coded into the op + * code. + */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, (opcode & 0x= 7)); + } + /* + * Regardless of where the branch ended up we want the + * sequencer to continue shifting so we have to clear + * RDR_full. + */ + sc->status_reg =3D SETFIELD(STATUS_REG_RDR_FULL, + sc->status_reg, 0); + } else { + SPI_DEBUG(qemu_log("RDR not full for 0x6x opcode! Stopping= " + "sequencer.\n")); + stop =3D true; + sc->status_reg =3D SETFIELD(STATUS_REG_SHIFTER_FSM, + sc->status_reg, FSM_WAIT); + } + break; + + case SEQ_OP_TRANSFER_TDR: + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_EXECUTE); + qemu_log_mask(LOG_GUEST_ERROR, "Transfer TDR is not supported\= n"); + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, + SEQ_STATE_INDEX_INCREMENT); + /* status_reg.sequencer_index++ */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, + (GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1)); + break; + + case SEQ_OP_BRANCH_IFNEQ_INC_1: + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_EXECUTE); + SPI_DEBUG(qemu_log("Sequencer BRANCH_IFNEQ_INC_1 at index =3D " + "0x%llx, next index =3D %d, count_compare_1= =3D " + "0x%llx, loop_counter_1 =3D %d\n", GETFIELD( + STATUS_REG_SEQUENCER_INDEX, sc->status_reg), + (opcode & 0x07), + GETFIELD(COUNTER_CONFIG_REG_COUNT_COMPARE1, + sc->status_reg), sc->loop_counter_1)); + if (sc->loop_counter_1 !=3D + GETFIELD(COUNTER_CONFIG_REG_COUNT_COMPARE1, + sc->counter_config_reg)) { + /* + * If the next opcode is 0x10, which deselects the SPI dev= ice + * and we know that the next opcode is the last one in the + * loop then the next shift is the last shift + */ + uint8_t condition1 =3D sc->sequencer_operation_reg[ + GETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1]; + uint8_t condition2 =3D + GETFIELD(COUNTER_CONFIG_REG_COUNT_COMPARE1, + sc->counter_config_reg); + if ((condition1 =3D=3D SEQ_OP_SELECT_SLAVE) && + ((sc->loop_counter_1 + 1) =3D=3D condition2)) { + sc->last =3D 1; + } + /* + * Next index is the lower nibble of the branch operation = ID, + * mask off all but the first three bits so we don't try to + * access beyond the sequencer_operation_reg boundary. + */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, (opcode & 0x7)); + sc->loop_counter_1++; + SPI_DEBUG(qemu_log("Branching to index =3D %d, loop_counte= r_1 =3D " + "%d\n", (opcode & 0x7), sc->loop_counter_1)); + } else { + /* Continue to next index if loop counter is reached */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, + SEQ_STATE_INDEX_INCREMENT); + /* status_reg.sequencer_index++ */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, + (GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1)); + SPI_DEBUG(qemu_log("loop counter 1 achieved, next sequence= r " + "index =3D 0x%llx\n", GETFIELD(STATUS_REG_SEQUENCER_= INDEX, + sc->status_reg))); + } + break; + + case SEQ_OP_BRANCH_IFNEQ_INC_2: + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_EXECUTE); + SPI_DEBUG(qemu_log("Sequencer BRANCH_IFNEQ_INC_2 at index =3D " + "0x%llx, next index =3D %d, count_compare_2= =3D " + "0x%llx, loop_counter_2 =3D %d\n", GETFIELD( + STATUS_REG_SEQUENCER_INDEX, sc->status_reg), + (opcode & 0x07), GETFIELD( + COUNTER_CONFIG_REG_COUNT_COMPARE2, + sc->status_reg), sc->loop_counter_2)); + /* + * If the next opcode is 0x10, which deselects the SPI device + * and we know that the next opcode is the last one in the + * loop then the next shift is the last shift + */ + uint8_t condition1 =3D sc->sequencer_operation_reg[ + GETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1]; + + uint8_t condition2 =3D GETFIELD(COUNTER_CONFIG_REG_COUNT_COMPA= RE2, + sc->counter_config_reg); + + if ((condition1 =3D=3D SEQ_OP_SELECT_SLAVE) && + ((sc->loop_counter_2 + 1) =3D=3D condition2)) { + sc->last =3D 1; + } + if (sc->loop_counter_2 !=3D condition2) { + /* + * Next index is the lower nibble of the branch operation = ID, + * mask off all but the first three bits so we don't try to + * access beyond the sequencer_operation_reg boundary. + */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, + (opcode & 0x7)); + sc->loop_counter_2++; + SPI_DEBUG(qemu_log("Branching to index =3D %d, loop_counte= r_2 " + "=3D %d", (opcode & 0x7), + sc->loop_counter_2)); + } else { + /* Continue to next index if loop counter is reached */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, + SEQ_STATE_INDEX_INCREMENT); + /* status_reg.sequencer_index++ */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, + (GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1)); + SPI_DEBUG(qemu_log("loop counter 2 achieved, next sequence= r " + "index =3D 0x%llx\n", GETFIELD(STATUS_REG_SEQUENCER_= INDEX, + sc->status_reg))); + } + break; + + default: + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_EXECUTE); + qemu_log_mask(LOG_GUEST_ERROR, "Sequencer opcode 0x%x is not " + "supported\n", opcode); + /* Ignore unsupported operations. */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, + SEQ_STATE_INDEX_INCREMENT); + /* status_reg.sequencer_index++ */ + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, + (GETFIELD( + STATUS_REG_SEQUENCER_INDEX, + sc->status_reg) + 1)); + break; + } /* end of switch */ + /* + * If we used all 8 opcodes without seeing a 00 - STOP in the sequ= ence + * we need to go ahead and end things as if there was a STOP at the + * end. + */ + if (GETFIELD(STATUS_REG_SEQUENCER_INDEX, sc->status_reg) =3D=3D 8)= { + SPI_DEBUG(qemu_log("All 8 opcodes completed, sequencer " + "idling\n")); + sc->status_reg =3D SETFIELD(STATUS_REG_SHIFTER_FSM, sc->status= _reg, + FSM_IDLE); + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_INDEX, + sc->status_reg, 0); + sc->loop_counter_1 =3D 0; + sc->loop_counter_2 =3D 0; + sc->status_reg =3D SETFIELD(STATUS_REG_SEQUENCER_FSM, + sc->status_reg, SEQ_STATE_IDLE); + break; + } + /* Break the loop if a stop was requested */ + if (stop) { + break; + } + } /* end of while */ + return; +} /* end of operation_sequencer() */ + +/* + * Calculate the N1 counters based on passed in opcode and + * internal register values. + * The method assumes that the opcode is a Shift_N1 opcode + * and doesn't test it. + * The counters returned are: + * N1 bits: Number of bits in the payload data that are significant + * to the responder. + * N1_bytes: Total count of payload bytes for the N1 (portion of the) fram= e. + * N1_tx: Total number of bytes taken from TDR for N1 + * N1_rx: Total number of bytes taken from the payload for N1 + */ +void calculate_N1(PnvSpiController *sc, uint8_t opcode) +{ + /* + * Shift_N1 opcode form: 0x3M + * Implicit mode: + * If M !=3D 0 the shift count is M bytes and M is the number of tx by= tes. + * Forced Implicit mode: + * M is the shift count but tx and rx is determined by the count contr= ol + * register fields. Note that we only check for forced Implicit mode = when + * M !=3D 0 since the mode doesn't make sense when M =3D 0. + * Explicit mode: + * If M =3D=3D 0 then shift count is number of bits defined in the + * Counter Configuration Register's shift_count_N1 field. + */ + if (GETFIELD(PPC_BITMASK8(4, 7), opcode) =3D=3D 0) { + /* Explicit mode */ + sc->N1_bits =3D GETFIELD(COUNTER_CONFIG_REG_SHIFT_COUNT_N1, + sc->counter_config_reg); + sc->N1_bytes =3D ceil(sc->N1_bits / 8); + sc->N1_tx =3D 0; + sc->N1_rx =3D 0; + /* If tx count control for N1 is set, load the tx value */ + if (GETFIELD(PPC_BIT(50), sc->counter_config_reg) =3D=3D 1) { + sc->N1_tx =3D sc->N1_bytes; + } + /* If rx count control for N1 is set, load the rx value */ + if (GETFIELD(PPC_BIT(51), sc->counter_config_reg) =3D=3D 1) { + sc->N1_rx =3D sc->N1_bytes; + } + } else { + /* Implicit mode/Forced Implicit mode, use M field from opcode */ + sc->N1_bytes =3D GETFIELD(PPC_BITMASK8(4, 7), opcode); + sc->N1_bits =3D sc->N1_bytes * 8; + /* + * Assume that we are going to transmit the count + * (pure Implicit only) + */ + sc->N1_tx =3D sc->N1_bytes; + sc->N1_rx =3D 0; + /* Let Forced Implicit mode have an effect on the counts */ + if (GETFIELD(PPC_BIT(49), sc->counter_config_reg) =3D=3D 1) { + /* + * If Forced Implicit mode and count control doesn't + * indicate transmit then reset the tx count to 0 + */ + if (GETFIELD(PPC_BIT(50), sc->counter_config_reg) =3D=3D 0) { + sc->N1_tx =3D 0; + } + /* If rx count control for N1 is set, load the rx value */ + if (GETFIELD(PPC_BIT(51), sc->counter_config_reg) =3D=3D 1) { + sc->N1_rx =3D sc->N1_bytes; + } + } + } + /* + * Enforce an upper limit on the size of N1 that is equal to the known= size + * of the shift register, 64 bits or 72 bits if ECC is enabled. + * If the size exceeds 72 bits it is a user error so log an error, + * cap the size at a max of 64 bits or 72 bits and set the sequencer F= SM + * error bit. + */ + uint8_t ecc_control =3D GETFIELD(CLOCK_CONFIG_REG_ECC_CONTROL, + sc->clock_config_reset_control); + uint8_t ecc_control_1_2 =3D GETFIELD(PPC_BITMASK8(1, 2), ecc_control); + if (ecc_control_1_2 =3D=3D 0 || ecc_control_1_2 =3D=3D 2) { + if (sc->N1_bytes > (SPI_CONTROLLER_REG_SIZE + 1)) { + qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size when= " + "ECC enabled, bytes =3D 0x%x, bits =3D 0x%x\n", + sc->N1_bytes, sc->N1_bits); + sc->N1_bytes =3D SPI_CONTROLLER_REG_SIZE + 1; + sc->N1_bits =3D sc->N1_bytes * 8; + } + } else if (sc->N1_bytes > SPI_CONTROLLER_REG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size, " + "bytes =3D 0x%x, bits =3D 0x%x\n", + sc->N1_bytes, sc->N1_bits); + sc->N1_bytes =3D SPI_CONTROLLER_REG_SIZE; + sc->N1_bits =3D sc->N1_bytes * 8; + } +} /* end of calculate_N1 */ + +/* + * Shift_N1 operation handler method + */ +bool operation_shiftn1(PnvSpiController *sc, uint8_t opcode, + xfer_buffer **payload, bool send_n1_alone) +{ + bool stop =3D false; + /* + * If there isn't a current payload left over from a stopped sequence + * create a new one. + */ + if (*payload =3D=3D NULL) { + SPI_DEBUG(qemu_log("Creating new payload xfer_buffer\n")); + *payload =3D xfer_buffer_new(); + } + /* + * Use a combination of N1 counters to build the N1 portion of the + * transmit payload. + * We only care about transmit at this time since the request payload + * only represents data going out on the controller output line. + * Leave mode specific considerations in the calculate function since + * all we really care about are counters that tell use exactly how + * many bytes are in the payload and how many of those bytes to + * include from the TDR into the payload. + */ + calculate_N1(sc, opcode); + SPI_DEBUG(qemu_log("Shift N1 started..\n")); + log_all_N_counts(sc); + /* + * Zero out the N2 counters here in case there is no N2 operation foll= owing + * the N1 operation in the sequencer. This keeps leftover N2 informat= ion + * from interfering with spi_response logic. + */ + sc->N2_bits =3D 0; + sc->N2_bytes =3D 0; + sc->N2_tx =3D 0; + sc->N2_rx =3D 0; + /* + * N1_bytes is the overall size of the N1 portion of the frame regardl= ess of + * whether N1 is used for tx, rx or both. Loop over the size to build= a + * payload that is N1_bytes long. + * N1_tx is the count of bytes to take from the TDR and "shift" into t= he + * frame which means append those bytes to the payload for the N1 port= ion + * of the frame. + * If N1_tx is 0 or if the count exceeds the size of the TDR append 0x= FF to + * the frame until the overall N1 count is reached. + */ + uint8_t n1_count =3D 0; + while (n1_count < sc->N1_bytes) { + /* + * Assuming that if N1_tx is not equal to 0 then it is the same as + * N1_bytes. + */ + if ((sc->N1_tx !=3D 0) && (n1_count < SPI_CONTROLLER_REG_SIZE)) { + + if (GETFIELD(STATUS_REG_TDR_FULL, sc->status_reg) =3D=3D 1) { + /* + * Note that we are only appending to the payload IF the T= DR + * is full otherwise we don't touch the payload because we= are + * going to NOT send the payload and instead tell the sequ= encer + * that called us to stop and wait for a TDR write so we h= ave + * data to load into the payload. + */ + uint8_t n1_byte =3D 0x00; + n1_byte =3D get_from_offset(sc, n1_count); + SPI_DEBUG(qemu_log("Extracting tx n1_byte =3D 0x%2.2x at i= ndex " + "%d from TDR\n", n1_byte, n1_count)); + if (sc->reverse_bits) { + SPI_DEBUG(qemu_log("Reversing bit order of tx n1_byte\= n")); + n1_byte =3D reverse_bits8(n1_byte); + } + SPI_DEBUG(qemu_log("Appending tx n1_byte =3D 0x%2.2x to Pa= yload\n" + , n1_byte)); + *(xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) =3D + n1_byte; + } else { + /* + * We hit a shift_n1 opcode TX but the TDR is empty, tell = the + * sequencer to stop and break this loop. + */ + SPI_DEBUG(qemu_log("Shift N1 set for transmit but TDR is e= mpty," + " requesting sequencer stop\n")); + stop =3D true; + break; + } + } else { + /* + * Cases here: + * - we are receiving during the N1 frame segment and the RDR + * is full so we need to stop until the RDR is read + * - we are transmitting and we don't care about RDR status + * since we won't be loading RDR during the frame segment. + * - we are receiving and the RDR is empty so we allow the ope= ration + * to proceed. + */ + if ((sc->N1_rx !=3D 0) && (GETFIELD(STATUS_REG_RDR_FULL, + sc->status_reg) =3D=3D 1)) { + SPI_DEBUG(qemu_log("Shift N1 set for receive but RDR is fu= ll, " + "requesting sequencer stop\n")); + stop =3D true; + break; + } else { + SPI_DEBUG(qemu_log("Appending tx n1_byte =3D 0xFF to Paylo= ad\n")); + *(xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) =3D= 0xff; + } + } + n1_count++; + } /* end of while */ + /* + * If we are not stopping due to an empty TDR and we are doing an N1 TX + * and the TDR is full we need to clear the TDR_full status. + * Do this here instead of up in the loop above so we don't log the me= ssage + * in every loop iteration. + * Ignore the send_n1_alone flag, all that does is defer the TX until = the N2 + * operation, which was found immediately after the current opcode. T= he TDR + * was unloaded and will be shifted so we have to clear the TDR_full s= tatus. + */ + if (!stop && (sc->N1_tx !=3D 0) && + (GETFIELD(STATUS_REG_TDR_FULL, sc->status_reg) =3D=3D 1)) { + + sc->status_reg =3D SETFIELD(STATUS_REG_TDR_FULL, sc->status_reg, 0= ); + SPI_DEBUG(qemu_log("TDR_full set to 0\n")); + } + /* + * There are other reasons why the shifter would stop, such as a TDR e= mpty + * or RDR full condition with N1 set to receive. If we haven't stoppe= d due + * to either one of those conditions then check if the send_n1_alone f= lag is + * equal to False, indicating the next opcode is an N2 operation, AND = if + * the N2 counter reload switch (bit 0 of the N2 count control field) = is + * set. This condition requires a pacing write to "kick" off the N2 + * shift which includes the N1 shift as well when send_n1_alone is Fal= se. + */ + if (!stop && !send_n1_alone && + (GETFIELD(PPC_BIT(52), sc->counter_config_reg) =3D=3D 1)) { + SPI_DEBUG(qemu_log("N2 counter reload active, stop N1 shift, " + "TDR_underrun set to 1\n")); + stop =3D true; + sc->status_reg =3D SETFIELD(STATUS_REG_TDR_UNDERRUN, sc->status_re= g, 1); + } + /* + * If send_n1_alone is set AND we have a full TDR then this is the fir= st and + * last payload to send and we don't have an N2 frame segment to add t= o the + * payload. + */ + if (send_n1_alone && !stop) { + /* We have a TX and a full TDR or an RX and an empty RDR */ + SPI_DEBUG(qemu_log("Shifting N1 frame: first =3D %d, last =3D %d, " + "n1 bits =3D %d\n", sc->first, sc->last, + sc->N1_bits)); + xfer_buffer *rsp_payload =3D NULL; + rsp_payload =3D spi_request(sc->spi_bus, sc->first, sc->last, + sc->N1_bits, *payload); + if (rsp_payload !=3D NULL) { + spi_response(sc, sc->N1_bits, rsp_payload); + } + sc->first =3D 0; + sc->last =3D 0; + /* The N1 frame shift is complete so reset the N1 counters */ + sc->N2_bits =3D 0; + sc->N2_bytes =3D 0; + sc->N2_tx =3D 0; + sc->N2_rx =3D 0; + xfer_buffer_free(*payload); + *payload =3D NULL; + SPI_DEBUG(qemu_log("Payload buffer freed\n")); + } else { + SPI_DEBUG(qemu_log("Not shifting N1, send_n1_alone =3D %d, stop = =3D %d\n", + send_n1_alone, stop)); + } + return stop; +} /* end of operation_shiftn1() */ + +/* + * Calculate the N2 counters based on passed in opcode and + * internal register values. + * The method assumes that the opcode is a Shift_N2 opcode + * and doesn't test it. + * The counters returned are: + * N2 bits: Number of bits in the payload data that are significant + * to the responder. + * N2_bytes: Total count of payload bytes for the N2 frame. + * N2_tx: Total number of bytes taken from TDR for N2 + * N2_rx: Total number of bytes taken from the payload for N2 + */ +void calculate_N2(PnvSpiController *sc, uint8_t opcode) +{ + /* + * Shift_N2 opcode form: 0x4M + * Implicit mode: + * If M!=3D0 the shift count is M bytes and M is the number of rx byte= s. + * Forced Implicit mode: + * M is the shift count but tx and rx is determined by the count contr= ol + * register fields. Note that we only check for Forced Implicit mode = when + * M !=3D 0 since the mode doesn't make sense when M =3D 0. + * Explicit mode: + * If M=3D=3D0 then shift count is number of bits defined in the + * Counter Configuration Register's shift_count_N1 field. + */ + if (GETFIELD(PPC_BITMASK8(4, 7), opcode) =3D=3D 0) { + /* Explicit mode */ + sc->N2_bits =3D GETFIELD(COUNTER_CONFIG_REG_SHIFT_COUNT_N2, + sc->counter_config_reg); + sc->N2_bytes =3D ceil(sc->N2_bits / 8); + sc->N2_tx =3D 0; + sc->N2_rx =3D 0; + /* If tx count control for N2 is set, load the tx value */ + if (GETFIELD(PPC_BIT(54), sc->counter_config_reg) =3D=3D 1) { + sc->N2_tx =3D sc->N2_bytes; + } + /* If rx count control for N2 is set, load the rx value */ + if (GETFIELD(PPC_BIT(55), sc->counter_config_reg) =3D=3D 1) { + sc->N2_rx =3D sc->N2_bytes; + } + } else { + /* Implicit mode/Forced Implicit mode, use M field from opcode */ + sc->N2_bytes =3D GETFIELD(PPC_BITMASK8(4, 7), opcode); + sc->N2_bits =3D sc->N2_bytes * 8; + /* Assume that we are going to receive the count */ + sc->N2_rx =3D sc->N2_bytes; + sc->N2_tx =3D 0; + /* Let Forced Implicit mode have an effect on the counts */ + if (GETFIELD(PPC_BIT(53), sc->counter_config_reg) =3D=3D 1) { + /* + * If Forced Implicit mode and count control doesn't + * indicate a receive then reset the rx count to 0 + */ + if (GETFIELD(PPC_BIT(55), sc->counter_config_reg) =3D=3D 0) { + sc->N2_rx =3D 0; + } + /* If tx count control for N2 is set, load the tx value */ + if (GETFIELD(PPC_BIT(54), sc->counter_config_reg) =3D=3D 1) { + sc->N2_tx =3D sc->N2_bytes; + } + } + } + /* + * Enforce an upper limit on the size of N1 that is equal to the + * known size of the shift register, 64 bits or 72 bits if ECC + * is enabled. + * If the size exceeds 72 bits it is a user error so log an error, + * cap the size at a max of 64 bits or 72 bits and set the sequencer F= SM + * error bit. + */ + uint8_t ecc_control =3D GETFIELD(CLOCK_CONFIG_REG_ECC_CONTROL, + sc->clock_config_reset_control); + uint8_t ecc_control_1_2 =3D GETFIELD(PPC_BITMASK8(1, 2), ecc_control); + if (ecc_control_1_2 =3D=3D 0 || ecc_control_1_2 =3D=3D 2) { + if (sc->N2_bytes > (SPI_CONTROLLER_REG_SIZE + 1)) { + SPI_DEBUG(qemu_log("Unsupported N2 shift size when ECC enabled= , " + "bytes =3D 0x%x, bits =3D 0x%x\n", + sc->N2_bytes, sc->N2_bits)); + sc->N2_bytes =3D SPI_CONTROLLER_REG_SIZE + 1; + sc->N2_bits =3D sc->N2_bytes * 8; + } + } else if (sc->N2_bytes > SPI_CONTROLLER_REG_SIZE) { + SPI_DEBUG(qemu_log("Unsupported N2 shift size, bytes =3D 0x%x, " + "bits =3D 0x%x\n", sc->N2_bytes, sc->N2_bits)); + sc->N2_bytes =3D SPI_CONTROLLER_REG_SIZE; + sc->N2_bits =3D sc->N2_bytes * 8; + } +} /* end of calculate_N2 */ + +/* + * Shift_N2 operation handler method + */ + +bool operation_shiftn2(PnvSpiController *sc, uint8_t opcode, + xfer_buffer **payload) +{ + bool stop =3D false; + /* + * If there isn't a current payload left over from a stopped sequence + * create a new one. + */ + if (*payload =3D=3D NULL) { + SPI_DEBUG(qemu_log("Creating new payload xfer_buffer\n")); + *payload =3D xfer_buffer_new(); + } + /* + * Use a combination of N2 counters to build the N2 portion of the + * transmit payload. + */ + calculate_N2(sc, opcode); + SPI_DEBUG(qemu_log("Shift N2 started\n")); + log_all_N_counts(sc); + /* + * The only difference between this code and the code for shift N1 is + * that this code has to account for the possible presence of N1 trans= mit + * bytes already taken from the TDR. + * If there are bytes to be transmitted for the N2 portion of the frame + * and there are still bytes in TDR that have not been copied into the + * TX data of the payload, this code will handle transmitting those + * remaining bytes. + * If for some reason the transmit count(s) add up to more than the si= ze + * of the TDR we will just append 0xFF to the transmit payload data un= til + * the payload is N1 + N2 bytes long. + */ + uint8_t n2_count =3D 0; + while (n2_count < sc->N2_bytes) { + /* + * If the RDR is full and we need to RX just bail out, letting the + * code continue will end up building the payload twice in the same + * buffer since RDR full causes a sequence stop and restart. + */ + if ((sc->N2_rx !=3D 0) && + (GETFIELD(STATUS_REG_RDR_FULL, sc->status_reg) =3D=3D 1)) { + SPI_DEBUG(qemu_log("Shift N2 set for receive but RDR is full, " + "requesting sequencer stop\n")); + stop =3D true; + break; + } + if ((sc->N2_tx !=3D 0) && ((sc->N1_tx + n2_count) < + SPI_CONTROLLER_REG_SIZE)) { + /* Always append data for the N2 segment if it is set for TX */ + uint8_t n2_byte =3D 0x00; + n2_byte =3D get_from_offset(sc, (sc->N1_tx + n2_count)); + SPI_DEBUG(qemu_log("Extracting tx n2_byte =3D 0x%2.2x at index= %d " + "from TDR\n", n2_byte, (sc->N1_tx + n2_count))); + if (sc->reverse_bits) { + SPI_DEBUG(qemu_log("Reversing bit order of tx n2_byte\n")); + n2_byte =3D reverse_bits8(n2_byte); + } + SPI_DEBUG(qemu_log("Appending tx n2_byte =3D 0x%2.2x to Payloa= d\n", + n2_byte)); + *(xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) =3D n2_= byte; + } else { + /* + * Regardless of whether or not N2 is set for TX or RX, we need + * the number of bytes in the payload to match the overall len= gth + * of the operation. + */ + SPI_DEBUG(qemu_log("Appending tx n2_byte =3D 0xFF to Payload\n= ")); + *(xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) =3D 0xf= f; + } + n2_count++; + } /* end of while */ + if (!stop) { + /* We have a TX and a full TDR or an RX and an empty RDR */ + SPI_DEBUG(qemu_log("Shifting N2 frame: first =3D %d, last =3D %d, " + "n1+n2 bits =3D %d\n", sc->first, sc->last, + (sc->N1_bits + sc->N2_bits))); + xfer_buffer *rsp_payload =3D NULL; + rsp_payload =3D spi_request(sc->spi_bus, sc->first, sc->last, + (sc->N1_bits + sc->N2_bits), *payload); + if (rsp_payload !=3D NULL) { + spi_response(sc, (sc->N1_bits + sc->N2_bits), rsp_payload); + } + sc->first =3D 0; + sc->last =3D 0; + /* + * If we are doing an N2 TX and the TDR is full we need to clear t= he + * TDR_full status. Do this here instead of up in the loop above s= o we + * don't log the message in every loop iteration. + */ + if ((sc->N2_tx !=3D 0) && + (GETFIELD(STATUS_REG_TDR_FULL, sc->status_reg) =3D=3D 1)) { + sc->status_reg =3D SETFIELD(STATUS_REG_TDR_FULL, sc->status_re= g, 0); + SPI_DEBUG(qemu_log("TDR_full set to 0\n")); + } + /* + * The N2 frame shift is complete so reset the N2 counters. + * Reset the N1 counters also in case the frame was a combination = of + * N1 and N2 segments. + */ + sc->N2_bits =3D 0; + sc->N2_bytes =3D 0; + sc->N2_tx =3D 0; + sc->N2_rx =3D 0; + sc->N1_bits =3D 0; + sc->N1_bytes =3D 0; + sc->N1_tx =3D 0; + sc->N1_rx =3D 0; + xfer_buffer_free(*payload); + *payload =3D NULL; + SPI_DEBUG(qemu_log("Payload buffer freed\n")); + } else { + SPI_DEBUG(qemu_log("Not shifting N2, stop =3D %d\n", stop)); + } + return stop; +} /* end of operation_shiftn2()*/ + +/* + * The SPIC engine and its internal sequencer can be interrupted and reset= by + * a hardware signal, the sbe_spicst_hard_reset bits from Pervasive + * Miscellaneous Register of sbe_register_bo device. + * Reset immediately aborts any SPI transaction in progress and returns the + * sequencer and state machines to idle state. + * The configuration register values are not changed. The status register = is + * not reset. The engine registers are not reset. + * The SPIC engine reset does not have any affect on the attached devices. + * Reset handling of any attached devices is beyond the scope of the engin= e. + */ +void do_reset(PnvSpiController *sc) +{ + SPI_DEBUG(qemu_log("Resetting spic engine sequencer configuration and = spi " + "communication\n")); + /* Reset all N1 and N2 counters, and other constants */ + sc->first =3D 0; + sc->last =3D 0; + sc->N2_bits =3D 0; + sc->N2_bytes =3D 0; + sc->N2_tx =3D 0; + sc->N2_rx =3D 0; + sc->N1_bits =3D 0; + sc->N1_bytes =3D 0; + sc->N1_tx =3D 0; + sc->N1_rx =3D 0; + sc->loop_counter_1 =3D 0; + sc->loop_counter_2 =3D 0; + if (spi_disconnect_controller(sc->spi_bus)) { + SPI_DEBUG(qemu_log("Disconnected from responder\n")); + } +} + static Property pnv_spi_controller_properties[] =3D { DEFINE_PROP_UINT32("spic_num", PnvSpiController, spic_num, 0), + DEFINE_PROP_BOOL("reverse_bits", PnvSpiController, reverse_bits, false= ), DEFINE_PROP_END_OF_LIST(), }; =20 --=20 2.31.1