]>
git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - drivers/thunderbolt/switch.c
2 * Thunderbolt Cactus Ridge driver - switch/port utility functions
4 * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
7 #include <linux/delay.h>
11 /* port utility functions */
13 static const char *tb_port_type(struct tb_regs_port_header
*port
)
15 switch (port
->type
>> 16) {
17 switch ((u8
) port
->type
) {
42 static void tb_dump_port(struct tb
*tb
, struct tb_regs_port_header
*port
)
45 " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n",
46 port
->port_number
, port
->vendor_id
, port
->device_id
,
47 port
->revision
, port
->thunderbolt_version
, tb_port_type(port
),
49 tb_info(tb
, " Max hop id (in/out): %d/%d\n",
50 port
->max_in_hop_id
, port
->max_out_hop_id
);
51 tb_info(tb
, " Max counters: %d\n", port
->max_counters
);
52 tb_info(tb
, " NFC Credits: %#x\n", port
->nfc_credits
);
56 * tb_init_port() - initialize a port
58 * This is a helper method for tb_switch_alloc. Does not check or initialize
59 * any downstream switches.
61 * Return: Returns 0 on success or an error code on failure.
63 static int tb_init_port(struct tb_switch
*sw
, u8 port_nr
)
66 struct tb_port
*port
= &sw
->ports
[port_nr
];
70 res
= tb_port_read(port
, &port
->config
, TB_CFG_PORT
, 0, 8);
74 tb_dump_port(sw
->tb
, &port
->config
);
76 /* TODO: Read dual link port, DP port and more from EEPROM. */
81 /* switch utility functions */
83 static void tb_dump_switch(struct tb
*tb
, struct tb_regs_switch_header
*sw
)
86 " Switch: %x:%x (Revision: %d, TB Version: %d)\n",
87 sw
->vendor_id
, sw
->device_id
, sw
->revision
,
88 sw
->thunderbolt_version
);
89 tb_info(tb
, " Max Port Number: %d\n", sw
->max_port_number
);
90 tb_info(tb
, " Config:\n");
92 " Upstream Port Number: %d Depth: %d Route String: %#llx Enabled: %d, PlugEventsDelay: %dms\n",
93 sw
->upstream_port_number
, sw
->depth
,
94 (((u64
) sw
->route_hi
) << 32) | sw
->route_lo
,
95 sw
->enabled
, sw
->plug_events_delay
);
97 " unknown1: %#x unknown4: %#x\n",
98 sw
->__unknown1
, sw
->__unknown4
);
102 * tb_switch_free() - free a tb_switch and all downstream switches
104 void tb_switch_free(struct tb_switch
*sw
)
107 /* port 0 is the switch itself and never has a remote */
108 for (i
= 1; i
<= sw
->config
.max_port_number
; i
++) {
109 if (tb_is_upstream_port(&sw
->ports
[i
]))
111 if (sw
->ports
[i
].remote
)
112 tb_switch_free(sw
->ports
[i
].remote
->sw
);
113 sw
->ports
[i
].remote
= NULL
;
121 * tb_switch_alloc() - allocate and initialize a switch
123 * Return: Returns a NULL on failure.
125 struct tb_switch
*tb_switch_alloc(struct tb
*tb
, u64 route
)
128 struct tb_switch
*sw
;
129 int upstream_port
= tb_cfg_get_upstream_port(tb
->ctl
, route
);
130 if (upstream_port
< 0)
133 sw
= kzalloc(sizeof(*sw
), GFP_KERNEL
);
138 if (tb_cfg_read(tb
->ctl
, &sw
->config
, route
, 0, 2, 0, 5))
141 "initializing Switch at %#llx (depth: %d, up port: %d)\n",
142 route
, tb_route_length(route
), upstream_port
);
143 tb_info(tb
, "old switch config:\n");
144 tb_dump_switch(tb
, &sw
->config
);
146 /* configure switch */
147 sw
->config
.upstream_port_number
= upstream_port
;
148 sw
->config
.depth
= tb_route_length(route
);
149 sw
->config
.route_lo
= route
;
150 sw
->config
.route_hi
= route
>> 32;
151 sw
->config
.enabled
= 1;
152 /* from here on we may use the tb_sw_* functions & macros */
154 if (sw
->config
.vendor_id
!= 0x8086)
155 tb_sw_warn(sw
, "unknown switch vendor id %#x\n",
156 sw
->config
.vendor_id
);
158 if (sw
->config
.device_id
!= 0x1547 && sw
->config
.device_id
!= 0x1549)
159 tb_sw_warn(sw
, "unsupported switch device id %#x\n",
160 sw
->config
.device_id
);
162 /* upload configuration */
163 if (tb_sw_write(sw
, 1 + (u32
*) &sw
->config
, TB_CFG_SWITCH
, 1, 3))
166 /* initialize ports */
167 sw
->ports
= kcalloc(sw
->config
.max_port_number
+ 1, sizeof(*sw
->ports
),
172 for (i
= 0; i
<= sw
->config
.max_port_number
; i
++) {
173 if (tb_init_port(sw
, i
))
175 /* TODO: check if port is disabled (EEPROM) */
178 /* TODO: I2C, IECS, EEPROM, link controller */