1223 lines
29 KiB
C
1223 lines
29 KiB
C
|
/*
|
||
|
* DSPG DBMDX SPI interface driver
|
||
|
*
|
||
|
* Copyright (C) 2014 DSP Group
|
||
|
*
|
||
|
* This software is licensed under the terms of the GNU General Public
|
||
|
* License version 2, as published by the Free Software Foundation, and
|
||
|
* may be copied, distributed, and modified under those terms.
|
||
|
*
|
||
|
* This program is distributed 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
|
||
|
* GNU General Public License for more details.
|
||
|
*/
|
||
|
/* #define DEBUG */
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/spi/spi.h>
|
||
|
#if IS_ENABLED(CONFIG_OF)
|
||
|
#include <linux/of.h>
|
||
|
#include <linux/of_gpio.h>
|
||
|
#endif
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/mutex.h>
|
||
|
#include <linux/clk.h>
|
||
|
#include <linux/firmware.h>
|
||
|
|
||
|
#include "dbmdx-interface.h"
|
||
|
#include "dbmdx-va-regmap.h"
|
||
|
#include "dbmdx-vqe-regmap.h"
|
||
|
#include "dbmdx-spi.h"
|
||
|
|
||
|
#define DEFAULT_SPI_WRITE_CHUNK_SIZE 8
|
||
|
#define MAX_SPI_WRITE_CHUNK_SIZE 0x40000
|
||
|
#define DEFAULT_SPI_READ_CHUNK_SIZE 8
|
||
|
#define MAX_SPI_READ_CHUNK_SIZE 8192
|
||
|
|
||
|
|
||
|
#define DBMDX_USE_EXPIRABLE_WAKELOCK 1
|
||
|
#define DBMDX_WAKELOCK_TIMEOUT_MS 70*1000
|
||
|
|
||
|
static DECLARE_WAIT_QUEUE_HEAD(dbmdx_wq);
|
||
|
|
||
|
int spi_set_speed(struct dbmdx_private *p, int index)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
struct spi_device *spi = spi_p->client;
|
||
|
int ret = 0;
|
||
|
u32 bits_per_word = 0;
|
||
|
u32 spi_rate = 0;
|
||
|
u16 spi_mode = SPI_MODE_0;
|
||
|
|
||
|
if (index >= DBMDX_VA_NR_OF_SPEEDS) {
|
||
|
dev_err(spi_p->dev, "%s: Invalid speed index %x\n",
|
||
|
__func__, index);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
spi_rate = p->pdata->va_speed_cfg[index].spi_rate -
|
||
|
(p->pdata->va_speed_cfg[index].spi_rate % 1000);
|
||
|
|
||
|
bits_per_word = p->pdata->va_speed_cfg[index].spi_rate % 100;
|
||
|
|
||
|
spi_mode = (u16)(((p->pdata->va_speed_cfg[index].spi_rate % 1000) -
|
||
|
bits_per_word) / 100);
|
||
|
|
||
|
|
||
|
if (bits_per_word != 8 && bits_per_word != 16 && bits_per_word != 32)
|
||
|
bits_per_word = 8;
|
||
|
|
||
|
if (spi_mode == 0)
|
||
|
spi_mode = SPI_MODE_0;
|
||
|
else if (spi_mode == 1)
|
||
|
spi_mode = SPI_MODE_1;
|
||
|
else if (spi_mode == 2)
|
||
|
spi_mode = SPI_MODE_2;
|
||
|
else if (spi_mode == 3)
|
||
|
spi_mode = SPI_MODE_3;
|
||
|
else
|
||
|
spi_mode = SPI_MODE_0;
|
||
|
|
||
|
if (spi->max_speed_hz != spi_rate || spi->mode != spi_mode) {
|
||
|
|
||
|
spi->max_speed_hz = spi_rate;
|
||
|
spi->mode = spi_mode;
|
||
|
|
||
|
spi->bits_per_word = bits_per_word;
|
||
|
|
||
|
spi_p->pdata->bits_per_word = spi->bits_per_word;
|
||
|
spi_p->pdata->bytes_per_word = spi->bits_per_word / 8;
|
||
|
|
||
|
dev_info(spi_p->dev,
|
||
|
"%s Update SPI Max Speed to %d Hz, bpw: %d, mode: %d\n",
|
||
|
__func__,
|
||
|
spi->max_speed_hz,
|
||
|
spi->bits_per_word,
|
||
|
spi->mode);
|
||
|
|
||
|
ret = spi_setup(spi);
|
||
|
if (ret < 0)
|
||
|
dev_err(spi_p->dev, "%s:failed %x\n", __func__, ret);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ssize_t send_spi_cmd_vqe(struct dbmdx_private *p,
|
||
|
u32 command, u16 *response)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
u8 send[4];
|
||
|
u8 recv[4];
|
||
|
ssize_t ret = 0;
|
||
|
int retries = 10;
|
||
|
|
||
|
send[0] = (command >> 24) & 0xff;
|
||
|
send[1] = (command >> 16) & 0xff;
|
||
|
send[2] = (command >> 8) & 0xff;
|
||
|
send[3] = command & 0xff;
|
||
|
|
||
|
ret = send_spi_data(p, send, 4);
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev,
|
||
|
"%s: cmd:0x%08X - send_spi_data failed ret:%zd\n",
|
||
|
__func__, command, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
usleep_range(DBMDX_USLEEP_SPI_VQE_CMD_AFTER_SEND,
|
||
|
DBMDX_USLEEP_SPI_VQE_CMD_AFTER_SEND + 1000);
|
||
|
|
||
|
if ((command == (DBMDX_VQE_SET_POWER_STATE_CMD |
|
||
|
DBMDX_VQE_SET_POWER_STATE_HIBERNATE)) ||
|
||
|
(command == DBMDX_VQE_SET_SWITCH_TO_BOOT_CMD))
|
||
|
return 0;
|
||
|
|
||
|
/* we need additional sleep till system is ready */
|
||
|
if (command == (DBMDX_VQE_SET_SYSTEM_CONFIG_CMD |
|
||
|
DBMDX_VQE_SET_SYSTEM_CONFIG_PRIMARY_CFG))
|
||
|
msleep(DBMDX_MSLEEP_SPI_VQE_SYS_CFG_CMD);
|
||
|
|
||
|
/* read response */
|
||
|
do {
|
||
|
ret = spi_read(spi_p->client, recv, 4);
|
||
|
if (ret < 0) {
|
||
|
/* Wait before polling again */
|
||
|
usleep_range(10000, 11000);
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
/*
|
||
|
* Check that the first two bytes of the response match
|
||
|
* (the ack is in those bytes)
|
||
|
*/
|
||
|
if ((send[0] == recv[0]) && (send[1] == recv[1])) {
|
||
|
if (response)
|
||
|
*response = (recv[2] << 8) | recv[3];
|
||
|
ret = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
dev_warn(spi_p->dev,
|
||
|
"%s: incorrect ack (got 0x%.2x%.2x)\n",
|
||
|
__func__, recv[0], recv[1]);
|
||
|
ret = -EINVAL;
|
||
|
|
||
|
/* Wait before polling again */
|
||
|
usleep_range(10000, 11000);
|
||
|
} while (--retries);
|
||
|
|
||
|
if (!retries)
|
||
|
dev_err(spi_p->dev,
|
||
|
"%s: cmd:0x%08X - wrong ack, giving up\n",
|
||
|
__func__, command);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ssize_t send_spi_cmd_va(struct dbmdx_private *p, u32 command,
|
||
|
u16 *response)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
char tmp[3];
|
||
|
u8 send[7];
|
||
|
u8 recv[6] = {0, 0, 0, 0, 0, 0};
|
||
|
int ret;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s: Send 0x%02x\n", __func__, command);
|
||
|
|
||
|
p->wakeup_toggle(p);
|
||
|
|
||
|
if (response) {
|
||
|
|
||
|
ret = snprintf(tmp, 3, "%02x", (command >> 16) & 0xff);
|
||
|
send[0] = 0;
|
||
|
send[1] = tmp[0];
|
||
|
send[2] = tmp[1];
|
||
|
send[3] = 'r';
|
||
|
|
||
|
ret = send_spi_data(p, send, 4);
|
||
|
if (ret != 4)
|
||
|
goto out;
|
||
|
|
||
|
usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND,
|
||
|
DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND + 100);
|
||
|
|
||
|
if (p->va_debug_mode & DBMDX_DEBUG_MODE_FW_LOG)
|
||
|
msleep(DBMDX_MSLEEP_DBG_MODE_CMD_RX);
|
||
|
|
||
|
ret = 0;
|
||
|
|
||
|
/* The sleep command cannot be acked before the device
|
||
|
* goes to sleep
|
||
|
*/
|
||
|
ret = read_spi_data(p, recv, 5);
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev, "%s:spi_read failed =%d\n",
|
||
|
__func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
recv[5] = 0;
|
||
|
ret = kstrtou16((const char *)&recv[1], 16, response);
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev, "%s failed -%d\n", __func__, ret);
|
||
|
dev_err(spi_p->dev, "%s: %x:%x:%x:%x:\n",
|
||
|
__func__, recv[1], recv[2],
|
||
|
recv[3], recv[4]);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s: Received 0x%02x\n", __func__,
|
||
|
*response);
|
||
|
|
||
|
ret = 0;
|
||
|
} else {
|
||
|
ret = snprintf(tmp, 3, "%02x", (command >> 16) & 0xff);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
send[0] = tmp[0];
|
||
|
send[1] = tmp[1];
|
||
|
send[2] = 'w';
|
||
|
|
||
|
ret = snprintf(tmp, 3, "%02x", (command >> 8) & 0xff);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
send[3] = tmp[0];
|
||
|
send[4] = tmp[1];
|
||
|
|
||
|
ret = snprintf(tmp, 3, "%02x", command & 0xff);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
send[5] = tmp[0];
|
||
|
send[6] = tmp[1];
|
||
|
|
||
|
ret = send_spi_data(p, send, 7);
|
||
|
if (ret != 7)
|
||
|
goto out;
|
||
|
ret = 0;
|
||
|
|
||
|
}
|
||
|
out:
|
||
|
usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND_2,
|
||
|
DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND_2 + 100);
|
||
|
|
||
|
if (p->va_debug_mode & DBMDX_DEBUG_MODE_FW_LOG)
|
||
|
msleep(DBMDX_MSLEEP_DBG_MODE_CMD_TX);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ssize_t send_spi_cmd_va_padded(struct dbmdx_private *p,
|
||
|
u32 command,
|
||
|
u16 *response)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
char tmp[3];
|
||
|
u8 send[DBMDX_VA_SPI_CMD_PADDED_SIZE] = {0};
|
||
|
u8 recv[DBMDX_VA_SPI_CMD_PADDED_SIZE] = {0};
|
||
|
int ret;
|
||
|
u32 padded_cmd_w_size = spi_p->pdata->dma_min_buffer_size;
|
||
|
u32 padded_cmd_r_size = 5;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s: Send 0x%02x\n", __func__, command);
|
||
|
|
||
|
p->wakeup_toggle(p);
|
||
|
|
||
|
if (response) {
|
||
|
|
||
|
ret = snprintf(tmp, 3, "%02x", (command >> 16) & 0xff);
|
||
|
send[padded_cmd_w_size - 3] = tmp[0];
|
||
|
send[padded_cmd_w_size - 2] = tmp[1];
|
||
|
send[padded_cmd_w_size - 1] = 'r';
|
||
|
|
||
|
ret = send_spi_data(p, send, padded_cmd_w_size);
|
||
|
if (ret != padded_cmd_w_size)
|
||
|
goto out;
|
||
|
|
||
|
usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND,
|
||
|
DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND + 100);
|
||
|
|
||
|
if (p->va_debug_mode & DBMDX_DEBUG_MODE_FW_LOG)
|
||
|
msleep(DBMDX_MSLEEP_DBG_MODE_CMD_RX);
|
||
|
|
||
|
ret = 0;
|
||
|
|
||
|
/* the sleep command cannot be acked before the device
|
||
|
* goes to sleep
|
||
|
*/
|
||
|
ret = read_spi_data(p, recv, padded_cmd_r_size);
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev, "%s:spi_read failed =%d\n",
|
||
|
__func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
recv[5] = 0;
|
||
|
ret = kstrtou16((const char *)&recv[1], 16, response);
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev, "%s failed -%d\n", __func__, ret);
|
||
|
dev_err(spi_p->dev, "%s: %x:%x:%x:%x:\n",
|
||
|
__func__, recv[1], recv[2],
|
||
|
recv[3], recv[4]);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s: Received 0x%02x\n", __func__,
|
||
|
*response);
|
||
|
|
||
|
ret = 0;
|
||
|
} else {
|
||
|
ret = snprintf(tmp, 3, "%02x", (command >> 16) & 0xff);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
send[padded_cmd_w_size - 7] = tmp[0];
|
||
|
send[padded_cmd_w_size - 6] = tmp[1];
|
||
|
send[padded_cmd_w_size - 5] = 'w';
|
||
|
|
||
|
ret = snprintf(tmp, 3, "%02x", (command >> 8) & 0xff);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
send[padded_cmd_w_size - 4] = tmp[0];
|
||
|
send[padded_cmd_w_size - 3] = tmp[1];
|
||
|
|
||
|
ret = snprintf(tmp, 3, "%02x", command & 0xff);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
send[padded_cmd_w_size - 2] = tmp[0];
|
||
|
send[padded_cmd_w_size - 1] = tmp[1];
|
||
|
|
||
|
ret = send_spi_data(p, send, padded_cmd_w_size);
|
||
|
if (ret != padded_cmd_w_size)
|
||
|
goto out;
|
||
|
ret = 0;
|
||
|
|
||
|
}
|
||
|
out:
|
||
|
usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND_2,
|
||
|
DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND_2 + 100);
|
||
|
|
||
|
if (p->va_debug_mode & DBMDX_DEBUG_MODE_FW_LOG)
|
||
|
msleep(DBMDX_MSLEEP_DBG_MODE_CMD_TX);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
ssize_t read_spi_data(struct dbmdx_private *p, void *buf, size_t len)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
size_t count = spi_p->pdata->read_chunk_size;
|
||
|
u32 bytes_per_word = spi_p->pdata->bytes_per_word;
|
||
|
u8 *recv = spi_p->pdata->recv;
|
||
|
ssize_t i;
|
||
|
size_t pad_size = 0;
|
||
|
int ret;
|
||
|
u8 *d = (u8 *)buf;
|
||
|
/* if stuck for more than 10s, something is wrong */
|
||
|
unsigned long timeout = jiffies + msecs_to_jiffies(10000);
|
||
|
|
||
|
for (i = 0; i < len; i += count) {
|
||
|
if ((i + count) > len) {
|
||
|
count = len - i;
|
||
|
if (count % (size_t)bytes_per_word != 0)
|
||
|
pad_size = (size_t)bytes_per_word -
|
||
|
(size_t)(count % (size_t)bytes_per_word);
|
||
|
count = count + pad_size;
|
||
|
}
|
||
|
|
||
|
ret = spi_read(spi_p->client, recv, count);
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev, "%s: spi_read failed\n",
|
||
|
__func__);
|
||
|
i = -EIO;
|
||
|
goto out;
|
||
|
}
|
||
|
memcpy(d + i, recv, count - pad_size);
|
||
|
|
||
|
if (!time_before(jiffies, timeout)) {
|
||
|
dev_err(spi_p->dev,
|
||
|
"%s: read data timed out after %zd bytes\n",
|
||
|
__func__, i);
|
||
|
i = -ETIMEDOUT;
|
||
|
goto out;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return len;
|
||
|
out:
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
|
||
|
ssize_t write_spi_data(struct dbmdx_private *p, const u8 *buf,
|
||
|
size_t len)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
struct spi_device *spi = spi_p->client;
|
||
|
int rc;
|
||
|
|
||
|
rc = spi_write(spi, buf, len);
|
||
|
if (rc != 0) {
|
||
|
dev_err(spi_p->dev, "%s(): error %d writing SR\n",
|
||
|
__func__, rc);
|
||
|
return rc;
|
||
|
} else
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
|
||
|
ssize_t send_spi_data(struct dbmdx_private *p, const void *buf,
|
||
|
size_t len)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
u32 bytes_per_word = spi_p->pdata->bytes_per_word;
|
||
|
int ret = 0;
|
||
|
const u8 *cmds = (const u8 *)buf;
|
||
|
size_t to_copy = len;
|
||
|
size_t max_size = (size_t)(spi_p->pdata->write_chunk_size);
|
||
|
size_t pad_size = 0;
|
||
|
size_t cur_send_size = 0;
|
||
|
u8 *send = spi_p->pdata->send;
|
||
|
|
||
|
while (to_copy > 0) {
|
||
|
if (to_copy < max_size) {
|
||
|
memset(send, 0, max_size);
|
||
|
memcpy(send, cmds, to_copy);
|
||
|
|
||
|
if (to_copy % (size_t)bytes_per_word != 0)
|
||
|
pad_size = (size_t)bytes_per_word -
|
||
|
(size_t)(to_copy % (size_t)bytes_per_word);
|
||
|
|
||
|
cur_send_size = to_copy + pad_size;
|
||
|
} else {
|
||
|
memcpy(send, cmds, max_size);
|
||
|
cur_send_size = max_size;
|
||
|
}
|
||
|
|
||
|
ret = write_spi_data(p, send, cur_send_size);
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev, "%s: Failed ret=%d\n",
|
||
|
__func__, ret);
|
||
|
break;
|
||
|
}
|
||
|
to_copy -= (ret - pad_size);
|
||
|
cmds += (ret - pad_size);
|
||
|
}
|
||
|
|
||
|
return len - to_copy;
|
||
|
}
|
||
|
|
||
|
int send_spi_cmd_boot(struct dbmdx_private *p, u32 command)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
u8 send[4];
|
||
|
int ret = 0;
|
||
|
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s: command = %x\n", __func__, command);
|
||
|
send[0] = 0;
|
||
|
send[1] = 0;
|
||
|
send[2] = (command >> 16) & 0xff;
|
||
|
send[3] = (command >> 8) & 0xff;
|
||
|
|
||
|
ret = send_spi_data(p, send, 4);
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev, "%s: ret = %d\n", __func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* A host command received will blocked until the current audio frame
|
||
|
* processing is finished, which can take up to 10 ms
|
||
|
*/
|
||
|
usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_BOOT,
|
||
|
DBMDX_USLEEP_SPI_VA_CMD_AFTER_BOOT + 1000);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int spi_verify_boot_checksum(struct dbmdx_private *p,
|
||
|
const void *checksum, size_t chksum_len)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
int ret;
|
||
|
u8 rx_checksum[11] = {0};
|
||
|
|
||
|
if (!checksum)
|
||
|
return 0;
|
||
|
|
||
|
if (chksum_len > 8) {
|
||
|
dev_err(spi_p->dev, "%s: illegal checksum length\n", __func__);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
ret = send_spi_cmd_boot(p, DBMDX_READ_CHECKSUM);
|
||
|
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev, "%s: could not read checksum\n", __func__);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
ret = read_spi_data(p, (void *)rx_checksum, chksum_len + 3);
|
||
|
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev, "%s: could not read checksum data\n",
|
||
|
__func__);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
ret = p->verify_checksum(p, checksum, &rx_checksum[3], chksum_len);
|
||
|
if (ret) {
|
||
|
dev_err(spi_p->dev, "%s: checksum mismatch\n", __func__);
|
||
|
return -EILSEQ;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int spi_verify_chip_id(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
int ret;
|
||
|
u8 idr_read_cmd[] = {0x5A, 0x07, 0x68, 0x00, 0x00, 0x03};
|
||
|
u8 idr_read_result[7] = {0};
|
||
|
u8 chip_rev_id_low_a = 0;
|
||
|
u8 chip_rev_id_low_b = 0;
|
||
|
u8 chip_rev_id_high = 0;
|
||
|
|
||
|
u8 recv_chip_rev_id_high = 0;
|
||
|
u8 recv_chip_rev_id_low = 0;
|
||
|
|
||
|
if (p->cur_firmware_id == DBMDX_FIRMWARE_ID_DBMD2) {
|
||
|
idr_read_cmd[2] = 0x68;
|
||
|
chip_rev_id_high = 0x0d;
|
||
|
chip_rev_id_low_a = 0xb0;
|
||
|
chip_rev_id_low_b = 0xb1;
|
||
|
} else if (p->cur_firmware_id == DBMDX_FIRMWARE_ID_DBMD4) {
|
||
|
idr_read_cmd[2] = 0x74;
|
||
|
chip_rev_id_high = 0xdb;
|
||
|
chip_rev_id_low_a = 0x40;
|
||
|
chip_rev_id_low_b = 0x40;
|
||
|
} else if (p->cur_firmware_id == DBMDX_FIRMWARE_ID_DBMD6) {
|
||
|
idr_read_cmd[2] = 0x74;
|
||
|
chip_rev_id_high = 0xdb;
|
||
|
chip_rev_id_low_a = 0x60;
|
||
|
chip_rev_id_low_b = 0x60;
|
||
|
} else {
|
||
|
idr_read_cmd[2] = 0x74;
|
||
|
chip_rev_id_high = 0xdb;
|
||
|
chip_rev_id_low_a = 0x80;
|
||
|
chip_rev_id_low_b = 0x80;
|
||
|
}
|
||
|
|
||
|
ret = send_spi_data(p, idr_read_cmd, 6);
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev, "%s: idr_read_cmd ret = %d\n",
|
||
|
__func__, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_BOOT,
|
||
|
DBMDX_USLEEP_SPI_VA_CMD_AFTER_BOOT + 1000);
|
||
|
|
||
|
ret = read_spi_data(p, (void *)idr_read_result, 7);
|
||
|
|
||
|
if (ret < 0) {
|
||
|
dev_err(spi_p->dev, "%s: could not idr register data\n",
|
||
|
__func__);
|
||
|
return -EIO;
|
||
|
}
|
||
|
/* Verify answer */
|
||
|
if ((idr_read_result[1] != idr_read_cmd[0]) ||
|
||
|
(idr_read_result[2] != idr_read_cmd[1]) ||
|
||
|
(idr_read_result[5] != 0x00) ||
|
||
|
(idr_read_result[6] != 0x00)) {
|
||
|
dev_err(spi_p->dev, "%s: Wrong IDR resp: %x:%x:%x:%x:%x:%x\n",
|
||
|
__func__,
|
||
|
idr_read_result[1],
|
||
|
idr_read_result[2],
|
||
|
idr_read_result[3],
|
||
|
idr_read_result[4],
|
||
|
idr_read_result[5],
|
||
|
idr_read_result[6]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
recv_chip_rev_id_high = idr_read_result[4];
|
||
|
recv_chip_rev_id_low = idr_read_result[3];
|
||
|
|
||
|
if ((recv_chip_rev_id_high != chip_rev_id_high) ||
|
||
|
((recv_chip_rev_id_low != chip_rev_id_low_a) &&
|
||
|
(recv_chip_rev_id_low != chip_rev_id_low_b))) {
|
||
|
|
||
|
dev_err(spi_p->dev,
|
||
|
"%s: Wrong chip ID: Received 0x%2x%2x Expected: 0x%2x%2x | 0x%2x%2x\n",
|
||
|
__func__,
|
||
|
recv_chip_rev_id_high,
|
||
|
recv_chip_rev_id_low,
|
||
|
chip_rev_id_high,
|
||
|
chip_rev_id_low_a,
|
||
|
chip_rev_id_high,
|
||
|
chip_rev_id_low_b);
|
||
|
return -EILSEQ;
|
||
|
}
|
||
|
|
||
|
dev_info(spi_p->dev,
|
||
|
"%s: Chip ID was successfully verified: 0x%2x%2x\n",
|
||
|
__func__,
|
||
|
recv_chip_rev_id_high,
|
||
|
recv_chip_rev_id_low);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int spi_can_boot(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int spi_prepare_boot(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
int ret = 0;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
|
||
|
ret = spi_set_speed(p, DBMDX_VA_SPEED_MAX);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int spi_finish_boot(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
int ret = 0;
|
||
|
|
||
|
ret = spi_set_speed(p, DBMDX_VA_SPEED_NORMAL);
|
||
|
if (ret < 0)
|
||
|
dev_err(spi_p->dev, "%s:failed %x\n", __func__, ret);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int spi_dump_state(struct chip_interface *chip, char *buf)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)chip->pdata;
|
||
|
int off = 0;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
|
||
|
off += snprintf(buf + off, PAGE_SIZE - off,
|
||
|
"\t===SPI Interface Dump====\n");
|
||
|
off += snprintf(buf + off, PAGE_SIZE - off,
|
||
|
"\tSPI Write Chunk Size:\t\t%d\n",
|
||
|
spi_p->pdata->write_chunk_size);
|
||
|
off += snprintf(buf + off, PAGE_SIZE - off,
|
||
|
"\tSPI Read Chunk Size:\t\t%d\n",
|
||
|
spi_p->pdata->read_chunk_size);
|
||
|
|
||
|
off += snprintf(buf + off, PAGE_SIZE - off,
|
||
|
"\tSPI DMA Min Buffer Size:\t\t%d\n",
|
||
|
spi_p->pdata->dma_min_buffer_size);
|
||
|
|
||
|
off += snprintf(buf + off, PAGE_SIZE - off,
|
||
|
"\tInterface resumed:\t%s\n",
|
||
|
spi_p->interface_enabled ? "ON" : "OFF");
|
||
|
|
||
|
return off;
|
||
|
}
|
||
|
|
||
|
static int spi_set_va_firmware_ready(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int spi_set_vqe_firmware_ready(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void spi_transport_enable(struct dbmdx_private *p, bool enable)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
int ret = 0;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s (%s)\n", __func__, enable ? "ON" : "OFF");
|
||
|
|
||
|
if (enable) {
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_PM_WAKELOCKS)
|
||
|
#if IS_ENABLED(DBMDX_USE_EXPIRABLE_WAKELOCK)
|
||
|
if (spi_p->ps_nosuspend_wl)
|
||
|
__pm_wakeup_event(spi_p->ps_nosuspend_wl,
|
||
|
DBMDX_WAKELOCK_TIMEOUT_MS);
|
||
|
#else
|
||
|
if (spi_p->ps_nosuspend_wl)
|
||
|
__pm_stay_awake(spi_p->ps_nosuspend_wl);
|
||
|
#endif
|
||
|
#endif
|
||
|
ret = wait_event_interruptible(dbmdx_wq,
|
||
|
spi_p->interface_enabled);
|
||
|
|
||
|
if (ret)
|
||
|
dev_dbg(spi_p->dev,
|
||
|
"%s, waiting for interface was interrupted",
|
||
|
__func__);
|
||
|
else
|
||
|
dev_dbg(spi_p->dev, "%s, interface is active\n",
|
||
|
__func__);
|
||
|
}
|
||
|
|
||
|
if (enable) {
|
||
|
p->wakeup_set(p);
|
||
|
if (p->asleep)
|
||
|
msleep(DBMDX_MSLEEP_SPI_WAKEUP);
|
||
|
} else {
|
||
|
#if IS_ENABLED(CONFIG_PM_WAKELOCKS)
|
||
|
if (spi_p->ps_nosuspend_wl)
|
||
|
__pm_relax(spi_p->ps_nosuspend_wl);
|
||
|
#endif
|
||
|
p->wakeup_release(p);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void spi_resume(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
|
||
|
spi_interface_resume(spi_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
void spi_interface_resume(struct dbmdx_spi_private *spi_p)
|
||
|
{
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
|
||
|
spi_p->interface_enabled = 1;
|
||
|
wake_up_interruptible(&dbmdx_wq);
|
||
|
}
|
||
|
|
||
|
static void spi_suspend(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
|
||
|
spi_interface_suspend(spi_p);
|
||
|
}
|
||
|
|
||
|
|
||
|
void spi_interface_suspend(struct dbmdx_spi_private *spi_p)
|
||
|
{
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
|
||
|
spi_p->interface_enabled = 0;
|
||
|
}
|
||
|
|
||
|
static int spi_prepare_buffering(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#define DBMDX_AUDIO_DEBUG_EXT 0
|
||
|
#define DBMDX_SAMPLE_CHUNK_VERIFICATION_FLAG 0xdbd0
|
||
|
|
||
|
static int spi_read_audio_data(struct dbmdx_private *p,
|
||
|
void *buf,
|
||
|
size_t samples,
|
||
|
bool to_read_metadata,
|
||
|
size_t *available_samples,
|
||
|
size_t *data_offset)
|
||
|
{
|
||
|
size_t bytes_to_read;
|
||
|
int ret;
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
|
||
|
ret = send_spi_cmd_va(p,
|
||
|
DBMDX_VA_READ_AUDIO_BUFFER | samples, NULL);
|
||
|
|
||
|
if (ret) {
|
||
|
dev_err(p->dev, "%s: failed to request %zu audio samples\n",
|
||
|
__func__, samples);
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
*available_samples = 0;
|
||
|
|
||
|
if (to_read_metadata)
|
||
|
*data_offset = 8;
|
||
|
else
|
||
|
*data_offset = 2;
|
||
|
|
||
|
bytes_to_read = samples * 8 * p->bytes_per_sample + *data_offset;
|
||
|
|
||
|
ret = read_spi_data(p, buf, bytes_to_read);
|
||
|
|
||
|
if (ret != bytes_to_read) {
|
||
|
dev_err(p->dev,
|
||
|
"%s: read audio failed, %zu bytes to read, res(%d)\n",
|
||
|
__func__,
|
||
|
bytes_to_read,
|
||
|
ret);
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
/* Word #4 contains current number of available samples */
|
||
|
if (to_read_metadata) {
|
||
|
u16 verif_flag;
|
||
|
*available_samples = (size_t)(((u16 *)buf)[3]);
|
||
|
verif_flag = (u16)(((u16 *)buf)[2]);
|
||
|
|
||
|
#if DBMDX_AUDIO_DEBUG_EXT
|
||
|
dev_err(spi_p->dev, "%s: %x:%x:%x:%x:%x:%x:%x:%x\n",
|
||
|
__func__,
|
||
|
((u8 *)buf)[0],
|
||
|
((u8 *)buf)[1],
|
||
|
((u8 *)buf)[2],
|
||
|
((u8 *)buf)[3],
|
||
|
((u8 *)buf)[4],
|
||
|
((u8 *)buf)[5],
|
||
|
((u8 *)buf)[6],
|
||
|
((u8 *)buf)[7]);
|
||
|
#endif
|
||
|
|
||
|
if (verif_flag != DBMDX_SAMPLE_CHUNK_VERIFICATION_FLAG) {
|
||
|
|
||
|
*available_samples = 0;
|
||
|
|
||
|
dev_err(p->dev,
|
||
|
"%s: Flag verificaiton failed %x:%x\n",
|
||
|
__func__,
|
||
|
((u8 *)buf)[4],
|
||
|
((u8 *)buf)[5]);
|
||
|
|
||
|
ret = -1;
|
||
|
goto out;
|
||
|
}
|
||
|
} else
|
||
|
*available_samples = samples;
|
||
|
|
||
|
ret = samples;
|
||
|
|
||
|
/* FW performes SPI reset after each chunk transaction
|
||
|
* Thus delay is required
|
||
|
*/
|
||
|
usleep_range(DBMDX_USLEEP_SPI_AFTER_CHUNK_READ,
|
||
|
DBMDX_USLEEP_SPI_AFTER_CHUNK_READ + 100);
|
||
|
out:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int spi_finish_buffering(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
int ret = 0;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int spi_prepare_amodel_loading(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int spi_finish_amodel_loading(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s\n", __func__);
|
||
|
/* do the same as for finishing buffering */
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static u32 spi_get_read_chunk_size(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s SPI read chunk is %u\n",
|
||
|
__func__, spi_p->pdata->read_chunk_size);
|
||
|
|
||
|
return spi_p->pdata->read_chunk_size;
|
||
|
}
|
||
|
|
||
|
static u32 spi_get_write_chunk_size(struct dbmdx_private *p)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s SPI write chunk is %u\n",
|
||
|
__func__, spi_p->pdata->write_chunk_size);
|
||
|
|
||
|
return spi_p->pdata->write_chunk_size;
|
||
|
}
|
||
|
|
||
|
static int spi_set_read_chunk_size(struct dbmdx_private *p, u32 size)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
if (size > MAX_SPI_READ_CHUNK_SIZE) {
|
||
|
dev_err(spi_p->dev,
|
||
|
"%s Error setting SPI read chunk. Max chunk size: %u\n",
|
||
|
__func__, MAX_SPI_READ_CHUNK_SIZE);
|
||
|
return -EINVAL;
|
||
|
} else if ((size % 4) != 0) {
|
||
|
dev_err(spi_p->dev,
|
||
|
"%s Error SPI read chunk should be multiply of 4\n",
|
||
|
__func__);
|
||
|
return -EINVAL;
|
||
|
} else if (size == 0)
|
||
|
spi_p->pdata->read_chunk_size = DEFAULT_SPI_READ_CHUNK_SIZE;
|
||
|
else
|
||
|
spi_p->pdata->read_chunk_size = size;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s SPI read chunk was set to %u\n",
|
||
|
__func__, spi_p->pdata->read_chunk_size);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int spi_set_write_chunk_size(struct dbmdx_private *p, u32 size)
|
||
|
{
|
||
|
struct dbmdx_spi_private *spi_p =
|
||
|
(struct dbmdx_spi_private *)p->chip->pdata;
|
||
|
|
||
|
if (size > MAX_SPI_WRITE_CHUNK_SIZE) {
|
||
|
dev_err(spi_p->dev,
|
||
|
"%s Error setting SPI write chunk. Max chunk size: %u\n",
|
||
|
__func__, MAX_SPI_WRITE_CHUNK_SIZE);
|
||
|
return -EINVAL;
|
||
|
} else if ((size % 4) != 0) {
|
||
|
dev_err(spi_p->dev,
|
||
|
"%s Error SPI write chunk should be multiply of 4\n",
|
||
|
__func__);
|
||
|
return -EINVAL;
|
||
|
} else if (size == 0)
|
||
|
spi_p->pdata->write_chunk_size = DEFAULT_SPI_WRITE_CHUNK_SIZE;
|
||
|
else
|
||
|
spi_p->pdata->write_chunk_size = size;
|
||
|
|
||
|
dev_dbg(spi_p->dev, "%s SPI write chunk was set to %u\n",
|
||
|
__func__, spi_p->pdata->write_chunk_size);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int spi_common_probe(struct spi_device *client)
|
||
|
{
|
||
|
#if IS_ENABLED(CONFIG_OF)
|
||
|
struct device_node *np;
|
||
|
#endif
|
||
|
int ret;
|
||
|
struct dbmdx_spi_private *p;
|
||
|
struct dbmdx_spi_data *pdata;
|
||
|
|
||
|
dev_dbg(&client->dev, "%s(): dbmdx\n", __func__);
|
||
|
|
||
|
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||
|
if (p == NULL)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
p->client = client;
|
||
|
p->dev = &client->dev;
|
||
|
|
||
|
p->chip.pdata = p;
|
||
|
#if IS_ENABLED(CONFIG_OF)
|
||
|
np = p->dev->of_node;
|
||
|
if (!np) {
|
||
|
dev_err(p->dev, "%s: no devicetree entry\n", __func__);
|
||
|
ret = -EINVAL;
|
||
|
goto out_err_kfree;
|
||
|
}
|
||
|
|
||
|
pdata = kzalloc(sizeof(struct dbmdx_spi_data), GFP_KERNEL);
|
||
|
if (!pdata) {
|
||
|
ret = -ENOMEM;
|
||
|
goto out_err_kfree;
|
||
|
}
|
||
|
#else
|
||
|
pdata = dev_get_platdata(&client->dev);
|
||
|
if (pdata == NULL) {
|
||
|
dev_err(p->dev, "%s: dbmdx, no platform data found\n",
|
||
|
__func__);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_OF)
|
||
|
ret = of_property_read_u32(np, "spi-max-frequency",
|
||
|
&(pdata->spi_speed));
|
||
|
if (ret && ret != -EINVAL)
|
||
|
pdata->spi_speed = 2000000;
|
||
|
#endif
|
||
|
dev_dbg(p->dev, "%s: spi speed is %u\n", __func__, pdata->spi_speed);
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_OF)
|
||
|
ret = of_property_read_u32(np, "read-chunk-size",
|
||
|
&pdata->read_chunk_size);
|
||
|
if (ret != 0) {
|
||
|
/*
|
||
|
* read-chunk-size not set, set it to default
|
||
|
*/
|
||
|
pdata->read_chunk_size = DEFAULT_SPI_READ_CHUNK_SIZE;
|
||
|
dev_info(p->dev,
|
||
|
"%s: Setting spi read chunk to default val: %u bytes\n",
|
||
|
__func__, pdata->read_chunk_size);
|
||
|
}
|
||
|
#endif
|
||
|
if (pdata->read_chunk_size % 4 != 0)
|
||
|
pdata->read_chunk_size += (4 - (pdata->read_chunk_size % 4));
|
||
|
|
||
|
if (pdata->read_chunk_size > MAX_SPI_READ_CHUNK_SIZE)
|
||
|
pdata->read_chunk_size = MAX_SPI_READ_CHUNK_SIZE;
|
||
|
if (pdata->read_chunk_size == 0)
|
||
|
pdata->read_chunk_size = DEFAULT_SPI_READ_CHUNK_SIZE;
|
||
|
|
||
|
dev_info(p->dev, "%s: Setting spi read chunk to %u bytes\n",
|
||
|
__func__, pdata->read_chunk_size);
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_OF)
|
||
|
ret = of_property_read_u32(np, "write-chunk-size",
|
||
|
&pdata->write_chunk_size);
|
||
|
if (ret != 0) {
|
||
|
/*
|
||
|
* write-chunk-size not set, set it to default
|
||
|
*/
|
||
|
pdata->write_chunk_size = DEFAULT_SPI_WRITE_CHUNK_SIZE;
|
||
|
dev_info(p->dev,
|
||
|
"%s: Setting spi write chunk to default val: %u bytes\n",
|
||
|
__func__, pdata->write_chunk_size);
|
||
|
}
|
||
|
#endif
|
||
|
if (pdata->write_chunk_size % 4 != 0)
|
||
|
pdata->write_chunk_size += (4 - (pdata->write_chunk_size % 4));
|
||
|
|
||
|
if (pdata->write_chunk_size > MAX_SPI_WRITE_CHUNK_SIZE)
|
||
|
pdata->write_chunk_size = MAX_SPI_WRITE_CHUNK_SIZE;
|
||
|
if (pdata->write_chunk_size == 0)
|
||
|
pdata->write_chunk_size = DEFAULT_SPI_WRITE_CHUNK_SIZE;
|
||
|
|
||
|
dev_info(p->dev, "%s: Setting spi write chunk to %u bytes\n",
|
||
|
__func__, pdata->write_chunk_size);
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_OF)
|
||
|
ret = of_property_read_u32(np, "dma_min_buffer_size",
|
||
|
&pdata->dma_min_buffer_size);
|
||
|
if (ret != 0) {
|
||
|
/*
|
||
|
* read-chunk-size not set, set it to default
|
||
|
*/
|
||
|
pdata->dma_min_buffer_size = 0;
|
||
|
dev_info(p->dev,
|
||
|
"%s: Setting Min DMA Cmd Size to default: %u bytes\n",
|
||
|
__func__, pdata->dma_min_buffer_size);
|
||
|
}
|
||
|
#endif
|
||
|
if (pdata->dma_min_buffer_size > DBMDX_VA_SPI_CMD_PADDED_SIZE)
|
||
|
pdata->dma_min_buffer_size = DBMDX_VA_SPI_CMD_PADDED_SIZE;
|
||
|
if (pdata->dma_min_buffer_size < 7 && pdata->dma_min_buffer_size > 0)
|
||
|
pdata->dma_min_buffer_size = 7;
|
||
|
|
||
|
dev_info(p->dev, "%s: Setting Min DMA Cmd Size to default: %u bytes\n",
|
||
|
__func__, pdata->dma_min_buffer_size);
|
||
|
|
||
|
p->pdata = pdata;
|
||
|
|
||
|
pdata->send = kmalloc(MAX_SPI_WRITE_CHUNK_SIZE, GFP_KERNEL | GFP_DMA);
|
||
|
if (!pdata->send)
|
||
|
goto out_err_mem_free;
|
||
|
|
||
|
pdata->recv = kmalloc(MAX_SPI_READ_CHUNK_SIZE, GFP_KERNEL | GFP_DMA);
|
||
|
if (!pdata->recv)
|
||
|
goto out_err_mem_free1;
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_PM_WAKELOCKS)
|
||
|
p->ps_nosuspend_wl =
|
||
|
wakeup_source_create("dbmdx_nosuspend_wakelock_spi");
|
||
|
|
||
|
if (p->ps_nosuspend_wl)
|
||
|
wakeup_source_add(p->ps_nosuspend_wl);
|
||
|
else
|
||
|
dev_err(p->dev,
|
||
|
"%s: Error creating WS: dbmdx_nosuspend_wakelock_spi\n",
|
||
|
__func__);
|
||
|
#endif
|
||
|
|
||
|
/* fill in chip interface functions */
|
||
|
p->chip.can_boot = spi_can_boot;
|
||
|
p->chip.prepare_boot = spi_prepare_boot;
|
||
|
p->chip.finish_boot = spi_finish_boot;
|
||
|
p->chip.dump = spi_dump_state;
|
||
|
p->chip.set_va_firmware_ready = spi_set_va_firmware_ready;
|
||
|
p->chip.set_vqe_firmware_ready = spi_set_vqe_firmware_ready;
|
||
|
p->chip.transport_enable = spi_transport_enable;
|
||
|
p->chip.read = read_spi_data;
|
||
|
p->chip.write = send_spi_data;
|
||
|
p->chip.send_cmd_vqe = send_spi_cmd_vqe;
|
||
|
p->chip.send_cmd_va = send_spi_cmd_va;
|
||
|
p->chip.send_cmd_boot = send_spi_cmd_boot;
|
||
|
p->chip.verify_boot_checksum = spi_verify_boot_checksum;
|
||
|
p->chip.prepare_buffering = spi_prepare_buffering;
|
||
|
p->chip.read_audio_data = spi_read_audio_data;
|
||
|
p->chip.finish_buffering = spi_finish_buffering;
|
||
|
p->chip.prepare_amodel_loading = spi_prepare_amodel_loading;
|
||
|
p->chip.finish_amodel_loading = spi_finish_amodel_loading;
|
||
|
p->chip.get_write_chunk_size = spi_get_write_chunk_size;
|
||
|
p->chip.get_read_chunk_size = spi_get_read_chunk_size;
|
||
|
p->chip.set_write_chunk_size = spi_set_write_chunk_size;
|
||
|
p->chip.set_read_chunk_size = spi_set_read_chunk_size;
|
||
|
p->chip.resume = spi_resume;
|
||
|
p->chip.suspend = spi_suspend;
|
||
|
|
||
|
p->interface_enabled = 1;
|
||
|
|
||
|
spi_set_drvdata(client, &p->chip);
|
||
|
|
||
|
dev_info(&client->dev, "%s: successfully probed\n", __func__);
|
||
|
ret = 0;
|
||
|
goto out;
|
||
|
out_err_mem_free1:
|
||
|
kfree(pdata->send);
|
||
|
#if IS_ENABLED(CONFIG_OF)
|
||
|
out_err_kfree:
|
||
|
#endif
|
||
|
out_err_mem_free:
|
||
|
kfree(p);
|
||
|
out:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int spi_common_remove(struct spi_device *client)
|
||
|
{
|
||
|
struct chip_interface *ci = spi_get_drvdata(client);
|
||
|
struct dbmdx_spi_private *p = (struct dbmdx_spi_private *)ci->pdata;
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_PM_WAKELOCKS)
|
||
|
if (p->ps_nosuspend_wl) {
|
||
|
wakeup_source_remove(p->ps_nosuspend_wl);
|
||
|
wakeup_source_destroy(p->ps_nosuspend_wl);
|
||
|
}
|
||
|
#endif
|
||
|
kfree(p->pdata->send);
|
||
|
kfree(p->pdata->recv);
|
||
|
kfree(p);
|
||
|
|
||
|
spi_set_drvdata(client, NULL);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|