/*
* Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc.
* Copyright (c) 2014, I2SE GmbH
*
* Permission to use, copy, modify, and/or distribute this software
* for any purpose with or without fee is hereby granted, provided
* that the above copyright notice and this permission notice appear
* in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* This module implements the Qualcomm Atheros SPI protocol for
* kernel-based SPI device; it is essentially an Ethernet-to-SPI
* serial converter;
*/
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netdevice.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_net.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include "qca_7k.h"
#include "qca_7k_common.h"
#include "qca_debug.h"
#include "qca_spi.h"
#define MAX_DMA_BURST_LEN 5000
/* Modules parameters */
#define QCASPI_CLK_SPEED_MIN 1000000
#define QCASPI_CLK_SPEED_MAX 16000000
#define QCASPI_CLK_SPEED 8000000
static int qcaspi_clkspeed;
module_param(qcaspi_clkspeed, int, 0);
MODULE_PARM_DESC(qcaspi_clkspeed, "SPI bus clock speed (Hz). Use 1000000-16000000.");
#define QCASPI_BURST_LEN_MIN 1
#define QCASPI_BURST_LEN_MAX MAX_DMA_BURST_LEN
static int qcaspi_burst_len = MAX_DMA_BURST_LEN;
module_param(qcaspi_burst_len, int, 0);
MODULE_PARM_DESC(qcaspi_burst_len, "Number of data bytes per burst. Use 1-5000.");
#define QCASPI_PLUGGABLE_MIN 0
#define QCASPI_PLUGGABLE_MAX 1
static int qcaspi_pluggable = QCASPI_PLUGGABLE_MIN;
module_param(qcaspi_pluggable, int, 0);
MODULE_PARM_DESC(qcaspi_pluggable, "Pluggable SPI connection (yes/no).");
#define QCASPI_TX_TIMEOUT (1 * HZ)
#define QCASPI_QCA7K_REBOOT_TIME_MS 1000
static void
start_spi_intr_handling(struct qcaspi *qca, u16 *intr_cause)
{
*intr_cause = 0;
qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, 0);
qcaspi_read_register(qca, SPI_REG_INTR_CAUSE, intr_cause);
netdev_dbg(qca->net_dev, "interrupts: 0x%04x\n", *intr_cause);
}
static void
end_spi_intr_handling(struct qcaspi *qca, u16 intr_cause)
{
u16 intr_enable = (SPI_INT_CPU_ON |
SPI_INT_PKT_AVLBL |
SPI_INT_RDBUF_ERR |
SPI_INT_WRBUF_ERR);
qcaspi_write_register(qca, SPI_REG_INTR_CAUSE, intr_cause);
qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, intr_enable);
netdev_dbg(qca->net_dev, "acking int: 0x%04x\n", intr_cause);
}
static u32
qcaspi_write_burst(struct qcaspi *qca, u8 *src, u32 len)
{
__be16 cmd;
struct spi_message *msg = &qca->spi_msg2;
struct spi_transfer *transfer = &qca->spi_xfer2[0];
int ret;
cmd = cpu_to_be16(QCA7K_SPI_WRITE | QCA7K_SPI_EXTERNAL);
transfer->tx_buf = &cmd;
transfer->rx_buf = NULL;
transfer->len = QCASPI_CMD_LEN;
transfer = &qca->spi_xfer2[1];
transfer->tx_buf = src;
transfer->rx_buf = NULL;
transfer->len = len;
ret = spi_sync(qca->spi_dev, msg);
if (ret || (msg->actual_length != QCASPI_CMD_LEN + len)) {
qcaspi_spi_error(qca);
return 0;
}
return len;
}
static u32
qcaspi_write_legacy(struct qcaspi *qca, u8 *src, u32 len)
{
struct spi_message *msg = &qca->spi_msg1;
struct spi_transfer *transfer = &qca->spi_xfer1;
int ret;
transfer->tx_buf = src;
transfer->rx_buf = NULL;
transfer->len = len;
ret = spi_sync(qca->spi_dev, msg);
if (ret || (msg->actual_length != len)) {
qcaspi_spi_error(qca);
return 0;
}
return len;
}
static u32
qcaspi_read_burst(struct qcaspi *qca, u8 *dst, u32 len)
{
struct spi_message *msg = &qca->spi_msg2;
__be16 cmd;
struct spi_transfer *transfer = &qca->spi_xfer2[0];
int ret;
cmd = cpu_to_be16(QCA7K_SPI_READ | QCA7K_SPI_EXTERNAL);
transfer->tx_buf = &cmd;
transfer->rx_buf = NULL;
transfer->len