1291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren/*
2291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *
3291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc.
4291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   Copyright (c) 2014, I2SE GmbH
5291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *
6291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   Permission to use, copy, modify, and/or distribute this software
7291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   for any purpose with or without fee is hereby granted, provided
8291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   that the above copyright notice and this permission notice appear
9291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   in all copies.
10291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *
11291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
12291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
15291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
16291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
17291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *
20291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren */
21291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
22291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren/*   This module implements the Qualcomm Atheros SPI protocol for
23291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren *   kernel-based SPI device.
24291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren */
25291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
26291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren#include <linux/init.h>
27291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren#include <linux/module.h>
28291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren#include <linux/moduleparam.h>
29291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren#include <linux/spi/spi.h>
30291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren#include <linux/version.h>
31291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
32291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren#include "qca_7k.h"
33291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
34291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahrenvoid
35291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahrenqcaspi_spi_error(struct qcaspi *qca)
36291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren{
37291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	if (qca->sync != QCASPI_SYNC_READY)
38291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		return;
39291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
40291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	netdev_err(qca->net_dev, "spi error\n");
41291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	qca->sync = QCASPI_SYNC_UNKNOWN;
42291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	qca->stats.spi_err++;
43291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren}
44291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
45291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahrenint
46291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahrenqcaspi_read_register(struct qcaspi *qca, u16 reg, u16 *result)
47291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren{
48291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	__be16 rx_data;
49291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	__be16 tx_data;
50291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	struct spi_transfer *transfer;
51291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	struct spi_message *msg;
52291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	int ret;
53291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
54291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	tx_data = cpu_to_be16(QCA7K_SPI_READ | QCA7K_SPI_INTERNAL | reg);
55291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
56291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	if (qca->legacy_mode) {
57291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		msg = &qca->spi_msg1;
58291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer = &qca->spi_xfer1;
59291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->tx_buf = &tx_data;
60291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->rx_buf = NULL;
61291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->len = QCASPI_CMD_LEN;
62291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		spi_sync(qca->spi_dev, msg);
63291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	} else {
64291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		msg = &qca->spi_msg2;
65291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer = &qca->spi_xfer2[0];
66291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->tx_buf = &tx_data;
67291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->rx_buf = NULL;
68291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->len = QCASPI_CMD_LEN;
69291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer = &qca->spi_xfer2[1];
70291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	}
71291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	transfer->tx_buf = NULL;
72291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	transfer->rx_buf = &rx_data;
73291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	transfer->len = QCASPI_CMD_LEN;
74291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	ret = spi_sync(qca->spi_dev, msg);
75291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
76291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	if (!ret)
77291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		ret = msg->status;
78291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
79291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	if (ret)
80291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		qcaspi_spi_error(qca);
81291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	else
82291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		*result = be16_to_cpu(rx_data);
83291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
84291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	return ret;
85291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren}
86291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
87291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahrenint
88291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahrenqcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value)
89291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren{
90291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	__be16 tx_data[2];
91291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	struct spi_transfer *transfer;
92291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	struct spi_message *msg;
93291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	int ret;
94291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
95291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	tx_data[0] = cpu_to_be16(QCA7K_SPI_WRITE | QCA7K_SPI_INTERNAL | reg);
96291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	tx_data[1] = cpu_to_be16(value);
97291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
98291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	if (qca->legacy_mode) {
99291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		msg = &qca->spi_msg1;
100291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer = &qca->spi_xfer1;
101291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->tx_buf = &tx_data[0];
102291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->rx_buf = NULL;
103291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->len = QCASPI_CMD_LEN;
104291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		spi_sync(qca->spi_dev, msg);
105291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	} else {
106291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		msg = &qca->spi_msg2;
107291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer = &qca->spi_xfer2[0];
108291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->tx_buf = &tx_data[0];
109291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->rx_buf = NULL;
110291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer->len = QCASPI_CMD_LEN;
111291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		transfer = &qca->spi_xfer2[1];
112291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	}
113291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	transfer->tx_buf = &tx_data[1];
114291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	transfer->rx_buf = NULL;
115291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	transfer->len = QCASPI_CMD_LEN;
116291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	ret = spi_sync(qca->spi_dev, msg);
117291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
118291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	if (!ret)
119291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		ret = msg->status;
120291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
121291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	if (ret)
122291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		qcaspi_spi_error(qca);
123291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
124291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	return ret;
125291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren}
126291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
127291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahrenint
128291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahrenqcaspi_tx_cmd(struct qcaspi *qca, u16 cmd)
129291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren{
130291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	__be16 tx_data;
131291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	struct spi_message *msg = &qca->spi_msg1;
132291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	struct spi_transfer *transfer = &qca->spi_xfer1;
133291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	int ret;
134291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
135291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	tx_data = cpu_to_be16(cmd);
136291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	transfer->len = sizeof(tx_data);
137291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	transfer->tx_buf = &tx_data;
138291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	transfer->rx_buf = NULL;
139291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
140291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	ret = spi_sync(qca->spi_dev, msg);
141291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
142291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	if (!ret)
143291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		ret = msg->status;
144291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
145291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	if (ret)
146291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren		qcaspi_spi_error(qca);
147291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren
148291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren	return ret;
149291ab06ecf6765aa0c73332b745ffb3a44ed30c6Stefan Wahren}
150