// SPDX-License-Identifier: GPL-2.0 /* Copyright 2015 Broadcom Corporation * * The Broadcom GPS SPI driver * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bbd.h" #include "bcm_gps_spi.h" /** * bcm_dreg_write - write to direct addressable registers in command controller * @priv: @bcm_spi_priv structure data * @id: name of indirect register, just for debug print * @offset: the command offset of the direct addressable register * @buf: pointer of data to be written to the register * @size: number of bytes to write * Return: upon successful completion, this function returns number of * written bytes otherwise exit with specific error code <0 */ int bcm_dreg_write(struct bcm_spi_priv *priv, char *id, u8 offset, u8 *buf, u8 size) { int i; struct bcm_ssi_tx_frame *tx = priv->tx_buf; struct bcm_ssi_rx_frame *rx = priv->rx_buf; if (size > MAX_SPI_DREG_FRAME_LEN) return -1; /* * writing in 1 transaction * 0x80 : < CHWXX > = half-duplex, SPI write command packet */ tx->cmd = SSI_MODE_DEBUG | SSI_MODE_HALF_DUPLEX | SSI_WRITE_TRANS; /* * bit-8 is a write transaction and the lower 7-bits is * the command offset of the */ tx->data[0] = offset & 0x7F; /* the number of valid bytes available on MOSI following this byte */ tx->data[1] = size; memcpy(&tx->data[2], buf, size); rx->status = 0; if (bcm_spi_sync(priv, tx, rx, size + 3, size + 3)) return -1; for (i = 0; i < size; i++) { dev_dbg(&priv->spi->dev, "regW REG %s @ [%02X]: %08X", id, offset, buf[i]); } return size; } /** * bcm_dreg_read - read direct addressable registers in command controller * @priv: @bcm_spi_priv structure data * @offset: the command offset of the direct addressable register. * @buf pointer data to be read from the register * @size number of bytes to read * Return: upon successful completion, this function returns number of * read bytes otherwise exit with specific error code <0 */ int bcm_dreg_read(struct bcm_spi_priv *priv, char *id, u8 offset, u8 *buf, u8 size) { /* Reading in 2 transactions */ int i = 0; /* int status; */ struct bcm_ssi_tx_frame *tx = priv->tx_buf; struct bcm_ssi_rx_frame *rx = priv->rx_buf; if (size > MAX_SPI_DREG_FRAME_LEN) return -1; /* * First transaction will setup SPI Command Logic * to Read Data from Register. * 0x80 : < CHWXX > = half-duplex, SPI write command packet */ tx->cmd = SSI_MODE_DEBUG | SSI_MODE_HALF_DUPLEX | SSI_WRITE_TRANS; /* * <8?h1000_0100> = Bit-8 is a read transaction and the lower 7-bits * is the command offset of the register */ tx->data[0] = 0x80 | (offset & 0x7F); /* * The number of bytes host will read from this offset * in next packet */ tx->data[1] = size; rx->status = 0; if (bcm_spi_sync(priv, tx, rx, 3, 3)) return -1; dev_dbg(&priv->spi->dev, "regR: REG(W) %s @ [%02X]: %08X ", id, offset, size); /* * Second Transaction will read data * 0xa0 : < CHRXX > = half-duplex, SPI read command packet */ tx->cmd = SSI_MODE_DEBUG | SSI_MODE_HALF_DUPLEX | SSI_READ_TRANS; /* * READ : the number of valid read bytes available plus one * to account for the read offset address that accompanies * the read data == */ tx->data[0] = 0; /* READ : the command offset of the register == . */ tx->data[1] = 0; rx->status = 0; memset(&tx->data[2], 0, size); if (bcm_spi_sync(priv, tx, rx, size + 3, size + 3)) return -1; memcpy(buf, &rx->data[2], size); for (i = 0 ; i < size ; i++) dev_dbg(&priv->spi->dev, "regR: REG(R) %s @ [%02X]: %08X", id, offset, buf[i]); return size; } /** * bcm_ireg_write - write to indirect addressable register * @priv: @bcm_spi_priv structure data * @id: name of indirect register, just for debug print * @regaddr: the stream address of the indirect addressable register. * @regval: pointer data to be read from the register * Return: upon successful completion, this function returns number * of written bytes otherwise exit with specific error code <0 */ int bcm_ireg_write(struct bcm_spi_priv *priv, char *id, u32 regaddr, u32 regval) { union long_union_t swap_addr, swap_reg; struct bcm_ssi_tx_frame *tx = priv->tx_buf; struct bcm_ssi_rx_frame *rx = priv->rx_buf; /* * Writing in 2 transactions * First transaction will set up the SPI Debug Logic to Write Data * into Configuration Register or Memory Location. * * 0x80 : * 0xD1 : * 0x00 : < SPI Offset of DMA Start Addr > * 0x00, 0x00, 0x00, 0x00 : < Start Address>, * Should be set from 'regaddr' * 0x04, 0x00 : * 0x01 : * * uint8_t transaction_1st[13] = { * 0x80, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x03 }; * uint8_t transaction_1st[19] = { * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, * 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; */ swap_addr.ul = regaddr; swap_reg.ul = regval; /* * First transaction will setup SPI Command Logic * to Read Data from Register. * HSI_MOSI_COMMAND_PCKT | HSI_MOSI_HALF_DUPLEX | HSI_MOSI_WRITE_TRANS; * 0x80 : < CHWXX > = half-duplex, SPI write command packet */ tx->cmd = 0x80; /* * <8’h0100_0000> = Bit-8 is a write transaction. * The lower 7-bits is the command offset of */ tx->data[0] = 0x20; /* * The register (CONFIG_REG_DATA) the host is starting to write from. * the number of valid bytes available on MOSI following this byte */ tx->data[1] = 9; tx->data[2] = swap_reg.uc[0]; tx->data[3] = swap_reg.uc[1]; tx->data[4] = swap_reg.uc[2]; tx->data[5] = swap_reg.uc[3]; tx->data[6] = swap_addr.uc[0]; tx->data[7] = swap_addr.uc[1]; tx->data[8] = swap_addr.uc[2]; tx->data[9] = swap_addr.uc[3]; tx->data[10] = 0x03; tx->data[11] = 0x00; tx->data[12] = 0x00; tx->data[13] = 0x00; rx->status = 0; if (bcm_spi_sync(priv, tx, rx, 15, 15)) return -1; if (id) dev_dbg(&priv->spi->dev, "reg32w: %s @ : [%08X] %08X ", id, (unsigned int)swap_addr.ul, (unsigned int)swap_reg.ul); return 1; } /** * bcm_ireg_read - read indirect addressable register * @priv: @bcm_spi_priv structure data * @id: the name of indirect register. Just for debug print. * @regaddr: the stream address of the indirect addressable register. * @regval: the pointer data to be read from the register * Return: upon successful completion, this function returns number of * read bytes otherwise exit with specific error code <0 */ int bcm_ireg_read(struct bcm_spi_priv *priv, char *id, u32 regaddr, u32 *regval, s32 n) { s32 i; union long_union_t swap_addr, swap_reg; union long_union_t swap_addr2; struct bcm_ssi_tx_frame *tx = priv->tx_buf; struct bcm_ssi_rx_frame *rx = priv->rx_buf; for (i = 0; i < n; i++) { swap_addr.ul = regaddr + (i * 4); /* * First transaction will setup SPI Command Logic * to Read Data from Register. * HSI_MOSI_COMMAND_PCKT | * HSI_MOSI_HALF_DUPLEX | * HSI_MOSI_WRITE_TRANS; * 0x80 : < CHWXX > = half-duplex, SPI write command packet */ tx->cmd = 0x80; /* * <8?h0100_0000> = Bit-8 is a write transaction. * The lower 7-bits is the command offset of */ tx->data[0] = 0x24; /* * The register (RFIFO Read DATA) the host is * starting to write from. the number of valid bytes * available on MOSI following this byte */ tx->data[1] = 5; tx->data[2] = swap_addr.uc[0]; tx->data[3] = swap_addr.uc[1]; tx->data[4] = swap_addr.uc[2]; tx->data[5] = swap_addr.uc[3]; tx->data[6] = 0x02; /* AHB read transaction */ rx->status = 0; if (bcm_spi_sync(priv, tx, rx, 8, 8)) return -1; tx->cmd = 0xa0; memset(tx->data, 0, 11); rx->status = 0; if (bcm_spi_sync(priv, tx, rx, 12, 8)) return -1; swap_addr2.uc[0] = rx->data[1]; swap_addr2.uc[1] = rx->data[2]; swap_addr2.uc[2] = rx->data[3]; swap_addr2.uc[3] = rx->data[4]; swap_reg.uc[0] = rx->data[5]; swap_reg.uc[1] = rx->data[6]; swap_reg.uc[2] = rx->data[7]; swap_reg.uc[3] = rx->data[8]; if (id) dev_dbg(&priv->spi->dev, "reg32r: %s @ : [%08X] %08X ", id, (unsigned int)swap_addr2.ul, (unsigned int)swap_reg.ul); if (regval) *regval++ = swap_reg.ul; } return i; }