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