1// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
2/*
3 *   Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc.
4 *   Copyright (c) 2014, I2SE GmbH
5 */
6
7/*   This module implements the Qualcomm Atheros SPI protocol for
8 *   kernel-based SPI device.
9 */
10
11#include <linux/kernel.h>
12#include <linux/netdevice.h>
13#include <linux/spi/spi.h>
14
15#include "qca_7k.h"
16
17void
18qcaspi_spi_error(struct qcaspi *qca)
19{
20	if (qca->sync != QCASPI_SYNC_READY)
21		return;
22
23	netdev_err(qca->net_dev, "spi error\n");
24	qca->sync = QCASPI_SYNC_UNKNOWN;
25	qca->stats.spi_err++;
26}
27
28int
29qcaspi_read_register(struct qcaspi *qca, u16 reg, u16 *result)
30{
31	__be16 rx_data;
32	__be16 tx_data;
33	struct spi_transfer transfer[2];
34	struct spi_message msg;
35	int ret;
36
37	memset(transfer, 0, sizeof(transfer));
38
39	spi_message_init(&msg);
40
41	tx_data = cpu_to_be16(QCA7K_SPI_READ | QCA7K_SPI_INTERNAL | reg);
42	*result = 0;
43
44	transfer[0].tx_buf = &tx_data;
45	transfer[0].len = QCASPI_CMD_LEN;
46	transfer[1].rx_buf = &rx_data;
47	transfer[1].len = QCASPI_CMD_LEN;
48
49	spi_message_add_tail(&transfer[0], &msg);
50
51	if (qca->legacy_mode) {
52		spi_sync(qca->spi_dev, &msg);
53		spi_message_init(&msg);
54	}
55	spi_message_add_tail(&transfer[1], &msg);
56	ret = spi_sync(qca->spi_dev, &msg);
57
58	if (!ret)
59		ret = msg.status;
60
61	if (ret)
62		qcaspi_spi_error(qca);
63	else
64		*result = be16_to_cpu(rx_data);
65
66	return ret;
67}
68
69static int
70__qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value)
71{
72	__be16 tx_data[2];
73	struct spi_transfer transfer[2];
74	struct spi_message msg;
75	int ret;
76
77	memset(&transfer, 0, sizeof(transfer));
78
79	spi_message_init(&msg);
80
81	tx_data[0] = cpu_to_be16(QCA7K_SPI_WRITE | QCA7K_SPI_INTERNAL | reg);
82	tx_data[1] = cpu_to_be16(value);
83
84	transfer[0].tx_buf = &tx_data[0];
85	transfer[0].len = QCASPI_CMD_LEN;
86	transfer[1].tx_buf = &tx_data[1];
87	transfer[1].len = QCASPI_CMD_LEN;
88
89	spi_message_add_tail(&transfer[0], &msg);
90	if (qca->legacy_mode) {
91		spi_sync(qca->spi_dev, &msg);
92		spi_message_init(&msg);
93	}
94	spi_message_add_tail(&transfer[1], &msg);
95	ret = spi_sync(qca->spi_dev, &msg);
96
97	if (!ret)
98		ret = msg.status;
99
100	if (ret)
101		qcaspi_spi_error(qca);
102
103	return ret;
104}
105
106int
107qcaspi_write_register(struct qcaspi *qca, u16 reg, u16 value, int retry)
108{
109	int ret, i = 0;
110	u16 confirmed;
111
112	do {
113		ret = __qcaspi_write_register(qca, reg, value);
114		if (ret)
115			return ret;
116
117		if (!retry)
118			return 0;
119
120		ret = qcaspi_read_register(qca, reg, &confirmed);
121		if (ret)
122			return ret;
123
124		ret = confirmed != value;
125		if (!ret)
126			return 0;
127
128		i++;
129		qca->stats.write_verify_failed++;
130
131	} while (i <= retry);
132
133	return ret;
134}
135