Linux Audio

Check our new training course

Loading...
  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}