From 3565e0eb75489d56ebe5fed97d4aae616e4ba1a0 Mon Sep 17 00:00:00 2001 From: Srinivas Ramachandran Date: Fri, 17 May 2019 16:16:10 -0700 Subject: [PATCH] nvethernet: Add temperature based pad calibration. Ethernet pad calibration needs to be triggered for temperature changes in steps of 35C from -40C to 110C. In order to get indication of current operating temperature, register a ethernet cooling device with thermal zones defined as per above requirement. When the temperature trips these zones, callback function is invoked from kernel thermal framework, and ethernet driver can trigger pad calibration. Bug 1679250 Change-Id: Iaeccca650e371843fa571f7b368bb5464e106314 Signed-off-by: Srinivas Ramachandran Reviewed-on: https://git-master.nvidia.com/r/2120431 GVS: Gerrit_Virtual_Submit Reviewed-by: Ajay Gupta Reviewed-by: Ashutosh Jha Reviewed-by: mobile promotions Tested-by: mobile promotions --- .../net/ethernet/nvidia/nvethernet/Makefile | 2 + .../ethernet/nvidia/nvethernet/ether_linux.c | 145 ++++++++++++++++++ .../ethernet/nvidia/nvethernet/ether_linux.h | 20 +++ 3 files changed, 167 insertions(+) diff --git a/drivers/net/ethernet/nvidia/nvethernet/Makefile b/drivers/net/ethernet/nvidia/nvethernet/Makefile index f416350e..aba3dbf6 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/Makefile +++ b/drivers/net/ethernet/nvidia/nvethernet/Makefile @@ -17,6 +17,8 @@ OSI_DMA := ../../../../../../nvethernetrm/osi/dma obj-$(CONFIG_NVETHERNET) += nvethernet.o +EXTRA_CFLAGS += -DTHERMAL_CAL + nvethernet-objs:= ether_linux.o \ osd.o \ ethtool.o \ diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index 5e389499..acde80f5 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -2543,6 +2543,133 @@ static void ether_set_ndev_features(struct net_device *ndev, pdata->hw_feat_cur_state = features; } +#ifdef THERMAL_CAL +/** + * ether_get_max_therm_state - Set current thermal state. + * @tcd: Ethernet thermal cooling device pointer. + * @state: Variable to read max thermal state. + * + * Algorithm: Fill the max supported thermal state for ethernet cooling + * device in variable provided by caller. + * + * Dependencies: MAC needs to be out of reset. Once cooling device ops are + * registered, it can be called anytime from kernel. MAC has to be in + * sufficient state to allow pad calibration. + * + * Protection: None. + * + * Return: 0 - succcess. Does not fail as function is only reading var. + */ +static int ether_get_max_therm_state(struct thermal_cooling_device *tcd, + unsigned long *state) +{ + *state = ETHER_MAX_THERM_STATE; + + return 0; +} + +/** + * ether_get_cur_therm_state - Get current thermal state. + * @tcd: Ethernet thermal cooling device pointer. + * @state: Variable to read current thermal state. + * + * Algorithm: Atomically get the current thermal state of etherent + * cooling device. + * + * Dependencies: MAC needs to be out of reset. Once cooling device ops are + * registered, it can be called anytime from kernel. MAC has to be in + * sufficient state to allow pad calibration. + * + * Protection: None. + * + * Return: 0 - succcess. Does not fail as function is only reading var. + */ +static int ether_get_cur_therm_state(struct thermal_cooling_device *tcd, + unsigned long *state) +{ + struct ether_priv_data *pdata = tcd->devdata; + + *state = (unsigned long)atomic_read(&pdata->therm_state); + + return 0; +} + +/** + * ether_set_cur_therm_state - Set current thermal state. + * @tcd: Ethernet thermal cooling device pointer. + * @state: The thermal state to set. + * + * Algorithm: Atomically set the desired state provided as argument. + * Trigger pad calibration for each state change. + * + * Dependencies: MAC needs to be out of reset. Once cooling device ops are + * registered, it can be called anytime from kernel. MAC has to be in + * sufficient state to allow pad calibration. + * + * Protection: None. + * + * Return: 0 - succcess, -ve value - failure. + */ +static int ether_set_cur_therm_state(struct thermal_cooling_device *tcd, + unsigned long state) +{ + struct ether_priv_data *pdata = tcd->devdata; + struct device *dev = pdata->dev; + + /* Thermal framework will take care of making sure state is within + * valid bounds, based on the get_max_state callback. So no need + * to validate bounds again here. + */ + dev_info(dev, "Therm state change from %d to %lu\n", + atomic_read(&pdata->therm_state), state); + + atomic_set(&pdata->therm_state, state); + + if (osi_pad_calibrate(pdata->osi_core) < 0) { + dev_err(dev, "Therm state changed, failed pad calibration\n"); + return -1; + } + + return 0; +} + +static struct thermal_cooling_device_ops ether_cdev_ops = { + .get_max_state = ether_get_max_therm_state, + .get_cur_state = ether_get_cur_therm_state, + .set_cur_state = ether_set_cur_therm_state, +}; + +/** + * ether_therm_init - Register thermal cooling device with kernel. + * @pdata: Pointer to driver private data structure. + * + * Algorithm: Register thermal cooling device read from DT. The cooling + * device ops struct passed as argument will be used by thermal framework + * to callback the ethernet driver when temperature trip points are + * triggered, so that ethernet driver can do pad calibration. + * + * Dependencies: MAC needs to be out of reset. Once cooling device ops are + * registered, it can be called anytime from kernel. MAC has to be in + * sufficient state to allow pad calibration. + * + * Protection: None. + * + * Return: 0 - succcess, -ve value - failure. + */ +static int ether_therm_init(struct ether_priv_data *pdata) +{ + pdata->tcd = thermal_cooling_device_register("tegra-eqos", pdata, + ðer_cdev_ops); + if (!pdata->tcd) { + return -ENODEV; + } else if (IS_ERR(pdata->tcd)) { + return PTR_ERR(pdata->tcd); + } + + return 0; +} +#endif /* THERMAL_CAL */ + /** * ether_probe - Ethernet platform driver probe. * @pdev: platform device associated with platform driver. @@ -2681,6 +2808,16 @@ static int ether_probe(struct platform_device *pdev) goto err_sysfs; } +#ifdef THERMAL_CAL + atomic_set(&pdata->therm_state, 0); + ret = ether_therm_init(pdata); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register cooling device (%d)\n", + ret); + goto err_therm; + } +#endif /* THERMAL_CAL */ + ret = register_netdev(ndev); if (ret < 0) { dev_err(&pdev->dev, "failed to register netdev\n"); @@ -2694,6 +2831,10 @@ static int ether_probe(struct platform_device *pdev) return 0; err_netdev: +#ifdef THERMAL_CAL + thermal_cooling_device_unregister(pdata->tcd); +err_therm: +#endif /* THERMAL_CAL */ ether_sysfs_unregister(pdata->dev); err_sysfs: err_napi: @@ -2725,6 +2866,10 @@ static int ether_remove(struct platform_device *pdev) unregister_netdev(ndev); +#ifdef THERMAL_CAL + thermal_cooling_device_unregister(pdata->tcd); +#endif /* THERMAL_CAL */ + /* remove nvethernet sysfs group under /sys/devices// */ ether_sysfs_unregister(pdata->dev); diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index a30add26..3732b388 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +72,13 @@ static inline int ether_avail_txdesc_cnt(struct osi_tx_ring *tx_ring) (TX_DESC_CNT - 1)); } +#ifdef THERMAL_CAL +/* The DT binding for ethernet device has 5 thermal zones in steps of + * 35 degress from -40C to 110C. Each zone corresponds to a state. + */ +#define ETHER_MAX_THERM_STATE 5UL +#endif /* THERMAL_CAL */ + /** * struct ether_tx_napi - DMA Transmit Channel NAPI * @chan: Transmit channel number @@ -131,6 +139,13 @@ struct ether_rx_napi { * @mac_loopback_mode: MAC loopback mode * @q_prio: Array of MTL queue TX priority * @hw_feat_cur_state: Current state of features enabled in HW + * @tcd: Pointer to thermal cooling device which this driver + * registers with the kernel. Kernel will invoke the + * callback ops for this cooling device when temperate + * in thermal zone defined in DT binding for this driver + * is tripped. + * @therm_state: Atomic variable to hold the current temperature zone + * which has triggered. */ struct ether_priv_data { struct osi_core_priv_data *osi_core; @@ -174,6 +189,11 @@ struct ether_priv_data { /* for MAC loopback */ unsigned int mac_loopback_mode; unsigned int q_prio[OSI_EQOS_MAX_NUM_CHANS]; + +#ifdef THERMAL_CAL + struct thermal_cooling_device *tcd; + atomic_t therm_state; +#endif /* THERMAL_CAL */ }; void ether_set_ethtool_ops(struct net_device *ndev);