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);