nvethernet: Move thermal cooling device registration

Issue: Tegra-eqos thermal cooling device is registered
during driver probe(). Once this registration is done,
callbacks via cooling_device_ops can be invoked at any time.
This implies even if driver is just probed, callbacks can try
to trigger a pad calibration due to temperature change and
result in failure trying to access MAC registers when the
MAC netdev interface is not up.

Fix: Move cooling device register/unregister to ether_open/
ether_close() routines respectively, so that callbacks can
be invoked only when interface is actually up.

Bug 1679250

Change-Id: Iaf181ceb3af4b9def188171606d9a9c141d06ccc
Signed-off-by: Srinivas Ramachandran <srinivasra@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/2138382
Reviewed-by: Narayan Reddy <narayanr@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
Srinivas Ramachandran
2019-06-18 14:55:04 -07:00
committed by Revanth Kumar Uppala
parent 8c7d9510f2
commit d1bf60985d

View File

@@ -874,6 +874,133 @@ static int ether_allocate_dma_resources(struct ether_priv_data *pdata)
return ret;
}
#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,
&ether_cdev_ops);
if (!pdata->tcd) {
return -ENODEV;
} else if (IS_ERR(pdata->tcd)) {
return PTR_ERR(pdata->tcd);
}
return 0;
}
#endif /* THERMAL_CAL */
/**
* ether_open - Call back to handle bring up of Ethernet interface
* @dev: Net device data structure.
@@ -921,6 +1048,16 @@ static int ether_open(struct net_device *dev)
goto err_alloc;
}
#ifdef THERMAL_CAL
atomic_set(&pdata->therm_state, 0);
ret = ether_therm_init(pdata);
if (ret < 0) {
dev_err(pdata->dev, "Failed to register cooling device (%d)\n",
ret);
goto err_therm;
}
#endif /* THERMAL_CAL */
/* initialize MAC/MTL/DMA Common registers */
ret = osi_hw_core_init(pdata->osi_core,
pdata->hw_feat.tx_fifo_size,
@@ -964,6 +1101,10 @@ static int ether_open(struct net_device *dev)
return ret;
err_hw_init:
#ifdef THERMAL_CAL
thermal_cooling_device_unregister(pdata->tcd);
err_therm:
#endif /* THERMAL_CAL */
free_dma_resources(pdata->osi_dma, pdata->dev);
err_alloc:
ether_free_irqs(pdata);
@@ -1013,6 +1154,10 @@ static int ether_close(struct net_device *dev)
/* DMA De init */
osi_hw_dma_deinit(pdata->osi_dma);
#ifdef THERMAL_CAL
thermal_cooling_device_unregister(pdata->tcd);
#endif /* THERMAL_CAL */
/* free DMA resources after DMA stop */
free_dma_resources(pdata->osi_dma, pdata->dev);
@@ -2930,133 +3075,6 @@ 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,
&ether_cdev_ops);
if (!pdata->tcd) {
return -ENODEV;
} else if (IS_ERR(pdata->tcd)) {
return PTR_ERR(pdata->tcd);
}
return 0;
}
#endif /* THERMAL_CAL */
/**
* init_filter_values- static function to initialize filter reg
* count in private data structure
@@ -3232,16 +3250,6 @@ 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");
@@ -3259,10 +3267,6 @@ 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:
@@ -3294,10 +3298,6 @@ 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_device>/ */
ether_sysfs_unregister(pdata->dev);