162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Thunderbolt/USB4 retimer support. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020, Intel Corporation 662306a36Sopenharmony_ci * Authors: Kranthi Kuntala <kranthi.kuntala@intel.com> 762306a36Sopenharmony_ci * Mika Westerberg <mika.westerberg@linux.intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1262306a36Sopenharmony_ci#include <linux/sched/signal.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "sb_regs.h" 1562306a36Sopenharmony_ci#include "tb.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define TB_MAX_RETIMER_INDEX 6 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/** 2062306a36Sopenharmony_ci * tb_retimer_nvm_read() - Read contents of retimer NVM 2162306a36Sopenharmony_ci * @rt: Retimer device 2262306a36Sopenharmony_ci * @address: NVM address (in bytes) to start reading 2362306a36Sopenharmony_ci * @buf: Data read from NVM is stored here 2462306a36Sopenharmony_ci * @size: Number of bytes to read 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Reads retimer NVM and copies the contents to @buf. Returns %0 if the 2762306a36Sopenharmony_ci * read was successful and negative errno in case of failure. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ciint tb_retimer_nvm_read(struct tb_retimer *rt, unsigned int address, void *buf, 3062306a36Sopenharmony_ci size_t size) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci return usb4_port_retimer_nvm_read(rt->port, rt->index, address, buf, size); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int nvm_read(void *priv, unsigned int offset, void *val, size_t bytes) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct tb_nvm *nvm = priv; 3862306a36Sopenharmony_ci struct tb_retimer *rt = tb_to_retimer(nvm->dev); 3962306a36Sopenharmony_ci int ret; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci pm_runtime_get_sync(&rt->dev); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (!mutex_trylock(&rt->tb->lock)) { 4462306a36Sopenharmony_ci ret = restart_syscall(); 4562306a36Sopenharmony_ci goto out; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci ret = tb_retimer_nvm_read(rt, offset, val, bytes); 4962306a36Sopenharmony_ci mutex_unlock(&rt->tb->lock); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ciout: 5262306a36Sopenharmony_ci pm_runtime_mark_last_busy(&rt->dev); 5362306a36Sopenharmony_ci pm_runtime_put_autosuspend(&rt->dev); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return ret; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int nvm_write(void *priv, unsigned int offset, void *val, size_t bytes) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct tb_nvm *nvm = priv; 6162306a36Sopenharmony_ci struct tb_retimer *rt = tb_to_retimer(nvm->dev); 6262306a36Sopenharmony_ci int ret = 0; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (!mutex_trylock(&rt->tb->lock)) 6562306a36Sopenharmony_ci return restart_syscall(); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci ret = tb_nvm_write_buf(nvm, offset, val, bytes); 6862306a36Sopenharmony_ci mutex_unlock(&rt->tb->lock); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return ret; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int tb_retimer_nvm_add(struct tb_retimer *rt) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct tb_nvm *nvm; 7662306a36Sopenharmony_ci int ret; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci nvm = tb_nvm_alloc(&rt->dev); 7962306a36Sopenharmony_ci if (IS_ERR(nvm)) { 8062306a36Sopenharmony_ci ret = PTR_ERR(nvm) == -EOPNOTSUPP ? 0 : PTR_ERR(nvm); 8162306a36Sopenharmony_ci goto err_nvm; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci ret = tb_nvm_read_version(nvm); 8562306a36Sopenharmony_ci if (ret) 8662306a36Sopenharmony_ci goto err_nvm; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ret = tb_nvm_add_active(nvm, nvm_read); 8962306a36Sopenharmony_ci if (ret) 9062306a36Sopenharmony_ci goto err_nvm; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ret = tb_nvm_add_non_active(nvm, nvm_write); 9362306a36Sopenharmony_ci if (ret) 9462306a36Sopenharmony_ci goto err_nvm; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci rt->nvm = nvm; 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cierr_nvm: 10062306a36Sopenharmony_ci dev_dbg(&rt->dev, "NVM upgrade disabled\n"); 10162306a36Sopenharmony_ci if (!IS_ERR(nvm)) 10262306a36Sopenharmony_ci tb_nvm_free(nvm); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return ret; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int tb_retimer_nvm_validate_and_write(struct tb_retimer *rt) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci unsigned int image_size; 11062306a36Sopenharmony_ci const u8 *buf; 11162306a36Sopenharmony_ci int ret; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci ret = tb_nvm_validate(rt->nvm); 11462306a36Sopenharmony_ci if (ret) 11562306a36Sopenharmony_ci return ret; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci buf = rt->nvm->buf_data_start; 11862306a36Sopenharmony_ci image_size = rt->nvm->buf_data_size; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ret = usb4_port_retimer_nvm_write(rt->port, rt->index, 0, buf, 12162306a36Sopenharmony_ci image_size); 12262306a36Sopenharmony_ci if (ret) 12362306a36Sopenharmony_ci return ret; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci rt->nvm->flushed = true; 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int tb_retimer_nvm_authenticate(struct tb_retimer *rt, bool auth_only) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci u32 status; 13262306a36Sopenharmony_ci int ret; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (auth_only) { 13562306a36Sopenharmony_ci ret = usb4_port_retimer_nvm_set_offset(rt->port, rt->index, 0); 13662306a36Sopenharmony_ci if (ret) 13762306a36Sopenharmony_ci return ret; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ret = usb4_port_retimer_nvm_authenticate(rt->port, rt->index); 14162306a36Sopenharmony_ci if (ret) 14262306a36Sopenharmony_ci return ret; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci usleep_range(100, 150); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * Check the status now if we still can access the retimer. It 14862306a36Sopenharmony_ci * is expected that the below fails. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci ret = usb4_port_retimer_nvm_authenticate_status(rt->port, rt->index, 15162306a36Sopenharmony_ci &status); 15262306a36Sopenharmony_ci if (!ret) { 15362306a36Sopenharmony_ci rt->auth_status = status; 15462306a36Sopenharmony_ci return status ? -EINVAL : 0; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic ssize_t device_show(struct device *dev, struct device_attribute *attr, 16162306a36Sopenharmony_ci char *buf) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct tb_retimer *rt = tb_to_retimer(dev); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return sysfs_emit(buf, "%#x\n", rt->device); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(device); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic ssize_t nvm_authenticate_show(struct device *dev, 17062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct tb_retimer *rt = tb_to_retimer(dev); 17362306a36Sopenharmony_ci int ret; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (!mutex_trylock(&rt->tb->lock)) 17662306a36Sopenharmony_ci return restart_syscall(); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!rt->nvm) 17962306a36Sopenharmony_ci ret = -EAGAIN; 18062306a36Sopenharmony_ci else if (rt->no_nvm_upgrade) 18162306a36Sopenharmony_ci ret = -EOPNOTSUPP; 18262306a36Sopenharmony_ci else 18362306a36Sopenharmony_ci ret = sysfs_emit(buf, "%#x\n", rt->auth_status); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci mutex_unlock(&rt->tb->lock); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return ret; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void tb_retimer_nvm_authenticate_status(struct tb_port *port, u32 *status) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int i; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci tb_port_dbg(port, "reading NVM authentication status of retimers\n"); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* 19762306a36Sopenharmony_ci * Before doing anything else, read the authentication status. 19862306a36Sopenharmony_ci * If the retimer has it set, store it for the new retimer 19962306a36Sopenharmony_ci * device instance. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) 20262306a36Sopenharmony_ci usb4_port_retimer_nvm_authenticate_status(port, i, &status[i]); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void tb_retimer_set_inbound_sbtx(struct tb_port *port) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci int i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* 21062306a36Sopenharmony_ci * When USB4 port is online sideband communications are 21162306a36Sopenharmony_ci * already up. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci if (!usb4_port_device_is_offline(port->usb4)) 21462306a36Sopenharmony_ci return; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci tb_port_dbg(port, "enabling sideband transactions\n"); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) 21962306a36Sopenharmony_ci usb4_port_retimer_set_inbound_sbtx(port, i); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void tb_retimer_unset_inbound_sbtx(struct tb_port *port) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci int i; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * When USB4 port is offline we need to keep the sideband 22862306a36Sopenharmony_ci * communications up to make it possible to communicate with 22962306a36Sopenharmony_ci * the connected retimers. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci if (usb4_port_device_is_offline(port->usb4)) 23262306a36Sopenharmony_ci return; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci tb_port_dbg(port, "disabling sideband transactions\n"); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci for (i = TB_MAX_RETIMER_INDEX; i >= 1; i--) 23762306a36Sopenharmony_ci usb4_port_retimer_unset_inbound_sbtx(port, i); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic ssize_t nvm_authenticate_store(struct device *dev, 24162306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct tb_retimer *rt = tb_to_retimer(dev); 24462306a36Sopenharmony_ci int val, ret; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci pm_runtime_get_sync(&rt->dev); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (!mutex_trylock(&rt->tb->lock)) { 24962306a36Sopenharmony_ci ret = restart_syscall(); 25062306a36Sopenharmony_ci goto exit_rpm; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (!rt->nvm) { 25462306a36Sopenharmony_ci ret = -EAGAIN; 25562306a36Sopenharmony_ci goto exit_unlock; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ret = kstrtoint(buf, 10, &val); 25962306a36Sopenharmony_ci if (ret) 26062306a36Sopenharmony_ci goto exit_unlock; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* Always clear status */ 26362306a36Sopenharmony_ci rt->auth_status = 0; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (val) { 26662306a36Sopenharmony_ci /* 26762306a36Sopenharmony_ci * When NVM authentication starts the retimer is not 26862306a36Sopenharmony_ci * accessible so calling tb_retimer_unset_inbound_sbtx() 26962306a36Sopenharmony_ci * will fail and therefore we do not call it. Exception 27062306a36Sopenharmony_ci * is when the validation fails or we only write the new 27162306a36Sopenharmony_ci * NVM image without authentication. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci tb_retimer_set_inbound_sbtx(rt->port); 27462306a36Sopenharmony_ci if (val == AUTHENTICATE_ONLY) { 27562306a36Sopenharmony_ci ret = tb_retimer_nvm_authenticate(rt, true); 27662306a36Sopenharmony_ci } else { 27762306a36Sopenharmony_ci if (!rt->nvm->flushed) { 27862306a36Sopenharmony_ci if (!rt->nvm->buf) { 27962306a36Sopenharmony_ci ret = -EINVAL; 28062306a36Sopenharmony_ci goto exit_unlock; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci ret = tb_retimer_nvm_validate_and_write(rt); 28462306a36Sopenharmony_ci if (ret || val == WRITE_ONLY) 28562306a36Sopenharmony_ci goto exit_unlock; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci if (val == WRITE_AND_AUTHENTICATE) 28862306a36Sopenharmony_ci ret = tb_retimer_nvm_authenticate(rt, false); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciexit_unlock: 29362306a36Sopenharmony_ci if (ret || val == WRITE_ONLY) 29462306a36Sopenharmony_ci tb_retimer_unset_inbound_sbtx(rt->port); 29562306a36Sopenharmony_ci mutex_unlock(&rt->tb->lock); 29662306a36Sopenharmony_ciexit_rpm: 29762306a36Sopenharmony_ci pm_runtime_mark_last_busy(&rt->dev); 29862306a36Sopenharmony_ci pm_runtime_put_autosuspend(&rt->dev); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (ret) 30162306a36Sopenharmony_ci return ret; 30262306a36Sopenharmony_ci return count; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(nvm_authenticate); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic ssize_t nvm_version_show(struct device *dev, 30762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct tb_retimer *rt = tb_to_retimer(dev); 31062306a36Sopenharmony_ci int ret; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (!mutex_trylock(&rt->tb->lock)) 31362306a36Sopenharmony_ci return restart_syscall(); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (!rt->nvm) 31662306a36Sopenharmony_ci ret = -EAGAIN; 31762306a36Sopenharmony_ci else 31862306a36Sopenharmony_ci ret = sysfs_emit(buf, "%x.%x\n", rt->nvm->major, rt->nvm->minor); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci mutex_unlock(&rt->tb->lock); 32162306a36Sopenharmony_ci return ret; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(nvm_version); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic ssize_t vendor_show(struct device *dev, struct device_attribute *attr, 32662306a36Sopenharmony_ci char *buf) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct tb_retimer *rt = tb_to_retimer(dev); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return sysfs_emit(buf, "%#x\n", rt->vendor); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(vendor); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic struct attribute *retimer_attrs[] = { 33562306a36Sopenharmony_ci &dev_attr_device.attr, 33662306a36Sopenharmony_ci &dev_attr_nvm_authenticate.attr, 33762306a36Sopenharmony_ci &dev_attr_nvm_version.attr, 33862306a36Sopenharmony_ci &dev_attr_vendor.attr, 33962306a36Sopenharmony_ci NULL 34062306a36Sopenharmony_ci}; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic const struct attribute_group retimer_group = { 34362306a36Sopenharmony_ci .attrs = retimer_attrs, 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic const struct attribute_group *retimer_groups[] = { 34762306a36Sopenharmony_ci &retimer_group, 34862306a36Sopenharmony_ci NULL 34962306a36Sopenharmony_ci}; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void tb_retimer_release(struct device *dev) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct tb_retimer *rt = tb_to_retimer(dev); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci kfree(rt); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistruct device_type tb_retimer_type = { 35962306a36Sopenharmony_ci .name = "thunderbolt_retimer", 36062306a36Sopenharmony_ci .groups = retimer_groups, 36162306a36Sopenharmony_ci .release = tb_retimer_release, 36262306a36Sopenharmony_ci}; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct tb_retimer *rt; 36762306a36Sopenharmony_ci u32 vendor, device; 36862306a36Sopenharmony_ci int ret; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci ret = usb4_port_retimer_read(port, index, USB4_SB_VENDOR_ID, &vendor, 37162306a36Sopenharmony_ci sizeof(vendor)); 37262306a36Sopenharmony_ci if (ret) { 37362306a36Sopenharmony_ci if (ret != -ENODEV) 37462306a36Sopenharmony_ci tb_port_warn(port, "failed read retimer VendorId: %d\n", ret); 37562306a36Sopenharmony_ci return ret; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ret = usb4_port_retimer_read(port, index, USB4_SB_PRODUCT_ID, &device, 37962306a36Sopenharmony_ci sizeof(device)); 38062306a36Sopenharmony_ci if (ret) { 38162306a36Sopenharmony_ci if (ret != -ENODEV) 38262306a36Sopenharmony_ci tb_port_warn(port, "failed read retimer ProductId: %d\n", ret); 38362306a36Sopenharmony_ci return ret; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* 38762306a36Sopenharmony_ci * Check that it supports NVM operations. If not then don't add 38862306a36Sopenharmony_ci * the device at all. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_ci ret = usb4_port_retimer_nvm_sector_size(port, index); 39162306a36Sopenharmony_ci if (ret < 0) 39262306a36Sopenharmony_ci return ret; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci rt = kzalloc(sizeof(*rt), GFP_KERNEL); 39562306a36Sopenharmony_ci if (!rt) 39662306a36Sopenharmony_ci return -ENOMEM; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci rt->index = index; 39962306a36Sopenharmony_ci rt->vendor = vendor; 40062306a36Sopenharmony_ci rt->device = device; 40162306a36Sopenharmony_ci rt->auth_status = auth_status; 40262306a36Sopenharmony_ci rt->port = port; 40362306a36Sopenharmony_ci rt->tb = port->sw->tb; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci rt->dev.parent = &port->usb4->dev; 40662306a36Sopenharmony_ci rt->dev.bus = &tb_bus_type; 40762306a36Sopenharmony_ci rt->dev.type = &tb_retimer_type; 40862306a36Sopenharmony_ci dev_set_name(&rt->dev, "%s:%u.%u", dev_name(&port->sw->dev), 40962306a36Sopenharmony_ci port->port, index); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci ret = device_register(&rt->dev); 41262306a36Sopenharmony_ci if (ret) { 41362306a36Sopenharmony_ci dev_err(&rt->dev, "failed to register retimer: %d\n", ret); 41462306a36Sopenharmony_ci put_device(&rt->dev); 41562306a36Sopenharmony_ci return ret; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ret = tb_retimer_nvm_add(rt); 41962306a36Sopenharmony_ci if (ret) { 42062306a36Sopenharmony_ci dev_err(&rt->dev, "failed to add NVM devices: %d\n", ret); 42162306a36Sopenharmony_ci device_unregister(&rt->dev); 42262306a36Sopenharmony_ci return ret; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci dev_info(&rt->dev, "new retimer found, vendor=%#x device=%#x\n", 42662306a36Sopenharmony_ci rt->vendor, rt->device); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci pm_runtime_no_callbacks(&rt->dev); 42962306a36Sopenharmony_ci pm_runtime_set_active(&rt->dev); 43062306a36Sopenharmony_ci pm_runtime_enable(&rt->dev); 43162306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&rt->dev, TB_AUTOSUSPEND_DELAY); 43262306a36Sopenharmony_ci pm_runtime_mark_last_busy(&rt->dev); 43362306a36Sopenharmony_ci pm_runtime_use_autosuspend(&rt->dev); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic void tb_retimer_remove(struct tb_retimer *rt) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci dev_info(&rt->dev, "retimer disconnected\n"); 44162306a36Sopenharmony_ci tb_nvm_free(rt->nvm); 44262306a36Sopenharmony_ci device_unregister(&rt->dev); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistruct tb_retimer_lookup { 44662306a36Sopenharmony_ci const struct tb_port *port; 44762306a36Sopenharmony_ci u8 index; 44862306a36Sopenharmony_ci}; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic int retimer_match(struct device *dev, void *data) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci const struct tb_retimer_lookup *lookup = data; 45362306a36Sopenharmony_ci struct tb_retimer *rt = tb_to_retimer(dev); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return rt && rt->port == lookup->port && rt->index == lookup->index; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic struct tb_retimer *tb_port_find_retimer(struct tb_port *port, u8 index) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct tb_retimer_lookup lookup = { .port = port, .index = index }; 46162306a36Sopenharmony_ci struct device *dev; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci dev = device_find_child(&port->usb4->dev, &lookup, retimer_match); 46462306a36Sopenharmony_ci if (dev) 46562306a36Sopenharmony_ci return tb_to_retimer(dev); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return NULL; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/** 47162306a36Sopenharmony_ci * tb_retimer_scan() - Scan for on-board retimers under port 47262306a36Sopenharmony_ci * @port: USB4 port to scan 47362306a36Sopenharmony_ci * @add: If true also registers found retimers 47462306a36Sopenharmony_ci * 47562306a36Sopenharmony_ci * Brings the sideband into a state where retimers can be accessed. 47662306a36Sopenharmony_ci * Then Tries to enumerate on-board retimers connected to @port. Found 47762306a36Sopenharmony_ci * retimers are registered as children of @port if @add is set. Does 47862306a36Sopenharmony_ci * not scan for cable retimers for now. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ciint tb_retimer_scan(struct tb_port *port, bool add) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci u32 status[TB_MAX_RETIMER_INDEX + 1] = {}; 48362306a36Sopenharmony_ci int ret, i, last_idx = 0; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* 48662306a36Sopenharmony_ci * Send broadcast RT to make sure retimer indices facing this 48762306a36Sopenharmony_ci * port are set. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci ret = usb4_port_enumerate_retimers(port); 49062306a36Sopenharmony_ci if (ret) 49162306a36Sopenharmony_ci return ret; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* 49462306a36Sopenharmony_ci * Immediately after sending enumerate retimers read the 49562306a36Sopenharmony_ci * authentication status of each retimer. 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ci tb_retimer_nvm_authenticate_status(port, status); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* 50062306a36Sopenharmony_ci * Enable sideband channel for each retimer. We can do this 50162306a36Sopenharmony_ci * regardless whether there is device connected or not. 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_ci tb_retimer_set_inbound_sbtx(port); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) { 50662306a36Sopenharmony_ci /* 50762306a36Sopenharmony_ci * Last retimer is true only for the last on-board 50862306a36Sopenharmony_ci * retimer (the one connected directly to the Type-C 50962306a36Sopenharmony_ci * port). 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ci ret = usb4_port_retimer_is_last(port, i); 51262306a36Sopenharmony_ci if (ret > 0) 51362306a36Sopenharmony_ci last_idx = i; 51462306a36Sopenharmony_ci else if (ret < 0) 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci tb_retimer_unset_inbound_sbtx(port); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (!last_idx) 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Add on-board retimers if they do not exist already */ 52462306a36Sopenharmony_ci ret = 0; 52562306a36Sopenharmony_ci for (i = 1; i <= last_idx; i++) { 52662306a36Sopenharmony_ci struct tb_retimer *rt; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci rt = tb_port_find_retimer(port, i); 52962306a36Sopenharmony_ci if (rt) { 53062306a36Sopenharmony_ci put_device(&rt->dev); 53162306a36Sopenharmony_ci } else if (add) { 53262306a36Sopenharmony_ci ret = tb_retimer_add(port, i, status[i]); 53362306a36Sopenharmony_ci if (ret && ret != -EOPNOTSUPP) 53462306a36Sopenharmony_ci break; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci return ret; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int remove_retimer(struct device *dev, void *data) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct tb_retimer *rt = tb_to_retimer(dev); 54462306a36Sopenharmony_ci struct tb_port *port = data; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (rt && rt->port == port) 54762306a36Sopenharmony_ci tb_retimer_remove(rt); 54862306a36Sopenharmony_ci return 0; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci/** 55262306a36Sopenharmony_ci * tb_retimer_remove_all() - Remove all retimers under port 55362306a36Sopenharmony_ci * @port: USB4 port whose retimers to remove 55462306a36Sopenharmony_ci * 55562306a36Sopenharmony_ci * This removes all previously added retimers under @port. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_civoid tb_retimer_remove_all(struct tb_port *port) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct usb4_port *usb4; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci usb4 = port->usb4; 56262306a36Sopenharmony_ci if (usb4) 56362306a36Sopenharmony_ci device_for_each_child_reverse(&usb4->dev, port, 56462306a36Sopenharmony_ci remove_retimer); 56562306a36Sopenharmony_ci} 566