blob: 0592d2fd33053f860998ba0251770839134f178a [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* This file is part of the QM35 UCI stack for linux.
*
* Copyright (c) 2021 Qorvo US, Inc.
*
* This software is provided under the GNU General Public License, version 2
* (GPLv2), as well as under a Qorvo commercial license.
*
* You may choose to use this software under the terms of the GPLv2 License,
* version 2 ("GPLv2"), as published by the Free Software Foundation.
* You should have received a copy of the GPLv2 along with this program. If
* not, see <http://www.gnu.org/licenses/>.
*
* This program is distributed under the GPLv2 in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GPLv2 for more
* details.
*
* If you cannot meet the requirements of the GPLv2, you may not use this
* software for any purpose without first obtaining a commercial license from
* Qorvo.
* Please contact Qorvo to inquire about licensing terms.
*
* QM35 FW ROM protocol SPI ops
*/
#include <linux/spi/spi.h>
#include <qmrom_spi.h>
#include <spi_rom_protocol.h>
#include "qm35.h"
static const char *fwname = NULL;
static unsigned int speed_hz;
extern int trace_spi_xfers;
void qmrom_set_fwname(const char *name)
{
fwname = name;
}
int qmrom_spi_transfer(void *handle, char *rbuf, const char *wbuf, size_t size)
{
struct spi_device *spi = (struct spi_device *)handle;
int rc;
struct spi_transfer xfer[] = {
{
.tx_buf = wbuf,
.rx_buf = rbuf,
.len = size,
.speed_hz = qmrom_spi_get_freq(),
},
};
rc = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
if (trace_spi_xfers) {
print_hex_dump(KERN_DEBUG, "tx:", DUMP_PREFIX_NONE, 16, 1, wbuf,
size, false);
print_hex_dump(KERN_DEBUG, "rx:", DUMP_PREFIX_NONE, 16, 1, rbuf,
size, false);
}
return rc;
}
int qmrom_spi_set_cs_level(void *handle, int level)
{
struct spi_device *spi = (struct spi_device *)handle;
uint8_t dummy = 0;
struct spi_transfer xfer[] = {
{
.tx_buf = &dummy,
.len = 1,
.cs_change = !level,
.speed_hz = qmrom_spi_get_freq(),
},
};
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
}
int qmrom_spi_reset_device(void *reset_handle)
{
struct qm35_ctx *qm35_hdl = (struct qm35_ctx *)reset_handle;
return qm35_reset(qm35_hdl, SPI_RST_LOW_DELAY_MS, true);
}
const struct firmware *qmrom_spi_get_firmware(void *handle,
struct qmrom_handle *qmrom_h,
bool use_prod_fw)
{
const struct firmware *fw;
struct spi_device *spi = handle;
char _fw_name[16]; /* enough room to store "qm35_xx_xxx.bin" */
const char *fw_name = _fw_name;
int ret;
enum chip_revision_e revision = qmrom_h->chip_rev;
int lcs_state = qmrom_h->lcs_state;
if (!fwname) {
if (revision == CHIP_REVISION_A0)
fw_name = "qm35_a0.bin";
else if (lcs_state == CC_BSV_SECURE_LCS) {
if (use_prod_fw)
fw_name = "qm35_b0_oem_prod.bin";
else
fw_name = "qm35_b0_oem.bin";
} else
fw_name = "qm35_b0_icv.bin";
} else {
fw_name = fwname;
}
dev_info(&spi->dev, "Requesting fw %s!\n", fw_name);
ret = request_firmware(&fw, fw_name, &spi->dev);
if (ret) {
release_firmware(fw);
dev_err(&spi->dev,
"request_firmware failed (ret=%d) for '%s'\n", ret,
fw_name);
return NULL;
}
dev_info(&spi->dev, "Firmware size is %zu!\n", fw->size);
return fw;
}
void qmrom_spi_release_firmware(const struct firmware *fw)
{
release_firmware(fw);
}
int qmrom_spi_wait_for_ready_line(void *handle, unsigned int timeout_ms)
{
int count_down = (int)timeout_ms;
while (!gpiod_get_value(handle) && (--count_down >= 0)) {
usleep_range(1000, 1100);
}
return gpiod_get_value(handle) ? 0 : -1;
}
void qmrom_spi_set_freq(unsigned int freq)
{
speed_hz = freq;
}
unsigned int qmrom_spi_get_freq()
{
return speed_hz;
}