1 /* 2 * Copyright (C) 2015-2017 Netronome Systems, Inc. 3 * 4 * This software is dual licensed under the GNU General License Version 2, 5 * June 1991 as shown in the file COPYING in the top-level directory of this 6 * source tree or the BSD 2-Clause License provided below. You have the 7 * option to license this software under the complete terms of either license. 8 * 9 * The BSD 2-Clause License: 10 * 11 * Redistribution and use in source and binary forms, with or 12 * without modification, are permitted provided that the following 13 * conditions are met: 14 * 15 * 1. Redistributions of source code must retain the above 16 * copyright notice, this list of conditions and the following 17 * disclaimer. 18 * 19 * 2. Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials 22 * provided with the distribution. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 * SOFTWARE. 32 */ 33 34 /* 35 * nfp_cpplib.c 36 * Library of functions to access the NFP's CPP bus 37 * Authors: Jakub Kicinski <jakub.kicinski@netronome.com> 38 * Jason McMullan <jason.mcmullan@netronome.com> 39 * Rolf Neugebauer <rolf.neugebauer@netronome.com> 40 */ 41 42 #include <asm/unaligned.h> 43 #include <linux/bitfield.h> 44 #include <linux/delay.h> 45 #include <linux/kernel.h> 46 #include <linux/module.h> 47 #include <linux/slab.h> 48 #include <linux/sched.h> 49 50 #include "nfp_cpp.h" 51 #include "nfp6000/nfp6000.h" 52 #include "nfp6000/nfp_xpb.h" 53 54 /* NFP6000 PL */ 55 #define NFP_PL_DEVICE_ID 0x00000004 56 #define NFP_PL_DEVICE_ID_MASK GENMASK(7, 0) 57 58 #define NFP6000_ARM_GCSR_SOFTMODEL0 0x00400144 59 60 /** 61 * nfp_cpp_readl() - Read a u32 word from a CPP location 62 * @cpp: CPP device handle 63 * @cpp_id: CPP ID for operation 64 * @address: Address for operation 65 * @value: Pointer to read buffer 66 * 67 * Return: 0 on success, or -ERRNO 68 */ 69 int nfp_cpp_readl(struct nfp_cpp *cpp, u32 cpp_id, 70 unsigned long long address, u32 *value) 71 { 72 u8 tmp[4]; 73 int n; 74 75 n = nfp_cpp_read(cpp, cpp_id, address, tmp, sizeof(tmp)); 76 if (n != sizeof(tmp)) 77 return n < 0 ? n : -EIO; 78 79 *value = get_unaligned_le32(tmp); 80 return 0; 81 } 82 83 /** 84 * nfp_cpp_writel() - Write a u32 word to a CPP location 85 * @cpp: CPP device handle 86 * @cpp_id: CPP ID for operation 87 * @address: Address for operation 88 * @value: Value to write 89 * 90 * Return: 0 on success, or -ERRNO 91 */ 92 int nfp_cpp_writel(struct nfp_cpp *cpp, u32 cpp_id, 93 unsigned long long address, u32 value) 94 { 95 u8 tmp[4]; 96 int n; 97 98 put_unaligned_le32(value, tmp); 99 n = nfp_cpp_write(cpp, cpp_id, address, tmp, sizeof(tmp)); 100 101 return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO; 102 } 103 104 /** 105 * nfp_cpp_readq() - Read a u64 word from a CPP location 106 * @cpp: CPP device handle 107 * @cpp_id: CPP ID for operation 108 * @address: Address for operation 109 * @value: Pointer to read buffer 110 * 111 * Return: 0 on success, or -ERRNO 112 */ 113 int nfp_cpp_readq(struct nfp_cpp *cpp, u32 cpp_id, 114 unsigned long long address, u64 *value) 115 { 116 u8 tmp[8]; 117 int n; 118 119 n = nfp_cpp_read(cpp, cpp_id, address, tmp, sizeof(tmp)); 120 if (n != sizeof(tmp)) 121 return n < 0 ? n : -EIO; 122 123 *value = get_unaligned_le64(tmp); 124 return 0; 125 } 126 127 /** 128 * nfp_cpp_writeq() - Write a u64 word to a CPP location 129 * @cpp: CPP device handle 130 * @cpp_id: CPP ID for operation 131 * @address: Address for operation 132 * @value: Value to write 133 * 134 * Return: 0 on success, or -ERRNO 135 */ 136 int nfp_cpp_writeq(struct nfp_cpp *cpp, u32 cpp_id, 137 unsigned long long address, u64 value) 138 { 139 u8 tmp[8]; 140 int n; 141 142 put_unaligned_le64(value, tmp); 143 n = nfp_cpp_write(cpp, cpp_id, address, tmp, sizeof(tmp)); 144 145 return n == sizeof(tmp) ? 0 : n < 0 ? n : -EIO; 146 } 147 148 /* NOTE: This code should not use nfp_xpb_* functions, 149 * as those are model-specific 150 */ 151 int nfp_cpp_model_autodetect(struct nfp_cpp *cpp, u32 *model) 152 { 153 const u32 arm_id = NFP_CPP_ID(NFP_CPP_TARGET_ARM, 0, 0); 154 u32 reg; 155 int err; 156 157 err = nfp_cpp_readl(cpp, arm_id, NFP6000_ARM_GCSR_SOFTMODEL0, model); 158 if (err < 0) 159 return err; 160 161 /* The PL's PluDeviceID revision code is authoratative */ 162 *model &= ~0xff; 163 err = nfp_xpb_readl(cpp, NFP_XPB_DEVICE(1, 1, 16) + NFP_PL_DEVICE_ID, 164 ®); 165 if (err < 0) 166 return err; 167 168 *model |= (NFP_PL_DEVICE_ID_MASK & reg) - 0x10; 169 170 return 0; 171 } 172 173 static u8 nfp_bytemask(int width, u64 addr) 174 { 175 if (width == 8) 176 return 0xff; 177 else if (width == 4) 178 return 0x0f << (addr & 4); 179 else if (width == 2) 180 return 0x03 << (addr & 6); 181 else if (width == 1) 182 return 0x01 << (addr & 7); 183 else 184 return 0; 185 } 186 187 int nfp_cpp_explicit_read(struct nfp_cpp *cpp, u32 cpp_id, 188 u64 addr, void *buff, size_t len, int width_read) 189 { 190 struct nfp_cpp_explicit *expl; 191 char *tmp = buff; 192 int err, i, incr; 193 u8 byte_mask; 194 195 if (len & (width_read - 1)) 196 return -EINVAL; 197 198 expl = nfp_cpp_explicit_acquire(cpp); 199 if (!expl) 200 return -EBUSY; 201 202 incr = min_t(int, 16 * width_read, 128); 203 incr = min_t(int, incr, len); 204 205 /* Translate a NFP_CPP_ACTION_RW to action 0 */ 206 if (NFP_CPP_ID_ACTION_of(cpp_id) == NFP_CPP_ACTION_RW) 207 cpp_id = NFP_CPP_ID(NFP_CPP_ID_TARGET_of(cpp_id), 0, 208 NFP_CPP_ID_TOKEN_of(cpp_id)); 209 210 byte_mask = nfp_bytemask(width_read, addr); 211 212 nfp_cpp_explicit_set_target(expl, cpp_id, 213 incr / width_read - 1, byte_mask); 214 nfp_cpp_explicit_set_posted(expl, 1, 0, NFP_SIGNAL_PUSH, 215 0, NFP_SIGNAL_NONE); 216 217 for (i = 0; i < len; i += incr, addr += incr, tmp += incr) { 218 if (i + incr > len) { 219 incr = len - i; 220 nfp_cpp_explicit_set_target(expl, cpp_id, 221 incr / width_read - 1, 222 0xff); 223 } 224 225 err = nfp_cpp_explicit_do(expl, addr); 226 if (err < 0) 227 goto exit_release; 228 229 err = nfp_cpp_explicit_get(expl, tmp, incr); 230 if (err < 0) 231 goto exit_release; 232 } 233 err = len; 234 exit_release: 235 nfp_cpp_explicit_release(expl); 236 237 return err; 238 } 239 240 int nfp_cpp_explicit_write(struct nfp_cpp *cpp, u32 cpp_id, u64 addr, 241 const void *buff, size_t len, int width_write) 242 { 243 struct nfp_cpp_explicit *expl; 244 const char *tmp = buff; 245 int err, i, incr; 246 u8 byte_mask; 247 248 if (len & (width_write - 1)) 249 return -EINVAL; 250 251 expl = nfp_cpp_explicit_acquire(cpp); 252 if (!expl) 253 return -EBUSY; 254 255 incr = min_t(int, 16 * width_write, 128); 256 incr = min_t(int, incr, len); 257 258 /* Translate a NFP_CPP_ACTION_RW to action 1 */ 259 if (NFP_CPP_ID_ACTION_of(cpp_id) == NFP_CPP_ACTION_RW) 260 cpp_id = NFP_CPP_ID(NFP_CPP_ID_TARGET_of(cpp_id), 1, 261 NFP_CPP_ID_TOKEN_of(cpp_id)); 262 263 byte_mask = nfp_bytemask(width_write, addr); 264 265 nfp_cpp_explicit_set_target(expl, cpp_id, 266 incr / width_write - 1, byte_mask); 267 nfp_cpp_explicit_set_posted(expl, 1, 0, NFP_SIGNAL_PULL, 268 0, NFP_SIGNAL_NONE); 269 270 for (i = 0; i < len; i += incr, addr += incr, tmp += incr) { 271 if (i + incr > len) { 272 incr = len - i; 273 nfp_cpp_explicit_set_target(expl, cpp_id, 274 incr / width_write - 1, 275 0xff); 276 } 277 278 err = nfp_cpp_explicit_put(expl, tmp, incr); 279 if (err < 0) 280 goto exit_release; 281 282 err = nfp_cpp_explicit_do(expl, addr); 283 if (err < 0) 284 goto exit_release; 285 } 286 err = len; 287 exit_release: 288 nfp_cpp_explicit_release(expl); 289 290 return err; 291 } 292 293 /** 294 * nfp_cpp_map_area() - Helper function to map an area 295 * @cpp: NFP CPP handler 296 * @name: Name for the area 297 * @domain: CPP domain 298 * @target: CPP target 299 * @addr: CPP address 300 * @size: Size of the area 301 * @area: Area handle (output) 302 * 303 * Map an area of IOMEM access. To undo the effect of this function call 304 * @nfp_cpp_area_release_free(*area). 305 * 306 * Return: Pointer to memory mapped area or ERR_PTR 307 */ 308 u8 __iomem * 309 nfp_cpp_map_area(struct nfp_cpp *cpp, const char *name, int domain, int target, 310 u64 addr, unsigned long size, struct nfp_cpp_area **area) 311 { 312 u8 __iomem *res; 313 u32 dest; 314 315 dest = NFP_CPP_ISLAND_ID(target, NFP_CPP_ACTION_RW, 0, domain); 316 317 *area = nfp_cpp_area_alloc_acquire(cpp, name, dest, addr, size); 318 if (!*area) 319 goto err_eio; 320 321 res = nfp_cpp_area_iomem(*area); 322 if (!res) 323 goto err_release_free; 324 325 return res; 326 327 err_release_free: 328 nfp_cpp_area_release_free(*area); 329 err_eio: 330 return (u8 __iomem *)ERR_PTR(-EIO); 331 } 332