1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* Copyright (C) 2015-2018 Netronome Systems, Inc. */ 3 4 /* 5 * nfp_cpplib.c 6 * Library of functions to access the NFP's CPP bus 7 * Authors: Jakub Kicinski <jakub.kicinski@netronome.com> 8 * Jason McMullan <jason.mcmullan@netronome.com> 9 * Rolf Neugebauer <rolf.neugebauer@netronome.com> 10 */ 11 12 #include <asm/unaligned.h> 13 #include <linux/bitfield.h> 14 #include <linux/delay.h> 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/slab.h> 18 #include <linux/sched.h> 19 20 #include "nfp_cpp.h" 21 #include "nfp6000/nfp6000.h" 22 #include "nfp6000/nfp_xpb.h" 23 24 /* NFP6000 PL */ 25 #define NFP_PL_DEVICE_ID 0x00000004 26 #define NFP_PL_DEVICE_ID_MASK GENMASK(7, 0) 27 #define NFP_PL_DEVICE_PART_MASK GENMASK(31, 16) 28 #define NFP_PL_DEVICE_MODEL_MASK (NFP_PL_DEVICE_PART_MASK | \ 29 NFP_PL_DEVICE_ID_MASK) 30 31 /** 32 * nfp_cpp_readl() - Read a u32 word from a CPP location 33 * @cpp: CPP device handle 34 * @cpp_id: CPP ID for operation 35 * @address: Address for operation 36 * @value: Pointer to read buffer 37 * 38 * Return: 0 on success, or -ERRNO 39 */ 40 int nfp_cpp_readl(struct nfp_cpp *cpp, u32 cpp_id, 41 unsigned long long address, u32 *value) 42 { 43 u8 tmp[4]; 44 int n; 45 46 n = nfp_cpp_read(cpp, cpp_id, address, tmp, sizeof(tmp)); 47 if (n != sizeof(tmp)) 48 return n < 0 ? n : -EIO; 49 50 *value = get_unaligned_le32(tmp); 51 return 0; 52 } 53 54 /** 55 * nfp_cpp_writel() - Write a u32 word to a CPP location 56 * @cpp: CPP device handle 57 * @cpp_id: CPP ID for operation 58 * @address: Address for operation 59 * @value: Value to write 60 * 61 * Return: 0 on success, or -ERRNO 62 */ 63 int nfp_cpp_writel(struct nfp_cpp *cpp, u32 cpp_id, 64 unsigned long long address, u32 value) 65 { 66 u8 tmp[4]; 67 int n; 68 69 put_unaligned_le32(value, tmp); 70 n = nfp_cpp_write(cpp, cpp_id, address, tmp, sizeof(tmp)); 71 72 return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO; 73 } 74 75 /** 76 * nfp_cpp_readq() - Read a u64 word from a CPP location 77 * @cpp: CPP device handle 78 * @cpp_id: CPP ID for operation 79 * @address: Address for operation 80 * @value: Pointer to read buffer 81 * 82 * Return: 0 on success, or -ERRNO 83 */ 84 int nfp_cpp_readq(struct nfp_cpp *cpp, u32 cpp_id, 85 unsigned long long address, u64 *value) 86 { 87 u8 tmp[8]; 88 int n; 89 90 n = nfp_cpp_read(cpp, cpp_id, address, tmp, sizeof(tmp)); 91 if (n != sizeof(tmp)) 92 return n < 0 ? n : -EIO; 93 94 *value = get_unaligned_le64(tmp); 95 return 0; 96 } 97 98 /** 99 * nfp_cpp_writeq() - Write a u64 word to a CPP location 100 * @cpp: CPP device handle 101 * @cpp_id: CPP ID for operation 102 * @address: Address for operation 103 * @value: Value to write 104 * 105 * Return: 0 on success, or -ERRNO 106 */ 107 int nfp_cpp_writeq(struct nfp_cpp *cpp, u32 cpp_id, 108 unsigned long long address, u64 value) 109 { 110 u8 tmp[8]; 111 int n; 112 113 put_unaligned_le64(value, tmp); 114 n = nfp_cpp_write(cpp, cpp_id, address, tmp, sizeof(tmp)); 115 116 return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO; 117 } 118 119 /* NOTE: This code should not use nfp_xpb_* functions, 120 * as those are model-specific 121 */ 122 int nfp_cpp_model_autodetect(struct nfp_cpp *cpp, u32 *model) 123 { 124 u32 reg; 125 int err; 126 127 err = nfp_xpb_readl(cpp, NFP_XPB_DEVICE(1, 1, 16) + NFP_PL_DEVICE_ID, 128 ®); 129 if (err < 0) 130 return err; 131 132 *model = reg & NFP_PL_DEVICE_MODEL_MASK; 133 if (*model & NFP_PL_DEVICE_ID_MASK) 134 *model -= 0x10; 135 136 return 0; 137 } 138 139 static u8 nfp_bytemask(int width, u64 addr) 140 { 141 if (width == 8) 142 return 0xff; 143 else if (width == 4) 144 return 0x0f << (addr & 4); 145 else if (width == 2) 146 return 0x03 << (addr & 6); 147 else if (width == 1) 148 return 0x01 << (addr & 7); 149 else 150 return 0; 151 } 152 153 int nfp_cpp_explicit_read(struct nfp_cpp *cpp, u32 cpp_id, 154 u64 addr, void *buff, size_t len, int width_read) 155 { 156 struct nfp_cpp_explicit *expl; 157 char *tmp = buff; 158 int err, i, incr; 159 u8 byte_mask; 160 161 if (len & (width_read - 1)) 162 return -EINVAL; 163 164 expl = nfp_cpp_explicit_acquire(cpp); 165 if (!expl) 166 return -EBUSY; 167 168 incr = min_t(int, 16 * width_read, 128); 169 incr = min_t(int, incr, len); 170 171 /* Translate a NFP_CPP_ACTION_RW to action 0 */ 172 if (NFP_CPP_ID_ACTION_of(cpp_id) == NFP_CPP_ACTION_RW) 173 cpp_id = NFP_CPP_ID(NFP_CPP_ID_TARGET_of(cpp_id), 0, 174 NFP_CPP_ID_TOKEN_of(cpp_id)); 175 176 byte_mask = nfp_bytemask(width_read, addr); 177 178 nfp_cpp_explicit_set_target(expl, cpp_id, 179 incr / width_read - 1, byte_mask); 180 nfp_cpp_explicit_set_posted(expl, 1, 0, NFP_SIGNAL_PUSH, 181 0, NFP_SIGNAL_NONE); 182 183 for (i = 0; i < len; i += incr, addr += incr, tmp += incr) { 184 if (i + incr > len) { 185 incr = len - i; 186 nfp_cpp_explicit_set_target(expl, cpp_id, 187 incr / width_read - 1, 188 0xff); 189 } 190 191 err = nfp_cpp_explicit_do(expl, addr); 192 if (err < 0) 193 goto exit_release; 194 195 err = nfp_cpp_explicit_get(expl, tmp, incr); 196 if (err < 0) 197 goto exit_release; 198 } 199 err = len; 200 exit_release: 201 nfp_cpp_explicit_release(expl); 202 203 return err; 204 } 205 206 int nfp_cpp_explicit_write(struct nfp_cpp *cpp, u32 cpp_id, u64 addr, 207 const void *buff, size_t len, int width_write) 208 { 209 struct nfp_cpp_explicit *expl; 210 const char *tmp = buff; 211 int err, i, incr; 212 u8 byte_mask; 213 214 if (len & (width_write - 1)) 215 return -EINVAL; 216 217 expl = nfp_cpp_explicit_acquire(cpp); 218 if (!expl) 219 return -EBUSY; 220 221 incr = min_t(int, 16 * width_write, 128); 222 incr = min_t(int, incr, len); 223 224 /* Translate a NFP_CPP_ACTION_RW to action 1 */ 225 if (NFP_CPP_ID_ACTION_of(cpp_id) == NFP_CPP_ACTION_RW) 226 cpp_id = NFP_CPP_ID(NFP_CPP_ID_TARGET_of(cpp_id), 1, 227 NFP_CPP_ID_TOKEN_of(cpp_id)); 228 229 byte_mask = nfp_bytemask(width_write, addr); 230 231 nfp_cpp_explicit_set_target(expl, cpp_id, 232 incr / width_write - 1, byte_mask); 233 nfp_cpp_explicit_set_posted(expl, 1, 0, NFP_SIGNAL_PULL, 234 0, NFP_SIGNAL_NONE); 235 236 for (i = 0; i < len; i += incr, addr += incr, tmp += incr) { 237 if (i + incr > len) { 238 incr = len - i; 239 nfp_cpp_explicit_set_target(expl, cpp_id, 240 incr / width_write - 1, 241 0xff); 242 } 243 244 err = nfp_cpp_explicit_put(expl, tmp, incr); 245 if (err < 0) 246 goto exit_release; 247 248 err = nfp_cpp_explicit_do(expl, addr); 249 if (err < 0) 250 goto exit_release; 251 } 252 err = len; 253 exit_release: 254 nfp_cpp_explicit_release(expl); 255 256 return err; 257 } 258 259 /** 260 * nfp_cpp_map_area() - Helper function to map an area 261 * @cpp: NFP CPP handler 262 * @name: Name for the area 263 * @cpp_id: CPP ID for operation 264 * @addr: CPP address 265 * @size: Size of the area 266 * @area: Area handle (output) 267 * 268 * Map an area of IOMEM access. To undo the effect of this function call 269 * @nfp_cpp_area_release_free(*area). 270 * 271 * Return: Pointer to memory mapped area or ERR_PTR 272 */ 273 u8 __iomem * 274 nfp_cpp_map_area(struct nfp_cpp *cpp, const char *name, u32 cpp_id, u64 addr, 275 unsigned long size, struct nfp_cpp_area **area) 276 { 277 u8 __iomem *res; 278 279 *area = nfp_cpp_area_alloc_acquire(cpp, name, cpp_id, addr, size); 280 if (!*area) 281 goto err_eio; 282 283 res = nfp_cpp_area_iomem(*area); 284 if (!res) 285 goto err_release_free; 286 287 return res; 288 289 err_release_free: 290 nfp_cpp_area_release_free(*area); 291 err_eio: 292 return (u8 __iomem *)ERR_PTR(-EIO); 293 } 294