12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Thunderbolt driver - Tunneling support
- *
- * Copyright (c) 2014 Andreas Noever <[email protected]>
- * Copyright (C) 2019, Intel Corporation
- */
- #include <linux/delay.h>
- #include <linux/slab.h>
- #include <linux/list.h>
- #include "tunnel.h"
- #include "tb.h"
- /* PCIe adapters use always HopID of 8 for both directions */
- #define TB_PCI_HOPID 8
- #define TB_PCI_PATH_DOWN 0
- #define TB_PCI_PATH_UP 1
- /* USB3 adapters use always HopID of 8 for both directions */
- #define TB_USB3_HOPID 8
- #define TB_USB3_PATH_DOWN 0
- #define TB_USB3_PATH_UP 1
- /* DP adapters use HopID 8 for AUX and 9 for Video */
- #define TB_DP_AUX_TX_HOPID 8
- #define TB_DP_AUX_RX_HOPID 8
- #define TB_DP_VIDEO_HOPID 9
- #define TB_DP_VIDEO_PATH_OUT 0
- #define TB_DP_AUX_PATH_OUT 1
- #define TB_DP_AUX_PATH_IN 2
- /* Minimum number of credits needed for PCIe path */
- #define TB_MIN_PCIE_CREDITS 6U
- /*
- * Number of credits we try to allocate for each DMA path if not limited
- * by the host router baMaxHI.
- */
- #define TB_DMA_CREDITS 14U
- /* Minimum number of credits for DMA path */
- #define TB_MIN_DMA_CREDITS 1U
- static const char * const tb_tunnel_names[] = { "PCI", "DP", "DMA", "USB3" };
- #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \
- do { \
- struct tb_tunnel *__tunnel = (tunnel); \
- level(__tunnel->tb, "%llx:%x <-> %llx:%x (%s): " fmt, \
- tb_route(__tunnel->src_port->sw), \
- __tunnel->src_port->port, \
- tb_route(__tunnel->dst_port->sw), \
- __tunnel->dst_port->port, \
- tb_tunnel_names[__tunnel->type], \
- ## arg); \
- } while (0)
- #define tb_tunnel_WARN(tunnel, fmt, arg...) \
- __TB_TUNNEL_PRINT(tb_WARN, tunnel, fmt, ##arg)
- #define tb_tunnel_warn(tunnel, fmt, arg...) \
- __TB_TUNNEL_PRINT(tb_warn, tunnel, fmt, ##arg)
- #define tb_tunnel_info(tunnel, fmt, arg...) \
- __TB_TUNNEL_PRINT(tb_info, tunnel, fmt, ##arg)
- #define tb_tunnel_dbg(tunnel, fmt, arg...) \
- __TB_TUNNEL_PRINT(tb_dbg, tunnel, fmt, ##arg)
- static inline unsigned int tb_usable_credits(const struct tb_port *port)
- {
- return port->total_credits - port->ctl_credits;
- }
- /**
- * tb_available_credits() - Available credits for PCIe and DMA
- * @port: Lane adapter to check
- * @max_dp_streams: If non-%NULL stores maximum number of simultaneous DP
- * streams possible through this lane adapter
- */
- static unsigned int tb_available_credits(const struct tb_port *port,
- size_t *max_dp_streams)
- {
- const struct tb_switch *sw = port->sw;
- int credits, usb3, pcie, spare;
- size_t ndp;
- usb3 = tb_acpi_may_tunnel_usb3() ? sw->max_usb3_credits : 0;
- pcie = tb_acpi_may_tunnel_pcie() ? sw->max_pcie_credits : 0;
- if (tb_acpi_is_xdomain_allowed()) {
- spare = min_not_zero(sw->max_dma_credits, TB_DMA_CREDITS);
- /* Add some credits for potential second DMA tunnel */
- spare += TB_MIN_DMA_CREDITS;
- } else {
- spare = 0;
- }
- credits = tb_usable_credits(port);
- if (tb_acpi_may_tunnel_dp()) {
- /*
- * Maximum number of DP streams possible through the
- * lane adapter.
- */
- if (sw->min_dp_aux_credits + sw->min_dp_main_credits)
- ndp = (credits - (usb3 + pcie + spare)) /
- (sw->min_dp_aux_credits + sw->min_dp_main_credits);
- else
- ndp = 0;
- } else {
- ndp = 0;
- }
- credits -= ndp * (sw->min_dp_aux_credits + sw->min_dp_main_credits);
- credits -= usb3;
- if (max_dp_streams)
- *max_dp_streams = ndp;
- return credits > 0 ? credits : 0;
- }
- static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths,
- enum tb_tunnel_type type)
- {
- struct tb_tunnel *tunnel;
- tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL);
- if (!tunnel)
- return NULL;
- tunnel->paths = kcalloc(npaths, sizeof(tunnel->paths[0]), GFP_KERNEL);
- if (!tunnel->paths) {
- tb_tunnel_free(tunnel);
- return NULL;
- }
- INIT_LIST_HEAD(&tunnel->list);
- tunnel->tb = tb;
- tunnel->npaths = npaths;
- tunnel->type = type;
- return tunnel;
- }
- static int tb_pci_activate(struct tb_tunnel *tunnel, bool activate)
- {
- int res;
- res = tb_pci_port_enable(tunnel->src_port, activate);
- if (res)
- return res;
- if (tb_port_is_pcie_up(tunnel->dst_port))
- return tb_pci_port_enable(tunnel->dst_port, activate);
- return 0;
- }
- static int tb_pci_init_credits(struct tb_path_hop *hop)
- {
- struct tb_port *port = hop->in_port;
- struct tb_switch *sw = port->sw;
- unsigned int credits;
- if (tb_port_use_credit_allocation(port)) {
- unsigned int available;
- available = tb_available_credits(port, NULL);
- credits = min(sw->max_pcie_credits, available);
- if (credits < TB_MIN_PCIE_CREDITS)
- return -ENOSPC;
- credits = max(TB_MIN_PCIE_CREDITS, credits);
- } else {
- if (tb_port_is_null(port))
- credits = port->bonded ? 32 : 16;
- else
- credits = 7;
- }
- hop->initial_credits = credits;
- return 0;
- }
- static int tb_pci_init_path(struct tb_path *path)
- {
- struct tb_path_hop *hop;
- path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
- path->egress_shared_buffer = TB_PATH_NONE;
- path->ingress_fc_enable = TB_PATH_ALL;
- path->ingress_shared_buffer = TB_PATH_NONE;
- path->priority = 3;
- path->weight = 1;
- path->drop_packages = 0;
- tb_path_for_each_hop(path, hop) {
- int ret;
- ret = tb_pci_init_credits(hop);
- if (ret)
- return ret;
- }
- return 0;
- }
- /**
- * tb_tunnel_discover_pci() - Discover existing PCIe tunnels
- * @tb: Pointer to the domain structure
- * @down: PCIe downstream adapter
- * @alloc_hopid: Allocate HopIDs from visited ports
- *
- * If @down adapter is active, follows the tunnel to the PCIe upstream
- * adapter and back. Returns the discovered tunnel or %NULL if there was
- * no tunnel.
- */
- struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,
- bool alloc_hopid)
- {
- struct tb_tunnel *tunnel;
- struct tb_path *path;
- if (!tb_pci_port_is_enabled(down))
- return NULL;
- tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_PCI);
- if (!tunnel)
- return NULL;
- tunnel->activate = tb_pci_activate;
- tunnel->src_port = down;
- /*
- * Discover both paths even if they are not complete. We will
- * clean them up by calling tb_tunnel_deactivate() below in that
- * case.
- */
- path = tb_path_discover(down, TB_PCI_HOPID, NULL, -1,
- &tunnel->dst_port, "PCIe Up", alloc_hopid);
- if (!path) {
- /* Just disable the downstream port */
- tb_pci_port_enable(down, false);
- goto err_free;
- }
- tunnel->paths[TB_PCI_PATH_UP] = path;
- if (tb_pci_init_path(tunnel->paths[TB_PCI_PATH_UP]))
- goto err_free;
- path = tb_path_discover(tunnel->dst_port, -1, down, TB_PCI_HOPID, NULL,
- "PCIe Down", alloc_hopid);
- if (!path)
- goto err_deactivate;
- tunnel->paths[TB_PCI_PATH_DOWN] = path;
- if (tb_pci_init_path(tunnel->paths[TB_PCI_PATH_DOWN]))
- goto err_deactivate;
- /* Validate that the tunnel is complete */
- if (!tb_port_is_pcie_up(tunnel->dst_port)) {
- tb_port_warn(tunnel->dst_port,
- "path does not end on a PCIe adapter, cleaning up\n");
- goto err_deactivate;
- }
- if (down != tunnel->src_port) {
- tb_tunnel_warn(tunnel, "path is not complete, cleaning up\n");
- goto err_deactivate;
- }
- if (!tb_pci_port_is_enabled(tunnel->dst_port)) {
- tb_tunnel_warn(tunnel,
- "tunnel is not fully activated, cleaning up\n");
- goto err_deactivate;
- }
- tb_tunnel_dbg(tunnel, "discovered\n");
- return tunnel;
- err_deactivate:
- tb_tunnel_deactivate(tunnel);
- err_free:
- tb_tunnel_free(tunnel);
- return NULL;
- }
- /**
- * tb_tunnel_alloc_pci() - allocate a pci tunnel
- * @tb: Pointer to the domain structure
- * @up: PCIe upstream adapter port
- * @down: PCIe downstream adapter port
- *
- * Allocate a PCI tunnel. The ports must be of type TB_TYPE_PCIE_UP and
- * TB_TYPE_PCIE_DOWN.
- *
- * Return: Returns a tb_tunnel on success or NULL on failure.
- */
- struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
- struct tb_port *down)
- {
- struct tb_tunnel *tunnel;
- struct tb_path *path;
- tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_PCI);
- if (!tunnel)
- return NULL;
- tunnel->activate = tb_pci_activate;
- tunnel->src_port = down;
- tunnel->dst_port = up;
- path = tb_path_alloc(tb, down, TB_PCI_HOPID, up, TB_PCI_HOPID, 0,
- "PCIe Down");
- if (!path)
- goto err_free;
- tunnel->paths[TB_PCI_PATH_DOWN] = path;
- if (tb_pci_init_path(path))
- goto err_free;
- path = tb_path_alloc(tb, up, TB_PCI_HOPID, down, TB_PCI_HOPID, 0,
- "PCIe Up");
- if (!path)
- goto err_free;
- tunnel->paths[TB_PCI_PATH_UP] = path;
- if (tb_pci_init_path(path))
- goto err_free;
- return tunnel;
- err_free:
- tb_tunnel_free(tunnel);
- return NULL;
- }
- static bool tb_dp_is_usb4(const struct tb_switch *sw)
- {
- /* Titan Ridge DP adapters need the same treatment as USB4 */
- return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw);
- }
- static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out)
- {
- int timeout = 10;
- u32 val;
- int ret;
- /* Both ends need to support this */
- if (!tb_dp_is_usb4(in->sw) || !tb_dp_is_usb4(out->sw))
- return 0;
- ret = tb_port_read(out, &val, TB_CFG_PORT,
- out->cap_adap + DP_STATUS_CTRL, 1);
- if (ret)
- return ret;
- val |= DP_STATUS_CTRL_UF | DP_STATUS_CTRL_CMHS;
- ret = tb_port_write(out, &val, TB_CFG_PORT,
- out->cap_adap + DP_STATUS_CTRL, 1);
- if (ret)
- return ret;
- do {
- ret = tb_port_read(out, &val, TB_CFG_PORT,
- out->cap_adap + DP_STATUS_CTRL, 1);
- if (ret)
- return ret;
- if (!(val & DP_STATUS_CTRL_CMHS))
- return 0;
- usleep_range(10, 100);
- } while (timeout--);
- return -ETIMEDOUT;
- }
- static inline u32 tb_dp_cap_get_rate(u32 val)
- {
- u32 rate = (val & DP_COMMON_CAP_RATE_MASK) >> DP_COMMON_CAP_RATE_SHIFT;
- switch (rate) {
- case DP_COMMON_CAP_RATE_RBR:
- return 1620;
- case DP_COMMON_CAP_RATE_HBR:
- return 2700;
- case DP_COMMON_CAP_RATE_HBR2:
- return 5400;
- case DP_COMMON_CAP_RATE_HBR3:
- return 8100;
- default:
- return 0;
- }
- }
- static inline u32 tb_dp_cap_set_rate(u32 val, u32 rate)
- {
- val &= ~DP_COMMON_CAP_RATE_MASK;
- switch (rate) {
- default:
- WARN(1, "invalid rate %u passed, defaulting to 1620 MB/s\n", rate);
- fallthrough;
- case 1620:
- val |= DP_COMMON_CAP_RATE_RBR << DP_COMMON_CAP_RATE_SHIFT;
- break;
- case 2700:
- val |= DP_COMMON_CAP_RATE_HBR << DP_COMMON_CAP_RATE_SHIFT;
- break;
- case 5400:
- val |= DP_COMMON_CAP_RATE_HBR2 << DP_COMMON_CAP_RATE_SHIFT;
- break;
- case 8100:
- val |= DP_COMMON_CAP_RATE_HBR3 << DP_COMMON_CAP_RATE_SHIFT;
- break;
- }
- return val;
- }
- static inline u32 tb_dp_cap_get_lanes(u32 val)
- {
- u32 lanes = (val & DP_COMMON_CAP_LANES_MASK) >> DP_COMMON_CAP_LANES_SHIFT;
- switch (lanes) {
- case DP_COMMON_CAP_1_LANE:
- return 1;
- case DP_COMMON_CAP_2_LANES:
- return 2;
- case DP_COMMON_CAP_4_LANES:
- return 4;
- default:
- return 0;
- }
- }
- static inline u32 tb_dp_cap_set_lanes(u32 val, u32 lanes)
- {
- val &= ~DP_COMMON_CAP_LANES_MASK;
- switch (lanes) {
- default:
- WARN(1, "invalid number of lanes %u passed, defaulting to 1\n",
- lanes);
- fallthrough;
- case 1:
- val |= DP_COMMON_CAP_1_LANE << DP_COMMON_CAP_LANES_SHIFT;
- break;
- case 2:
- val |= DP_COMMON_CAP_2_LANES << DP_COMMON_CAP_LANES_SHIFT;
- break;
- case 4:
- val |= DP_COMMON_CAP_4_LANES << DP_COMMON_CAP_LANES_SHIFT;
- break;
- }
- return val;
- }
- static unsigned int tb_dp_bandwidth(unsigned int rate, unsigned int lanes)
- {
- /* Tunneling removes the DP 8b/10b encoding */
- return rate * lanes * 8 / 10;
- }
- static int tb_dp_reduce_bandwidth(int max_bw, u32 in_rate, u32 in_lanes,
- u32 out_rate, u32 out_lanes, u32 *new_rate,
- u32 *new_lanes)
- {
- static const u32 dp_bw[][2] = {
- /* Mb/s, lanes */
- { 8100, 4 }, /* 25920 Mb/s */
- { 5400, 4 }, /* 17280 Mb/s */
- { 8100, 2 }, /* 12960 Mb/s */
- { 2700, 4 }, /* 8640 Mb/s */
- { 5400, 2 }, /* 8640 Mb/s */
- { 8100, 1 }, /* 6480 Mb/s */
- { 1620, 4 }, /* 5184 Mb/s */
- { 5400, 1 }, /* 4320 Mb/s */
- { 2700, 2 }, /* 4320 Mb/s */
- { 1620, 2 }, /* 2592 Mb/s */
- { 2700, 1 }, /* 2160 Mb/s */
- { 1620, 1 }, /* 1296 Mb/s */
- };
- unsigned int i;
- /*
- * Find a combination that can fit into max_bw and does not
- * exceed the maximum rate and lanes supported by the DP OUT and
- * DP IN adapters.
- */
- for (i = 0; i < ARRAY_SIZE(dp_bw); i++) {
- if (dp_bw[i][0] > out_rate || dp_bw[i][1] > out_lanes)
- continue;
- if (dp_bw[i][0] > in_rate || dp_bw[i][1] > in_lanes)
- continue;
- if (tb_dp_bandwidth(dp_bw[i][0], dp_bw[i][1]) <= max_bw) {
- *new_rate = dp_bw[i][0];
- *new_lanes = dp_bw[i][1];
- return 0;
- }
- }
- return -ENOSR;
- }
- static int tb_dp_xchg_caps(struct tb_tunnel *tunnel)
- {
- u32 out_dp_cap, out_rate, out_lanes, in_dp_cap, in_rate, in_lanes, bw;
- struct tb_port *out = tunnel->dst_port;
- struct tb_port *in = tunnel->src_port;
- int ret, max_bw;
- /*
- * Copy DP_LOCAL_CAP register to DP_REMOTE_CAP register for
- * newer generation hardware.
- */
- if (in->sw->generation < 2 || out->sw->generation < 2)
- return 0;
- /*
- * Perform connection manager handshake between IN and OUT ports
- * before capabilities exchange can take place.
- */
- ret = tb_dp_cm_handshake(in, out);
- if (ret)
- return ret;
- /* Read both DP_LOCAL_CAP registers */
- ret = tb_port_read(in, &in_dp_cap, TB_CFG_PORT,
- in->cap_adap + DP_LOCAL_CAP, 1);
- if (ret)
- return ret;
- ret = tb_port_read(out, &out_dp_cap, TB_CFG_PORT,
- out->cap_adap + DP_LOCAL_CAP, 1);
- if (ret)
- return ret;
- /* Write IN local caps to OUT remote caps */
- ret = tb_port_write(out, &in_dp_cap, TB_CFG_PORT,
- out->cap_adap + DP_REMOTE_CAP, 1);
- if (ret)
- return ret;
- in_rate = tb_dp_cap_get_rate(in_dp_cap);
- in_lanes = tb_dp_cap_get_lanes(in_dp_cap);
- tb_port_dbg(in, "maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n",
- in_rate, in_lanes, tb_dp_bandwidth(in_rate, in_lanes));
- /*
- * If the tunnel bandwidth is limited (max_bw is set) then see
- * if we need to reduce bandwidth to fit there.
- */
- out_rate = tb_dp_cap_get_rate(out_dp_cap);
- out_lanes = tb_dp_cap_get_lanes(out_dp_cap);
- bw = tb_dp_bandwidth(out_rate, out_lanes);
- tb_port_dbg(out, "maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n",
- out_rate, out_lanes, bw);
- if (in->sw->config.depth < out->sw->config.depth)
- max_bw = tunnel->max_down;
- else
- max_bw = tunnel->max_up;
- if (max_bw && bw > max_bw) {
- u32 new_rate, new_lanes, new_bw;
- ret = tb_dp_reduce_bandwidth(max_bw, in_rate, in_lanes,
- out_rate, out_lanes, &new_rate,
- &new_lanes);
- if (ret) {
- tb_port_info(out, "not enough bandwidth for DP tunnel\n");
- return ret;
- }
- new_bw = tb_dp_bandwidth(new_rate, new_lanes);
- tb_port_dbg(out, "bandwidth reduced to %u Mb/s x%u = %u Mb/s\n",
- new_rate, new_lanes, new_bw);
- /*
- * Set new rate and number of lanes before writing it to
- * the IN port remote caps.
- */
- out_dp_cap = tb_dp_cap_set_rate(out_dp_cap, new_rate);
- out_dp_cap = tb_dp_cap_set_lanes(out_dp_cap, new_lanes);
- }
- /*
- * Titan Ridge does not disable AUX timers when it gets
- * SET_CONFIG with SET_LTTPR_MODE set. This causes problems with
- * DP tunneling.
- */
- if (tb_route(out->sw) && tb_switch_is_titan_ridge(out->sw)) {
- out_dp_cap |= DP_COMMON_CAP_LTTPR_NS;
- tb_port_dbg(out, "disabling LTTPR\n");
- }
- return tb_port_write(in, &out_dp_cap, TB_CFG_PORT,
- in->cap_adap + DP_REMOTE_CAP, 1);
- }
- static int tb_dp_activate(struct tb_tunnel *tunnel, bool active)
- {
- int ret;
- if (active) {
- struct tb_path **paths;
- int last;
- paths = tunnel->paths;
- last = paths[TB_DP_VIDEO_PATH_OUT]->path_length - 1;
- tb_dp_port_set_hops(tunnel->src_port,
- paths[TB_DP_VIDEO_PATH_OUT]->hops[0].in_hop_index,
- paths[TB_DP_AUX_PATH_OUT]->hops[0].in_hop_index,
- paths[TB_DP_AUX_PATH_IN]->hops[last].next_hop_index);
- tb_dp_port_set_hops(tunnel->dst_port,
- paths[TB_DP_VIDEO_PATH_OUT]->hops[last].next_hop_index,
- paths[TB_DP_AUX_PATH_IN]->hops[0].in_hop_index,
- paths[TB_DP_AUX_PATH_OUT]->hops[last].next_hop_index);
- } else {
- tb_dp_port_hpd_clear(tunnel->src_port);
- tb_dp_port_set_hops(tunnel->src_port, 0, 0, 0);
- if (tb_port_is_dpout(tunnel->dst_port))
- tb_dp_port_set_hops(tunnel->dst_port, 0, 0, 0);
- }
- ret = tb_dp_port_enable(tunnel->src_port, active);
- if (ret)
- return ret;
- if (tb_port_is_dpout(tunnel->dst_port))
- return tb_dp_port_enable(tunnel->dst_port, active);
- return 0;
- }
- static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
- int *consumed_down)
- {
- struct tb_port *in = tunnel->src_port;
- const struct tb_switch *sw = in->sw;
- u32 val, rate = 0, lanes = 0;
- int ret;
- if (tb_dp_is_usb4(sw)) {
- int timeout = 20;
- /*
- * Wait for DPRX done. Normally it should be already set
- * for active tunnel.
- */
- do {
- ret = tb_port_read(in, &val, TB_CFG_PORT,
- in->cap_adap + DP_COMMON_CAP, 1);
- if (ret)
- return ret;
- if (val & DP_COMMON_CAP_DPRX_DONE) {
- rate = tb_dp_cap_get_rate(val);
- lanes = tb_dp_cap_get_lanes(val);
- break;
- }
- msleep(250);
- } while (timeout--);
- if (!timeout)
- return -ETIMEDOUT;
- } else if (sw->generation >= 2) {
- /*
- * Read from the copied remote cap so that we take into
- * account if capabilities were reduced during exchange.
- */
- ret = tb_port_read(in, &val, TB_CFG_PORT,
- in->cap_adap + DP_REMOTE_CAP, 1);
- if (ret)
- return ret;
- rate = tb_dp_cap_get_rate(val);
- lanes = tb_dp_cap_get_lanes(val);
- } else {
- /* No bandwidth management for legacy devices */
- *consumed_up = 0;
- *consumed_down = 0;
- return 0;
- }
- if (in->sw->config.depth < tunnel->dst_port->sw->config.depth) {
- *consumed_up = 0;
- *consumed_down = tb_dp_bandwidth(rate, lanes);
- } else {
- *consumed_up = tb_dp_bandwidth(rate, lanes);
- *consumed_down = 0;
- }
- return 0;
- }
- static void tb_dp_init_aux_credits(struct tb_path_hop *hop)
- {
- struct tb_port *port = hop->in_port;
- struct tb_switch *sw = port->sw;
- if (tb_port_use_credit_allocation(port))
- hop->initial_credits = sw->min_dp_aux_credits;
- else
- hop->initial_credits = 1;
- }
- static void tb_dp_init_aux_path(struct tb_path *path)
- {
- struct tb_path_hop *hop;
- path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
- path->egress_shared_buffer = TB_PATH_NONE;
- path->ingress_fc_enable = TB_PATH_ALL;
- path->ingress_shared_buffer = TB_PATH_NONE;
- path->priority = 2;
- path->weight = 1;
- tb_path_for_each_hop(path, hop)
- tb_dp_init_aux_credits(hop);
- }
- static int tb_dp_init_video_credits(struct tb_path_hop *hop)
- {
- struct tb_port *port = hop->in_port;
- struct tb_switch *sw = port->sw;
- if (tb_port_use_credit_allocation(port)) {
- unsigned int nfc_credits;
- size_t max_dp_streams;
- tb_available_credits(port, &max_dp_streams);
- /*
- * Read the number of currently allocated NFC credits
- * from the lane adapter. Since we only use them for DP
- * tunneling we can use that to figure out how many DP
- * tunnels already go through the lane adapter.
- */
- nfc_credits = port->config.nfc_credits &
- ADP_CS_4_NFC_BUFFERS_MASK;
- if (nfc_credits / sw->min_dp_main_credits > max_dp_streams)
- return -ENOSPC;
- hop->nfc_credits = sw->min_dp_main_credits;
- } else {
- hop->nfc_credits = min(port->total_credits - 2, 12U);
- }
- return 0;
- }
- static int tb_dp_init_video_path(struct tb_path *path)
- {
- struct tb_path_hop *hop;
- path->egress_fc_enable = TB_PATH_NONE;
- path->egress_shared_buffer = TB_PATH_NONE;
- path->ingress_fc_enable = TB_PATH_NONE;
- path->ingress_shared_buffer = TB_PATH_NONE;
- path->priority = 1;
- path->weight = 1;
- tb_path_for_each_hop(path, hop) {
- int ret;
- ret = tb_dp_init_video_credits(hop);
- if (ret)
- return ret;
- }
- return 0;
- }
- /**
- * tb_tunnel_discover_dp() - Discover existing Display Port tunnels
- * @tb: Pointer to the domain structure
- * @in: DP in adapter
- * @alloc_hopid: Allocate HopIDs from visited ports
- *
- * If @in adapter is active, follows the tunnel to the DP out adapter
- * and back. Returns the discovered tunnel or %NULL if there was no
- * tunnel.
- *
- * Return: DP tunnel or %NULL if no tunnel found.
- */
- struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
- bool alloc_hopid)
- {
- struct tb_tunnel *tunnel;
- struct tb_port *port;
- struct tb_path *path;
- if (!tb_dp_port_is_enabled(in))
- return NULL;
- tunnel = tb_tunnel_alloc(tb, 3, TB_TUNNEL_DP);
- if (!tunnel)
- return NULL;
- tunnel->init = tb_dp_xchg_caps;
- tunnel->activate = tb_dp_activate;
- tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth;
- tunnel->src_port = in;
- path = tb_path_discover(in, TB_DP_VIDEO_HOPID, NULL, -1,
- &tunnel->dst_port, "Video", alloc_hopid);
- if (!path) {
- /* Just disable the DP IN port */
- tb_dp_port_enable(in, false);
- goto err_free;
- }
- tunnel->paths[TB_DP_VIDEO_PATH_OUT] = path;
- if (tb_dp_init_video_path(tunnel->paths[TB_DP_VIDEO_PATH_OUT]))
- goto err_free;
- path = tb_path_discover(in, TB_DP_AUX_TX_HOPID, NULL, -1, NULL, "AUX TX",
- alloc_hopid);
- if (!path)
- goto err_deactivate;
- tunnel->paths[TB_DP_AUX_PATH_OUT] = path;
- tb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_OUT]);
- path = tb_path_discover(tunnel->dst_port, -1, in, TB_DP_AUX_RX_HOPID,
- &port, "AUX RX", alloc_hopid);
- if (!path)
- goto err_deactivate;
- tunnel->paths[TB_DP_AUX_PATH_IN] = path;
- tb_dp_init_aux_path(tunnel->paths[TB_DP_AUX_PATH_IN]);
- /* Validate that the tunnel is complete */
- if (!tb_port_is_dpout(tunnel->dst_port)) {
- tb_port_warn(in, "path does not end on a DP adapter, cleaning up\n");
- goto err_deactivate;
- }
- if (!tb_dp_port_is_enabled(tunnel->dst_port))
- goto err_deactivate;
- if (!tb_dp_port_hpd_is_active(tunnel->dst_port))
- goto err_deactivate;
- if (port != tunnel->src_port) {
- tb_tunnel_warn(tunnel, "path is not complete, cleaning up\n");
- goto err_deactivate;
- }
- tb_tunnel_dbg(tunnel, "discovered\n");
- return tunnel;
- err_deactivate:
- tb_tunnel_deactivate(tunnel);
- err_free:
- tb_tunnel_free(tunnel);
- return NULL;
- }
- /**
- * tb_tunnel_alloc_dp() - allocate a Display Port tunnel
- * @tb: Pointer to the domain structure
- * @in: DP in adapter port
- * @out: DP out adapter port
- * @link_nr: Preferred lane adapter when the link is not bonded
- * @max_up: Maximum available upstream bandwidth for the DP tunnel (%0
- * if not limited)
- * @max_down: Maximum available downstream bandwidth for the DP tunnel
- * (%0 if not limited)
- *
- * Allocates a tunnel between @in and @out that is capable of tunneling
- * Display Port traffic.
- *
- * Return: Returns a tb_tunnel on success or NULL on failure.
- */
- struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
- struct tb_port *out, int link_nr,
- int max_up, int max_down)
- {
- struct tb_tunnel *tunnel;
- struct tb_path **paths;
- struct tb_path *path;
- if (WARN_ON(!in->cap_adap || !out->cap_adap))
- return NULL;
- tunnel = tb_tunnel_alloc(tb, 3, TB_TUNNEL_DP);
- if (!tunnel)
- return NULL;
- tunnel->init = tb_dp_xchg_caps;
- tunnel->activate = tb_dp_activate;
- tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth;
- tunnel->src_port = in;
- tunnel->dst_port = out;
- tunnel->max_up = max_up;
- tunnel->max_down = max_down;
- paths = tunnel->paths;
- path = tb_path_alloc(tb, in, TB_DP_VIDEO_HOPID, out, TB_DP_VIDEO_HOPID,
- link_nr, "Video");
- if (!path)
- goto err_free;
- tb_dp_init_video_path(path);
- paths[TB_DP_VIDEO_PATH_OUT] = path;
- path = tb_path_alloc(tb, in, TB_DP_AUX_TX_HOPID, out,
- TB_DP_AUX_TX_HOPID, link_nr, "AUX TX");
- if (!path)
- goto err_free;
- tb_dp_init_aux_path(path);
- paths[TB_DP_AUX_PATH_OUT] = path;
- path = tb_path_alloc(tb, out, TB_DP_AUX_RX_HOPID, in,
- TB_DP_AUX_RX_HOPID, link_nr, "AUX RX");
- if (!path)
- goto err_free;
- tb_dp_init_aux_path(path);
- paths[TB_DP_AUX_PATH_IN] = path;
- return tunnel;
- err_free:
- tb_tunnel_free(tunnel);
- return NULL;
- }
- static unsigned int tb_dma_available_credits(const struct tb_port *port)
- {
- const struct tb_switch *sw = port->sw;
- int credits;
- credits = tb_available_credits(port, NULL);
- if (tb_acpi_may_tunnel_pcie())
- credits -= sw->max_pcie_credits;
- credits -= port->dma_credits;
- return credits > 0 ? credits : 0;
- }
- static int tb_dma_reserve_credits(struct tb_path_hop *hop, unsigned int credits)
- {
- struct tb_port *port = hop->in_port;
- if (tb_port_use_credit_allocation(port)) {
- unsigned int available = tb_dma_available_credits(port);
- /*
- * Need to have at least TB_MIN_DMA_CREDITS, otherwise
- * DMA path cannot be established.
- */
- if (available < TB_MIN_DMA_CREDITS)
- return -ENOSPC;
- while (credits > available)
- credits--;
- tb_port_dbg(port, "reserving %u credits for DMA path\n",
- credits);
- port->dma_credits += credits;
- } else {
- if (tb_port_is_null(port))
- credits = port->bonded ? 14 : 6;
- else
- credits = min(port->total_credits, credits);
- }
- hop->initial_credits = credits;
- return 0;
- }
- /* Path from lane adapter to NHI */
- static int tb_dma_init_rx_path(struct tb_path *path, unsigned int credits)
- {
- struct tb_path_hop *hop;
- unsigned int i, tmp;
- path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
- path->ingress_fc_enable = TB_PATH_ALL;
- path->egress_shared_buffer = TB_PATH_NONE;
- path->ingress_shared_buffer = TB_PATH_NONE;
- path->priority = 5;
- path->weight = 1;
- path->clear_fc = true;
- /*
- * First lane adapter is the one connected to the remote host.
- * We don't tunnel other traffic over this link so can use all
- * the credits (except the ones reserved for control traffic).
- */
- hop = &path->hops[0];
- tmp = min(tb_usable_credits(hop->in_port), credits);
- hop->initial_credits = tmp;
- hop->in_port->dma_credits += tmp;
- for (i = 1; i < path->path_length; i++) {
- int ret;
- ret = tb_dma_reserve_credits(&path->hops[i], credits);
- if (ret)
- return ret;
- }
- return 0;
- }
- /* Path from NHI to lane adapter */
- static int tb_dma_init_tx_path(struct tb_path *path, unsigned int credits)
- {
- struct tb_path_hop *hop;
- path->egress_fc_enable = TB_PATH_ALL;
- path->ingress_fc_enable = TB_PATH_ALL;
- path->egress_shared_buffer = TB_PATH_NONE;
- path->ingress_shared_buffer = TB_PATH_NONE;
- path->priority = 5;
- path->weight = 1;
- path->clear_fc = true;
- tb_path_for_each_hop(path, hop) {
- int ret;
- ret = tb_dma_reserve_credits(hop, credits);
- if (ret)
- return ret;
- }
- return 0;
- }
- static void tb_dma_release_credits(struct tb_path_hop *hop)
- {
- struct tb_port *port = hop->in_port;
- if (tb_port_use_credit_allocation(port)) {
- port->dma_credits -= hop->initial_credits;
- tb_port_dbg(port, "released %u DMA path credits\n",
- hop->initial_credits);
- }
- }
- static void tb_dma_deinit_path(struct tb_path *path)
- {
- struct tb_path_hop *hop;
- tb_path_for_each_hop(path, hop)
- tb_dma_release_credits(hop);
- }
- static void tb_dma_deinit(struct tb_tunnel *tunnel)
- {
- int i;
- for (i = 0; i < tunnel->npaths; i++) {
- if (!tunnel->paths[i])
- continue;
- tb_dma_deinit_path(tunnel->paths[i]);
- }
- }
- /**
- * tb_tunnel_alloc_dma() - allocate a DMA tunnel
- * @tb: Pointer to the domain structure
- * @nhi: Host controller port
- * @dst: Destination null port which the other domain is connected to
- * @transmit_path: HopID used for transmitting packets
- * @transmit_ring: NHI ring number used to send packets towards the
- * other domain. Set to %-1 if TX path is not needed.
- * @receive_path: HopID used for receiving packets
- * @receive_ring: NHI ring number used to receive packets from the
- * other domain. Set to %-1 if RX path is not needed.
- *
- * Return: Returns a tb_tunnel on success or NULL on failure.
- */
- struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
- struct tb_port *dst, int transmit_path,
- int transmit_ring, int receive_path,
- int receive_ring)
- {
- struct tb_tunnel *tunnel;
- size_t npaths = 0, i = 0;
- struct tb_path *path;
- int credits;
- if (receive_ring > 0)
- npaths++;
- if (transmit_ring > 0)
- npaths++;
- if (WARN_ON(!npaths))
- return NULL;
- tunnel = tb_tunnel_alloc(tb, npaths, TB_TUNNEL_DMA);
- if (!tunnel)
- return NULL;
- tunnel->src_port = nhi;
- tunnel->dst_port = dst;
- tunnel->deinit = tb_dma_deinit;
- credits = min_not_zero(TB_DMA_CREDITS, nhi->sw->max_dma_credits);
- if (receive_ring > 0) {
- path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0,
- "DMA RX");
- if (!path)
- goto err_free;
- tunnel->paths[i++] = path;
- if (tb_dma_init_rx_path(path, credits)) {
- tb_tunnel_dbg(tunnel, "not enough buffers for RX path\n");
- goto err_free;
- }
- }
- if (transmit_ring > 0) {
- path = tb_path_alloc(tb, nhi, transmit_ring, dst, transmit_path, 0,
- "DMA TX");
- if (!path)
- goto err_free;
- tunnel->paths[i++] = path;
- if (tb_dma_init_tx_path(path, credits)) {
- tb_tunnel_dbg(tunnel, "not enough buffers for TX path\n");
- goto err_free;
- }
- }
- return tunnel;
- err_free:
- tb_tunnel_free(tunnel);
- return NULL;
- }
- /**
- * tb_tunnel_match_dma() - Match DMA tunnel
- * @tunnel: Tunnel to match
- * @transmit_path: HopID used for transmitting packets. Pass %-1 to ignore.
- * @transmit_ring: NHI ring number used to send packets towards the
- * other domain. Pass %-1 to ignore.
- * @receive_path: HopID used for receiving packets. Pass %-1 to ignore.
- * @receive_ring: NHI ring number used to receive packets from the
- * other domain. Pass %-1 to ignore.
- *
- * This function can be used to match specific DMA tunnel, if there are
- * multiple DMA tunnels going through the same XDomain connection.
- * Returns true if there is match and false otherwise.
- */
- bool tb_tunnel_match_dma(const struct tb_tunnel *tunnel, int transmit_path,
- int transmit_ring, int receive_path, int receive_ring)
- {
- const struct tb_path *tx_path = NULL, *rx_path = NULL;
- int i;
- if (!receive_ring || !transmit_ring)
- return false;
- for (i = 0; i < tunnel->npaths; i++) {
- const struct tb_path *path = tunnel->paths[i];
- if (!path)
- continue;
- if (tb_port_is_nhi(path->hops[0].in_port))
- tx_path = path;
- else if (tb_port_is_nhi(path->hops[path->path_length - 1].out_port))
- rx_path = path;
- }
- if (transmit_ring > 0 || transmit_path > 0) {
- if (!tx_path)
- return false;
- if (transmit_ring > 0 &&
- (tx_path->hops[0].in_hop_index != transmit_ring))
- return false;
- if (transmit_path > 0 &&
- (tx_path->hops[tx_path->path_length - 1].next_hop_index != transmit_path))
- return false;
- }
- if (receive_ring > 0 || receive_path > 0) {
- if (!rx_path)
- return false;
- if (receive_path > 0 &&
- (rx_path->hops[0].in_hop_index != receive_path))
- return false;
- if (receive_ring > 0 &&
- (rx_path->hops[rx_path->path_length - 1].next_hop_index != receive_ring))
- return false;
- }
- return true;
- }
- static int tb_usb3_max_link_rate(struct tb_port *up, struct tb_port *down)
- {
- int ret, up_max_rate, down_max_rate;
- ret = usb4_usb3_port_max_link_rate(up);
- if (ret < 0)
- return ret;
- up_max_rate = ret;
- ret = usb4_usb3_port_max_link_rate(down);
- if (ret < 0)
- return ret;
- down_max_rate = ret;
- return min(up_max_rate, down_max_rate);
- }
- static int tb_usb3_init(struct tb_tunnel *tunnel)
- {
- tb_tunnel_dbg(tunnel, "allocating initial bandwidth %d/%d Mb/s\n",
- tunnel->allocated_up, tunnel->allocated_down);
- return usb4_usb3_port_allocate_bandwidth(tunnel->src_port,
- &tunnel->allocated_up,
- &tunnel->allocated_down);
- }
- static int tb_usb3_activate(struct tb_tunnel *tunnel, bool activate)
- {
- int res;
- res = tb_usb3_port_enable(tunnel->src_port, activate);
- if (res)
- return res;
- if (tb_port_is_usb3_up(tunnel->dst_port))
- return tb_usb3_port_enable(tunnel->dst_port, activate);
- return 0;
- }
- static int tb_usb3_consumed_bandwidth(struct tb_tunnel *tunnel,
- int *consumed_up, int *consumed_down)
- {
- int pcie_enabled = tb_acpi_may_tunnel_pcie();
- /*
- * PCIe tunneling, if enabled, affects the USB3 bandwidth so
- * take that it into account here.
- */
- *consumed_up = tunnel->allocated_up * (3 + pcie_enabled) / 3;
- *consumed_down = tunnel->allocated_down * (3 + pcie_enabled) / 3;
- return 0;
- }
- static int tb_usb3_release_unused_bandwidth(struct tb_tunnel *tunnel)
- {
- int ret;
- ret = usb4_usb3_port_release_bandwidth(tunnel->src_port,
- &tunnel->allocated_up,
- &tunnel->allocated_down);
- if (ret)
- return ret;
- tb_tunnel_dbg(tunnel, "decreased bandwidth allocation to %d/%d Mb/s\n",
- tunnel->allocated_up, tunnel->allocated_down);
- return 0;
- }
- static void tb_usb3_reclaim_available_bandwidth(struct tb_tunnel *tunnel,
- int *available_up,
- int *available_down)
- {
- int ret, max_rate, allocate_up, allocate_down;
- ret = usb4_usb3_port_actual_link_rate(tunnel->src_port);
- if (ret < 0) {
- tb_tunnel_warn(tunnel, "failed to read actual link rate\n");
- return;
- } else if (!ret) {
- /* Use maximum link rate if the link valid is not set */
- ret = tb_usb3_max_link_rate(tunnel->dst_port, tunnel->src_port);
- if (ret < 0) {
- tb_tunnel_warn(tunnel, "failed to read maximum link rate\n");
- return;
- }
- }
- /*
- * 90% of the max rate can be allocated for isochronous
- * transfers.
- */
- max_rate = ret * 90 / 100;
- /* No need to reclaim if already at maximum */
- if (tunnel->allocated_up >= max_rate &&
- tunnel->allocated_down >= max_rate)
- return;
- /* Don't go lower than what is already allocated */
- allocate_up = min(max_rate, *available_up);
- if (allocate_up < tunnel->allocated_up)
- allocate_up = tunnel->allocated_up;
- allocate_down = min(max_rate, *available_down);
- if (allocate_down < tunnel->allocated_down)
- allocate_down = tunnel->allocated_down;
- /* If no changes no need to do more */
- if (allocate_up == tunnel->allocated_up &&
- allocate_down == tunnel->allocated_down)
- return;
- ret = usb4_usb3_port_allocate_bandwidth(tunnel->src_port, &allocate_up,
- &allocate_down);
- if (ret) {
- tb_tunnel_info(tunnel, "failed to allocate bandwidth\n");
- return;
- }
- tunnel->allocated_up = allocate_up;
- *available_up -= tunnel->allocated_up;
- tunnel->allocated_down = allocate_down;
- *available_down -= tunnel->allocated_down;
- tb_tunnel_dbg(tunnel, "increased bandwidth allocation to %d/%d Mb/s\n",
- tunnel->allocated_up, tunnel->allocated_down);
- }
- static void tb_usb3_init_credits(struct tb_path_hop *hop)
- {
- struct tb_port *port = hop->in_port;
- struct tb_switch *sw = port->sw;
- unsigned int credits;
- if (tb_port_use_credit_allocation(port)) {
- credits = sw->max_usb3_credits;
- } else {
- if (tb_port_is_null(port))
- credits = port->bonded ? 32 : 16;
- else
- credits = 7;
- }
- hop->initial_credits = credits;
- }
- static void tb_usb3_init_path(struct tb_path *path)
- {
- struct tb_path_hop *hop;
- path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL;
- path->egress_shared_buffer = TB_PATH_NONE;
- path->ingress_fc_enable = TB_PATH_ALL;
- path->ingress_shared_buffer = TB_PATH_NONE;
- path->priority = 3;
- path->weight = 3;
- path->drop_packages = 0;
- tb_path_for_each_hop(path, hop)
- tb_usb3_init_credits(hop);
- }
- /**
- * tb_tunnel_discover_usb3() - Discover existing USB3 tunnels
- * @tb: Pointer to the domain structure
- * @down: USB3 downstream adapter
- * @alloc_hopid: Allocate HopIDs from visited ports
- *
- * If @down adapter is active, follows the tunnel to the USB3 upstream
- * adapter and back. Returns the discovered tunnel or %NULL if there was
- * no tunnel.
- */
- struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down,
- bool alloc_hopid)
- {
- struct tb_tunnel *tunnel;
- struct tb_path *path;
- if (!tb_usb3_port_is_enabled(down))
- return NULL;
- tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_USB3);
- if (!tunnel)
- return NULL;
- tunnel->activate = tb_usb3_activate;
- tunnel->src_port = down;
- /*
- * Discover both paths even if they are not complete. We will
- * clean them up by calling tb_tunnel_deactivate() below in that
- * case.
- */
- path = tb_path_discover(down, TB_USB3_HOPID, NULL, -1,
- &tunnel->dst_port, "USB3 Down", alloc_hopid);
- if (!path) {
- /* Just disable the downstream port */
- tb_usb3_port_enable(down, false);
- goto err_free;
- }
- tunnel->paths[TB_USB3_PATH_DOWN] = path;
- tb_usb3_init_path(tunnel->paths[TB_USB3_PATH_DOWN]);
- path = tb_path_discover(tunnel->dst_port, -1, down, TB_USB3_HOPID, NULL,
- "USB3 Up", alloc_hopid);
- if (!path)
- goto err_deactivate;
- tunnel->paths[TB_USB3_PATH_UP] = path;
- tb_usb3_init_path(tunnel->paths[TB_USB3_PATH_UP]);
- /* Validate that the tunnel is complete */
- if (!tb_port_is_usb3_up(tunnel->dst_port)) {
- tb_port_warn(tunnel->dst_port,
- "path does not end on an USB3 adapter, cleaning up\n");
- goto err_deactivate;
- }
- if (down != tunnel->src_port) {
- tb_tunnel_warn(tunnel, "path is not complete, cleaning up\n");
- goto err_deactivate;
- }
- if (!tb_usb3_port_is_enabled(tunnel->dst_port)) {
- tb_tunnel_warn(tunnel,
- "tunnel is not fully activated, cleaning up\n");
- goto err_deactivate;
- }
- if (!tb_route(down->sw)) {
- int ret;
- /*
- * Read the initial bandwidth allocation for the first
- * hop tunnel.
- */
- ret = usb4_usb3_port_allocated_bandwidth(down,
- &tunnel->allocated_up, &tunnel->allocated_down);
- if (ret)
- goto err_deactivate;
- tb_tunnel_dbg(tunnel, "currently allocated bandwidth %d/%d Mb/s\n",
- tunnel->allocated_up, tunnel->allocated_down);
- tunnel->init = tb_usb3_init;
- tunnel->consumed_bandwidth = tb_usb3_consumed_bandwidth;
- tunnel->release_unused_bandwidth =
- tb_usb3_release_unused_bandwidth;
- tunnel->reclaim_available_bandwidth =
- tb_usb3_reclaim_available_bandwidth;
- }
- tb_tunnel_dbg(tunnel, "discovered\n");
- return tunnel;
- err_deactivate:
- tb_tunnel_deactivate(tunnel);
- err_free:
- tb_tunnel_free(tunnel);
- return NULL;
- }
- /**
- * tb_tunnel_alloc_usb3() - allocate a USB3 tunnel
- * @tb: Pointer to the domain structure
- * @up: USB3 upstream adapter port
- * @down: USB3 downstream adapter port
- * @max_up: Maximum available upstream bandwidth for the USB3 tunnel (%0
- * if not limited).
- * @max_down: Maximum available downstream bandwidth for the USB3 tunnel
- * (%0 if not limited).
- *
- * Allocate an USB3 tunnel. The ports must be of type @TB_TYPE_USB3_UP and
- * @TB_TYPE_USB3_DOWN.
- *
- * Return: Returns a tb_tunnel on success or %NULL on failure.
- */
- struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up,
- struct tb_port *down, int max_up,
- int max_down)
- {
- struct tb_tunnel *tunnel;
- struct tb_path *path;
- int max_rate = 0;
- /*
- * Check that we have enough bandwidth available for the new
- * USB3 tunnel.
- */
- if (max_up > 0 || max_down > 0) {
- max_rate = tb_usb3_max_link_rate(down, up);
- if (max_rate < 0)
- return NULL;
- /* Only 90% can be allocated for USB3 isochronous transfers */
- max_rate = max_rate * 90 / 100;
- tb_port_dbg(up, "required bandwidth for USB3 tunnel %d Mb/s\n",
- max_rate);
- if (max_rate > max_up || max_rate > max_down) {
- tb_port_warn(up, "not enough bandwidth for USB3 tunnel\n");
- return NULL;
- }
- }
- tunnel = tb_tunnel_alloc(tb, 2, TB_TUNNEL_USB3);
- if (!tunnel)
- return NULL;
- tunnel->activate = tb_usb3_activate;
- tunnel->src_port = down;
- tunnel->dst_port = up;
- tunnel->max_up = max_up;
- tunnel->max_down = max_down;
- path = tb_path_alloc(tb, down, TB_USB3_HOPID, up, TB_USB3_HOPID, 0,
- "USB3 Down");
- if (!path) {
- tb_tunnel_free(tunnel);
- return NULL;
- }
- tb_usb3_init_path(path);
- tunnel->paths[TB_USB3_PATH_DOWN] = path;
- path = tb_path_alloc(tb, up, TB_USB3_HOPID, down, TB_USB3_HOPID, 0,
- "USB3 Up");
- if (!path) {
- tb_tunnel_free(tunnel);
- return NULL;
- }
- tb_usb3_init_path(path);
- tunnel->paths[TB_USB3_PATH_UP] = path;
- if (!tb_route(down->sw)) {
- tunnel->allocated_up = max_rate;
- tunnel->allocated_down = max_rate;
- tunnel->init = tb_usb3_init;
- tunnel->consumed_bandwidth = tb_usb3_consumed_bandwidth;
- tunnel->release_unused_bandwidth =
- tb_usb3_release_unused_bandwidth;
- tunnel->reclaim_available_bandwidth =
- tb_usb3_reclaim_available_bandwidth;
- }
- return tunnel;
- }
- /**
- * tb_tunnel_free() - free a tunnel
- * @tunnel: Tunnel to be freed
- *
- * Frees a tunnel. The tunnel does not need to be deactivated.
- */
- void tb_tunnel_free(struct tb_tunnel *tunnel)
- {
- int i;
- if (!tunnel)
- return;
- if (tunnel->deinit)
- tunnel->deinit(tunnel);
- for (i = 0; i < tunnel->npaths; i++) {
- if (tunnel->paths[i])
- tb_path_free(tunnel->paths[i]);
- }
- kfree(tunnel->paths);
- kfree(tunnel);
- }
- /**
- * tb_tunnel_is_invalid - check whether an activated path is still valid
- * @tunnel: Tunnel to check
- */
- bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel)
- {
- int i;
- for (i = 0; i < tunnel->npaths; i++) {
- WARN_ON(!tunnel->paths[i]->activated);
- if (tb_path_is_invalid(tunnel->paths[i]))
- return true;
- }
- return false;
- }
- /**
- * tb_tunnel_restart() - activate a tunnel after a hardware reset
- * @tunnel: Tunnel to restart
- *
- * Return: 0 on success and negative errno in case if failure
- */
- int tb_tunnel_restart(struct tb_tunnel *tunnel)
- {
- int res, i;
- tb_tunnel_dbg(tunnel, "activating\n");
- /*
- * Make sure all paths are properly disabled before enabling
- * them again.
- */
- for (i = 0; i < tunnel->npaths; i++) {
- if (tunnel->paths[i]->activated) {
- tb_path_deactivate(tunnel->paths[i]);
- tunnel->paths[i]->activated = false;
- }
- }
- if (tunnel->init) {
- res = tunnel->init(tunnel);
- if (res)
- return res;
- }
- for (i = 0; i < tunnel->npaths; i++) {
- res = tb_path_activate(tunnel->paths[i]);
- if (res)
- goto err;
- }
- if (tunnel->activate) {
- res = tunnel->activate(tunnel, true);
- if (res)
- goto err;
- }
- return 0;
- err:
- tb_tunnel_warn(tunnel, "activation failed\n");
- tb_tunnel_deactivate(tunnel);
- return res;
- }
- /**
- * tb_tunnel_activate() - activate a tunnel
- * @tunnel: Tunnel to activate
- *
- * Return: Returns 0 on success or an error code on failure.
- */
- int tb_tunnel_activate(struct tb_tunnel *tunnel)
- {
- int i;
- for (i = 0; i < tunnel->npaths; i++) {
- if (tunnel->paths[i]->activated) {
- tb_tunnel_WARN(tunnel,
- "trying to activate an already activated tunnel\n");
- return -EINVAL;
- }
- }
- return tb_tunnel_restart(tunnel);
- }
- /**
- * tb_tunnel_deactivate() - deactivate a tunnel
- * @tunnel: Tunnel to deactivate
- */
- void tb_tunnel_deactivate(struct tb_tunnel *tunnel)
- {
- int i;
- tb_tunnel_dbg(tunnel, "deactivating\n");
- if (tunnel->activate)
- tunnel->activate(tunnel, false);
- for (i = 0; i < tunnel->npaths; i++) {
- if (tunnel->paths[i] && tunnel->paths[i]->activated)
- tb_path_deactivate(tunnel->paths[i]);
- }
- }
- /**
- * tb_tunnel_port_on_path() - Does the tunnel go through port
- * @tunnel: Tunnel to check
- * @port: Port to check
- *
- * Returns true if @tunnel goes through @port (direction does not matter),
- * false otherwise.
- */
- bool tb_tunnel_port_on_path(const struct tb_tunnel *tunnel,
- const struct tb_port *port)
- {
- int i;
- for (i = 0; i < tunnel->npaths; i++) {
- if (!tunnel->paths[i])
- continue;
- if (tb_path_port_on_path(tunnel->paths[i], port))
- return true;
- }
- return false;
- }
- static bool tb_tunnel_is_active(const struct tb_tunnel *tunnel)
- {
- int i;
- for (i = 0; i < tunnel->npaths; i++) {
- if (!tunnel->paths[i])
- return false;
- if (!tunnel->paths[i]->activated)
- return false;
- }
- return true;
- }
- /**
- * tb_tunnel_consumed_bandwidth() - Return bandwidth consumed by the tunnel
- * @tunnel: Tunnel to check
- * @consumed_up: Consumed bandwidth in Mb/s from @dst_port to @src_port.
- * Can be %NULL.
- * @consumed_down: Consumed bandwidth in Mb/s from @src_port to @dst_port.
- * Can be %NULL.
- *
- * Stores the amount of isochronous bandwidth @tunnel consumes in
- * @consumed_up and @consumed_down. In case of success returns %0,
- * negative errno otherwise.
- */
- int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
- int *consumed_down)
- {
- int up_bw = 0, down_bw = 0;
- if (!tb_tunnel_is_active(tunnel))
- goto out;
- if (tunnel->consumed_bandwidth) {
- int ret;
- ret = tunnel->consumed_bandwidth(tunnel, &up_bw, &down_bw);
- if (ret)
- return ret;
- tb_tunnel_dbg(tunnel, "consumed bandwidth %d/%d Mb/s\n", up_bw,
- down_bw);
- }
- out:
- if (consumed_up)
- *consumed_up = up_bw;
- if (consumed_down)
- *consumed_down = down_bw;
- return 0;
- }
- /**
- * tb_tunnel_release_unused_bandwidth() - Release unused bandwidth
- * @tunnel: Tunnel whose unused bandwidth to release
- *
- * If tunnel supports dynamic bandwidth management (USB3 tunnels at the
- * moment) this function makes it to release all the unused bandwidth.
- *
- * Returns %0 in case of success and negative errno otherwise.
- */
- int tb_tunnel_release_unused_bandwidth(struct tb_tunnel *tunnel)
- {
- if (!tb_tunnel_is_active(tunnel))
- return 0;
- if (tunnel->release_unused_bandwidth) {
- int ret;
- ret = tunnel->release_unused_bandwidth(tunnel);
- if (ret)
- return ret;
- }
- return 0;
- }
- /**
- * tb_tunnel_reclaim_available_bandwidth() - Reclaim available bandwidth
- * @tunnel: Tunnel reclaiming available bandwidth
- * @available_up: Available upstream bandwidth (in Mb/s)
- * @available_down: Available downstream bandwidth (in Mb/s)
- *
- * Reclaims bandwidth from @available_up and @available_down and updates
- * the variables accordingly (e.g decreases both according to what was
- * reclaimed by the tunnel). If nothing was reclaimed the values are
- * kept as is.
- */
- void tb_tunnel_reclaim_available_bandwidth(struct tb_tunnel *tunnel,
- int *available_up,
- int *available_down)
- {
- if (!tb_tunnel_is_active(tunnel))
- return;
- if (tunnel->reclaim_available_bandwidth)
- tunnel->reclaim_available_bandwidth(tunnel, available_up,
- available_down);
- }
|