1/*
2 *
3 *   Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc.
4 *   Copyright (c) 2014, I2SE GmbH
5 *
6 *   Permission to use, copy, modify, and/or distribute this software
7 *   for any purpose with or without fee is hereby granted, provided
8 *   that the above copyright notice and this permission notice appear
9 *   in all copies.
10 *
11 *   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
12 *   WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13 *   WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14 *   THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
15 *   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
16 *   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
17 *   NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
18 *   CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *
20 */
21
22/*   This module implements the Qualcomm Atheros SPI protocol for
23 *   kernel-based SPI device.
24 */
25
26#include <linux/kernel.h>
27#include <linux/netdevice.h>
28#include <linux/spi/spi.h>
29
30#include "qca_7k.h"
31
32void
33qcaspi_spi_error(struct qcaspi *qca)
34{
35	if (qca->sync != QCASPI_SYNC_READY)
36		return;
37
38	netdev_err(qca->net_dev, "spi error\n");
39	qca->sync = QCASPI_SYNC_UNKNOWN;
40	qca->stats.spi_err++;
41}
42
43int
44qcaspi_read_register(struct qcaspi *qca, u16 reg, u16 *result)
45{
46	__be16 rx_data;
47	__be16 tx_data;
48	struct spi_transfer transfer[2];
49	struct spi_message msg;
50	int ret;
51
52	memset(transfer, 0, sizeof(transfer));
53
54	spi_message_init(&msg);
55
56	tx_data = cpu_to_be16(QCA7K_SPI_READ | QCA7K_SPI_INTERNAL | reg);
57	*result = 0;
58
59	transfer[0].tx_buf = &tx_data;
60	transfer[0].len = QCASPI_CMD_LEN;
61	transfer[1].rx_buf = &rx_data;
62	transfer[1].len = QCASPI_CMD_LEN;
63
64	spi_message_add_tail(&transfer[0], &msg);
65
66	if (qca->legacy_mode) {
67		spi_sync(qca->spi_dev, &msg);
68		spi_message_init(&msg);
69	}
70	spi_message_add_tail(&transfer[1], &msg);
71	ret = spi_sync(qca->spi_dev, &msg);
72
73	if (!ret)
74		ret = msg.status;
75
76	if (ret)
77		qcaspi_spi_error(qca);
78	else
79		*result = be16_to_cpu(rx_data);
80
81	return ret;
82}
83
84static int
85__qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value)
86{
87	__be16 tx_data[2];
88	struct spi_transfer transfer[2];
89	struct spi_message msg;
90	int ret;
91
92	memset(&transfer, 0, sizeof(transfer));
93
94	spi_message_init(&msg);
95
96	tx_data[0] = cpu_to_be16(QCA7K_SPI_WRITE | QCA7K_SPI_INTERNAL | reg);
97	tx_data[1] = cpu_to_be16(value);
98
99	transfer[0].tx_buf = &tx_data[0];
100	transfer[0].len = QCASPI_CMD_LEN;
101	transfer[1].tx_buf = &tx_data[1];
102	transfer[1].len = QCASPI_CMD_LEN;
103
104	spi_message_add_tail(&transfer[0], &msg);
105	if (qca->legacy_mode) {
106		spi_sync(qca->spi_dev, &msg);
107		spi_message_init(&msg);
108	}
109	spi_message_add_tail(&transfer[1], &msg);
110	ret = spi_sync(qca->spi_dev, &msg);
111
112	if (!ret)
113		ret = msg.status;
114
115	if (ret)
116		qcaspi_spi_error(qca);
117
118	return ret;
119}
120
121int
122qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value, int retry)
123{
124	int ret, i = 0;
125	u16 confirmed;
126
127	do {
128		ret = __qcaspi_write_register(qca, reg, value);
129		if (ret)
130			return ret;
131
132		if (!retry)
133			return 0;
134
135		ret = qcaspi_read_register(qca, reg, &confirmed);
136		if (ret)
137			return ret;
138
139		ret = confirmed != value;
140		if (!ret)
141			return 0;
142
143		i++;
144		qca->stats.write_verify_failed++;
145
146	} while (i <= retry);
147
148	return ret;
149}
150