| // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB | |
| /* Copyright (c) Amazon.com, Inc. or its affiliates. | |
| * All rights reserved. | |
| */ | |
| #include "linux/pci.h" | |
| #include "ena_devlink.h" | |
| #include "ena_phc.h" | |
| static int ena_devlink_enable_phc_validate(struct devlink *devlink, u32 id, | |
| union devlink_param_value *val, | |
| struct netlink_ext_ack *extack) | |
| { | |
| struct ena_adapter *adapter = ENA_DEVLINK_PRIV(devlink); | |
| if (!val->vbool) | |
| return 0; | |
| if (!ena_com_phc_supported(adapter->ena_dev)) { | |
| NL_SET_ERR_MSG_MOD(extack, "Device doesn't support PHC"); | |
| return -EOPNOTSUPP; | |
| } | |
| return 0; | |
| } | |
| static const struct devlink_param ena_devlink_params[] = { | |
| DEVLINK_PARAM_GENERIC(ENABLE_PHC, | |
| BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), | |
| NULL, | |
| NULL, | |
| ena_devlink_enable_phc_validate), | |
| }; | |
| void ena_devlink_params_get(struct devlink *devlink) | |
| { | |
| struct ena_adapter *adapter = ENA_DEVLINK_PRIV(devlink); | |
| union devlink_param_value val; | |
| int err; | |
| err = devl_param_driverinit_value_get(devlink, | |
| DEVLINK_PARAM_GENERIC_ID_ENABLE_PHC, | |
| &val); | |
| if (err) { | |
| netdev_err(adapter->netdev, "Failed to query PHC param\n"); | |
| return; | |
| } | |
| ena_phc_enable(adapter, val.vbool); | |
| } | |
| void ena_devlink_disable_phc_param(struct devlink *devlink) | |
| { | |
| union devlink_param_value value; | |
| devl_lock(devlink); | |
| value.vbool = false; | |
| devl_param_driverinit_value_set(devlink, | |
| DEVLINK_PARAM_GENERIC_ID_ENABLE_PHC, | |
| &value); | |
| devl_unlock(devlink); | |
| } | |
| static void ena_devlink_port_register(struct devlink *devlink) | |
| { | |
| struct ena_adapter *adapter = ENA_DEVLINK_PRIV(devlink); | |
| struct devlink_port_attrs attrs = {}; | |
| attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; | |
| devlink_port_attrs_set(&adapter->devlink_port, &attrs); | |
| devl_port_register(devlink, &adapter->devlink_port, 0); | |
| } | |
| static void ena_devlink_port_unregister(struct devlink *devlink) | |
| { | |
| struct ena_adapter *adapter = ENA_DEVLINK_PRIV(devlink); | |
| devl_port_unregister(&adapter->devlink_port); | |
| } | |
| static int ena_devlink_reload_down(struct devlink *devlink, | |
| bool netns_change, | |
| enum devlink_reload_action action, | |
| enum devlink_reload_limit limit, | |
| struct netlink_ext_ack *extack) | |
| { | |
| struct ena_adapter *adapter = ENA_DEVLINK_PRIV(devlink); | |
| if (netns_change) { | |
| NL_SET_ERR_MSG_MOD(extack, | |
| "Namespace change is not supported"); | |
| return -EOPNOTSUPP; | |
| } | |
| ena_devlink_port_unregister(devlink); | |
| rtnl_lock(); | |
| ena_destroy_device(adapter, false); | |
| rtnl_unlock(); | |
| return 0; | |
| } | |
| static int ena_devlink_reload_up(struct devlink *devlink, | |
| enum devlink_reload_action action, | |
| enum devlink_reload_limit limit, | |
| u32 *actions_performed, | |
| struct netlink_ext_ack *extack) | |
| { | |
| struct ena_adapter *adapter = ENA_DEVLINK_PRIV(devlink); | |
| int err = 0; | |
| rtnl_lock(); | |
| /* Check that no other routine initialized the device (e.g. | |
| * ena_fw_reset_device()). Also we're under devlink_mutex here, | |
| * so devlink isn't freed under our feet. | |
| */ | |
| if (!test_bit(ENA_FLAG_DEVICE_RUNNING, &adapter->flags)) | |
| err = ena_restore_device(adapter); | |
| rtnl_unlock(); | |
| ena_devlink_port_register(devlink); | |
| if (!err) | |
| *actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT); | |
| return err; | |
| } | |
| static const struct devlink_ops ena_devlink_ops = { | |
| .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT), | |
| .reload_down = ena_devlink_reload_down, | |
| .reload_up = ena_devlink_reload_up, | |
| }; | |
| static int ena_devlink_configure_params(struct devlink *devlink) | |
| { | |
| struct ena_adapter *adapter = ENA_DEVLINK_PRIV(devlink); | |
| union devlink_param_value value; | |
| int rc; | |
| rc = devlink_params_register(devlink, ena_devlink_params, | |
| ARRAY_SIZE(ena_devlink_params)); | |
| if (rc) { | |
| netdev_err(adapter->netdev, "Failed to register devlink params\n"); | |
| return rc; | |
| } | |
| devl_lock(devlink); | |
| value.vbool = ena_phc_is_enabled(adapter); | |
| devl_param_driverinit_value_set(devlink, | |
| DEVLINK_PARAM_GENERIC_ID_ENABLE_PHC, | |
| &value); | |
| devl_unlock(devlink); | |
| return 0; | |
| } | |
| struct devlink *ena_devlink_alloc(struct ena_adapter *adapter) | |
| { | |
| struct device *dev = &adapter->pdev->dev; | |
| struct devlink *devlink; | |
| devlink = devlink_alloc(&ena_devlink_ops, | |
| sizeof(struct ena_adapter *), | |
| dev); | |
| if (!devlink) { | |
| netdev_err(adapter->netdev, | |
| "Failed to allocate devlink struct\n"); | |
| return NULL; | |
| } | |
| ENA_DEVLINK_PRIV(devlink) = adapter; | |
| adapter->devlink = devlink; | |
| if (ena_devlink_configure_params(devlink)) | |
| goto free_devlink; | |
| return devlink; | |
| free_devlink: | |
| devlink_free(devlink); | |
| return NULL; | |
| } | |
| static void ena_devlink_configure_params_clean(struct devlink *devlink) | |
| { | |
| devlink_params_unregister(devlink, ena_devlink_params, | |
| ARRAY_SIZE(ena_devlink_params)); | |
| } | |
| void ena_devlink_free(struct devlink *devlink) | |
| { | |
| ena_devlink_configure_params_clean(devlink); | |
| devlink_free(devlink); | |
| } | |
| void ena_devlink_register(struct devlink *devlink, struct device *dev) | |
| { | |
| devl_lock(devlink); | |
| ena_devlink_port_register(devlink); | |
| devl_register(devlink); | |
| devl_unlock(devlink); | |
| } | |
| void ena_devlink_unregister(struct devlink *devlink) | |
| { | |
| devl_lock(devlink); | |
| ena_devlink_port_unregister(devlink); | |
| devl_unregister(devlink); | |
| devl_unlock(devlink); | |
| } |