thunderbolt: Split NVM read/write generic functions out from usb4.c

We do this for Thunderbolt 2/3 devices through DMA port, USB4 devices
and retimers pretty much the same way. Only the actual block read/write
is different. For this reason split out the NVM read/write functions
from usb4.c to nvm.c and make USB4 device code call these when needed.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
This commit is contained in:
Mika Westerberg 2021-04-01 16:54:15 +03:00
parent fe6f6f9591
commit 9b38303777
3 changed files with 124 additions and 92 deletions

View File

@ -164,6 +164,101 @@ void tb_nvm_free(struct tb_nvm *nvm)
kfree(nvm);
}
/**
* tb_nvm_read_data() - Read data from NVM
* @address: Start address on the flash
* @buf: Buffer where the read data is copied
* @size: Size of the buffer in bytes
* @retries: Number of retries if block read fails
* @read_block: Function that reads block from the flash
* @read_block_data: Data passsed to @read_block
*
* This is a generic function that reads data from NVM or NVM like
* device.
*
* Returns %0 on success and negative errno otherwise.
*/
int tb_nvm_read_data(unsigned int address, void *buf, size_t size,
unsigned int retries, read_block_fn read_block,
void *read_block_data)
{
do {
unsigned int dwaddress, dwords, offset;
u8 data[NVM_DATA_DWORDS * 4];
size_t nbytes;
int ret;
offset = address & 3;
nbytes = min_t(size_t, size + offset, NVM_DATA_DWORDS * 4);
dwaddress = address / 4;
dwords = ALIGN(nbytes, 4) / 4;
ret = read_block(read_block_data, dwaddress, data, dwords);
if (ret) {
if (ret != -ENODEV && retries--)
continue;
return ret;
}
nbytes -= offset;
memcpy(buf, data + offset, nbytes);
size -= nbytes;
address += nbytes;
buf += nbytes;
} while (size > 0);
return 0;
}
/**
* tb_nvm_write_data() - Write data to NVM
* @address: Start address on the flash
* @buf: Buffer where the data is copied from
* @size: Size of the buffer in bytes
* @retries: Number of retries if the block write fails
* @write_block: Function that writes block to the flash
* @write_block_data: Data passwd to @write_block
*
* This is generic function that writes data to NVM or NVM like device.
*
* Returns %0 on success and negative errno otherwise.
*/
int tb_nvm_write_data(unsigned int address, const void *buf, size_t size,
unsigned int retries, write_block_fn write_block,
void *write_block_data)
{
do {
unsigned int offset, dwaddress;
u8 data[NVM_DATA_DWORDS * 4];
size_t nbytes;
int ret;
offset = address & 3;
nbytes = min_t(u32, size + offset, NVM_DATA_DWORDS * 4);
memcpy(data + offset, buf, nbytes);
dwaddress = address / 4;
ret = write_block(write_block_data, dwaddress, data, nbytes / 4);
if (ret) {
if (ret == -ETIMEDOUT) {
if (retries--)
continue;
ret = -EIO;
}
return ret;
}
size -= nbytes;
address += nbytes;
buf += nbytes;
} while (size > 0);
return 0;
}
void tb_nvm_exit(void)
{
ida_destroy(&nvm_ida);

View File

@ -20,6 +20,7 @@
#define NVM_MIN_SIZE SZ_32K
#define NVM_MAX_SIZE SZ_512K
#define NVM_DATA_DWORDS 16
/* Intel specific NVM offsets */
#define NVM_DEVID 0x05
@ -674,6 +675,16 @@ int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size,
void tb_nvm_free(struct tb_nvm *nvm);
void tb_nvm_exit(void);
typedef int (*read_block_fn)(void *, unsigned int, void *, size_t);
typedef int (*write_block_fn)(void *, unsigned int, const void *, size_t);
int tb_nvm_read_data(unsigned int address, void *buf, size_t size,
unsigned int retries, read_block_fn read_block,
void *read_block_data);
int tb_nvm_write_data(unsigned int address, const void *buf, size_t size,
unsigned int retries, write_block_fn write_next_block,
void *write_block_data);
struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent,
u64 route);
struct tb_switch *tb_switch_alloc_safe_mode(struct tb *tb,

View File

@ -13,7 +13,6 @@
#include "sb_regs.h"
#include "tb.h"
#define USB4_DATA_DWORDS 16
#define USB4_DATA_RETRIES 3
enum usb4_sb_target {
@ -37,9 +36,6 @@ enum usb4_sb_target {
#define USB4_NVM_SECTOR_SIZE_MASK GENMASK(23, 0)
typedef int (*read_block_fn)(void *, unsigned int, void *, size_t);
typedef int (*write_block_fn)(void *, const void *, size_t);
static int usb4_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit,
u32 value, int timeout_msec)
{
@ -62,76 +58,6 @@ static int usb4_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit,
return -ETIMEDOUT;
}
static int usb4_do_read_data(u16 address, void *buf, size_t size,
read_block_fn read_block, void *read_block_data)
{
unsigned int retries = USB4_DATA_RETRIES;
unsigned int offset;
do {
unsigned int dwaddress, dwords;
u8 data[USB4_DATA_DWORDS * 4];
size_t nbytes;
int ret;
offset = address & 3;
nbytes = min_t(size_t, size + offset, USB4_DATA_DWORDS * 4);
dwaddress = address / 4;
dwords = ALIGN(nbytes, 4) / 4;
ret = read_block(read_block_data, dwaddress, data, dwords);
if (ret) {
if (ret != -ENODEV && retries--)
continue;
return ret;
}
nbytes -= offset;
memcpy(buf, data + offset, nbytes);
size -= nbytes;
address += nbytes;
buf += nbytes;
} while (size > 0);
return 0;
}
static int usb4_do_write_data(unsigned int address, const void *buf, size_t size,
write_block_fn write_next_block, void *write_block_data)
{
unsigned int retries = USB4_DATA_RETRIES;
unsigned int offset;
offset = address & 3;
address = address & ~3;
do {
u32 nbytes = min_t(u32, size, USB4_DATA_DWORDS * 4);
u8 data[USB4_DATA_DWORDS * 4];
int ret;
memcpy(data + offset, buf, nbytes);
ret = write_next_block(write_block_data, data, nbytes / 4);
if (ret) {
if (ret == -ETIMEDOUT) {
if (retries--)
continue;
ret = -EIO;
}
return ret;
}
size -= nbytes;
address += nbytes;
buf += nbytes;
} while (size > 0);
return 0;
}
static int usb4_native_switch_op(struct tb_switch *sw, u16 opcode,
u32 *metadata, u8 *status,
const void *tx_data, size_t tx_dwords,
@ -193,7 +119,7 @@ static int __usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata,
{
const struct tb_cm_ops *cm_ops = sw->tb->cm_ops;
if (tx_dwords > USB4_DATA_DWORDS || rx_dwords > USB4_DATA_DWORDS)
if (tx_dwords > NVM_DATA_DWORDS || rx_dwords > NVM_DATA_DWORDS)
return -EINVAL;
/*
@ -414,7 +340,7 @@ static int usb4_switch_drom_read_block(void *data,
int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf,
size_t size)
{
return usb4_do_read_data(address, buf, size,
return tb_nvm_read_data(address, buf, size, USB4_DATA_RETRIES,
usb4_switch_drom_read_block, sw);
}
@ -595,7 +521,7 @@ static int usb4_switch_nvm_read_block(void *data,
int usb4_switch_nvm_read(struct tb_switch *sw, unsigned int address, void *buf,
size_t size)
{
return usb4_do_read_data(address, buf, size,
return tb_nvm_read_data(address, buf, size, USB4_DATA_RETRIES,
usb4_switch_nvm_read_block, sw);
}
@ -618,8 +544,8 @@ static int usb4_switch_nvm_set_offset(struct tb_switch *sw,
return status ? -EIO : 0;
}
static int usb4_switch_nvm_write_next_block(void *data, const void *buf,
size_t dwords)
static int usb4_switch_nvm_write_next_block(void *data, unsigned int dwaddress,
const void *buf, size_t dwords)
{
struct tb_switch *sw = data;
u8 status;
@ -652,7 +578,7 @@ int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address,
if (ret)
return ret;
return usb4_do_write_data(address, buf, size,
return tb_nvm_write_data(address, buf, size, USB4_DATA_RETRIES,
usb4_switch_nvm_write_next_block, sw);
}
@ -1029,7 +955,7 @@ static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit,
static int usb4_port_read_data(struct tb_port *port, void *data, size_t dwords)
{
if (dwords > USB4_DATA_DWORDS)
if (dwords > NVM_DATA_DWORDS)
return -EINVAL;
return tb_port_read(port, data, TB_CFG_PORT, port->cap_usb4 + PORT_CS_2,
@ -1039,7 +965,7 @@ static int usb4_port_read_data(struct tb_port *port, void *data, size_t dwords)
static int usb4_port_write_data(struct tb_port *port, const void *data,
size_t dwords)
{
if (dwords > USB4_DATA_DWORDS)
if (dwords > NVM_DATA_DWORDS)
return -EINVAL;
return tb_port_write(port, data, TB_CFG_PORT, port->cap_usb4 + PORT_CS_2,
@ -1316,8 +1242,8 @@ struct retimer_info {
u8 index;
};
static int usb4_port_retimer_nvm_write_next_block(void *data, const void *buf,
size_t dwords)
static int usb4_port_retimer_nvm_write_next_block(void *data,
unsigned int dwaddress, const void *buf, size_t dwords)
{
const struct retimer_info *info = data;
@ -1357,7 +1283,7 @@ int usb4_port_retimer_nvm_write(struct tb_port *port, u8 index, unsigned int add
if (ret)
return ret;
return usb4_do_write_data(address, buf, size,
return tb_nvm_write_data(address, buf, size, USB4_DATA_RETRIES,
usb4_port_retimer_nvm_write_next_block, &info);
}
@ -1442,7 +1368,7 @@ static int usb4_port_retimer_nvm_read_block(void *data, unsigned int dwaddress,
int ret;
metadata = dwaddress << USB4_NVM_READ_OFFSET_SHIFT;
if (dwords < USB4_DATA_DWORDS)
if (dwords < NVM_DATA_DWORDS)
metadata |= dwords << USB4_NVM_READ_LENGTH_SHIFT;
ret = usb4_port_retimer_write(port, index, USB4_SB_METADATA, &metadata,
@ -1475,7 +1401,7 @@ int usb4_port_retimer_nvm_read(struct tb_port *port, u8 index,
{
struct retimer_info info = { .port = port, .index = index };
return usb4_do_read_data(address, buf, size,
return tb_nvm_read_data(address, buf, size, USB4_DATA_RETRIES,
usb4_port_retimer_nvm_read_block, &info);
}