/*
* 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_WRITE_VERIFY_MIN 0
#define QCASPI_WRITE_VERIFY_MAX 3
static int wr_verify = QCASPI_WRITE_VERIFY_MIN;
module_param(wr_verify, int, 0);
MODULE_PARM_DESC(wr_verify, "SPI register write verify trails. Use 0-3.");
#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, wr_verify);
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, 0);
qcaspi_write_register(qca, SPI_REG_INTR_ENABLE, intr_enable, wr_verify);
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;
struct spi_transfer transfer[2];
int ret;
memset(&transfer, 0, sizeof(transfer));
spi_message_init(&msg);
cmd = cpu_to_be16(QCA7K_SPI_WRITE | QCA7K_SPI_EXTERNAL);
transfer[0].tx_buf = &cmd;
transfer[0].len = QCASPI_CMD_LEN;
transfer[1].tx_buf = src;
transfer[1].len = len;
spi_message_add_tail(&transfer[0], &msg);
spi_message_add_tail(&transfer[1], &msg);
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;
struct spi_transfer transfer;
int ret;
memset(&transfer, 0, sizeof(transfer));
spi_message_init(&msg);
transfer.tx_buf = src;
transfer.len = len;
spi_message_add_tail(&transfer, &msg);
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