thunderbolt: Add WRITE_ONLY and AUTHENTICATE_ONLY NVM operations for retimers
authorRajmohan Mani <rajmohan.mani@intel.com>
Mon, 12 Apr 2021 12:29:16 +0000 (15:29 +0300)
committerMika Westerberg <mika.westerberg@linux.intel.com>
Tue, 1 Jun 2021 07:53:32 +0000 (10:53 +0300)
The same way we support these two operations for USB4 routers we can
extend the retimer NVM operations to support retimers also.

Signed-off-by: Rajmohan Mani <rajmohan.mani@intel.com>
Co-developed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/ABI/testing/sysfs-bus-thunderbolt
drivers/thunderbolt/retimer.c
drivers/thunderbolt/tb.h
drivers/thunderbolt/usb4.c

index da580b5..95c21d6 100644 (file)
@@ -221,7 +221,7 @@ Description:        When new NVM image is written to the non-active NVM
                  and flush it to the storage area.
                - Writing "3" will authenticate the image that is
                  currently written in the storage area. This is only
-                 supported with USB4 devices.
+                 supported with USB4 devices and retimers.
 
                When read holds status of the last authentication
                operation if an error occurred during the process. This
index 05af0fe..3aa790a 100644 (file)
@@ -103,6 +103,7 @@ 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;
+       int ret;
 
        image_size = rt->nvm->buf_data_size;
        if (image_size < NVM_MIN_SIZE || image_size > NVM_MAX_SIZE)
@@ -140,8 +141,25 @@ static int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt)
        buf += hdr_size;
        image_size -= hdr_size;
 
-       return usb4_port_retimer_nvm_write(rt->port, rt->index, 0, buf,
-                                          image_size);
+       ret = usb4_port_retimer_nvm_write(rt->port, rt->index, 0, buf,
+                                        image_size);
+       if (!ret)
+               rt->nvm->flushed = true;
+
+       return ret;
+}
+
+static int tb_retimer_nvm_authenticate(struct tb_retimer *rt, bool auth_only)
+{
+       int ret;
+
+       if (auth_only) {
+               ret = usb4_port_retimer_nvm_set_offset(rt->port, rt->index, 0);
+               if (ret)
+                       return ret;
+       }
+
+       return usb4_port_retimer_nvm_authenticate(rt->port, rt->index);
 }
 
 static ssize_t device_show(struct device *dev, struct device_attribute *attr,
@@ -176,8 +194,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t count)
 {
        struct tb_retimer *rt = tb_to_retimer(dev);
-       bool val;
-       int ret;
+       int val, ret;
 
        pm_runtime_get_sync(&rt->dev);
 
@@ -191,7 +208,7 @@ static ssize_t nvm_authenticate_store(struct device *dev,
                goto exit_unlock;
        }
 
-       ret = kstrtobool(buf, &val);
+       ret = kstrtoint(buf, 10, &val);
        if (ret)
                goto exit_unlock;
 
@@ -199,16 +216,22 @@ static ssize_t nvm_authenticate_store(struct device *dev,
        rt->auth_status = 0;
 
        if (val) {
-               if (!rt->nvm->buf) {
-                       ret = -EINVAL;
-                       goto exit_unlock;
+               if (val == AUTHENTICATE_ONLY) {
+                       ret = tb_retimer_nvm_authenticate(rt, true);
+               } else {
+                       if (!rt->nvm->flushed) {
+                               if (!rt->nvm->buf) {
+                                       ret = -EINVAL;
+                                       goto exit_unlock;
+                               }
+
+                               ret = tb_retimer_nvm_validate_and_write(rt);
+                               if (ret || val == WRITE_ONLY)
+                                       goto exit_unlock;
+                       }
+                       if (val == WRITE_AND_AUTHENTICATE)
+                               ret = tb_retimer_nvm_authenticate(rt, false);
                }
-
-               ret = tb_retimer_nvm_validate_and_write(rt);
-               if (ret)
-                       goto exit_unlock;
-
-               ret = usb4_port_retimer_nvm_authenticate(rt->port, rt->index);
        }
 
 exit_unlock:
index 53f6bb8..725104c 100644 (file)
@@ -1082,6 +1082,8 @@ int usb4_port_retimer_write(struct tb_port *port, u8 index, u8 reg,
                            const void *buf, u8 size);
 int usb4_port_retimer_is_last(struct tb_port *port, u8 index);
 int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index);
+int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
+                                    unsigned int address);
 int usb4_port_retimer_nvm_write(struct tb_port *port, u8 index,
                                unsigned int address, const void *buf,
                                size_t size);
index 76d7335..ceddbe7 100644 (file)
@@ -1513,8 +1513,19 @@ int usb4_port_retimer_nvm_sector_size(struct tb_port *port, u8 index)
        return ret ? ret : metadata & USB4_NVM_SECTOR_SIZE_MASK;
 }
 
-static int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
-                                           unsigned int address)
+/**
+ * usb4_port_retimer_nvm_set_offset() - Set NVM write offset
+ * @port: USB4 port
+ * @index: Retimer index
+ * @address: Start offset
+ *
+ * Exlicitly sets NVM write offset. Normally when writing to NVM this is
+ * done automatically by usb4_port_retimer_nvm_write().
+ *
+ * Returns %0 in success and negative errno if there was a failure.
+ */
+int usb4_port_retimer_nvm_set_offset(struct tb_port *port, u8 index,
+                                    unsigned int address)
 {
        u32 metadata, dwaddress;
        int ret;