root/drivers/thunderbolt/cap.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tb_port_enable_tmu
  2. tb_port_dummy_read
  3. __tb_port_find_cap
  4. tb_port_find_cap
  5. tb_switch_find_cap
  6. tb_switch_find_vse_cap

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Thunderbolt driver - capabilities lookup
   4  *
   5  * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
   6  * Copyright (C) 2018, Intel Corporation
   7  */
   8 
   9 #include <linux/slab.h>
  10 #include <linux/errno.h>
  11 
  12 #include "tb.h"
  13 
  14 #define CAP_OFFSET_MAX          0xff
  15 #define VSE_CAP_OFFSET_MAX      0xffff
  16 #define TMU_ACCESS_EN           BIT(20)
  17 
  18 struct tb_cap_any {
  19         union {
  20                 struct tb_cap_basic basic;
  21                 struct tb_cap_extended_short extended_short;
  22                 struct tb_cap_extended_long extended_long;
  23         };
  24 } __packed;
  25 
  26 static int tb_port_enable_tmu(struct tb_port *port, bool enable)
  27 {
  28         struct tb_switch *sw = port->sw;
  29         u32 value, offset;
  30         int ret;
  31 
  32         /*
  33          * Legacy devices need to have TMU access enabled before port
  34          * space can be fully accessed.
  35          */
  36         if (tb_switch_is_lr(sw))
  37                 offset = 0x26;
  38         else if (tb_switch_is_er(sw))
  39                 offset = 0x2a;
  40         else
  41                 return 0;
  42 
  43         ret = tb_sw_read(sw, &value, TB_CFG_SWITCH, offset, 1);
  44         if (ret)
  45                 return ret;
  46 
  47         if (enable)
  48                 value |= TMU_ACCESS_EN;
  49         else
  50                 value &= ~TMU_ACCESS_EN;
  51 
  52         return tb_sw_write(sw, &value, TB_CFG_SWITCH, offset, 1);
  53 }
  54 
  55 static void tb_port_dummy_read(struct tb_port *port)
  56 {
  57         /*
  58          * When reading from next capability pointer location in port
  59          * config space the read data is not cleared on LR. To avoid
  60          * reading stale data on next read perform one dummy read after
  61          * port capabilities are walked.
  62          */
  63         if (tb_switch_is_lr(port->sw)) {
  64                 u32 dummy;
  65 
  66                 tb_port_read(port, &dummy, TB_CFG_PORT, 0, 1);
  67         }
  68 }
  69 
  70 static int __tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
  71 {
  72         u32 offset = 1;
  73 
  74         do {
  75                 struct tb_cap_any header;
  76                 int ret;
  77 
  78                 ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);
  79                 if (ret)
  80                         return ret;
  81 
  82                 if (header.basic.cap == cap)
  83                         return offset;
  84 
  85                 offset = header.basic.next;
  86         } while (offset);
  87 
  88         return -ENOENT;
  89 }
  90 
  91 /**
  92  * tb_port_find_cap() - Find port capability
  93  * @port: Port to find the capability for
  94  * @cap: Capability to look
  95  *
  96  * Returns offset to start of capability or %-ENOENT if no such
  97  * capability was found. Negative errno is returned if there was an
  98  * error.
  99  */
 100 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
 101 {
 102         int ret;
 103 
 104         ret = tb_port_enable_tmu(port, true);
 105         if (ret)
 106                 return ret;
 107 
 108         ret = __tb_port_find_cap(port, cap);
 109 
 110         tb_port_dummy_read(port);
 111         tb_port_enable_tmu(port, false);
 112 
 113         return ret;
 114 }
 115 
 116 static int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap)
 117 {
 118         int offset = sw->config.first_cap_offset;
 119 
 120         while (offset > 0 && offset < CAP_OFFSET_MAX) {
 121                 struct tb_cap_any header;
 122                 int ret;
 123 
 124                 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);
 125                 if (ret)
 126                         return ret;
 127 
 128                 if (header.basic.cap == cap)
 129                         return offset;
 130 
 131                 offset = header.basic.next;
 132         }
 133 
 134         return -ENOENT;
 135 }
 136 
 137 /**
 138  * tb_switch_find_vse_cap() - Find switch vendor specific capability
 139  * @sw: Switch to find the capability for
 140  * @vsec: Vendor specific capability to look
 141  *
 142  * Functions enumerates vendor specific capabilities (VSEC) of a switch
 143  * and returns offset when capability matching @vsec is found. If no
 144  * such capability is found returns %-ENOENT. In case of error returns
 145  * negative errno.
 146  */
 147 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec)
 148 {
 149         struct tb_cap_any header;
 150         int offset;
 151 
 152         offset = tb_switch_find_cap(sw, TB_SWITCH_CAP_VSE);
 153         if (offset < 0)
 154                 return offset;
 155 
 156         while (offset > 0 && offset < VSE_CAP_OFFSET_MAX) {
 157                 int ret;
 158 
 159                 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 2);
 160                 if (ret)
 161                         return ret;
 162 
 163                 /*
 164                  * Extended vendor specific capabilities come in two
 165                  * flavors: short and long. The latter is used when
 166                  * offset is over 0xff.
 167                  */
 168                 if (offset >= CAP_OFFSET_MAX) {
 169                         if (header.extended_long.vsec_id == vsec)
 170                                 return offset;
 171                         offset = header.extended_long.next;
 172                 } else {
 173                         if (header.extended_short.vsec_id == vsec)
 174                                 return offset;
 175                         if (!header.extended_short.length)
 176                                 return -ENOENT;
 177                         offset = header.extended_short.next;
 178                 }
 179         }
 180 
 181         return -ENOENT;
 182 }

/* [<][>][^][v][top][bottom][index][help] */