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