thunderbolt: Add support for ASMedia NVM firmware image format
Subsystems (2)
v9 cover thunderbolt: Add support for ASMedia NVM firmware image format
Hi all,
This series adds support for ASMedia NVM images. It is based on the work of Szuying Chen with a couple of changes from me. The first patches move Intel specific NVM validation into nvm.c and adds a vendor table for both routers and retimers that can be extended to support different vendor NVM image formats. Once that is done the ASMedia NVM image format support is added.
The previous version of the patch series can be found here:
https://lore.kernel.org/linux-usb/[email protected]/
Changes from the previous version:
* Shuffled the ordering of the patches a bit
* Add tb_retimer_nvm_read() and tb_switch_nvm_read()
* Add ->write_headers() callback that is used with Thunderbolt 2 routers
* Cover retimers too
* Minor cleanups and tweaks
I have tested this on Intel Thunderbolt 3 and Thunderbolt 4 routers. ASMedia folks, can you try this also on your side and let me know if it works or not?
Mika Westerberg (1):
thunderbolt: Provide tb_retimer_nvm_read() analogous to tb_switch_nvm_read()
Szuying Chen (5):
thunderbolt: Allow NVM upgrade of USB4 host routers
thunderbolt: Extend NVM version fields to 32-bits
thunderbolt: Rename and make nvm_read() available for other files
thunderbolt: Move vendor specific NVM handling into nvm.c
thunderbolt: Add support for ASMedia NVM image format
drivers/thunderbolt/nvm.c | 385 +++++++++++++++++++++++++++++++++- drivers/thunderbolt/retimer.c | 105 ++++------ drivers/thunderbolt/switch.c | 162 +++++--------- drivers/thunderbolt/tb.c | 5 +- drivers/thunderbolt/tb.h | 30 ++- 5 files changed, 498 insertions(+), 189 deletions(-)
Signature · 1 line
2.35.1
PATCH v9 1/6 thunderbolt: Allow NVM upgrade of USB4 host routers
From: Szuying Chen <[email protected]>
Intel pre-USB4 host routers required the firmware connection manager to be active in order to perform NVM firmware upgrade and for this reason it was disabled when software connection manager is active. However, this is not necessary for USB4 host routers as this functionality is part of router operations that the router implements if it wants to support this.
--- drivers/thunderbolt/tb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/thunderbolt/tb.c +4 -1
@@ -1442,8 +1442,11 @@ static int tb_start(struct tb *tb)* ICM firmware upgrade needs running firmware and in native * mode that is not available so disable firmware upgrade of the * root switch. +* +* However, USB4 routers support NVM firmware upgrade if they +* implement the necessary router operations. */ - tb->root_switch->no_nvm_upgrade = true; + tb->root_switch->no_nvm_upgrade = !tb_switch_is_usb4 (tb->root_switch);/* All USB4 routers support runtime PM */ tb->root_switch->rpm =tb_switch_is_usb4 (tb->root_switch);
Signature · 1 line
2.35.1
PATCH v9 2/6 thunderbolt: Extend NVM version fields to 32-bits
In reply to Mika WesterbergFrom: Szuying Chen <[email protected]>
In order to support non-Intel NVM image formats extend the NVM major and minor version to 32-bits to better accommondate different versioning schemes.
No functional impact.
--- drivers/thunderbolt/retimer.c | 4 ++-- drivers/thunderbolt/switch.c | 4 ++-- drivers/thunderbolt/tb.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-)
drivers/thunderbolt/retimer.c +2 -2
@@ -71,8 +71,8 @@ static int tb_retimer_nvm_add(struct tb_retimer *rt)if (ret) goto err_nvm; - nvm->major = val >>16 ; - nvm->minor = val >>8 ; + 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));
drivers/thunderbolt/switch.c +2 -2
@@ -427,8 +427,8 @@ static int tb_switch_nvm_add(struct tb_switch *sw)if (ret) goto err_nvm; - nvm->major = val >>16 ; - nvm->minor = val >>8 ; + nvm->major = (val >>16 ) &0xff ; + nvm->minor = (val >>8 ) &0xff ; ret =tb_nvm_add_active (nvm, nvm_size, tb_switch_nvm_read);if (ret)
drivers/thunderbolt/tb.h +2 -2
@@ -48,8 +48,8 @@*/ struct tb_nvm {struct device *dev; -u8 major; -u8 minor; +u32 major; +u32 minor;int id;struct nvmem_device *active;struct nvmem_device *non_active;
Signature · 1 line
2.35.1
PATCH v9 3/6 thunderbolt: Rename and make nvm_read() available for other files
In reply to Mika WesterbergFrom: Szuying Chen <[email protected]>
In order to support non-Intel NVM formats the vendor specific NVM validation code that will live in nvm.c needs to be able to read various parts of the NVM so make the function available outside of switch.c and rename it accordingly.
--- drivers/thunderbolt/switch.c | 44 +++++++++++++++++++++--------------- drivers/thunderbolt/tb.h | 2 ++ 2 files changed, 28 insertions(+), 18 deletions(-)
drivers/thunderbolt/switch.c +26 -18
@@ -300,14 +300,6 @@ static inline bool nvm_upgradeable(struct tb_switch *sw)return nvm_readable (sw); } -static inline int nvm_read (struct tb_switch *sw,unsigned int address, -void *buf,size_t size) -{ -if (tb_switch_is_usb4 (sw)) -return usb4_switch_nvm_read (sw, address, buf, size); -return dma_port_flash_read (sw->dma_port, address, buf, size); -} -static int nvm_authenticate (struct tb_switch *sw,bool auth_only) {int ret;
@@ -335,8 +327,26 @@ static int nvm_authenticate(struct tb_switch *sw, bool auth_only)return ret; } -static int tb_switch_nvm_read (void *priv,unsigned int offset,void *val, -size_t bytes) +/** +* tb_switch_nvm_read() - Read router NVM +* @sw: Router whose NVM to read +* @address: Start address on the NVM +* @buf: Buffer where the read data is copied +* @size: Size of the buffer in bytes +* +* Reads from router NVM and returns the requested data in @buf. Locking +* is up to the caller. Returns %0 in success and negative errno in case +* of failure. +*/ +int tb_switch_nvm_read (struct tb_switch *sw,unsigned int address,void *buf, +size_t size) +{ +if (tb_switch_is_usb4 (sw)) +return usb4_switch_nvm_read (sw, address, buf, size); +return dma_port_flash_read (sw->dma_port, address, buf, size); +} + +static int nvm_read (void *priv,unsigned int offset,void *val,size_t bytes) {struct tb_nvm *nvm = priv;struct tb_switch *sw =tb_to_switch (nvm->dev);
@@ -349,7 +359,7 @@ static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val, goto out; } - ret =nvm_read (sw, offset, val, bytes); + ret =tb_switch_nvm_read (sw, offset, val, bytes);mutex_unlock (&sw->tb->lock); out:
@@ -359,8 +369,7 @@ static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val,return ret; } -static int tb_switch_nvm_write (void *priv,unsigned int offset,void *val, -size_t bytes) +static int nvm_write (void *priv,unsigned int offset,void *val,size_t bytes) {struct tb_nvm *nvm = priv;struct tb_switch *sw =tb_to_switch (nvm->dev);
@@ -415,7 +424,7 @@ static int tb_switch_nvm_add(struct tb_switch *sw)if (!sw->safe_mode) {u32 nvm_size, hdr_size; - ret =nvm_read (sw,NVM_FLASH_SIZE , &val,sizeof (val)); + ret =tb_switch_nvm_read (sw,NVM_FLASH_SIZE , &val,sizeof (val));if (ret) goto err_nvm;
@@ -423,21 +432,20 @@ static int tb_switch_nvm_add(struct tb_switch *sw) nvm_size = (SZ_1M << (val &7 )) /8 ; nvm_size = (nvm_size - hdr_size) /2 ; - ret =nvm_read (sw,NVM_VERSION , &val,sizeof (val)); + 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, tb_switch_nvm_read); + ret =tb_nvm_add_active (nvm, nvm_size, nvm_read);if (ret) goto err_nvm; }if (!sw->no_nvm_upgrade) { - ret =tb_nvm_add_non_active (nvm,NVM_MAX_SIZE , - tb_switch_nvm_write); + ret =tb_nvm_add_non_active (nvm,NVM_MAX_SIZE , nvm_write);if (ret) goto err_nvm; }
drivers/thunderbolt/tb.h +2 -0
@@ -759,6 +759,8 @@ 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); +int tb_switch_nvm_read (struct tb_switch *sw,unsigned int address,void *buf, +size_t size);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,
Signature · 1 line
2.35.1
PATCH v9 4/6 thunderbolt: Provide tb_retimer_nvm_read() analogous to tb_switch_nvm_read()
In reply to Mika WesterbergAs we are moving the NVM vendor specifics into nvm.c we need to deal witht he retimer NVM formats too. For this reason provide retimer specific function that can be used to read the contents of the NVM and rename the internal ones accordingly analogous to what we do with routers.
--- drivers/thunderbolt/retimer.c | 28 +++++++++++++++++++++------- drivers/thunderbolt/tb.h | 2 ++ 2 files changed, 23 insertions(+), 7 deletions(-)
drivers/thunderbolt/retimer.c +21 -7
@@ -16,8 +16,23 @@#define TB_MAX_RETIMER_INDEX 6 -static int tb_retimer_nvm_read (void *priv,unsigned int offset,void *val, -size_t bytes) +/** +* tb_retimer_nvm_read() - Read contents of retimer NVM +* @rt: Retimer device +* @address: NVM address (in bytes) to start reading +* @buf: Data read from NVM is stored here +* @size: Number of bytes to read +* +* Reads retimer NVM and copies the contents to @buf. Returns %0 if the +* read was successful and negative errno in case of failure. +*/ +int tb_retimer_nvm_read (struct tb_retimer *rt,unsigned int address,void *buf, +size_t size) +{ +return usb4_port_retimer_nvm_read (rt->port, rt->index, address, buf, size); +} + +static int nvm_read (void *priv,unsigned int offset,void *val,size_t bytes) {struct tb_nvm *nvm = priv;struct tb_retimer *rt =tb_to_retimer (nvm->dev);
@@ -30,7 +45,7 @@ static int tb_retimer_nvm_read(void *priv, unsigned int offset, void *val, goto out; } - ret =usb4_port_retimer_nvm_read (rt->port, rt->index, offset, val, bytes); + ret =tb_retimer_nvm_read (rt, offset, val, bytes);mutex_unlock (&rt->tb->lock); out:
@@ -40,8 +55,7 @@ static int tb_retimer_nvm_read(void *priv, unsigned int offset, void *val,return ret; } -static int tb_retimer_nvm_write (void *priv,unsigned int offset,void *val, -size_t bytes) +static int nvm_write (void *priv,unsigned int offset,void *val,size_t bytes) {struct tb_nvm *nvm = priv;struct tb_retimer *rt =tb_to_retimer (nvm->dev);
@@ -82,11 +96,11 @@ static int tb_retimer_nvm_add(struct tb_retimer *rt) nvm_size = (SZ_1M << (val &7 )) /8 ; nvm_size = (nvm_size -SZ_16K ) /2 ; - ret =tb_nvm_add_active (nvm, nvm_size, tb_retimer_nvm_read); + ret =tb_nvm_add_active (nvm, nvm_size, nvm_read);if (ret) goto err_nvm; - ret =tb_nvm_add_non_active (nvm,NVM_MAX_SIZE , tb_retimer_nvm_write); + ret =tb_nvm_add_non_active (nvm,NVM_MAX_SIZE , nvm_write);if (ret) goto err_nvm;
drivers/thunderbolt/tb.h +2 -0
@@ -1144,6 +1144,8 @@ static inline struct tb_switch *tb_xdomain_parent(struct tb_xdomain *xd)return tb_to_switch (xd->dev.parent); } +int tb_retimer_nvm_read (struct tb_retimer *rt,unsigned int address,void *buf, +size_t size);int tb_retimer_scan (struct tb_port *port,bool add);void tb_retimer_remove_all (struct tb_port *port);
Signature · 1 line
2.35.1
PATCH v9 5/6 thunderbolt: Move vendor specific NVM handling into nvm.c
In reply to Mika WesterbergFrom: Szuying Chen <[email protected]>
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.
--- drivers/thunderbolt/nvm.c | 348 +++++++++++++++++++++++++++++++++- drivers/thunderbolt/retimer.c | 81 +++----- drivers/thunderbolt/switch.c | 126 ++++-------- drivers/thunderbolt/tb.h | 22 ++- 4 files changed, 410 insertions(+), 167 deletions(-)
drivers/thunderbolt/nvm.c +339 -9
@@ -12,19 +12,278 @@#include "tb.h" +/* Intel specific NVM offsets */ +#define INTEL_NVM_DEVID 0x05 +#define INTEL_NVM_VERSION 0x08 +#define INTEL_NVM_CSS 0x10 +#define INTEL_NVM_FLASH_SIZE 0x45 +static DEFINE_IDA (nvm_ida); +/** +* struct tb_nvm_vendor_ops - Vendor specific NVM operations +* @read_version: Reads out NVM version from the flash +* @validate: Validates the NVM image before update (optional) +* @write_headers: Writes headers before the rest of the image (optional) +*/ +struct tb_nvm_vendor_ops { +int (*read_version)(struct tb_nvm *nvm); +int (*validate)(struct tb_nvm *nvm); +int (*write_headers)(struct tb_nvm *nvm); +}; + +/** +* struct tb_nvm_vendor - Vendor to &struct tb_nvm_vendor_ops mapping +* @vendor: Vendor ID +* @vops: Vendor specific NVM operations +* +* Maps vendor ID to NVM vendor operations. If there is no mapping then +* NVM firmware upgrade is disabled for the device. +*/ +struct tb_nvm_vendor { +u16 vendor; +const struct tb_nvm_vendor_ops *vops; +}; + +static int intel_switch_nvm_version (struct tb_nvm *nvm) +{ +struct tb_switch *sw =tb_to_switch (nvm->dev); +u32 val, nvm_size, hdr_size; +int ret; + +/* +* If the switch is in safe-mode the only accessible portion of +* the NVM is the non-active one where userspace is expected to +* write new functional NVM. +*/ +if (sw->safe_mode) +return 0 ; + + ret =tb_switch_nvm_read (sw,INTEL_NVM_FLASH_SIZE , &val,sizeof (val)); +if (ret) +return ret; + + 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,INTEL_NVM_VERSION , &val,sizeof (val)); +if (ret) +return ret; + + nvm->major = (val >>16 ) &0xff ; + nvm->minor = (val >>8 ) &0xff ; + nvm->active_size = nvm_size; + +return 0 ; +} + +static int intel_switch_nvm_validate (struct tb_nvm *nvm) +{ +struct tb_switch *sw =tb_to_switch (nvm->dev); +unsigned int image_size, hdr_size; +u16 ds_size, device_id; +u8 *buf = nvm->buf; + + image_size = nvm->buf_data_size; + +/* +* 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 +INTEL_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) +return 0 ; + +/* +* Make sure the device ID in the image matches the one +* we read from the switch config space. +*/ + device_id = *(u16 *)(buf + hdr_size +INTEL_NVM_DEVID ); +if (device_id != sw->config.device_id) +return -EINVAL ; + +/* Skip headers in the image */ + nvm->buf_data_start = buf + hdr_size; + nvm->buf_data_size = image_size - hdr_size; + +return 0 ; +} + +static int intel_switch_nvm_write_headers (struct tb_nvm *nvm) +{ +struct tb_switch *sw =tb_to_switch (nvm->dev); + +if (sw->generation <3 ) { +int ret; + +/* Write CSS headers first */ + ret =dma_port_flash_write (sw->dma_port, +DMA_PORT_CSS_ADDRESS , nvm->buf +INTEL_NVM_CSS , +DMA_PORT_CSS_MAX_SIZE ); +if (ret) +return ret; + } + +return 0 ; +} + +static const struct tb_nvm_vendor_ops intel_switch_nvm_ops = { + .read_version = intel_switch_nvm_version, + .validate = intel_switch_nvm_validate, + .write_headers = intel_switch_nvm_write_headers, +}; + +/* Router vendor NVM support table */ +static const struct tb_nvm_vendor switch_nvm_vendors[] = { + {PCI_VENDOR_ID_INTEL , &intel_switch_nvm_ops }, + {0x8087 , &intel_switch_nvm_ops }, +}; + +static int intel_retimer_nvm_version (struct tb_nvm *nvm) +{ +struct tb_retimer *rt =tb_to_retimer (nvm->dev); +u32 val, nvm_size; +int ret; + + ret =tb_retimer_nvm_read (rt,INTEL_NVM_VERSION , &val,sizeof (val)); +if (ret) +return ret; + + nvm->major = (val >>16 ) &0xff ; + nvm->minor = (val >>8 ) &0xff ; + + ret =tb_retimer_nvm_read (rt,INTEL_NVM_FLASH_SIZE , &val,sizeof (val)); +if (ret) +return ret; + + nvm_size = (SZ_1M << (val &7 )) /8 ; + nvm_size = (nvm_size -SZ_16K ) /2 ; + nvm->active_size = nvm_size; + +return 0 ; +} + +static int intel_retimer_nvm_validate (struct tb_nvm *nvm) +{ +struct tb_retimer *rt =tb_to_retimer (nvm->dev); +unsigned int image_size, hdr_size; +u8 *buf = nvm->buf; +u16 ds_size, device; + + image_size = nvm->buf_data_size; + +/* +* 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 +INTEL_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 +INTEL_NVM_DEVID ); +if (device != rt->device) +return -EINVAL ; + +/* Skip headers in the image */ + nvm->buf_data_start = buf + hdr_size; + nvm->buf_data_size = image_size - hdr_size; + +return 0 ; +} + +static const struct tb_nvm_vendor_ops intel_retimer_nvm_ops = { + .read_version = intel_retimer_nvm_version, + .validate = intel_retimer_nvm_validate, +}; + +/* Retimer vendor NVM support table */ +static const struct tb_nvm_vendor retimer_nvm_vendors[] = { + {0x8087 , &intel_retimer_nvm_ops }, +}; +/** * tb_nvm_alloc() - Allocate new NVM structure * @dev: Device owning the NVM * * Allocates new NVM structure with unique @id and returns it. In case -* of error returns ERR_PTR(). +* of error returns ERR_PTR(). Specifically returns %-EOPNOTSUPP if the +* NVM format of the @dev is not known by the kernel. */ struct tb_nvm *tb_nvm_alloc (struct device *dev) { +const struct tb_nvm_vendor_ops *vops =NULL ;struct tb_nvm *nvm; -int ret; +int ret, i; + +if (tb_is_switch (dev)) { +const struct tb_switch *sw =tb_to_switch (dev); + +for (i =0 ; i <ARRAY_SIZE (switch_nvm_vendors); i++) { +const struct tb_nvm_vendor *v = &switch_nvm_vendors[i]; + +if (v->vendor == sw->config.vendor_id) { + vops = v->vops; +break ; + } + } + +if (!vops) { +tb_sw_dbg (sw,"router NVM format of vendor %#x unknown\n" , + sw->config.vendor_id); +return ERR_PTR (-EOPNOTSUPP ); + } + }else if (tb_is_retimer (dev)) { +const struct tb_retimer *rt =tb_to_retimer (dev); + +for (i =0 ; i <ARRAY_SIZE (retimer_nvm_vendors); i++) { +const struct tb_nvm_vendor *v = &retimer_nvm_vendors[i]; + +if (v->vendor == rt->vendor) { + vops = v->vops; +break ; + } + } + +if (!vops) { +dev_dbg (dev,"retimer NVM format of vendor %#x unknown\n" , + rt->vendor); +return ERR_PTR (-EOPNOTSUPP ); + } + }else { +return ERR_PTR (-EOPNOTSUPP ); + } nvm =kzalloc (sizeof (*nvm),GFP_KERNEL );if (!nvm)
@@ -38,14 +297,85 @@ struct tb_nvm *tb_nvm_alloc(struct device *dev) nvm->id = ret; nvm->dev = dev; + nvm->vops = vops;return nvm; } +/** +* tb_nvm_read_version() - Read and populate NVM version +* @nvm: NVM structure +* +* Uses vendor specific means to read out and fill in the existing +* active NVM version. Returns %0 in case of success and negative errno +* otherwise. +*/ +int tb_nvm_read_version (struct tb_nvm *nvm) +{ +const struct tb_nvm_vendor_ops *vops = nvm->vops; + +if (vops && vops->read_version) +return vops->read_version (nvm); + +return -EOPNOTSUPP ; +} + +/** +* tb_nvm_validate() - Validate new NVM image +* @nvm: NVM structure +* +* Runs vendor specific validation over the new NVM image and if all +* checks pass returns %0. As side effect updates @nvm->buf_data_start +* and @nvm->buf_data_size fields to match the actual data to be written +* to the NVM. +* +* If the validation does not pass then returns negative errno. +*/ +int tb_nvm_validate (struct tb_nvm *nvm) +{ +const struct tb_nvm_vendor_ops *vops = nvm->vops; +unsigned int image_size; +u8 *buf = nvm->buf; + +if (!buf) +return -EINVAL ; +if (!vops) +return -EOPNOTSUPP ; + +/* Just do basic image size checks */ + image_size = nvm->buf_data_size; +if (image_size <NVM_MIN_SIZE || image_size >NVM_MAX_SIZE ) +return -EINVAL ; + +/* +* Set the default data start in the buffer. The validate method +* below can change this if needed. +*/ + nvm->buf_data_start = buf; + +return vops->validate ? vops->validate (nvm) :0 ; +} + +/** +* tb_nvm_write_header() - Write headers before the rest of the image +* @nvm: NVM structure +* +* If the vendor NVM format requires writing headers before the rest of +* the image, this function does that. Can be called even if the device +* does not need this. +* +* Returns %0 in case of success and negative errno otherwise. +*/ +int tb_nvm_write_headers (struct tb_nvm *nvm) +{ +const struct tb_nvm_vendor_ops *vops = nvm->vops; + +return vops->write_headers ? vops->write_headers (nvm) :0 ; +} +/** * tb_nvm_add_active() - Adds active NVMem device to NVM * @nvm: NVM structure -* @size: Size of the active NVM in bytes * @reg_read: Pointer to the function to read the NVM (passed directly to the * NVMem device) *
@@ -54,7 +384,7 @@ struct tb_nvm *tb_nvm_alloc(struct device *dev)* needed. The first parameter passed to @reg_read is @nvm structure. * Returns %0 in success and negative errno otherwise. */ -int tb_nvm_add_active (struct tb_nvm *nvm,size_t size,nvmem_reg_read_t reg_read) +int tb_nvm_add_active (struct tb_nvm *nvm,nvmem_reg_read_t reg_read) {struct nvmem_config config;struct nvmem_device *nvmem;
@@ -67,7 +397,7 @@ int tb_nvm_add_active(struct tb_nvm *nvm, size_t size, nvmem_reg_read_t reg_read config.id = nvm->id; config.stride =4 ; config.word_size =4 ; - config.size = size; + config.size = nvm->active_size; config.dev = nvm->dev; config.owner =THIS_MODULE ; config.priv = nvm;
@@ -109,17 +439,17 @@ int tb_nvm_write_buf(struct tb_nvm *nvm, unsigned int offset, void *val,/** * tb_nvm_add_non_active() - Adds non-active NVMem device to NVM * @nvm: NVM structure -* @size: Size of the non-active NVM in bytes * @reg_write: Pointer to the function to write the NVM (passed directly * to the NVMem device) * * Registers new non-active NVmem device for @nvm. The @reg_write is called * directly from NVMem so it must handle possible concurrent access if * needed. The first parameter passed to @reg_write is @nvm structure. +* The size of the NVMem device is set to %NVM_MAX_SIZE. +* * Returns %0 in success and negative errno otherwise. */ -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) {struct nvmem_config config;struct nvmem_device *nvmem;
@@ -132,7 +462,7 @@ int tb_nvm_add_non_active(struct tb_nvm *nvm, size_t size, config.id = nvm->id; config.stride =4 ; config.word_size =4 ; - config.size = size; + config.size =NVM_MAX_SIZE ; config.dev = nvm->dev; config.owner =THIS_MODULE ; config.priv = nvm;
drivers/thunderbolt/retimer.c +23 -58
122 lines not on this page · raw message
drivers/thunderbolt/switch.c +34 -92
183 lines not on this page · raw message
drivers/thunderbolt/tb.h +14 -8
@@ -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 );
Signature · 1 line
2.35.1
PATCH v9 6/6 thunderbolt: Add support for ASMedia NVM image format
In reply to Mika WesterbergFrom: Szuying Chen <[email protected]>
Add support for ASMedia specific NVM image format. This makes it possible to upgrade the NVM firmware of ASMedia routers in addition to Intel ones.
--- drivers/thunderbolt/nvm.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
drivers/thunderbolt/nvm.c +37 -0
@@ -18,6 +18,10 @@#define INTEL_NVM_CSS 0x10#define INTEL_NVM_FLASH_SIZE 0x45 +/* ASMedia specific NVM offsets */ +#define ASMEDIA_NVM_DATE 0x1c +#define ASMEDIA_NVM_VERSION 0x28 +static DEFINE_IDA (nvm_ida);/**
@@ -149,8 +153,41 @@ static const struct tb_nvm_vendor_ops intel_switch_nvm_ops = { .write_headers = intel_switch_nvm_write_headers, }; +static int asmedia_switch_nvm_version (struct tb_nvm *nvm) +{ +struct tb_switch *sw =tb_to_switch (nvm->dev); +u32 val; +int ret; + + ret =tb_switch_nvm_read (sw,ASMEDIA_NVM_VERSION , &val,sizeof (val)); +if (ret) +return ret; + + nvm->major = (val <<16 ) &0xff0000 ; + nvm->minor |= val &0x00ff00 ; + nvm->major |= (val >>16 ) &0x0000ff ; + + ret =tb_switch_nvm_read (sw,ASMEDIA_NVM_DATE , &val,sizeof (val)); +if (ret) +return ret; + + nvm->minor = (val <<16 ) &0xff0000 ; + nvm->minor |= val &0x00ff00 ; + nvm->minor |= (val >>16 ) &0x0000ff ; + +/* ASMedia NVM size is fixed to 512k */ + nvm->active_size =SZ_512K ; + +return 0 ; +} + +static const struct tb_nvm_vendor_ops asmedia_switch_nvm_ops = { + .read_version = asmedia_switch_nvm_version, +}; +/* Router vendor NVM support table */ static const struct tb_nvm_vendor switch_nvm_vendors[] = { + {0x174c , &asmedia_switch_nvm_ops }, {PCI_VENDOR_ID_INTEL , &intel_switch_nvm_ops }, {0x8087 , &intel_switch_nvm_ops }, };
Signature · 1 line
2.35.1