Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Thunderbolt driver - capabilities lookup
0004  *
0005  * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
0006  * Copyright (C) 2018, Intel Corporation
0007  */
0008 
0009 #include <linux/slab.h>
0010 #include <linux/errno.h>
0011 
0012 #include "tb.h"
0013 
0014 #define CAP_OFFSET_MAX      0xff
0015 #define VSE_CAP_OFFSET_MAX  0xffff
0016 #define TMU_ACCESS_EN       BIT(20)
0017 
0018 static int tb_port_enable_tmu(struct tb_port *port, bool enable)
0019 {
0020     struct tb_switch *sw = port->sw;
0021     u32 value, offset;
0022     int ret;
0023 
0024     /*
0025      * Legacy devices need to have TMU access enabled before port
0026      * space can be fully accessed.
0027      */
0028     if (tb_switch_is_light_ridge(sw))
0029         offset = 0x26;
0030     else if (tb_switch_is_eagle_ridge(sw))
0031         offset = 0x2a;
0032     else
0033         return 0;
0034 
0035     ret = tb_sw_read(sw, &value, TB_CFG_SWITCH, offset, 1);
0036     if (ret)
0037         return ret;
0038 
0039     if (enable)
0040         value |= TMU_ACCESS_EN;
0041     else
0042         value &= ~TMU_ACCESS_EN;
0043 
0044     return tb_sw_write(sw, &value, TB_CFG_SWITCH, offset, 1);
0045 }
0046 
0047 static void tb_port_dummy_read(struct tb_port *port)
0048 {
0049     /*
0050      * When reading from next capability pointer location in port
0051      * config space the read data is not cleared on LR. To avoid
0052      * reading stale data on next read perform one dummy read after
0053      * port capabilities are walked.
0054      */
0055     if (tb_switch_is_light_ridge(port->sw)) {
0056         u32 dummy;
0057 
0058         tb_port_read(port, &dummy, TB_CFG_PORT, 0, 1);
0059     }
0060 }
0061 
0062 /**
0063  * tb_port_next_cap() - Return next capability in the linked list
0064  * @port: Port to find the capability for
0065  * @offset: Previous capability offset (%0 for start)
0066  *
0067  * Returns dword offset of the next capability in port config space
0068  * capability list and returns it. Passing %0 returns the first entry in
0069  * the capability list. If no next capability is found returns %0. In case
0070  * of failure returns negative errno.
0071  */
0072 int tb_port_next_cap(struct tb_port *port, unsigned int offset)
0073 {
0074     struct tb_cap_any header;
0075     int ret;
0076 
0077     if (!offset)
0078         return port->config.first_cap_offset;
0079 
0080     ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);
0081     if (ret)
0082         return ret;
0083 
0084     return header.basic.next;
0085 }
0086 
0087 static int __tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
0088 {
0089     int offset = 0;
0090 
0091     do {
0092         struct tb_cap_any header;
0093         int ret;
0094 
0095         offset = tb_port_next_cap(port, offset);
0096         if (offset < 0)
0097             return offset;
0098 
0099         ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);
0100         if (ret)
0101             return ret;
0102 
0103         if (header.basic.cap == cap)
0104             return offset;
0105     } while (offset > 0);
0106 
0107     return -ENOENT;
0108 }
0109 
0110 /**
0111  * tb_port_find_cap() - Find port capability
0112  * @port: Port to find the capability for
0113  * @cap: Capability to look
0114  *
0115  * Returns offset to start of capability or %-ENOENT if no such
0116  * capability was found. Negative errno is returned if there was an
0117  * error.
0118  */
0119 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
0120 {
0121     int ret;
0122 
0123     ret = tb_port_enable_tmu(port, true);
0124     if (ret)
0125         return ret;
0126 
0127     ret = __tb_port_find_cap(port, cap);
0128 
0129     tb_port_dummy_read(port);
0130     tb_port_enable_tmu(port, false);
0131 
0132     return ret;
0133 }
0134 
0135 /**
0136  * tb_switch_next_cap() - Return next capability in the linked list
0137  * @sw: Switch to find the capability for
0138  * @offset: Previous capability offset (%0 for start)
0139  *
0140  * Finds dword offset of the next capability in router config space
0141  * capability list and returns it. Passing %0 returns the first entry in
0142  * the capability list. If no next capability is found returns %0. In case
0143  * of failure returns negative errno.
0144  */
0145 int tb_switch_next_cap(struct tb_switch *sw, unsigned int offset)
0146 {
0147     struct tb_cap_any header;
0148     int ret;
0149 
0150     if (!offset)
0151         return sw->config.first_cap_offset;
0152 
0153     ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 2);
0154     if (ret)
0155         return ret;
0156 
0157     switch (header.basic.cap) {
0158     case TB_SWITCH_CAP_TMU:
0159         ret = header.basic.next;
0160         break;
0161 
0162     case TB_SWITCH_CAP_VSE:
0163         if (!header.extended_short.length)
0164             ret = header.extended_long.next;
0165         else
0166             ret = header.extended_short.next;
0167         break;
0168 
0169     default:
0170         tb_sw_dbg(sw, "unknown capability %#x at %#x\n",
0171               header.basic.cap, offset);
0172         ret = -EINVAL;
0173         break;
0174     }
0175 
0176     return ret >= VSE_CAP_OFFSET_MAX ? 0 : ret;
0177 }
0178 
0179 /**
0180  * tb_switch_find_cap() - Find switch capability
0181  * @sw: Switch to find the capability for
0182  * @cap: Capability to look
0183  *
0184  * Returns offset to start of capability or %-ENOENT if no such
0185  * capability was found. Negative errno is returned if there was an
0186  * error.
0187  */
0188 int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap)
0189 {
0190     int offset = 0;
0191 
0192     do {
0193         struct tb_cap_any header;
0194         int ret;
0195 
0196         offset = tb_switch_next_cap(sw, offset);
0197         if (offset < 0)
0198             return offset;
0199 
0200         ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);
0201         if (ret)
0202             return ret;
0203 
0204         if (header.basic.cap == cap)
0205             return offset;
0206     } while (offset);
0207 
0208     return -ENOENT;
0209 }
0210 
0211 /**
0212  * tb_switch_find_vse_cap() - Find switch vendor specific capability
0213  * @sw: Switch to find the capability for
0214  * @vsec: Vendor specific capability to look
0215  *
0216  * Functions enumerates vendor specific capabilities (VSEC) of a switch
0217  * and returns offset when capability matching @vsec is found. If no
0218  * such capability is found returns %-ENOENT. In case of error returns
0219  * negative errno.
0220  */
0221 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec)
0222 {
0223     int offset = 0;
0224 
0225     do {
0226         struct tb_cap_any header;
0227         int ret;
0228 
0229         offset = tb_switch_next_cap(sw, offset);
0230         if (offset < 0)
0231             return offset;
0232 
0233         ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);
0234         if (ret)
0235             return ret;
0236 
0237         if (header.extended_short.cap == TB_SWITCH_CAP_VSE &&
0238             header.extended_short.vsec_id == vsec)
0239             return offset;
0240     } while (offset);
0241 
0242     return -ENOENT;
0243 }