blob: 682352c3bee8a709681b37ba96eefc9ed25a69ab [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2015 Broadcom Corporation
*
* The Broadcom GPS SPI driver
*
*/
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/sysrq.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/kthread.h>
#include <linux/circ_buf.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/poll.h>
#include <linux/uaccess.h>
#include <linux/suspend.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/time.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <linux/kernel_stat.h>
#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 == <cmdByteNum+1>
*/
tx->data[0] = 0;
/* READ : the command offset of the register == <cmdRegOffset>. */
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 : <Command Byte>
* 0xD1 : <SPI Address Left Shifted with Write Bit as LSB>
* 0x00 : < SPI Offset of DMA Start Addr >
* 0x00, 0x00, 0x00, 0x00 : < Start Address>,
* Should be set from 'regaddr'
* 0x04, 0x00 : <Number of bytes to write>
* 0x01 : <Write Enable>
*
* 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;
}