summaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt/retimer.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt/retimer.c')
-rw-r--r--drivers/thunderbolt/retimer.c113
1 files changed, 46 insertions, 67 deletions
diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c
index 8c29bd556ae0..81252e31014a 100644
--- a/drivers/thunderbolt/retimer.c
+++ b/drivers/thunderbolt/retimer.c
@@ -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 @@ out:
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);
@@ -59,34 +73,23 @@ static int tb_retimer_nvm_write(void *priv, unsigned int offset, void *val,
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;
- nvm->minor = val >> 8;
-
- 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, tb_retimer_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, tb_retimer_nvm_write);
+ ret = tb_nvm_add_non_active(nvm, nvm_write);
if (ret)
goto err_nvm;
@@ -94,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)
@@ -185,7 +162,7 @@ static ssize_t device_show(struct device *dev, struct device_attribute *attr,
{
struct tb_retimer *rt = tb_to_retimer(dev);
- return sprintf(buf, "%#x\n", rt->device);
+ return sysfs_emit(buf, "%#x\n", rt->device);
}
static DEVICE_ATTR_RO(device);
@@ -200,8 +177,10 @@ 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);
+ ret = sysfs_emit(buf, "%#x\n", rt->auth_status);
mutex_unlock(&rt->tb->lock);
@@ -276,7 +255,7 @@ static ssize_t nvm_version_show(struct device *dev,
if (!rt->nvm)
ret = -EAGAIN;
else
- ret = sprintf(buf, "%x.%x\n", rt->nvm->major, rt->nvm->minor);
+ ret = sysfs_emit(buf, "%x.%x\n", rt->nvm->major, rt->nvm->minor);
mutex_unlock(&rt->tb->lock);
return ret;
@@ -288,7 +267,7 @@ static ssize_t vendor_show(struct device *dev, struct device_attribute *attr,
{
struct tb_retimer *rt = tb_to_retimer(dev);
- return sprintf(buf, "%#x\n", rt->vendor);
+ return sysfs_emit(buf, "%#x\n", rt->vendor);
}
static DEVICE_ATTR_RO(vendor);