mirror of https://gitee.com/openkylin/linux.git
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:
parent
fe6f6f9591
commit
9b38303777
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,8 +340,8 @@ 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,
|
||||
usb4_switch_drom_read_block, sw);
|
||||
return tb_nvm_read_data(address, buf, size, USB4_DATA_RETRIES,
|
||||
usb4_switch_drom_read_block, sw);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -595,8 +521,8 @@ 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,
|
||||
usb4_switch_nvm_read_block, sw);
|
||||
return tb_nvm_read_data(address, buf, size, USB4_DATA_RETRIES,
|
||||
usb4_switch_nvm_read_block, sw);
|
||||
}
|
||||
|
||||
static int usb4_switch_nvm_set_offset(struct tb_switch *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,8 +578,8 @@ int usb4_switch_nvm_write(struct tb_switch *sw, unsigned int address,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return usb4_do_write_data(address, buf, size,
|
||||
usb4_switch_nvm_write_next_block, sw);
|
||||
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,8 +1283,8 @@ 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,
|
||||
usb4_port_retimer_nvm_write_next_block, &info);
|
||||
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,8 +1401,8 @@ 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,
|
||||
usb4_port_retimer_nvm_read_block, &info);
|
||||
return tb_nvm_read_data(address, buf, size, USB4_DATA_RETRIES,
|
||||
usb4_port_retimer_nvm_read_block, &info);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue