diff options
Diffstat (limited to 'drivers/thunderbolt/retimer.c')
| -rw-r--r-- | drivers/thunderbolt/retimer.c | 295 |
1 files changed, 175 insertions, 120 deletions
diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index 8c29bd556ae0..13d64dbd2bc5 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -14,10 +14,30 @@ #include "sb_regs.h" #include "tb.h" +#if IS_ENABLED(CONFIG_USB4_DEBUGFS_MARGINING) #define TB_MAX_RETIMER_INDEX 6 +#else +#define TB_MAX_RETIMER_INDEX 2 +#endif -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. + * + * Return: %0 if the read was successful, 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 +50,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 +60,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,94 +78,61 @@ 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); - if (ret) - goto err_nvm; + if (!rt->no_nvm_upgrade) { + ret = tb_nvm_add_non_active(nvm, nvm_write); + if (ret) + goto err_nvm; + } rt->nvm = nvm; + dev_dbg(&rt->dev, "NVM version %x.%x\n", nvm->major, nvm->minor); return 0; err_nvm: - tb_nvm_free(nvm); + dev_dbg(&rt->dev, "NVM upgrade disabled\n"); + rt->no_nvm_upgrade = true; + 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 +171,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); @@ -201,13 +187,67 @@ static ssize_t nvm_authenticate_show(struct device *dev, if (!rt->nvm) ret = -EAGAIN; else - ret = sprintf(buf, "%#x\n", rt->auth_status); + ret = sysfs_emit(buf, "%#x\n", rt->auth_status); mutex_unlock(&rt->tb->lock); return ret; } +static void tb_retimer_nvm_authenticate_status(struct tb_port *port, u32 *status) +{ + int i; + + tb_port_dbg(port, "reading NVM authentication status of retimers\n"); + + /* + * Before doing anything else, read the authentication status. + * If the retimer has it set, store it for the new retimer + * device instance. + */ + for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) { + if (usb4_port_retimer_nvm_authenticate_status(port, i, &status[i])) + break; + } +} + +static void tb_retimer_set_inbound_sbtx(struct tb_port *port) +{ + int i; + + /* + * When USB4 port is online sideband communications are + * already up. + */ + if (!usb4_port_device_is_offline(port->usb4)) + return; + + tb_port_dbg(port, "enabling sideband transactions\n"); + + for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) + usb4_port_retimer_set_inbound_sbtx(port, i); +} + +static void tb_retimer_unset_inbound_sbtx(struct tb_port *port) +{ + int i; + + /* + * When USB4 port is offline we need to keep the sideband + * communications up to make it possible to communicate with + * the connected retimers. + */ + if (usb4_port_device_is_offline(port->usb4)) + return; + + tb_port_dbg(port, "disabling sideband transactions\n"); + + for (i = TB_MAX_RETIMER_INDEX; i >= 1; i--) { + if (usb4_port_retimer_unset_inbound_sbtx(port, i)) + break; + } +} + static ssize_t nvm_authenticate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -234,6 +274,14 @@ static ssize_t nvm_authenticate_store(struct device *dev, rt->auth_status = 0; if (val) { + /* + * When NVM authentication starts the retimer is not + * accessible so calling tb_retimer_unset_inbound_sbtx() + * will fail and therefore we do not call it. Exception + * is when the validation fails or we only write the new + * NVM image without authentication. + */ + tb_retimer_set_inbound_sbtx(rt->port); if (val == AUTHENTICATE_ONLY) { ret = tb_retimer_nvm_authenticate(rt, true); } else { @@ -253,6 +301,8 @@ static ssize_t nvm_authenticate_store(struct device *dev, } exit_unlock: + if (ret || val == WRITE_ONLY) + tb_retimer_unset_inbound_sbtx(rt->port); mutex_unlock(&rt->tb->lock); exit_rpm: pm_runtime_mark_last_busy(&rt->dev); @@ -276,7 +326,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,10 +338,23 @@ 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); +static umode_t retimer_is_visible(struct kobject *kobj, struct attribute *attr, + int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct tb_retimer *rt = tb_to_retimer(dev); + + if (attr == &dev_attr_nvm_authenticate.attr || + attr == &dev_attr_nvm_version.attr) + return rt->no_nvm_upgrade ? 0 : attr->mode; + + return attr->mode; +} + static struct attribute *retimer_attrs[] = { &dev_attr_device.attr, &dev_attr_nvm_authenticate.attr, @@ -301,6 +364,7 @@ static struct attribute *retimer_attrs[] = { }; static const struct attribute_group retimer_group = { + .is_visible = retimer_is_visible, .attrs = retimer_attrs, }; @@ -316,47 +380,35 @@ static void tb_retimer_release(struct device *dev) kfree(rt); } -struct device_type tb_retimer_type = { +const struct device_type tb_retimer_type = { .name = "thunderbolt_retimer", .groups = retimer_groups, .release = tb_retimer_release, }; -static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status) +static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status, + bool on_board) { struct tb_retimer *rt; u32 vendor, device; int ret; - ret = usb4_port_retimer_read(port, index, USB4_SB_VENDOR_ID, &vendor, - sizeof(vendor)); + ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index, + USB4_SB_VENDOR_ID, &vendor, sizeof(vendor)); if (ret) { if (ret != -ENODEV) tb_port_warn(port, "failed read retimer VendorId: %d\n", ret); return ret; } - ret = usb4_port_retimer_read(port, index, USB4_SB_PRODUCT_ID, &device, - sizeof(device)); + ret = usb4_port_sb_read(port, USB4_SB_TARGET_RETIMER, index, + USB4_SB_PRODUCT_ID, &device, sizeof(device)); if (ret) { if (ret != -ENODEV) tb_port_warn(port, "failed read retimer ProductId: %d\n", ret); return ret; } - if (vendor != PCI_VENDOR_ID_INTEL && vendor != 0x8087) { - tb_port_info(port, "retimer NVM format of vendor %#x is not supported\n", - vendor); - return -EOPNOTSUPP; - } - - /* - * Check that it supports NVM operations. If not then don't add - * the device at all. - */ - ret = usb4_port_retimer_nvm_sector_size(port, index); - if (ret < 0) - return ret; rt = kzalloc(sizeof(*rt), GFP_KERNEL); if (!rt) @@ -369,6 +421,13 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status) rt->port = port; rt->tb = port->sw->tb; + /* + * Only support NVM upgrade for on-board retimers. The retimers + * on the other side of the connection. + */ + if (!on_board || usb4_port_retimer_nvm_sector_size(port, index) <= 0) + rt->no_nvm_upgrade = true; + rt->dev.parent = &port->usb4->dev; rt->dev.bus = &tb_bus_type; rt->dev.type = &tb_retimer_type; @@ -399,12 +458,14 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status) pm_runtime_mark_last_busy(&rt->dev); pm_runtime_use_autosuspend(&rt->dev); + tb_retimer_debugfs_init(rt); return 0; } static void tb_retimer_remove(struct tb_retimer *rt) { dev_info(&rt->dev, "retimer disconnected\n"); + tb_retimer_debugfs_remove(rt); tb_nvm_free(rt->nvm); device_unregister(&rt->dev); } @@ -414,7 +475,7 @@ struct tb_retimer_lookup { u8 index; }; -static int retimer_match(struct device *dev, void *data) +static int retimer_match(struct device *dev, const void *data) { const struct tb_retimer_lookup *lookup = data; struct tb_retimer *rt = tb_to_retimer(dev); @@ -440,21 +501,16 @@ static struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index) * @add: If true also registers found retimers * * Brings the sideband into a state where retimers can be accessed. - * Then Tries to enumerate on-board retimers connected to @port. Found + * Then tries to enumerate on-board retimers connected to @port. Found * retimers are registered as children of @port if @add is set. Does * not scan for cable retimers for now. + * + * Return: %0 on success, negative errno otherwise. */ int tb_retimer_scan(struct tb_port *port, bool add) { u32 status[TB_MAX_RETIMER_INDEX + 1] = {}; - int ret, i, last_idx = 0; - struct usb4_port *usb4; - - usb4 = port->usb4; - if (!usb4) - return 0; - - pm_runtime_get_sync(&usb4->dev); + int ret, i, max, last_idx = 0; /* * Send broadcast RT to make sure retimer indices facing this @@ -462,24 +518,21 @@ int tb_retimer_scan(struct tb_port *port, bool add) */ ret = usb4_port_enumerate_retimers(port); if (ret) - goto out; + return ret; /* - * Enable sideband channel for each retimer. We can do this - * regardless whether there is device connected or not. + * Immediately after sending enumerate retimers read the + * authentication status of each retimer. */ - for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) - usb4_port_retimer_set_inbound_sbtx(port, i); + tb_retimer_nvm_authenticate_status(port, status); /* - * Before doing anything else, read the authentication status. - * If the retimer has it set, store it for the new retimer - * device instance. + * Enable sideband channel for each retimer. We can do this + * regardless whether there is device connected or not. */ - for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) - usb4_port_retimer_nvm_authenticate_status(port, i, &status[i]); + tb_retimer_set_inbound_sbtx(port); - for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) { + for (max = 1, i = 1; i <= TB_MAX_RETIMER_INDEX; i++) { /* * Last retimer is true only for the last on-board * retimer (the one connected directly to the Type-C @@ -490,31 +543,33 @@ int tb_retimer_scan(struct tb_port *port, bool add) last_idx = i; else if (ret < 0) break; - } - if (!last_idx) { - ret = 0; - goto out; + max = i; } - /* Add on-board retimers if they do not exist already */ - for (i = 1; i <= last_idx; i++) { + ret = 0; + if (!IS_ENABLED(CONFIG_USB4_DEBUGFS_MARGINING)) + max = min(last_idx, max); + + /* Add retimers if they do not exist already */ + for (i = 1; i <= max; i++) { struct tb_retimer *rt; + /* Skip cable retimers */ + if (usb4_port_retimer_is_cable(port, i)) + continue; + rt = tb_port_find_retimer(port, i); if (rt) { put_device(&rt->dev); } else if (add) { - ret = tb_retimer_add(port, i, status[i]); + ret = tb_retimer_add(port, i, status[i], i <= last_idx); if (ret && ret != -EOPNOTSUPP) break; } } -out: - pm_runtime_mark_last_busy(&usb4->dev); - pm_runtime_put_autosuspend(&usb4->dev); - + tb_retimer_unset_inbound_sbtx(port); return ret; } |
