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