1b217127eSJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2b217127eSJiri Pirko /* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */ 3b217127eSJiri Pirko 4b217127eSJiri Pirko #include <linux/kernel.h> 5b217127eSJiri Pirko #include <linux/module.h> 6b217127eSJiri Pirko #include <linux/err.h> 7b217127eSJiri Pirko #include <linux/types.h> 8b217127eSJiri Pirko #include <linux/string.h> 9b217127eSJiri Pirko #include <linux/workqueue.h> 10b217127eSJiri Pirko #include <linux/gfp.h> 11b217127eSJiri Pirko #include <linux/slab.h> 12b217127eSJiri Pirko #include <linux/list.h> 13b217127eSJiri Pirko #include <linux/vmalloc.h> 14b217127eSJiri Pirko 15b217127eSJiri Pirko #include "core.h" 16b217127eSJiri Pirko 17b217127eSJiri Pirko struct mlxsw_linecard_ini_file { 18b217127eSJiri Pirko __le16 size; 19b217127eSJiri Pirko union { 20b217127eSJiri Pirko u8 data[0]; 21b217127eSJiri Pirko struct { 22b217127eSJiri Pirko __be16 hw_revision; 23b217127eSJiri Pirko __be16 ini_version; 24b217127eSJiri Pirko u8 __dontcare[3]; 25b217127eSJiri Pirko u8 type; 26b217127eSJiri Pirko u8 name[20]; 27b217127eSJiri Pirko } format; 28b217127eSJiri Pirko }; 29b217127eSJiri Pirko }; 30b217127eSJiri Pirko 31b217127eSJiri Pirko struct mlxsw_linecard_types_info { 32b217127eSJiri Pirko struct mlxsw_linecard_ini_file **ini_files; 33b217127eSJiri Pirko unsigned int count; 34b217127eSJiri Pirko size_t data_size; 35b217127eSJiri Pirko char *data; 36b217127eSJiri Pirko }; 37b217127eSJiri Pirko 38b217127eSJiri Pirko #define MLXSW_LINECARD_STATUS_EVENT_TO (10 * MSEC_PER_SEC) 39b217127eSJiri Pirko 40b217127eSJiri Pirko static void 41b217127eSJiri Pirko mlxsw_linecard_status_event_to_schedule(struct mlxsw_linecard *linecard, 42b217127eSJiri Pirko enum mlxsw_linecard_status_event_type status_event_type) 43b217127eSJiri Pirko { 44b217127eSJiri Pirko cancel_delayed_work_sync(&linecard->status_event_to_dw); 45b217127eSJiri Pirko linecard->status_event_type_to = status_event_type; 46b217127eSJiri Pirko mlxsw_core_schedule_dw(&linecard->status_event_to_dw, 47b217127eSJiri Pirko msecs_to_jiffies(MLXSW_LINECARD_STATUS_EVENT_TO)); 48b217127eSJiri Pirko } 49b217127eSJiri Pirko 50b217127eSJiri Pirko static void 51b217127eSJiri Pirko mlxsw_linecard_status_event_done(struct mlxsw_linecard *linecard, 52b217127eSJiri Pirko enum mlxsw_linecard_status_event_type status_event_type) 53b217127eSJiri Pirko { 54b217127eSJiri Pirko if (linecard->status_event_type_to == status_event_type) 55b217127eSJiri Pirko cancel_delayed_work_sync(&linecard->status_event_to_dw); 56b217127eSJiri Pirko } 57b217127eSJiri Pirko 58b217127eSJiri Pirko static const char * 59b217127eSJiri Pirko mlxsw_linecard_types_lookup(struct mlxsw_linecards *linecards, u8 card_type) 60b217127eSJiri Pirko { 61b217127eSJiri Pirko struct mlxsw_linecard_types_info *types_info; 62b217127eSJiri Pirko struct mlxsw_linecard_ini_file *ini_file; 63b217127eSJiri Pirko int i; 64b217127eSJiri Pirko 65b217127eSJiri Pirko types_info = linecards->types_info; 66b217127eSJiri Pirko if (!types_info) 67b217127eSJiri Pirko return NULL; 68b217127eSJiri Pirko for (i = 0; i < types_info->count; i++) { 69b217127eSJiri Pirko ini_file = linecards->types_info->ini_files[i]; 70b217127eSJiri Pirko if (ini_file->format.type == card_type) 71b217127eSJiri Pirko return ini_file->format.name; 72b217127eSJiri Pirko } 73b217127eSJiri Pirko return NULL; 74b217127eSJiri Pirko } 75b217127eSJiri Pirko 76b217127eSJiri Pirko static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard) 77b217127eSJiri Pirko { 78b217127eSJiri Pirko struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 79b217127eSJiri Pirko char mddq_pl[MLXSW_REG_MDDQ_LEN]; 80b217127eSJiri Pirko int err; 81b217127eSJiri Pirko 82b217127eSJiri Pirko mlxsw_reg_mddq_slot_name_pack(mddq_pl, linecard->slot_index); 83b217127eSJiri Pirko err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 84b217127eSJiri Pirko if (err) 85b217127eSJiri Pirko return ERR_PTR(err); 86b217127eSJiri Pirko mlxsw_reg_mddq_slot_name_unpack(mddq_pl, linecard->name); 87b217127eSJiri Pirko return linecard->name; 88b217127eSJiri Pirko } 89b217127eSJiri Pirko 90*4da0eb2aSJiri Pirko static int mlxsw_linecard_device_info_update(struct mlxsw_linecard *linecard) 91*4da0eb2aSJiri Pirko { 92*4da0eb2aSJiri Pirko struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 93*4da0eb2aSJiri Pirko bool flashable_found = false; 94*4da0eb2aSJiri Pirko u8 msg_seq = 0; 95*4da0eb2aSJiri Pirko 96*4da0eb2aSJiri Pirko do { 97*4da0eb2aSJiri Pirko struct mlxsw_linecard_device_info info; 98*4da0eb2aSJiri Pirko char mddq_pl[MLXSW_REG_MDDQ_LEN]; 99*4da0eb2aSJiri Pirko bool flash_owner; 100*4da0eb2aSJiri Pirko bool data_valid; 101*4da0eb2aSJiri Pirko u8 device_index; 102*4da0eb2aSJiri Pirko int err; 103*4da0eb2aSJiri Pirko 104*4da0eb2aSJiri Pirko mlxsw_reg_mddq_device_info_pack(mddq_pl, linecard->slot_index, 105*4da0eb2aSJiri Pirko msg_seq); 106*4da0eb2aSJiri Pirko err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 107*4da0eb2aSJiri Pirko if (err) 108*4da0eb2aSJiri Pirko return err; 109*4da0eb2aSJiri Pirko mlxsw_reg_mddq_device_info_unpack(mddq_pl, &msg_seq, 110*4da0eb2aSJiri Pirko &data_valid, &flash_owner, 111*4da0eb2aSJiri Pirko &device_index, 112*4da0eb2aSJiri Pirko &info.fw_major, 113*4da0eb2aSJiri Pirko &info.fw_minor, 114*4da0eb2aSJiri Pirko &info.fw_sub_minor); 115*4da0eb2aSJiri Pirko if (!data_valid) 116*4da0eb2aSJiri Pirko break; 117*4da0eb2aSJiri Pirko if (!flash_owner) /* We care only about flashable ones. */ 118*4da0eb2aSJiri Pirko continue; 119*4da0eb2aSJiri Pirko if (flashable_found) { 120*4da0eb2aSJiri Pirko dev_warn_once(linecard->linecards->bus_info->dev, "linecard %u: More flashable devices present, exposing only the first one\n", 121*4da0eb2aSJiri Pirko linecard->slot_index); 122*4da0eb2aSJiri Pirko return 0; 123*4da0eb2aSJiri Pirko } 124*4da0eb2aSJiri Pirko linecard->device.info = info; 125*4da0eb2aSJiri Pirko flashable_found = true; 126*4da0eb2aSJiri Pirko } while (msg_seq); 127*4da0eb2aSJiri Pirko 128*4da0eb2aSJiri Pirko return 0; 129*4da0eb2aSJiri Pirko } 130*4da0eb2aSJiri Pirko 131b217127eSJiri Pirko static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard) 132b217127eSJiri Pirko { 133b217127eSJiri Pirko linecard->provisioned = false; 134ee7a70faSJiri Pirko linecard->ready = false; 135ee7a70faSJiri Pirko linecard->active = false; 136b217127eSJiri Pirko devlink_linecard_provision_fail(linecard->devlink_linecard); 137b217127eSJiri Pirko } 138b217127eSJiri Pirko 139de28976dSJiri Pirko struct mlxsw_linecards_event_ops_item { 140de28976dSJiri Pirko struct list_head list; 141de28976dSJiri Pirko const struct mlxsw_linecards_event_ops *event_ops; 142de28976dSJiri Pirko void *priv; 143de28976dSJiri Pirko }; 144de28976dSJiri Pirko 145de28976dSJiri Pirko static void 146de28976dSJiri Pirko mlxsw_linecard_event_op_call(struct mlxsw_linecard *linecard, 147de28976dSJiri Pirko mlxsw_linecards_event_op_t *op, void *priv) 148de28976dSJiri Pirko { 149de28976dSJiri Pirko struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 150de28976dSJiri Pirko 151de28976dSJiri Pirko if (!op) 152de28976dSJiri Pirko return; 153de28976dSJiri Pirko op(mlxsw_core, linecard->slot_index, priv); 154de28976dSJiri Pirko } 155de28976dSJiri Pirko 156de28976dSJiri Pirko static void 157de28976dSJiri Pirko mlxsw_linecard_active_ops_call(struct mlxsw_linecard *linecard) 158de28976dSJiri Pirko { 159de28976dSJiri Pirko struct mlxsw_linecards *linecards = linecard->linecards; 160de28976dSJiri Pirko struct mlxsw_linecards_event_ops_item *item; 161de28976dSJiri Pirko 162de28976dSJiri Pirko mutex_lock(&linecards->event_ops_list_lock); 163de28976dSJiri Pirko list_for_each_entry(item, &linecards->event_ops_list, list) 164de28976dSJiri Pirko mlxsw_linecard_event_op_call(linecard, 165de28976dSJiri Pirko item->event_ops->got_active, 166de28976dSJiri Pirko item->priv); 167de28976dSJiri Pirko mutex_unlock(&linecards->event_ops_list_lock); 168de28976dSJiri Pirko } 169de28976dSJiri Pirko 170de28976dSJiri Pirko static void 171de28976dSJiri Pirko mlxsw_linecard_inactive_ops_call(struct mlxsw_linecard *linecard) 172de28976dSJiri Pirko { 173de28976dSJiri Pirko struct mlxsw_linecards *linecards = linecard->linecards; 174de28976dSJiri Pirko struct mlxsw_linecards_event_ops_item *item; 175de28976dSJiri Pirko 176de28976dSJiri Pirko mutex_lock(&linecards->event_ops_list_lock); 177de28976dSJiri Pirko list_for_each_entry(item, &linecards->event_ops_list, list) 178de28976dSJiri Pirko mlxsw_linecard_event_op_call(linecard, 179de28976dSJiri Pirko item->event_ops->got_inactive, 180de28976dSJiri Pirko item->priv); 181de28976dSJiri Pirko mutex_unlock(&linecards->event_ops_list_lock); 182de28976dSJiri Pirko } 183de28976dSJiri Pirko 184de28976dSJiri Pirko static void 185de28976dSJiri Pirko mlxsw_linecards_event_ops_register_call(struct mlxsw_linecards *linecards, 186de28976dSJiri Pirko const struct mlxsw_linecards_event_ops_item *item) 187de28976dSJiri Pirko { 188de28976dSJiri Pirko struct mlxsw_linecard *linecard; 189de28976dSJiri Pirko int i; 190de28976dSJiri Pirko 191de28976dSJiri Pirko for (i = 0; i < linecards->count; i++) { 192de28976dSJiri Pirko linecard = mlxsw_linecard_get(linecards, i + 1); 193de28976dSJiri Pirko mutex_lock(&linecard->lock); 194de28976dSJiri Pirko if (linecard->active) 195de28976dSJiri Pirko mlxsw_linecard_event_op_call(linecard, 196de28976dSJiri Pirko item->event_ops->got_active, 197de28976dSJiri Pirko item->priv); 198de28976dSJiri Pirko mutex_unlock(&linecard->lock); 199de28976dSJiri Pirko } 200de28976dSJiri Pirko } 201de28976dSJiri Pirko 202de28976dSJiri Pirko static void 203de28976dSJiri Pirko mlxsw_linecards_event_ops_unregister_call(struct mlxsw_linecards *linecards, 204de28976dSJiri Pirko const struct mlxsw_linecards_event_ops_item *item) 205de28976dSJiri Pirko { 206de28976dSJiri Pirko struct mlxsw_linecard *linecard; 207de28976dSJiri Pirko int i; 208de28976dSJiri Pirko 209de28976dSJiri Pirko for (i = 0; i < linecards->count; i++) { 210de28976dSJiri Pirko linecard = mlxsw_linecard_get(linecards, i + 1); 211de28976dSJiri Pirko mutex_lock(&linecard->lock); 212de28976dSJiri Pirko if (linecard->active) 213de28976dSJiri Pirko mlxsw_linecard_event_op_call(linecard, 214de28976dSJiri Pirko item->event_ops->got_inactive, 215de28976dSJiri Pirko item->priv); 216de28976dSJiri Pirko mutex_unlock(&linecard->lock); 217de28976dSJiri Pirko } 218de28976dSJiri Pirko } 219de28976dSJiri Pirko 220de28976dSJiri Pirko int mlxsw_linecards_event_ops_register(struct mlxsw_core *mlxsw_core, 221de28976dSJiri Pirko struct mlxsw_linecards_event_ops *ops, 222de28976dSJiri Pirko void *priv) 223de28976dSJiri Pirko { 224de28976dSJiri Pirko struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 225de28976dSJiri Pirko struct mlxsw_linecards_event_ops_item *item; 226de28976dSJiri Pirko 227de28976dSJiri Pirko if (!linecards) 228de28976dSJiri Pirko return 0; 229de28976dSJiri Pirko item = kzalloc(sizeof(*item), GFP_KERNEL); 230de28976dSJiri Pirko if (!item) 231de28976dSJiri Pirko return -ENOMEM; 232de28976dSJiri Pirko item->event_ops = ops; 233de28976dSJiri Pirko item->priv = priv; 234de28976dSJiri Pirko 235de28976dSJiri Pirko mutex_lock(&linecards->event_ops_list_lock); 236de28976dSJiri Pirko list_add_tail(&item->list, &linecards->event_ops_list); 237de28976dSJiri Pirko mutex_unlock(&linecards->event_ops_list_lock); 238de28976dSJiri Pirko mlxsw_linecards_event_ops_register_call(linecards, item); 239de28976dSJiri Pirko return 0; 240de28976dSJiri Pirko } 241de28976dSJiri Pirko EXPORT_SYMBOL(mlxsw_linecards_event_ops_register); 242de28976dSJiri Pirko 243de28976dSJiri Pirko void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core, 244de28976dSJiri Pirko struct mlxsw_linecards_event_ops *ops, 245de28976dSJiri Pirko void *priv) 246de28976dSJiri Pirko { 247de28976dSJiri Pirko struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 248de28976dSJiri Pirko struct mlxsw_linecards_event_ops_item *item, *tmp; 249de28976dSJiri Pirko bool found = false; 250de28976dSJiri Pirko 251de28976dSJiri Pirko if (!linecards) 252de28976dSJiri Pirko return; 253de28976dSJiri Pirko mutex_lock(&linecards->event_ops_list_lock); 254de28976dSJiri Pirko list_for_each_entry_safe(item, tmp, &linecards->event_ops_list, list) { 255de28976dSJiri Pirko if (item->event_ops == ops && item->priv == priv) { 256de28976dSJiri Pirko list_del(&item->list); 257de28976dSJiri Pirko found = true; 258de28976dSJiri Pirko break; 259de28976dSJiri Pirko } 260de28976dSJiri Pirko } 261de28976dSJiri Pirko mutex_unlock(&linecards->event_ops_list_lock); 262de28976dSJiri Pirko 263de28976dSJiri Pirko if (!found) 264de28976dSJiri Pirko return; 265de28976dSJiri Pirko mlxsw_linecards_event_ops_unregister_call(linecards, item); 266de28976dSJiri Pirko kfree(item); 267de28976dSJiri Pirko } 268de28976dSJiri Pirko EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister); 269de28976dSJiri Pirko 2705ba325feSJiri Pirko int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard, 2715ba325feSJiri Pirko struct devlink_info_req *req, 2725ba325feSJiri Pirko struct netlink_ext_ack *extack) 2735ba325feSJiri Pirko { 2745ba325feSJiri Pirko char buf[32]; 2755ba325feSJiri Pirko int err; 2765ba325feSJiri Pirko 2775ba325feSJiri Pirko mutex_lock(&linecard->lock); 2785ba325feSJiri Pirko if (WARN_ON(!linecard->provisioned)) { 2795ba325feSJiri Pirko err = -EOPNOTSUPP; 2805ba325feSJiri Pirko goto unlock; 2815ba325feSJiri Pirko } 2825ba325feSJiri Pirko 2835ba325feSJiri Pirko sprintf(buf, "%d", linecard->hw_revision); 2845ba325feSJiri Pirko err = devlink_info_version_fixed_put(req, "hw.revision", buf); 2855ba325feSJiri Pirko if (err) 2865ba325feSJiri Pirko goto unlock; 2875ba325feSJiri Pirko 2885ba325feSJiri Pirko sprintf(buf, "%d", linecard->ini_version); 2895ba325feSJiri Pirko err = devlink_info_version_running_put(req, "ini.version", buf); 2905ba325feSJiri Pirko if (err) 2915ba325feSJiri Pirko goto unlock; 2925ba325feSJiri Pirko 293*4da0eb2aSJiri Pirko if (linecard->active) { 294*4da0eb2aSJiri Pirko struct mlxsw_linecard_device_info *info = &linecard->device.info; 295*4da0eb2aSJiri Pirko 296*4da0eb2aSJiri Pirko sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor, 297*4da0eb2aSJiri Pirko info->fw_sub_minor); 298*4da0eb2aSJiri Pirko err = devlink_info_version_running_put(req, 299*4da0eb2aSJiri Pirko DEVLINK_INFO_VERSION_GENERIC_FW, 300*4da0eb2aSJiri Pirko buf); 301*4da0eb2aSJiri Pirko if (err) 302*4da0eb2aSJiri Pirko goto unlock; 303*4da0eb2aSJiri Pirko } 304*4da0eb2aSJiri Pirko 3055ba325feSJiri Pirko unlock: 3065ba325feSJiri Pirko mutex_unlock(&linecard->lock); 3075ba325feSJiri Pirko return err; 3085ba325feSJiri Pirko } 3095ba325feSJiri Pirko 310b217127eSJiri Pirko static int 311b217127eSJiri Pirko mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type, 312b217127eSJiri Pirko u16 hw_revision, u16 ini_version) 313b217127eSJiri Pirko { 314b217127eSJiri Pirko struct mlxsw_linecards *linecards = linecard->linecards; 315b217127eSJiri Pirko const char *type; 316bd02fd76SJiri Pirko int err; 317b217127eSJiri Pirko 318b217127eSJiri Pirko type = mlxsw_linecard_types_lookup(linecards, card_type); 319b217127eSJiri Pirko mlxsw_linecard_status_event_done(linecard, 320b217127eSJiri Pirko MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION); 321b217127eSJiri Pirko if (!type) { 322b217127eSJiri Pirko /* It is possible for a line card to be provisioned before 323b217127eSJiri Pirko * driver initialization. Due to a missing INI bundle file 324b217127eSJiri Pirko * or an outdated one, the queried card's type might not 325b217127eSJiri Pirko * be recognized by the driver. In this case, try to query 326b217127eSJiri Pirko * the card's name from the device. 327b217127eSJiri Pirko */ 328b217127eSJiri Pirko type = mlxsw_linecard_type_name(linecard); 329b217127eSJiri Pirko if (IS_ERR(type)) { 330b217127eSJiri Pirko mlxsw_linecard_provision_fail(linecard); 331b217127eSJiri Pirko return PTR_ERR(type); 332b217127eSJiri Pirko } 333b217127eSJiri Pirko } 334b217127eSJiri Pirko linecard->provisioned = true; 335b217127eSJiri Pirko linecard->hw_revision = hw_revision; 336b217127eSJiri Pirko linecard->ini_version = ini_version; 337bd02fd76SJiri Pirko 338bd02fd76SJiri Pirko err = mlxsw_linecard_bdev_add(linecard); 339bd02fd76SJiri Pirko if (err) { 340bd02fd76SJiri Pirko linecard->provisioned = false; 341bd02fd76SJiri Pirko mlxsw_linecard_provision_fail(linecard); 342bd02fd76SJiri Pirko return err; 343bd02fd76SJiri Pirko } 344bd02fd76SJiri Pirko 345b217127eSJiri Pirko devlink_linecard_provision_set(linecard->devlink_linecard, type); 346b217127eSJiri Pirko return 0; 347b217127eSJiri Pirko } 348b217127eSJiri Pirko 349b217127eSJiri Pirko static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard) 350b217127eSJiri Pirko { 351b217127eSJiri Pirko mlxsw_linecard_status_event_done(linecard, 352b217127eSJiri Pirko MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION); 353bd02fd76SJiri Pirko mlxsw_linecard_bdev_del(linecard); 354b217127eSJiri Pirko linecard->provisioned = false; 355b217127eSJiri Pirko devlink_linecard_provision_clear(linecard->devlink_linecard); 356b217127eSJiri Pirko } 357b217127eSJiri Pirko 358ee7a70faSJiri Pirko static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard) 359ee7a70faSJiri Pirko { 360ee7a70faSJiri Pirko struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 361ee7a70faSJiri Pirko char mddc_pl[MLXSW_REG_MDDC_LEN]; 362ee7a70faSJiri Pirko int err; 363ee7a70faSJiri Pirko 364*4da0eb2aSJiri Pirko err = mlxsw_linecard_device_info_update(linecard); 365*4da0eb2aSJiri Pirko if (err) 366*4da0eb2aSJiri Pirko return err; 367*4da0eb2aSJiri Pirko 368ee7a70faSJiri Pirko mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true); 369ee7a70faSJiri Pirko err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl); 370ee7a70faSJiri Pirko if (err) 371ee7a70faSJiri Pirko return err; 372ee7a70faSJiri Pirko linecard->ready = true; 373ee7a70faSJiri Pirko return 0; 374ee7a70faSJiri Pirko } 375ee7a70faSJiri Pirko 376ee7a70faSJiri Pirko static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard) 377ee7a70faSJiri Pirko { 378ee7a70faSJiri Pirko struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core; 379ee7a70faSJiri Pirko char mddc_pl[MLXSW_REG_MDDC_LEN]; 380ee7a70faSJiri Pirko int err; 381ee7a70faSJiri Pirko 382ee7a70faSJiri Pirko mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false); 383ee7a70faSJiri Pirko err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl); 384ee7a70faSJiri Pirko if (err) 385ee7a70faSJiri Pirko return err; 386ee7a70faSJiri Pirko linecard->ready = false; 387ee7a70faSJiri Pirko return 0; 388ee7a70faSJiri Pirko } 389ee7a70faSJiri Pirko 390c4a67a21SJakub Kicinski static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard) 391ee7a70faSJiri Pirko { 392de28976dSJiri Pirko mlxsw_linecard_active_ops_call(linecard); 393ee7a70faSJiri Pirko linecard->active = true; 394ee7a70faSJiri Pirko devlink_linecard_activate(linecard->devlink_linecard); 395ee7a70faSJiri Pirko } 396ee7a70faSJiri Pirko 397ee7a70faSJiri Pirko static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard) 398ee7a70faSJiri Pirko { 399de28976dSJiri Pirko mlxsw_linecard_inactive_ops_call(linecard); 400ee7a70faSJiri Pirko linecard->active = false; 401ee7a70faSJiri Pirko devlink_linecard_deactivate(linecard->devlink_linecard); 402ee7a70faSJiri Pirko } 403ee7a70faSJiri Pirko 404b217127eSJiri Pirko static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards, 405b217127eSJiri Pirko struct mlxsw_linecard *linecard, 406b217127eSJiri Pirko const char *mddq_pl) 407b217127eSJiri Pirko { 408b217127eSJiri Pirko enum mlxsw_reg_mddq_slot_info_ready ready; 409b217127eSJiri Pirko bool provisioned, sr_valid, active; 410b217127eSJiri Pirko u16 ini_version, hw_revision; 411b217127eSJiri Pirko u8 slot_index, card_type; 412b217127eSJiri Pirko int err = 0; 413b217127eSJiri Pirko 414b217127eSJiri Pirko mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned, 415b217127eSJiri Pirko &sr_valid, &ready, &active, 416b217127eSJiri Pirko &hw_revision, &ini_version, 417b217127eSJiri Pirko &card_type); 418b217127eSJiri Pirko 419b217127eSJiri Pirko if (linecard) { 420b217127eSJiri Pirko if (WARN_ON(slot_index != linecard->slot_index)) 421b217127eSJiri Pirko return -EINVAL; 422b217127eSJiri Pirko } else { 423b217127eSJiri Pirko if (WARN_ON(slot_index > linecards->count)) 424b217127eSJiri Pirko return -EINVAL; 425b217127eSJiri Pirko linecard = mlxsw_linecard_get(linecards, slot_index); 426b217127eSJiri Pirko } 427b217127eSJiri Pirko 428b217127eSJiri Pirko mutex_lock(&linecard->lock); 429b217127eSJiri Pirko 430b217127eSJiri Pirko if (provisioned && linecard->provisioned != provisioned) { 431b217127eSJiri Pirko err = mlxsw_linecard_provision_set(linecard, card_type, 432b217127eSJiri Pirko hw_revision, ini_version); 433b217127eSJiri Pirko if (err) 434b217127eSJiri Pirko goto out; 435b217127eSJiri Pirko } 436b217127eSJiri Pirko 437ee7a70faSJiri Pirko if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) { 438ee7a70faSJiri Pirko err = mlxsw_linecard_ready_set(linecard); 439ee7a70faSJiri Pirko if (err) 440ee7a70faSJiri Pirko goto out; 441ee7a70faSJiri Pirko } 442ee7a70faSJiri Pirko 443c4a67a21SJakub Kicinski if (active && linecard->active != active) 444c4a67a21SJakub Kicinski mlxsw_linecard_active_set(linecard); 445ee7a70faSJiri Pirko 446ee7a70faSJiri Pirko if (!active && linecard->active != active) 447ee7a70faSJiri Pirko mlxsw_linecard_active_clear(linecard); 448ee7a70faSJiri Pirko 449ee7a70faSJiri Pirko if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && 450ee7a70faSJiri Pirko linecard->ready) { 451ee7a70faSJiri Pirko err = mlxsw_linecard_ready_clear(linecard); 452ee7a70faSJiri Pirko if (err) 453ee7a70faSJiri Pirko goto out; 454ee7a70faSJiri Pirko } 455ee7a70faSJiri Pirko 456b217127eSJiri Pirko if (!provisioned && linecard->provisioned != provisioned) 457b217127eSJiri Pirko mlxsw_linecard_provision_clear(linecard); 458b217127eSJiri Pirko 459b217127eSJiri Pirko out: 460b217127eSJiri Pirko mutex_unlock(&linecard->lock); 461b217127eSJiri Pirko return err; 462b217127eSJiri Pirko } 463b217127eSJiri Pirko 464b217127eSJiri Pirko static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core, 465b217127eSJiri Pirko struct mlxsw_linecards *linecards, 466b217127eSJiri Pirko struct mlxsw_linecard *linecard) 467b217127eSJiri Pirko { 468b217127eSJiri Pirko char mddq_pl[MLXSW_REG_MDDQ_LEN]; 469b217127eSJiri Pirko int err; 470b217127eSJiri Pirko 471b217127eSJiri Pirko mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false); 472b217127eSJiri Pirko err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 473b217127eSJiri Pirko if (err) 474b217127eSJiri Pirko return err; 475b217127eSJiri Pirko 476b217127eSJiri Pirko return mlxsw_linecard_status_process(linecards, linecard, mddq_pl); 477b217127eSJiri Pirko } 478b217127eSJiri Pirko 479b217127eSJiri Pirko static const char * const mlxsw_linecard_status_event_type_name[] = { 480b217127eSJiri Pirko [MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision", 481b217127eSJiri Pirko [MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision", 482b217127eSJiri Pirko }; 483b217127eSJiri Pirko 484b217127eSJiri Pirko static void mlxsw_linecard_status_event_to_work(struct work_struct *work) 485b217127eSJiri Pirko { 486b217127eSJiri Pirko struct mlxsw_linecard *linecard = 487b217127eSJiri Pirko container_of(work, struct mlxsw_linecard, 488b217127eSJiri Pirko status_event_to_dw.work); 489b217127eSJiri Pirko 490b217127eSJiri Pirko mutex_lock(&linecard->lock); 491b217127eSJiri Pirko dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event", 492b217127eSJiri Pirko linecard->slot_index, 493b217127eSJiri Pirko mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]); 494b217127eSJiri Pirko mlxsw_linecard_provision_fail(linecard); 495b217127eSJiri Pirko mutex_unlock(&linecard->lock); 496b217127eSJiri Pirko } 497b217127eSJiri Pirko 498b217127eSJiri Pirko static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard) 499b217127eSJiri Pirko { 500b217127eSJiri Pirko dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error", 501b217127eSJiri Pirko linecard->slot_index); 502b217127eSJiri Pirko mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 503b217127eSJiri Pirko MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false); 504b217127eSJiri Pirko return mlxsw_reg_write(linecard->linecards->mlxsw_core, 505b217127eSJiri Pirko MLXSW_REG(mbct), linecard->mbct_pl); 506b217127eSJiri Pirko } 507b217127eSJiri Pirko 508b217127eSJiri Pirko static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard, 509b217127eSJiri Pirko enum mlxsw_reg_mbct_fsm_state fsm_state) 510b217127eSJiri Pirko { 511b217127eSJiri Pirko if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR) 512b217127eSJiri Pirko return 0; 513b217127eSJiri Pirko return __mlxsw_linecard_fix_fsm_state(linecard); 514b217127eSJiri Pirko } 515b217127eSJiri Pirko 516b217127eSJiri Pirko static int 517b217127eSJiri Pirko mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard, 518b217127eSJiri Pirko enum mlxsw_reg_mbct_status *status, 519b217127eSJiri Pirko enum mlxsw_reg_mbct_fsm_state *fsm_state, 520b217127eSJiri Pirko struct netlink_ext_ack *extack) 521b217127eSJiri Pirko { 522b217127eSJiri Pirko int err; 523b217127eSJiri Pirko 524b217127eSJiri Pirko mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 525b217127eSJiri Pirko MLXSW_REG_MBCT_OP_QUERY_STATUS, false); 526b217127eSJiri Pirko err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct), 527b217127eSJiri Pirko linecard->mbct_pl); 528b217127eSJiri Pirko if (err) { 529b217127eSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status"); 530b217127eSJiri Pirko return err; 531b217127eSJiri Pirko } 532b217127eSJiri Pirko mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state); 533b217127eSJiri Pirko return err; 534b217127eSJiri Pirko } 535b217127eSJiri Pirko 536b217127eSJiri Pirko static int 537b217127eSJiri Pirko mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core, 538b217127eSJiri Pirko struct mlxsw_linecard *linecard, 539b217127eSJiri Pirko const struct mlxsw_linecard_ini_file *ini_file, 540b217127eSJiri Pirko struct netlink_ext_ack *extack) 541b217127eSJiri Pirko { 542b217127eSJiri Pirko enum mlxsw_reg_mbct_fsm_state fsm_state; 543b217127eSJiri Pirko enum mlxsw_reg_mbct_status status; 544b217127eSJiri Pirko size_t size_left; 545b217127eSJiri Pirko const u8 *data; 546b217127eSJiri Pirko int err; 547b217127eSJiri Pirko 548b217127eSJiri Pirko size_left = le16_to_cpu(ini_file->size); 549b217127eSJiri Pirko data = ini_file->data; 550b217127eSJiri Pirko while (size_left) { 551b217127eSJiri Pirko size_t data_size = MLXSW_REG_MBCT_DATA_LEN; 552b217127eSJiri Pirko bool is_last = false; 553b217127eSJiri Pirko 554b217127eSJiri Pirko if (size_left <= MLXSW_REG_MBCT_DATA_LEN) { 555b217127eSJiri Pirko data_size = size_left; 556b217127eSJiri Pirko is_last = true; 557b217127eSJiri Pirko } 558b217127eSJiri Pirko 559b217127eSJiri Pirko mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 560b217127eSJiri Pirko MLXSW_REG_MBCT_OP_DATA_TRANSFER, false); 561b217127eSJiri Pirko mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size, 562b217127eSJiri Pirko is_last, data); 563b217127eSJiri Pirko err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), 564b217127eSJiri Pirko linecard->mbct_pl); 565b217127eSJiri Pirko if (err) { 566b217127eSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer"); 567b217127eSJiri Pirko return err; 568b217127eSJiri Pirko } 569b217127eSJiri Pirko mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, 570b217127eSJiri Pirko &status, &fsm_state); 571b217127eSJiri Pirko if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) || 572b217127eSJiri Pirko (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) { 573b217127eSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data"); 574b217127eSJiri Pirko mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 575b217127eSJiri Pirko return -EINVAL; 576b217127eSJiri Pirko } 577b217127eSJiri Pirko size_left -= data_size; 578b217127eSJiri Pirko data += data_size; 579b217127eSJiri Pirko } 580b217127eSJiri Pirko 581b217127eSJiri Pirko return 0; 582b217127eSJiri Pirko } 583b217127eSJiri Pirko 584b217127eSJiri Pirko static int 585b217127eSJiri Pirko mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core, 586b217127eSJiri Pirko struct mlxsw_linecard *linecard, 587b217127eSJiri Pirko struct netlink_ext_ack *extack) 588b217127eSJiri Pirko { 589b217127eSJiri Pirko enum mlxsw_reg_mbct_fsm_state fsm_state; 590b217127eSJiri Pirko enum mlxsw_reg_mbct_status status; 591b217127eSJiri Pirko int err; 592b217127eSJiri Pirko 593b217127eSJiri Pirko mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 594b217127eSJiri Pirko MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false); 595b217127eSJiri Pirko err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), 596b217127eSJiri Pirko linecard->mbct_pl); 597b217127eSJiri Pirko if (err) { 598b217127eSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase"); 599b217127eSJiri Pirko return err; 600b217127eSJiri Pirko } 601b217127eSJiri Pirko mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state); 602b217127eSJiri Pirko switch (status) { 603b217127eSJiri Pirko case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE: 604b217127eSJiri Pirko break; 605b217127eSJiri Pirko default: 606b217127eSJiri Pirko /* Should not happen */ 607b217127eSJiri Pirko fallthrough; 608b217127eSJiri Pirko case MLXSW_REG_MBCT_STATUS_ERASE_FAILED: 609b217127eSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI"); 610b217127eSJiri Pirko goto fix_fsm_err_out; 611b217127eSJiri Pirko case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE: 612b217127eSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used"); 613b217127eSJiri Pirko goto fix_fsm_err_out; 614b217127eSJiri Pirko } 615b217127eSJiri Pirko return 0; 616b217127eSJiri Pirko 617b217127eSJiri Pirko fix_fsm_err_out: 618b217127eSJiri Pirko mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 619b217127eSJiri Pirko return -EINVAL; 620b217127eSJiri Pirko } 621b217127eSJiri Pirko 622b217127eSJiri Pirko static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core, 623b217127eSJiri Pirko const char *mbct_pl) 624b217127eSJiri Pirko { 625b217127eSJiri Pirko struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 626b217127eSJiri Pirko enum mlxsw_reg_mbct_fsm_state fsm_state; 627b217127eSJiri Pirko enum mlxsw_reg_mbct_status status; 628b217127eSJiri Pirko struct mlxsw_linecard *linecard; 629b217127eSJiri Pirko u8 slot_index; 630b217127eSJiri Pirko 631b217127eSJiri Pirko mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state); 632b217127eSJiri Pirko if (WARN_ON(slot_index > linecards->count)) 633b217127eSJiri Pirko return; 634b217127eSJiri Pirko linecard = mlxsw_linecard_get(linecards, slot_index); 635b217127eSJiri Pirko mutex_lock(&linecard->lock); 636b217127eSJiri Pirko if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) { 637b217127eSJiri Pirko dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI", 638b217127eSJiri Pirko linecard->slot_index); 639b217127eSJiri Pirko goto fix_fsm_out; 640b217127eSJiri Pirko } 641b217127eSJiri Pirko mutex_unlock(&linecard->lock); 642b217127eSJiri Pirko return; 643b217127eSJiri Pirko 644b217127eSJiri Pirko fix_fsm_out: 645b217127eSJiri Pirko mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 646b217127eSJiri Pirko mlxsw_linecard_provision_fail(linecard); 647b217127eSJiri Pirko mutex_unlock(&linecard->lock); 648b217127eSJiri Pirko } 649b217127eSJiri Pirko 650b217127eSJiri Pirko static int 651b217127eSJiri Pirko mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core, 652b217127eSJiri Pirko struct mlxsw_linecard *linecard, 653b217127eSJiri Pirko struct netlink_ext_ack *extack) 654b217127eSJiri Pirko { 655b217127eSJiri Pirko enum mlxsw_reg_mbct_fsm_state fsm_state; 656b217127eSJiri Pirko enum mlxsw_reg_mbct_status status; 657b217127eSJiri Pirko int err; 658b217127eSJiri Pirko 659b217127eSJiri Pirko mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index, 660b217127eSJiri Pirko MLXSW_REG_MBCT_OP_ACTIVATE, true); 661b217127eSJiri Pirko err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl); 662b217127eSJiri Pirko if (err) { 663b217127eSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation"); 664b217127eSJiri Pirko return err; 665b217127eSJiri Pirko } 666b217127eSJiri Pirko mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state); 667b217127eSJiri Pirko if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) { 668b217127eSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI"); 669b217127eSJiri Pirko goto fix_fsm_err_out; 670b217127eSJiri Pirko } 671b217127eSJiri Pirko 672b217127eSJiri Pirko return 0; 673b217127eSJiri Pirko 674b217127eSJiri Pirko fix_fsm_err_out: 675b217127eSJiri Pirko mlxsw_linecard_fix_fsm_state(linecard, fsm_state); 676b217127eSJiri Pirko return -EINVAL; 677b217127eSJiri Pirko } 678b217127eSJiri Pirko 679b217127eSJiri Pirko #define MLXSW_LINECARD_INI_WAIT_RETRIES 10 680b217127eSJiri Pirko #define MLXSW_LINECARD_INI_WAIT_MS 500 681b217127eSJiri Pirko 682b217127eSJiri Pirko static int 683b217127eSJiri Pirko mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core, 684b217127eSJiri Pirko struct mlxsw_linecard *linecard, 685b217127eSJiri Pirko struct netlink_ext_ack *extack) 686b217127eSJiri Pirko { 687b217127eSJiri Pirko enum mlxsw_reg_mbct_fsm_state fsm_state; 688b217127eSJiri Pirko enum mlxsw_reg_mbct_status status; 689b217127eSJiri Pirko unsigned int ini_wait_retries = 0; 690b217127eSJiri Pirko int err; 691b217127eSJiri Pirko 692b217127eSJiri Pirko query_ini_status: 693b217127eSJiri Pirko err = mlxsw_linecard_query_ini_status(linecard, &status, 694b217127eSJiri Pirko &fsm_state, extack); 695b217127eSJiri Pirko if (err) 696b217127eSJiri Pirko return err; 697b217127eSJiri Pirko 698b217127eSJiri Pirko switch (fsm_state) { 699b217127eSJiri Pirko case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE: 700b217127eSJiri Pirko if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) { 701b217127eSJiri Pirko NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused"); 702b217127eSJiri Pirko return -EINVAL; 703b217127eSJiri Pirko } 704b217127eSJiri Pirko mdelay(MLXSW_LINECARD_INI_WAIT_MS); 705b217127eSJiri Pirko goto query_ini_status; 706b217127eSJiri Pirko default: 707b217127eSJiri Pirko break; 708b217127eSJiri Pirko } 709b217127eSJiri Pirko return 0; 710b217127eSJiri Pirko } 711b217127eSJiri Pirko 7126445eef0SJiri Pirko static bool mlxsw_linecard_port_selector(void *priv, u16 local_port) 7136445eef0SJiri Pirko { 7146445eef0SJiri Pirko struct mlxsw_linecard *linecard = priv; 7156445eef0SJiri Pirko struct mlxsw_core *mlxsw_core; 7166445eef0SJiri Pirko 7176445eef0SJiri Pirko mlxsw_core = linecard->linecards->mlxsw_core; 7186445eef0SJiri Pirko return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port); 7196445eef0SJiri Pirko } 7206445eef0SJiri Pirko 721b217127eSJiri Pirko static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard, 722b217127eSJiri Pirko void *priv, const char *type, 723b217127eSJiri Pirko const void *type_priv, 724b217127eSJiri Pirko struct netlink_ext_ack *extack) 725b217127eSJiri Pirko { 726b217127eSJiri Pirko const struct mlxsw_linecard_ini_file *ini_file = type_priv; 727b217127eSJiri Pirko struct mlxsw_linecard *linecard = priv; 728b217127eSJiri Pirko struct mlxsw_core *mlxsw_core; 729b217127eSJiri Pirko int err; 730b217127eSJiri Pirko 731b217127eSJiri Pirko mutex_lock(&linecard->lock); 732b217127eSJiri Pirko 733b217127eSJiri Pirko mlxsw_core = linecard->linecards->mlxsw_core; 734b217127eSJiri Pirko 735b217127eSJiri Pirko err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack); 736b217127eSJiri Pirko if (err) 737b217127eSJiri Pirko goto err_out; 738b217127eSJiri Pirko 739b217127eSJiri Pirko err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard, 740b217127eSJiri Pirko ini_file, extack); 741b217127eSJiri Pirko if (err) 742b217127eSJiri Pirko goto err_out; 743b217127eSJiri Pirko 744b217127eSJiri Pirko mlxsw_linecard_status_event_to_schedule(linecard, 745b217127eSJiri Pirko MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION); 746b217127eSJiri Pirko err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack); 747b217127eSJiri Pirko if (err) 748b217127eSJiri Pirko goto err_out; 749b217127eSJiri Pirko 750b217127eSJiri Pirko goto out; 751b217127eSJiri Pirko 752b217127eSJiri Pirko err_out: 753b217127eSJiri Pirko mlxsw_linecard_provision_fail(linecard); 754b217127eSJiri Pirko out: 755b217127eSJiri Pirko mutex_unlock(&linecard->lock); 756b217127eSJiri Pirko return err; 757b217127eSJiri Pirko } 758b217127eSJiri Pirko 759b217127eSJiri Pirko static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard, 760b217127eSJiri Pirko void *priv, 761b217127eSJiri Pirko struct netlink_ext_ack *extack) 762b217127eSJiri Pirko { 763b217127eSJiri Pirko struct mlxsw_linecard *linecard = priv; 764b217127eSJiri Pirko struct mlxsw_core *mlxsw_core; 765b217127eSJiri Pirko int err; 766b217127eSJiri Pirko 767b217127eSJiri Pirko mutex_lock(&linecard->lock); 768b217127eSJiri Pirko 769b217127eSJiri Pirko mlxsw_core = linecard->linecards->mlxsw_core; 770b217127eSJiri Pirko 7716445eef0SJiri Pirko mlxsw_core_ports_remove_selected(mlxsw_core, 7726445eef0SJiri Pirko mlxsw_linecard_port_selector, 7736445eef0SJiri Pirko linecard); 7746445eef0SJiri Pirko 775b217127eSJiri Pirko err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack); 776b217127eSJiri Pirko if (err) 777b217127eSJiri Pirko goto err_out; 778b217127eSJiri Pirko 779b217127eSJiri Pirko mlxsw_linecard_status_event_to_schedule(linecard, 780b217127eSJiri Pirko MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION); 781b217127eSJiri Pirko err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack); 782b217127eSJiri Pirko if (err) 783b217127eSJiri Pirko goto err_out; 784b217127eSJiri Pirko 785b217127eSJiri Pirko goto out; 786b217127eSJiri Pirko 787b217127eSJiri Pirko err_out: 788b217127eSJiri Pirko mlxsw_linecard_provision_fail(linecard); 789b217127eSJiri Pirko out: 790b217127eSJiri Pirko mutex_unlock(&linecard->lock); 791b217127eSJiri Pirko return err; 792b217127eSJiri Pirko } 793b217127eSJiri Pirko 794b217127eSJiri Pirko static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard, 795b217127eSJiri Pirko void *priv, const char *type, 796b217127eSJiri Pirko const void *type_priv) 797b217127eSJiri Pirko { 798b217127eSJiri Pirko const struct mlxsw_linecard_ini_file *ini_file = type_priv; 799b217127eSJiri Pirko struct mlxsw_linecard *linecard = priv; 800b217127eSJiri Pirko bool ret; 801b217127eSJiri Pirko 802b217127eSJiri Pirko mutex_lock(&linecard->lock); 803b217127eSJiri Pirko ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) && 804b217127eSJiri Pirko linecard->ini_version == be16_to_cpu(ini_file->format.ini_version); 805b217127eSJiri Pirko mutex_unlock(&linecard->lock); 806b217127eSJiri Pirko return ret; 807b217127eSJiri Pirko } 808b217127eSJiri Pirko 809b217127eSJiri Pirko static unsigned int 810b217127eSJiri Pirko mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard, 811b217127eSJiri Pirko void *priv) 812b217127eSJiri Pirko { 813b217127eSJiri Pirko struct mlxsw_linecard *linecard = priv; 814b217127eSJiri Pirko 815b217127eSJiri Pirko return linecard->linecards->types_info ? 816b217127eSJiri Pirko linecard->linecards->types_info->count : 0; 817b217127eSJiri Pirko } 818b217127eSJiri Pirko 819b217127eSJiri Pirko static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard, 820b217127eSJiri Pirko void *priv, unsigned int index, 821b217127eSJiri Pirko const char **type, const void **type_priv) 822b217127eSJiri Pirko { 823b217127eSJiri Pirko struct mlxsw_linecard_types_info *types_info; 824b217127eSJiri Pirko struct mlxsw_linecard_ini_file *ini_file; 825b217127eSJiri Pirko struct mlxsw_linecard *linecard = priv; 826b217127eSJiri Pirko 827b217127eSJiri Pirko types_info = linecard->linecards->types_info; 828b217127eSJiri Pirko if (WARN_ON_ONCE(!types_info)) 829b217127eSJiri Pirko return; 830b217127eSJiri Pirko ini_file = types_info->ini_files[index]; 831b217127eSJiri Pirko *type = ini_file->format.name; 832b217127eSJiri Pirko *type_priv = ini_file; 833b217127eSJiri Pirko } 834b217127eSJiri Pirko 835b217127eSJiri Pirko static const struct devlink_linecard_ops mlxsw_linecard_ops = { 836b217127eSJiri Pirko .provision = mlxsw_linecard_provision, 837b217127eSJiri Pirko .unprovision = mlxsw_linecard_unprovision, 838b217127eSJiri Pirko .same_provision = mlxsw_linecard_same_provision, 839b217127eSJiri Pirko .types_count = mlxsw_linecard_types_count, 840b217127eSJiri Pirko .types_get = mlxsw_linecard_types_get, 841b217127eSJiri Pirko }; 842b217127eSJiri Pirko 843b217127eSJiri Pirko struct mlxsw_linecard_status_event { 844b217127eSJiri Pirko struct mlxsw_core *mlxsw_core; 845b217127eSJiri Pirko char mddq_pl[MLXSW_REG_MDDQ_LEN]; 846b217127eSJiri Pirko struct work_struct work; 847b217127eSJiri Pirko }; 848b217127eSJiri Pirko 849b217127eSJiri Pirko static void mlxsw_linecard_status_event_work(struct work_struct *work) 850b217127eSJiri Pirko { 851b217127eSJiri Pirko struct mlxsw_linecard_status_event *event; 852b217127eSJiri Pirko struct mlxsw_linecards *linecards; 853b217127eSJiri Pirko struct mlxsw_core *mlxsw_core; 854b217127eSJiri Pirko 855b217127eSJiri Pirko event = container_of(work, struct mlxsw_linecard_status_event, work); 856b217127eSJiri Pirko mlxsw_core = event->mlxsw_core; 857b217127eSJiri Pirko linecards = mlxsw_core_linecards(mlxsw_core); 858b217127eSJiri Pirko mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl); 859b217127eSJiri Pirko kfree(event); 860b217127eSJiri Pirko } 861b217127eSJiri Pirko 862b217127eSJiri Pirko static void 863b217127eSJiri Pirko mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg, 864b217127eSJiri Pirko char *mddq_pl, void *priv) 865b217127eSJiri Pirko { 866b217127eSJiri Pirko struct mlxsw_linecard_status_event *event; 867b217127eSJiri Pirko struct mlxsw_core *mlxsw_core = priv; 868b217127eSJiri Pirko 869b217127eSJiri Pirko event = kmalloc(sizeof(*event), GFP_ATOMIC); 870b217127eSJiri Pirko if (!event) 871b217127eSJiri Pirko return; 872b217127eSJiri Pirko event->mlxsw_core = mlxsw_core; 873b217127eSJiri Pirko memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl)); 874b217127eSJiri Pirko INIT_WORK(&event->work, mlxsw_linecard_status_event_work); 875b217127eSJiri Pirko mlxsw_core_schedule_work(&event->work); 876b217127eSJiri Pirko } 877b217127eSJiri Pirko 878b217127eSJiri Pirko struct mlxsw_linecard_bct_event { 879b217127eSJiri Pirko struct mlxsw_core *mlxsw_core; 880b217127eSJiri Pirko char mbct_pl[MLXSW_REG_MBCT_LEN]; 881b217127eSJiri Pirko struct work_struct work; 882b217127eSJiri Pirko }; 883b217127eSJiri Pirko 884b217127eSJiri Pirko static void mlxsw_linecard_bct_event_work(struct work_struct *work) 885b217127eSJiri Pirko { 886b217127eSJiri Pirko struct mlxsw_linecard_bct_event *event; 887b217127eSJiri Pirko struct mlxsw_core *mlxsw_core; 888b217127eSJiri Pirko 889b217127eSJiri Pirko event = container_of(work, struct mlxsw_linecard_bct_event, work); 890b217127eSJiri Pirko mlxsw_core = event->mlxsw_core; 891b217127eSJiri Pirko mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl); 892b217127eSJiri Pirko kfree(event); 893b217127eSJiri Pirko } 894b217127eSJiri Pirko 895b217127eSJiri Pirko static void 896b217127eSJiri Pirko mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg, 897b217127eSJiri Pirko char *mbct_pl, void *priv) 898b217127eSJiri Pirko { 899b217127eSJiri Pirko struct mlxsw_linecard_bct_event *event; 900b217127eSJiri Pirko struct mlxsw_core *mlxsw_core = priv; 901b217127eSJiri Pirko 902b217127eSJiri Pirko event = kmalloc(sizeof(*event), GFP_ATOMIC); 903b217127eSJiri Pirko if (!event) 904b217127eSJiri Pirko return; 905b217127eSJiri Pirko event->mlxsw_core = mlxsw_core; 906b217127eSJiri Pirko memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl)); 907b217127eSJiri Pirko INIT_WORK(&event->work, mlxsw_linecard_bct_event_work); 908b217127eSJiri Pirko mlxsw_core_schedule_work(&event->work); 909b217127eSJiri Pirko } 910b217127eSJiri Pirko 911b217127eSJiri Pirko static const struct mlxsw_listener mlxsw_linecard_listener[] = { 912b217127eSJiri Pirko MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC), 913b217127eSJiri Pirko MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE), 914b217127eSJiri Pirko }; 915b217127eSJiri Pirko 916b217127eSJiri Pirko static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core, 917b217127eSJiri Pirko struct mlxsw_linecard *linecard, 918b217127eSJiri Pirko bool enable) 919b217127eSJiri Pirko { 920b217127eSJiri Pirko char mddq_pl[MLXSW_REG_MDDQ_LEN]; 921b217127eSJiri Pirko 922b217127eSJiri Pirko mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable); 923b217127eSJiri Pirko return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl); 924b217127eSJiri Pirko } 925b217127eSJiri Pirko 926b217127eSJiri Pirko static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core, 927b217127eSJiri Pirko struct mlxsw_linecards *linecards, 928b217127eSJiri Pirko u8 slot_index) 929b217127eSJiri Pirko { 930b217127eSJiri Pirko struct devlink_linecard *devlink_linecard; 931b217127eSJiri Pirko struct mlxsw_linecard *linecard; 932b217127eSJiri Pirko int err; 933b217127eSJiri Pirko 934b217127eSJiri Pirko linecard = mlxsw_linecard_get(linecards, slot_index); 935b217127eSJiri Pirko linecard->slot_index = slot_index; 936b217127eSJiri Pirko linecard->linecards = linecards; 937b217127eSJiri Pirko mutex_init(&linecard->lock); 938b217127eSJiri Pirko 939b217127eSJiri Pirko devlink_linecard = devlink_linecard_create(priv_to_devlink(mlxsw_core), 940b217127eSJiri Pirko slot_index, &mlxsw_linecard_ops, 941b217127eSJiri Pirko linecard); 942b217127eSJiri Pirko if (IS_ERR(devlink_linecard)) { 943b217127eSJiri Pirko err = PTR_ERR(devlink_linecard); 944b217127eSJiri Pirko goto err_devlink_linecard_create; 945b217127eSJiri Pirko } 946b217127eSJiri Pirko linecard->devlink_linecard = devlink_linecard; 947b217127eSJiri Pirko INIT_DELAYED_WORK(&linecard->status_event_to_dw, 948b217127eSJiri Pirko &mlxsw_linecard_status_event_to_work); 949b217127eSJiri Pirko 950b217127eSJiri Pirko err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true); 951b217127eSJiri Pirko if (err) 952b217127eSJiri Pirko goto err_event_delivery_set; 953b217127eSJiri Pirko 954b217127eSJiri Pirko err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards, 955b217127eSJiri Pirko linecard); 956b217127eSJiri Pirko if (err) 957b217127eSJiri Pirko goto err_status_get_and_process; 958b217127eSJiri Pirko 959b217127eSJiri Pirko return 0; 960b217127eSJiri Pirko 961b217127eSJiri Pirko err_status_get_and_process: 962b217127eSJiri Pirko mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); 963b217127eSJiri Pirko err_event_delivery_set: 964b217127eSJiri Pirko devlink_linecard_destroy(linecard->devlink_linecard); 965b217127eSJiri Pirko err_devlink_linecard_create: 966b217127eSJiri Pirko mutex_destroy(&linecard->lock); 967b217127eSJiri Pirko return err; 968b217127eSJiri Pirko } 969b217127eSJiri Pirko 970b217127eSJiri Pirko static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core, 971b217127eSJiri Pirko struct mlxsw_linecards *linecards, 972b217127eSJiri Pirko u8 slot_index) 973b217127eSJiri Pirko { 974b217127eSJiri Pirko struct mlxsw_linecard *linecard; 975b217127eSJiri Pirko 976b217127eSJiri Pirko linecard = mlxsw_linecard_get(linecards, slot_index); 977b217127eSJiri Pirko mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false); 978b217127eSJiri Pirko cancel_delayed_work_sync(&linecard->status_event_to_dw); 979b217127eSJiri Pirko /* Make sure all scheduled events are processed */ 980b217127eSJiri Pirko mlxsw_core_flush_owq(); 981ee7a70faSJiri Pirko if (linecard->active) 982ee7a70faSJiri Pirko mlxsw_linecard_active_clear(linecard); 983bd02fd76SJiri Pirko mlxsw_linecard_bdev_del(linecard); 984b217127eSJiri Pirko devlink_linecard_destroy(linecard->devlink_linecard); 985b217127eSJiri Pirko mutex_destroy(&linecard->lock); 986b217127eSJiri Pirko } 987b217127eSJiri Pirko 988b217127eSJiri Pirko /* LINECARDS INI BUNDLE FILE 989b217127eSJiri Pirko * +----------------------------------+ 990b217127eSJiri Pirko * | MAGIC ("NVLCINI+") | 991b217127eSJiri Pirko * +----------------------------------+ +--------------------+ 992b217127eSJiri Pirko * | INI 0 +---> | __le16 size | 993b217127eSJiri Pirko * +----------------------------------+ | __be16 hw_revision | 994b217127eSJiri Pirko * | INI 1 | | __be16 ini_version | 995b217127eSJiri Pirko * +----------------------------------+ | u8 __dontcare[3] | 996b217127eSJiri Pirko * | ... | | u8 type | 997b217127eSJiri Pirko * +----------------------------------+ | u8 name[20] | 998b217127eSJiri Pirko * | INI N | | ... | 999b217127eSJiri Pirko * +----------------------------------+ +--------------------+ 1000b217127eSJiri Pirko */ 1001b217127eSJiri Pirko 1002b217127eSJiri Pirko #define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+" 1003b217127eSJiri Pirko 1004b217127eSJiri Pirko static int 1005b217127eSJiri Pirko mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards, 1006b217127eSJiri Pirko struct mlxsw_linecard_types_info *types_info) 1007b217127eSJiri Pirko { 1008b217127eSJiri Pirko size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC); 1009b217127eSJiri Pirko struct mlxsw_linecard_ini_file *ini_file; 1010b217127eSJiri Pirko size_t size = types_info->data_size; 1011b217127eSJiri Pirko const u8 *data = types_info->data; 1012b217127eSJiri Pirko unsigned int count = 0; 1013b217127eSJiri Pirko u16 ini_file_size; 1014b217127eSJiri Pirko 1015b217127eSJiri Pirko if (size < magic_size) { 1016b217127eSJiri Pirko dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n"); 1017b217127eSJiri Pirko return -EINVAL; 1018b217127eSJiri Pirko } 1019b217127eSJiri Pirko if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) { 1020b217127eSJiri Pirko dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n"); 1021b217127eSJiri Pirko return -EINVAL; 1022b217127eSJiri Pirko } 1023b217127eSJiri Pirko 1024b217127eSJiri Pirko data += magic_size; 1025b217127eSJiri Pirko size -= magic_size; 1026b217127eSJiri Pirko 1027b217127eSJiri Pirko while (size > 0) { 1028b217127eSJiri Pirko if (size < sizeof(*ini_file)) { 1029b217127eSJiri Pirko dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n"); 1030b217127eSJiri Pirko return -EINVAL; 1031b217127eSJiri Pirko } 1032b217127eSJiri Pirko ini_file = (struct mlxsw_linecard_ini_file *) data; 1033b217127eSJiri Pirko ini_file_size = le16_to_cpu(ini_file->size); 1034b217127eSJiri Pirko if (ini_file_size + sizeof(__le16) > size) { 1035b217127eSJiri Pirko dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n"); 1036b217127eSJiri Pirko return -EINVAL; 1037b217127eSJiri Pirko } 1038b217127eSJiri Pirko if (ini_file_size % 4) { 1039b217127eSJiri Pirko dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n"); 1040b217127eSJiri Pirko return -EINVAL; 1041b217127eSJiri Pirko } 1042b217127eSJiri Pirko data += ini_file_size + sizeof(__le16); 1043b217127eSJiri Pirko size -= ini_file_size + sizeof(__le16); 1044b217127eSJiri Pirko count++; 1045b217127eSJiri Pirko } 1046b217127eSJiri Pirko if (!count) { 1047b217127eSJiri Pirko dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n"); 1048b217127eSJiri Pirko return -EINVAL; 1049b217127eSJiri Pirko } 1050b217127eSJiri Pirko types_info->count = count; 1051b217127eSJiri Pirko return 0; 1052b217127eSJiri Pirko } 1053b217127eSJiri Pirko 1054b217127eSJiri Pirko static void 1055b217127eSJiri Pirko mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info) 1056b217127eSJiri Pirko { 1057b217127eSJiri Pirko size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC); 1058b217127eSJiri Pirko size_t size = types_info->data_size - magic_size; 1059b217127eSJiri Pirko const u8 *data = types_info->data + magic_size; 1060b217127eSJiri Pirko struct mlxsw_linecard_ini_file *ini_file; 1061b217127eSJiri Pirko unsigned int count = 0; 1062b217127eSJiri Pirko u16 ini_file_size; 1063b217127eSJiri Pirko int i; 1064b217127eSJiri Pirko 1065b217127eSJiri Pirko while (size) { 1066b217127eSJiri Pirko ini_file = (struct mlxsw_linecard_ini_file *) data; 1067b217127eSJiri Pirko ini_file_size = le16_to_cpu(ini_file->size); 1068b217127eSJiri Pirko for (i = 0; i < ini_file_size / 4; i++) { 1069b217127eSJiri Pirko u32 *val = &((u32 *) ini_file->data)[i]; 1070b217127eSJiri Pirko 1071b217127eSJiri Pirko *val = swab32(*val); 1072b217127eSJiri Pirko } 1073b217127eSJiri Pirko types_info->ini_files[count] = ini_file; 1074b217127eSJiri Pirko data += ini_file_size + sizeof(__le16); 1075b217127eSJiri Pirko size -= ini_file_size + sizeof(__le16); 1076b217127eSJiri Pirko count++; 1077b217127eSJiri Pirko } 1078b217127eSJiri Pirko } 1079b217127eSJiri Pirko 1080b217127eSJiri Pirko #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \ 1081b217127eSJiri Pirko "mellanox/lc_ini_bundle_%u_%u.bin" 1082b217127eSJiri Pirko #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \ 1083b217127eSJiri Pirko (sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4) 1084b217127eSJiri Pirko 1085b217127eSJiri Pirko static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core, 1086b217127eSJiri Pirko struct mlxsw_linecards *linecards) 1087b217127eSJiri Pirko { 1088b217127eSJiri Pirko const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev; 1089b217127eSJiri Pirko char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN]; 1090b217127eSJiri Pirko struct mlxsw_linecard_types_info *types_info; 1091b217127eSJiri Pirko const struct firmware *firmware; 1092b217127eSJiri Pirko int err; 1093b217127eSJiri Pirko 1094b217127eSJiri Pirko err = snprintf(filename, sizeof(filename), 1095b217127eSJiri Pirko MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT, 1096b217127eSJiri Pirko rev->minor, rev->subminor); 1097b217127eSJiri Pirko WARN_ON(err >= sizeof(filename)); 1098b217127eSJiri Pirko 1099b217127eSJiri Pirko err = request_firmware_direct(&firmware, filename, 1100b217127eSJiri Pirko linecards->bus_info->dev); 1101b217127eSJiri Pirko if (err) { 1102b217127eSJiri Pirko dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n", 1103b217127eSJiri Pirko filename); 1104b217127eSJiri Pirko return 0; 1105b217127eSJiri Pirko } 1106b217127eSJiri Pirko 1107b217127eSJiri Pirko types_info = kzalloc(sizeof(*types_info), GFP_KERNEL); 1108b217127eSJiri Pirko if (!types_info) { 1109b217127eSJiri Pirko release_firmware(firmware); 1110b217127eSJiri Pirko return -ENOMEM; 1111b217127eSJiri Pirko } 1112b217127eSJiri Pirko linecards->types_info = types_info; 1113b217127eSJiri Pirko 1114b217127eSJiri Pirko types_info->data_size = firmware->size; 1115b217127eSJiri Pirko types_info->data = vmalloc(types_info->data_size); 1116b217127eSJiri Pirko if (!types_info->data) { 1117b217127eSJiri Pirko err = -ENOMEM; 1118b217127eSJiri Pirko release_firmware(firmware); 1119b217127eSJiri Pirko goto err_data_alloc; 1120b217127eSJiri Pirko } 1121b217127eSJiri Pirko memcpy(types_info->data, firmware->data, types_info->data_size); 1122b217127eSJiri Pirko release_firmware(firmware); 1123b217127eSJiri Pirko 1124b217127eSJiri Pirko err = mlxsw_linecard_types_file_validate(linecards, types_info); 1125b217127eSJiri Pirko if (err) { 1126b217127eSJiri Pirko err = 0; 1127b217127eSJiri Pirko goto err_type_file_file_validate; 1128b217127eSJiri Pirko } 1129b217127eSJiri Pirko 1130b217127eSJiri Pirko types_info->ini_files = kmalloc_array(types_info->count, 1131869376d0SJiri Pirko sizeof(struct mlxsw_linecard_ini_file *), 1132b217127eSJiri Pirko GFP_KERNEL); 1133b217127eSJiri Pirko if (!types_info->ini_files) { 1134b217127eSJiri Pirko err = -ENOMEM; 1135b217127eSJiri Pirko goto err_ini_files_alloc; 1136b217127eSJiri Pirko } 1137b217127eSJiri Pirko 1138b217127eSJiri Pirko mlxsw_linecard_types_file_parse(types_info); 1139b217127eSJiri Pirko 1140b217127eSJiri Pirko return 0; 1141b217127eSJiri Pirko 1142b217127eSJiri Pirko err_ini_files_alloc: 1143b217127eSJiri Pirko err_type_file_file_validate: 1144b217127eSJiri Pirko vfree(types_info->data); 1145b217127eSJiri Pirko err_data_alloc: 1146b217127eSJiri Pirko kfree(types_info); 1147b217127eSJiri Pirko return err; 1148b217127eSJiri Pirko } 1149b217127eSJiri Pirko 1150b217127eSJiri Pirko static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards) 1151b217127eSJiri Pirko { 1152b217127eSJiri Pirko struct mlxsw_linecard_types_info *types_info = linecards->types_info; 1153b217127eSJiri Pirko 1154b217127eSJiri Pirko if (!types_info) 1155b217127eSJiri Pirko return; 1156b217127eSJiri Pirko kfree(types_info->ini_files); 1157b217127eSJiri Pirko vfree(types_info->data); 1158b217127eSJiri Pirko kfree(types_info); 1159b217127eSJiri Pirko } 1160b217127eSJiri Pirko 1161b217127eSJiri Pirko int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core, 1162b217127eSJiri Pirko const struct mlxsw_bus_info *bus_info) 1163b217127eSJiri Pirko { 1164b217127eSJiri Pirko char mgpir_pl[MLXSW_REG_MGPIR_LEN]; 1165b217127eSJiri Pirko struct mlxsw_linecards *linecards; 1166b217127eSJiri Pirko u8 slot_count; 1167b217127eSJiri Pirko int err; 1168b217127eSJiri Pirko int i; 1169b217127eSJiri Pirko 1170b217127eSJiri Pirko mlxsw_reg_mgpir_pack(mgpir_pl, 0); 1171b217127eSJiri Pirko err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl); 1172b217127eSJiri Pirko if (err) 1173b217127eSJiri Pirko return err; 1174b217127eSJiri Pirko 1175b217127eSJiri Pirko mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL, 1176b217127eSJiri Pirko NULL, &slot_count); 1177b217127eSJiri Pirko if (!slot_count) 1178b217127eSJiri Pirko return 0; 1179b217127eSJiri Pirko 1180b217127eSJiri Pirko linecards = vzalloc(struct_size(linecards, linecards, slot_count)); 1181b217127eSJiri Pirko if (!linecards) 1182b217127eSJiri Pirko return -ENOMEM; 1183b217127eSJiri Pirko linecards->count = slot_count; 1184b217127eSJiri Pirko linecards->mlxsw_core = mlxsw_core; 1185b217127eSJiri Pirko linecards->bus_info = bus_info; 1186de28976dSJiri Pirko INIT_LIST_HEAD(&linecards->event_ops_list); 1187de28976dSJiri Pirko mutex_init(&linecards->event_ops_list_lock); 1188b217127eSJiri Pirko 1189b217127eSJiri Pirko err = mlxsw_linecard_types_init(mlxsw_core, linecards); 1190b217127eSJiri Pirko if (err) 1191b217127eSJiri Pirko goto err_types_init; 1192b217127eSJiri Pirko 1193b217127eSJiri Pirko err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener, 1194b217127eSJiri Pirko ARRAY_SIZE(mlxsw_linecard_listener), 1195b217127eSJiri Pirko mlxsw_core); 1196b217127eSJiri Pirko if (err) 1197b217127eSJiri Pirko goto err_traps_register; 1198b217127eSJiri Pirko 1199b217127eSJiri Pirko mlxsw_core_linecards_set(mlxsw_core, linecards); 1200b217127eSJiri Pirko 1201b217127eSJiri Pirko for (i = 0; i < linecards->count; i++) { 1202b217127eSJiri Pirko err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1); 1203b217127eSJiri Pirko if (err) 1204b217127eSJiri Pirko goto err_linecard_init; 1205b217127eSJiri Pirko } 1206b217127eSJiri Pirko 1207b217127eSJiri Pirko return 0; 1208b217127eSJiri Pirko 1209b217127eSJiri Pirko err_linecard_init: 1210b217127eSJiri Pirko for (i--; i >= 0; i--) 1211b217127eSJiri Pirko mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); 1212b217127eSJiri Pirko mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, 1213b217127eSJiri Pirko ARRAY_SIZE(mlxsw_linecard_listener), 1214b217127eSJiri Pirko mlxsw_core); 1215b217127eSJiri Pirko err_traps_register: 1216b217127eSJiri Pirko mlxsw_linecard_types_fini(linecards); 1217b217127eSJiri Pirko err_types_init: 1218b217127eSJiri Pirko vfree(linecards); 1219b217127eSJiri Pirko return err; 1220b217127eSJiri Pirko } 1221b217127eSJiri Pirko 1222b217127eSJiri Pirko void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core) 1223b217127eSJiri Pirko { 1224b217127eSJiri Pirko struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core); 1225b217127eSJiri Pirko int i; 1226b217127eSJiri Pirko 1227b217127eSJiri Pirko if (!linecards) 1228b217127eSJiri Pirko return; 1229b217127eSJiri Pirko for (i = 0; i < linecards->count; i++) 1230b217127eSJiri Pirko mlxsw_linecard_fini(mlxsw_core, linecards, i + 1); 1231b217127eSJiri Pirko mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener, 1232b217127eSJiri Pirko ARRAY_SIZE(mlxsw_linecard_listener), 1233b217127eSJiri Pirko mlxsw_core); 1234b217127eSJiri Pirko mlxsw_linecard_types_fini(linecards); 1235de28976dSJiri Pirko mutex_destroy(&linecards->event_ops_list_lock); 1236de28976dSJiri Pirko WARN_ON(!list_empty(&linecards->event_ops_list)); 1237b217127eSJiri Pirko vfree(linecards); 1238b217127eSJiri Pirko } 1239