Commit aef9c693 authored by Szuying Chen's avatar Szuying Chen Committed by Mika Westerberg

thunderbolt: Move vendor specific NVM handling into nvm.c

As there will be more USB4 devices that support NVM firmware upgrade from
various vendors, it makes sense to split out the Intel specific NVM
image handling from the generic code. This moves the Intel specific NVM
handling into a new structure that will be matched by the device type
and the vendor ID. Do this for both routers and retimers.

This makes it easier to extend the NVM support to cover new vendors and
NVM image formats in the future.
Signed-off-by: default avatarSzuying Chen <Chloe_Chen@asmedia.com.tw>
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
parent 8b02b2da
This diff is collapsed.
......@@ -73,34 +73,23 @@ static int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes)
static int tb_retimer_nvm_add(struct tb_retimer *rt)
{
struct tb_nvm *nvm;
u32 val, nvm_size;
int ret;
nvm = tb_nvm_alloc(&rt->dev);
if (IS_ERR(nvm))
return PTR_ERR(nvm);
ret = usb4_port_retimer_nvm_read(rt->port, rt->index, NVM_VERSION, &val,
sizeof(val));
if (ret)
if (IS_ERR(nvm)) {
ret = PTR_ERR(nvm) == -EOPNOTSUPP ? 0 : PTR_ERR(nvm);
goto err_nvm;
}
nvm->major = (val >> 16) & 0xff;
nvm->minor = (val >> 8) & 0xff;
ret = usb4_port_retimer_nvm_read(rt->port, rt->index, NVM_FLASH_SIZE,
&val, sizeof(val));
ret = tb_nvm_read_version(nvm);
if (ret)
goto err_nvm;
nvm_size = (SZ_1M << (val & 7)) / 8;
nvm_size = (nvm_size - SZ_16K) / 2;
ret = tb_nvm_add_active(nvm, nvm_size, nvm_read);
ret = tb_nvm_add_active(nvm, nvm_read);
if (ret)
goto err_nvm;
ret = tb_nvm_add_non_active(nvm, NVM_MAX_SIZE, nvm_write);
ret = tb_nvm_add_non_active(nvm, nvm_write);
if (ret)
goto err_nvm;
......@@ -108,59 +97,33 @@ static int tb_retimer_nvm_add(struct tb_retimer *rt)
return 0;
err_nvm:
tb_nvm_free(nvm);
dev_dbg(&rt->dev, "NVM upgrade disabled\n");
if (!IS_ERR(nvm))
tb_nvm_free(nvm);
return ret;
}
static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt)
{
unsigned int image_size, hdr_size;
const u8 *buf = rt->nvm->buf;
u16 ds_size, device;
unsigned int image_size;
const u8 *buf;
int ret;
image_size = rt->nvm->buf_data_size;
if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
return -EINVAL;
/*
* FARB pointer must point inside the image and must at least
* contain parts of the digital section we will be reading here.
*/
hdr_size = (*(u32 *)buf) & 0xffffff;
if (hdr_size + NVM_DEVID + 2 >= image_size)
return -EINVAL;
/* Digital section start should be aligned to 4k page */
if (!IS_ALIGNED(hdr_size, SZ_4K))
return -EINVAL;
/*
* Read digital section size and check that it also fits inside
* the image.
*/
ds_size = *(u16 *)(buf + hdr_size);
if (ds_size >= image_size)
return -EINVAL;
/*
* Make sure the device ID in the image matches the retimer
* hardware.
*/
device = *(u16 *)(buf + hdr_size + NVM_DEVID);
if (device != rt->device)
return -EINVAL;
ret = tb_nvm_validate(rt->nvm);
if (ret)
return ret;
/* Skip headers in the image */
buf += hdr_size;
image_size -= hdr_size;
buf = rt->nvm->buf_data_start;
image_size = rt->nvm->buf_data_size;
ret = usb4_port_retimer_nvm_write(rt->port, rt->index, 0, buf,
image_size);
if (!ret)
rt->nvm->flushed = true;
if (ret)
return ret;
return ret;
rt->nvm->flushed = true;
return 0;
}
static int tb_retimer_nvm_authenticate(struct tb_retimer *rt, bool auth_only)
......@@ -214,6 +177,8 @@ static ssize_t nvm_authenticate_show(struct device *dev,
if (!rt->nvm)
ret = -EAGAIN;
else if (rt->no_nvm_upgrade)
ret = -EOPNOTSUPP;
else
ret = sprintf(buf, "%#x\n", rt->auth_status);
......
......@@ -19,8 +19,6 @@
/* Switch NVM support */
#define NVM_CSS 0x10
struct nvm_auth_status {
struct list_head list;
uuid_t uuid;
......@@ -102,70 +100,30 @@ static void nvm_clear_auth_status(const struct tb_switch *sw)
static int nvm_validate_and_write(struct tb_switch *sw)
{
unsigned int image_size, hdr_size;
const u8 *buf = sw->nvm->buf;
u16 ds_size;
unsigned int image_size;
const u8 *buf;
int ret;
if (!buf)
return -EINVAL;
image_size = sw->nvm->buf_data_size;
if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
return -EINVAL;
/*
* FARB pointer must point inside the image and must at least
* contain parts of the digital section we will be reading here.
*/
hdr_size = (*(u32 *)buf) & 0xffffff;
if (hdr_size + NVM_DEVID + 2 >= image_size)
return -EINVAL;
/* Digital section start should be aligned to 4k page */
if (!IS_ALIGNED(hdr_size, SZ_4K))
return -EINVAL;
/*
* Read digital section size and check that it also fits inside
* the image.
*/
ds_size = *(u16 *)(buf + hdr_size);
if (ds_size >= image_size)
return -EINVAL;
if (!sw->safe_mode) {
u16 device_id;
ret = tb_nvm_validate(sw->nvm);
if (ret)
return ret;
/*
* Make sure the device ID in the image matches the one
* we read from the switch config space.
*/
device_id = *(u16 *)(buf + hdr_size + NVM_DEVID);
if (device_id != sw->config.device_id)
return -EINVAL;
if (sw->generation < 3) {
/* Write CSS headers first */
ret = dma_port_flash_write(sw->dma_port,
DMA_PORT_CSS_ADDRESS, buf + NVM_CSS,
DMA_PORT_CSS_MAX_SIZE);
if (ret)
return ret;
}
ret = tb_nvm_write_headers(sw->nvm);
if (ret)
return ret;
/* Skip headers in the image */
buf += hdr_size;
image_size -= hdr_size;
}
buf = sw->nvm->buf_data_start;
image_size = sw->nvm->buf_data_size;
if (tb_switch_is_usb4(sw))
ret = usb4_switch_nvm_write(sw, 0, buf, image_size);
else
ret = dma_port_flash_write(sw->dma_port, 0, buf, image_size);
if (!ret)
sw->nvm->flushed = true;
return ret;
if (ret)
return ret;
sw->nvm->flushed = true;
return 0;
}
static int nvm_authenticate_host_dma_port(struct tb_switch *sw)
......@@ -393,28 +351,20 @@ static int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes)
static int tb_switch_nvm_add(struct tb_switch *sw)
{
struct tb_nvm *nvm;
u32 val;
int ret;
if (!nvm_readable(sw))
return 0;
/*
* The NVM format of non-Intel hardware is not known so
* currently restrict NVM upgrade for Intel hardware. We may
* relax this in the future when we learn other NVM formats.
*/
if (sw->config.vendor_id != PCI_VENDOR_ID_INTEL &&
sw->config.vendor_id != 0x8087) {
dev_info(&sw->dev,
"NVM format of vendor %#x is not known, disabling NVM upgrade\n",
sw->config.vendor_id);
return 0;
nvm = tb_nvm_alloc(&sw->dev);
if (IS_ERR(nvm)) {
ret = PTR_ERR(nvm) == -EOPNOTSUPP ? 0 : PTR_ERR(nvm);
goto err_nvm;
}
nvm = tb_nvm_alloc(&sw->dev);
if (IS_ERR(nvm))
return PTR_ERR(nvm);
ret = tb_nvm_read_version(nvm);
if (ret)
goto err_nvm;
/*
* If the switch is in safe-mode the only accessible portion of
......@@ -422,30 +372,13 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
* write new functional NVM.
*/
if (!sw->safe_mode) {
u32 nvm_size, hdr_size;
ret = tb_switch_nvm_read(sw, NVM_FLASH_SIZE, &val, sizeof(val));
if (ret)
goto err_nvm;
hdr_size = sw->generation < 3 ? SZ_8K : SZ_16K;
nvm_size = (SZ_1M << (val & 7)) / 8;
nvm_size = (nvm_size - hdr_size) / 2;
ret = tb_switch_nvm_read(sw, NVM_VERSION, &val, sizeof(val));
if (ret)
goto err_nvm;
nvm->major = (val >> 16) & 0xff;
nvm->minor = (val >> 8) & 0xff;
ret = tb_nvm_add_active(nvm, nvm_size, nvm_read);
ret = tb_nvm_add_active(nvm, nvm_read);
if (ret)
goto err_nvm;
}
if (!sw->no_nvm_upgrade) {
ret = tb_nvm_add_non_active(nvm, NVM_MAX_SIZE, nvm_write);
ret = tb_nvm_add_non_active(nvm, nvm_write);
if (ret)
goto err_nvm;
}
......@@ -454,7 +387,11 @@ static int tb_switch_nvm_add(struct tb_switch *sw)
return 0;
err_nvm:
tb_nvm_free(nvm);
tb_sw_dbg(sw, "NVM upgrade disabled\n");
sw->no_nvm_upgrade = true;
if (!IS_ERR(nvm))
tb_nvm_free(nvm);
return ret;
}
......@@ -2003,6 +1940,11 @@ static ssize_t nvm_authenticate_sysfs(struct device *dev, const char *buf,
goto exit_rpm;
}
if (sw->no_nvm_upgrade) {
ret = -EOPNOTSUPP;
goto exit_unlock;
}
/* If NVMem devices are not yet added */
if (!sw->nvm) {
ret = -EAGAIN;
......
......@@ -23,11 +23,6 @@
#define NVM_MAX_SIZE SZ_512K
#define NVM_DATA_DWORDS 16
/* Intel specific NVM offsets */
#define NVM_DEVID 0x05
#define NVM_VERSION 0x08
#define NVM_FLASH_SIZE 0x45
/**
* struct tb_nvm - Structure holding NVM information
* @dev: Owner of the NVM
......@@ -35,13 +30,17 @@
* @minor: Minor version number of the active NVM portion
* @id: Identifier used with both NVM portions
* @active: Active portion NVMem device
* @active_size: Size in bytes of the active NVM
* @non_active: Non-active portion NVMem device
* @buf: Buffer where the NVM image is stored before it is written to
* the actual NVM flash device
* @buf_data_start: Where the actual image starts after skipping
* possible headers
* @buf_data_size: Number of bytes actually consumed by the new NVM
* image
* @authenticating: The device is authenticating the new NVM
* @flushed: The image has been flushed to the storage area
* @vops: Router vendor specific NVM operations (optional)
*
* The user of this structure needs to handle serialization of possible
* concurrent access.
......@@ -52,11 +51,14 @@ struct tb_nvm {
u32 minor;
int id;
struct nvmem_device *active;
size_t active_size;
struct nvmem_device *non_active;
void *buf;
void *buf_data_start;
size_t buf_data_size;
bool authenticating;
bool flushed;
const struct tb_nvm_vendor_ops *vops;
};
enum tb_nvm_write_ops {
......@@ -300,6 +302,7 @@ struct usb4_port {
* @device: Device ID of the retimer
* @port: Pointer to the lane 0 adapter
* @nvm: Pointer to the NVM if the retimer has one (%NULL otherwise)
* @no_nvm_upgrade: Prevent NVM upgrade of this retimer
* @auth_status: Status of last NVM authentication
*/
struct tb_retimer {
......@@ -310,6 +313,7 @@ struct tb_retimer {
u32 device;
struct tb_port *port;
struct tb_nvm *nvm;
bool no_nvm_upgrade;
u32 auth_status;
};
......@@ -741,11 +745,13 @@ static inline void tb_domain_put(struct tb *tb)
}
struct tb_nvm *tb_nvm_alloc(struct device *dev);
int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read);
int tb_nvm_read_version(struct tb_nvm *nvm);
int tb_nvm_validate(struct tb_nvm *nvm);
int tb_nvm_write_headers(struct tb_nvm *nvm);
int tb_nvm_add_active(struct tb_nvm *nvm, nvmem_reg_read_t reg_read);
int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val,
size_t bytes);
int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size,
nvmem_reg_write_t reg_write);
int tb_nvm_add_non_active(struct tb_nvm *nvm, nvmem_reg_write_t reg_write);
void tb_nvm_free(struct tb_nvm *nvm);
void tb_nvm_exit(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment