| 1 | // SPDX-License-Identifier: GPL-2.0-only |
| 2 | /* Copyright(c) 2023 Intel Corporation */ |
| 3 | |
| 4 | #include <linux/array_size.h> |
| 5 | #include <linux/bitops.h> |
| 6 | #include <linux/export.h> |
| 7 | #include <linux/pci.h> |
| 8 | #include <linux/string.h> |
| 9 | #include "adf_cfg.h" |
| 10 | #include "adf_cfg_common.h" |
| 11 | #include "adf_cfg_services.h" |
| 12 | #include "adf_cfg_strings.h" |
| 13 | |
| 14 | static const char *const adf_cfg_services[] = { |
| 15 | [SVC_ASYM] = ADF_CFG_ASYM, |
| 16 | [SVC_SYM] = ADF_CFG_SYM, |
| 17 | [SVC_DC] = ADF_CFG_DC, |
| 18 | [SVC_DCC] = ADF_CFG_DCC, |
| 19 | [SVC_DECOMP] = ADF_CFG_DECOMP, |
| 20 | }; |
| 21 | |
| 22 | /* |
| 23 | * Ensure that the size of the array matches the number of services, |
| 24 | * SVC_COUNT, that is used to size the bitmap. |
| 25 | */ |
| 26 | static_assert(ARRAY_SIZE(adf_cfg_services) == SVC_COUNT); |
| 27 | |
| 28 | /* |
| 29 | * Ensure that the maximum number of concurrent services that can be |
| 30 | * enabled on a device is less than or equal to the number of total |
| 31 | * supported services. |
| 32 | */ |
| 33 | static_assert(ARRAY_SIZE(adf_cfg_services) >= MAX_NUM_CONCURR_SVC); |
| 34 | |
| 35 | /* |
| 36 | * Ensure that the number of services fit a single unsigned long, as each |
| 37 | * service is represented by a bit in the mask. |
| 38 | */ |
| 39 | static_assert(BITS_PER_LONG >= SVC_COUNT); |
| 40 | |
| 41 | /* |
| 42 | * Ensure that size of the concatenation of all service strings is smaller |
| 43 | * than the size of the buffer that will contain them. |
| 44 | */ |
| 45 | static_assert(sizeof(ADF_CFG_SYM ADF_SERVICES_DELIMITER |
| 46 | ADF_CFG_ASYM ADF_SERVICES_DELIMITER |
| 47 | ADF_CFG_DC ADF_SERVICES_DELIMITER |
| 48 | ADF_CFG_DECOMP ADF_SERVICES_DELIMITER |
| 49 | ADF_CFG_DCC) < ADF_CFG_MAX_VAL_LEN_IN_BYTES); |
| 50 | |
| 51 | static int adf_service_string_to_mask(struct adf_accel_dev *accel_dev, const char *buf, |
| 52 | size_t len, unsigned long *out_mask) |
| 53 | { |
| 54 | struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); |
| 55 | char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { }; |
| 56 | unsigned long mask = 0; |
| 57 | char *substr, *token; |
| 58 | int id, num_svc = 0; |
| 59 | |
| 60 | if (len > ADF_CFG_MAX_VAL_LEN_IN_BYTES - 1) |
| 61 | return -EINVAL; |
| 62 | |
| 63 | strscpy(services, buf, ADF_CFG_MAX_VAL_LEN_IN_BYTES); |
| 64 | substr = services; |
| 65 | |
| 66 | while ((token = strsep(&substr, ADF_SERVICES_DELIMITER))) { |
| 67 | id = sysfs_match_string(adf_cfg_services, token); |
| 68 | if (id < 0) |
| 69 | return id; |
| 70 | |
| 71 | if (test_and_set_bit(nr: id, addr: &mask)) |
| 72 | return -EINVAL; |
| 73 | |
| 74 | if (num_svc++ == MAX_NUM_CONCURR_SVC) |
| 75 | return -EINVAL; |
| 76 | } |
| 77 | |
| 78 | if (hw_data->services_supported && !hw_data->services_supported(mask)) |
| 79 | return -EINVAL; |
| 80 | |
| 81 | *out_mask = mask; |
| 82 | |
| 83 | return 0; |
| 84 | } |
| 85 | |
| 86 | static int adf_service_mask_to_string(unsigned long mask, char *buf, size_t len) |
| 87 | { |
| 88 | int offset = 0; |
| 89 | int bit; |
| 90 | |
| 91 | if (len < ADF_CFG_MAX_VAL_LEN_IN_BYTES) |
| 92 | return -ENOSPC; |
| 93 | |
| 94 | for_each_set_bit(bit, &mask, SVC_COUNT) { |
| 95 | if (offset) |
| 96 | offset += scnprintf(buf: buf + offset, size: len - offset, |
| 97 | ADF_SERVICES_DELIMITER); |
| 98 | |
| 99 | offset += scnprintf(buf: buf + offset, size: len - offset, fmt: "%s" , |
| 100 | adf_cfg_services[bit]); |
| 101 | } |
| 102 | |
| 103 | return 0; |
| 104 | } |
| 105 | |
| 106 | int adf_parse_service_string(struct adf_accel_dev *accel_dev, const char *in, |
| 107 | size_t in_len, char *out, size_t out_len) |
| 108 | { |
| 109 | unsigned long mask; |
| 110 | int ret; |
| 111 | |
| 112 | ret = adf_service_string_to_mask(accel_dev, buf: in, len: in_len, out_mask: &mask); |
| 113 | if (ret) |
| 114 | return ret; |
| 115 | |
| 116 | if (!mask) |
| 117 | return -EINVAL; |
| 118 | |
| 119 | return adf_service_mask_to_string(mask, buf: out, len: out_len); |
| 120 | } |
| 121 | |
| 122 | int adf_get_service_mask(struct adf_accel_dev *accel_dev, unsigned long *mask) |
| 123 | { |
| 124 | char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { }; |
| 125 | size_t len; |
| 126 | int ret; |
| 127 | |
| 128 | ret = adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC, |
| 129 | ADF_SERVICES_ENABLED, value: services); |
| 130 | if (ret) { |
| 131 | dev_err(&GET_DEV(accel_dev), "%s param not found\n" , |
| 132 | ADF_SERVICES_ENABLED); |
| 133 | return ret; |
| 134 | } |
| 135 | |
| 136 | len = strnlen(p: services, ADF_CFG_MAX_VAL_LEN_IN_BYTES); |
| 137 | ret = adf_service_string_to_mask(accel_dev, buf: services, len, out_mask: mask); |
| 138 | if (ret) |
| 139 | dev_err(&GET_DEV(accel_dev), "Invalid value of %s param: %s\n" , |
| 140 | ADF_SERVICES_ENABLED, services); |
| 141 | |
| 142 | return ret; |
| 143 | } |
| 144 | EXPORT_SYMBOL_GPL(adf_get_service_mask); |
| 145 | |
| 146 | int adf_get_service_enabled(struct adf_accel_dev *accel_dev) |
| 147 | { |
| 148 | unsigned long mask; |
| 149 | int ret; |
| 150 | |
| 151 | ret = adf_get_service_mask(accel_dev, &mask); |
| 152 | if (ret) |
| 153 | return ret; |
| 154 | |
| 155 | if (test_bit(SVC_SYM, &mask) && test_bit(SVC_ASYM, &mask)) |
| 156 | return SVC_SYM_ASYM; |
| 157 | |
| 158 | if (test_bit(SVC_SYM, &mask) && test_bit(SVC_DC, &mask)) |
| 159 | return SVC_SYM_DC; |
| 160 | |
| 161 | if (test_bit(SVC_ASYM, &mask) && test_bit(SVC_DC, &mask)) |
| 162 | return SVC_ASYM_DC; |
| 163 | |
| 164 | if (test_bit(SVC_SYM, &mask)) |
| 165 | return SVC_SYM; |
| 166 | |
| 167 | if (test_bit(SVC_ASYM, &mask)) |
| 168 | return SVC_ASYM; |
| 169 | |
| 170 | if (test_bit(SVC_DC, &mask)) |
| 171 | return SVC_DC; |
| 172 | |
| 173 | if (test_bit(SVC_DECOMP, &mask)) |
| 174 | return SVC_DECOMP; |
| 175 | |
| 176 | if (test_bit(SVC_DCC, &mask)) |
| 177 | return SVC_DCC; |
| 178 | |
| 179 | return -EINVAL; |
| 180 | } |
| 181 | EXPORT_SYMBOL_GPL(adf_get_service_enabled); |
| 182 | |
| 183 | enum adf_cfg_service_type adf_srv_to_cfg_svc_type(enum adf_base_services svc) |
| 184 | { |
| 185 | switch (svc) { |
| 186 | case SVC_ASYM: |
| 187 | return ASYM; |
| 188 | case SVC_SYM: |
| 189 | return SYM; |
| 190 | case SVC_DC: |
| 191 | return COMP; |
| 192 | case SVC_DECOMP: |
| 193 | return DECOMP; |
| 194 | default: |
| 195 | return UNUSED; |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | bool adf_is_service_enabled(struct adf_accel_dev *accel_dev, enum adf_base_services svc) |
| 200 | { |
| 201 | enum adf_cfg_service_type arb_srv = adf_srv_to_cfg_svc_type(svc); |
| 202 | struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev); |
| 203 | u8 rps_per_bundle = hw_data->num_banks_per_vf; |
| 204 | int i; |
| 205 | |
| 206 | for (i = 0; i < rps_per_bundle; i++) { |
| 207 | if (GET_SRV_TYPE(accel_dev, i) == arb_srv) |
| 208 | return true; |
| 209 | } |
| 210 | |
| 211 | return false; |
| 212 | } |
| 213 | |