370 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)2/* Copyright (C) 2017-2018 Netronome Systems, Inc. */3 4#include <linux/rtnetlink.h>5#include <net/devlink.h>6 7#include "nfpcore/nfp.h"8#include "nfpcore/nfp_nsp.h"9#include "nfp_app.h"10#include "nfp_main.h"11#include "nfp_port.h"12 13static int14nfp_devlink_fill_eth_port(struct nfp_port *port,15 struct nfp_eth_table_port *copy)16{17 struct nfp_eth_table_port *eth_port;18 19 eth_port = __nfp_port_get_eth_port(port);20 if (!eth_port)21 return -EINVAL;22 23 memcpy(copy, eth_port, sizeof(*eth_port));24 25 return 0;26}27 28static int29nfp_devlink_fill_eth_port_from_id(struct nfp_pf *pf,30 struct devlink_port *dl_port,31 struct nfp_eth_table_port *copy)32{33 struct nfp_port *port = container_of(dl_port, struct nfp_port, dl_port);34 35 return nfp_devlink_fill_eth_port(port, copy);36}37 38static int39nfp_devlink_set_lanes(struct nfp_pf *pf, unsigned int idx, unsigned int lanes)40{41 struct nfp_nsp *nsp;42 int ret;43 44 nsp = nfp_eth_config_start(pf->cpp, idx);45 if (IS_ERR(nsp))46 return PTR_ERR(nsp);47 48 ret = __nfp_eth_set_split(nsp, lanes);49 if (ret) {50 nfp_eth_config_cleanup_end(nsp);51 return ret;52 }53 54 ret = nfp_eth_config_commit_end(nsp);55 if (ret < 0)56 return ret;57 if (ret) /* no change */58 return 0;59 60 return nfp_net_refresh_port_table_sync(pf);61}62 63static int64nfp_devlink_port_split(struct devlink *devlink, struct devlink_port *port,65 unsigned int count, struct netlink_ext_ack *extack)66{67 struct nfp_pf *pf = devlink_priv(devlink);68 struct nfp_eth_table_port eth_port;69 unsigned int lanes;70 int ret;71 72 rtnl_lock();73 ret = nfp_devlink_fill_eth_port_from_id(pf, port, ð_port);74 rtnl_unlock();75 if (ret)76 return ret;77 78 if (eth_port.port_lanes % count) {79 NL_SET_ERR_MSG_MOD(extack, "invalid count");80 return -EINVAL;81 }82 83 /* Special case the 100G CXP -> 2x40G split */84 lanes = eth_port.port_lanes / count;85 if (eth_port.lanes == 10 && count == 2)86 lanes = 8 / count;87 88 return nfp_devlink_set_lanes(pf, eth_port.index, lanes);89}90 91static int92nfp_devlink_port_unsplit(struct devlink *devlink, struct devlink_port *port,93 struct netlink_ext_ack *extack)94{95 struct nfp_pf *pf = devlink_priv(devlink);96 struct nfp_eth_table_port eth_port;97 unsigned int lanes;98 int ret;99 100 rtnl_lock();101 ret = nfp_devlink_fill_eth_port_from_id(pf, port, ð_port);102 rtnl_unlock();103 if (ret)104 return ret;105 106 if (!eth_port.is_split) {107 NL_SET_ERR_MSG_MOD(extack, "port is not split");108 return -EINVAL;109 }110 111 /* Special case the 100G CXP -> 2x40G unsplit */112 lanes = eth_port.port_lanes;113 if (eth_port.port_lanes == 8)114 lanes = 10;115 116 return nfp_devlink_set_lanes(pf, eth_port.index, lanes);117}118 119static int120nfp_devlink_sb_pool_get(struct devlink *devlink, unsigned int sb_index,121 u16 pool_index, struct devlink_sb_pool_info *pool_info)122{123 struct nfp_pf *pf = devlink_priv(devlink);124 125 return nfp_shared_buf_pool_get(pf, sb_index, pool_index, pool_info);126}127 128static int129nfp_devlink_sb_pool_set(struct devlink *devlink, unsigned int sb_index,130 u16 pool_index,131 u32 size, enum devlink_sb_threshold_type threshold_type,132 struct netlink_ext_ack *extack)133{134 struct nfp_pf *pf = devlink_priv(devlink);135 136 return nfp_shared_buf_pool_set(pf, sb_index, pool_index,137 size, threshold_type);138}139 140static int nfp_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)141{142 struct nfp_pf *pf = devlink_priv(devlink);143 144 return nfp_app_eswitch_mode_get(pf->app, mode);145}146 147static int nfp_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,148 struct netlink_ext_ack *extack)149{150 struct nfp_pf *pf = devlink_priv(devlink);151 152 return nfp_app_eswitch_mode_set(pf->app, mode);153}154 155static const struct nfp_devlink_versions_simple {156 const char *key;157 const char *hwinfo;158} nfp_devlink_versions_hwinfo[] = {159 { DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, "assembly.partno", },160 { DEVLINK_INFO_VERSION_GENERIC_BOARD_REV, "assembly.revision", },161 { DEVLINK_INFO_VERSION_GENERIC_BOARD_MANUFACTURE, "assembly.vendor", },162 { "board.model", /* code name */ "assembly.model", },163 { DEVLINK_INFO_VERSION_GENERIC_BOARD_PART_NUMBER, "pn", },164};165 166static int167nfp_devlink_versions_get_hwinfo(struct nfp_pf *pf, struct devlink_info_req *req)168{169 unsigned int i;170 int err;171 172 for (i = 0; i < ARRAY_SIZE(nfp_devlink_versions_hwinfo); i++) {173 const struct nfp_devlink_versions_simple *info;174 const char *val;175 176 info = &nfp_devlink_versions_hwinfo[i];177 178 val = nfp_hwinfo_lookup(pf->hwinfo, info->hwinfo);179 if (!val)180 continue;181 182 err = devlink_info_version_fixed_put(req, info->key, val);183 if (err)184 return err;185 }186 187 return 0;188}189 190static const struct nfp_devlink_versions {191 enum nfp_nsp_versions id;192 const char *key;193} nfp_devlink_versions_nsp[] = {194 { NFP_VERSIONS_BUNDLE, DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID, },195 { NFP_VERSIONS_BSP, DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, },196 { NFP_VERSIONS_CPLD, "fw.cpld", },197 { NFP_VERSIONS_APP, DEVLINK_INFO_VERSION_GENERIC_FW_APP, },198 { NFP_VERSIONS_UNDI, DEVLINK_INFO_VERSION_GENERIC_FW_UNDI, },199 { NFP_VERSIONS_NCSI, DEVLINK_INFO_VERSION_GENERIC_FW_NCSI, },200 { NFP_VERSIONS_CFGR, "chip.init", },201};202 203static int204nfp_devlink_versions_get_nsp(struct devlink_info_req *req, bool flash,205 const u8 *buf, unsigned int size)206{207 unsigned int i;208 int err;209 210 for (i = 0; i < ARRAY_SIZE(nfp_devlink_versions_nsp); i++) {211 const struct nfp_devlink_versions *info;212 const char *version;213 214 info = &nfp_devlink_versions_nsp[i];215 216 version = nfp_nsp_versions_get(info->id, flash, buf, size);217 if (IS_ERR(version)) {218 if (PTR_ERR(version) == -ENOENT)219 continue;220 else221 return PTR_ERR(version);222 }223 224 if (flash)225 err = devlink_info_version_stored_put(req, info->key,226 version);227 else228 err = devlink_info_version_running_put(req, info->key,229 version);230 if (err)231 return err;232 }233 234 return 0;235}236 237static int238nfp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,239 struct netlink_ext_ack *extack)240{241 struct nfp_pf *pf = devlink_priv(devlink);242 const char *sn, *vendor, *part;243 struct nfp_nsp *nsp;244 char *buf = NULL;245 int err;246 247 vendor = nfp_hwinfo_lookup(pf->hwinfo, "assembly.vendor");248 part = nfp_hwinfo_lookup(pf->hwinfo, "assembly.partno");249 sn = nfp_hwinfo_lookup(pf->hwinfo, "assembly.serial");250 if (vendor && part && sn) {251 char *buf;252 253 buf = kmalloc(strlen(vendor) + strlen(part) + strlen(sn) + 1,254 GFP_KERNEL);255 if (!buf)256 return -ENOMEM;257 258 buf[0] = '\0';259 strcat(buf, vendor);260 strcat(buf, part);261 strcat(buf, sn);262 263 err = devlink_info_serial_number_put(req, buf);264 kfree(buf);265 if (err)266 return err;267 }268 269 nsp = nfp_nsp_open(pf->cpp);270 if (IS_ERR(nsp)) {271 NL_SET_ERR_MSG_MOD(extack, "can't access NSP");272 return PTR_ERR(nsp);273 }274 275 if (nfp_nsp_has_versions(nsp)) {276 buf = kzalloc(NFP_NSP_VERSION_BUFSZ, GFP_KERNEL);277 if (!buf) {278 err = -ENOMEM;279 goto err_close_nsp;280 }281 282 err = nfp_nsp_versions(nsp, buf, NFP_NSP_VERSION_BUFSZ);283 if (err)284 goto err_free_buf;285 286 err = nfp_devlink_versions_get_nsp(req, false,287 buf, NFP_NSP_VERSION_BUFSZ);288 if (err)289 goto err_free_buf;290 291 err = nfp_devlink_versions_get_nsp(req, true,292 buf, NFP_NSP_VERSION_BUFSZ);293 if (err)294 goto err_free_buf;295 296 kfree(buf);297 }298 299 nfp_nsp_close(nsp);300 301 return nfp_devlink_versions_get_hwinfo(pf, req);302 303err_free_buf:304 kfree(buf);305err_close_nsp:306 nfp_nsp_close(nsp);307 return err;308}309 310static int311nfp_devlink_flash_update(struct devlink *devlink,312 struct devlink_flash_update_params *params,313 struct netlink_ext_ack *extack)314{315 return nfp_flash_update_common(devlink_priv(devlink), params->fw, extack);316}317 318const struct devlink_ops nfp_devlink_ops = {319 .sb_pool_get = nfp_devlink_sb_pool_get,320 .sb_pool_set = nfp_devlink_sb_pool_set,321 .eswitch_mode_get = nfp_devlink_eswitch_mode_get,322 .eswitch_mode_set = nfp_devlink_eswitch_mode_set,323 .info_get = nfp_devlink_info_get,324 .flash_update = nfp_devlink_flash_update,325};326 327static const struct devlink_port_ops nfp_devlink_port_ops = {328 .port_split = nfp_devlink_port_split,329 .port_unsplit = nfp_devlink_port_unsplit,330};331 332int nfp_devlink_port_register(struct nfp_app *app, struct nfp_port *port)333{334 struct devlink_port_attrs attrs = {};335 struct nfp_eth_table_port eth_port;336 struct devlink *devlink;337 const u8 *serial;338 int serial_len;339 int ret;340 341 SET_NETDEV_DEVLINK_PORT(port->netdev, &port->dl_port);342 343 rtnl_lock();344 ret = nfp_devlink_fill_eth_port(port, ð_port);345 rtnl_unlock();346 if (ret)347 return ret;348 349 attrs.split = eth_port.is_split;350 attrs.splittable = eth_port.port_lanes > 1 && !attrs.split;351 attrs.lanes = eth_port.port_lanes;352 attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;353 attrs.phys.port_number = eth_port.label_port;354 attrs.phys.split_subport_number = eth_port.label_subport;355 serial_len = nfp_cpp_serial(port->app->cpp, &serial);356 memcpy(attrs.switch_id.id, serial, serial_len);357 attrs.switch_id.id_len = serial_len;358 devlink_port_attrs_set(&port->dl_port, &attrs);359 360 devlink = priv_to_devlink(app->pf);361 362 return devl_port_register_with_ops(devlink, &port->dl_port,363 port->eth_id, &nfp_devlink_port_ops);364}365 366void nfp_devlink_port_unregister(struct nfp_port *port)367{368 devl_port_unregister(&port->dl_port);369}370