Commit c4015bbe authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-stmmac-introduce-devres-helpers-for-stmmac-platform-drivers'

Bartosz Golaszewski says:

====================
net: stmmac: introduce devres helpers for stmmac platform drivers

The goal of this series is two-fold: to make the API for stmmac platforms more
logically correct (by providing functions that acquire resources with release
counterparts that undo only their actions and nothing more) and to provide
devres variants of commonly use registration functions that allows to
significantly simplify the platform drivers.

The current pattern for stmmac platform drivers is to call
stmmac_probe_config_dt(), possibly the platform's init() callback and then
call stmmac_drv_probe(). The resources allocated by these calls will then
be released by calling stmmac_pltfr_remove(). This goes against the commonly
accepted way of providing each function that allocated a resource with a
function that frees it.

First: provide wrappers around platform's init() and exit() callbacks that
allow users to skip checking if the callbacks exist manually.

Second: provide stmmac_pltfr_probe() which calls the platform init() callback
and then calls stmmac_drv_probe() together with a variant of
stmmac_pltfr_remove() that DOES NOT call stmmac_remove_config_dt(). For now
this variant is called stmmac_pltfr_remove_no_dt() but once all users of
the old stmmac_pltfr_remove() are converted to the devres helper, it will be
renamed back to stmmac_pltfr_remove() and the no_dt function removed.

Finally use the devres helpers in dwmac-qco-ethqos to show how much simplier
the driver's probe() becomes.

This series obviously just starts the conversion process and other platform
drivers will need to be converted once the helpers land in net/.
====================

Link: https://lore.kernel.org/r/20230623100417.93592-1-brgl@bgdev.plSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents cfd40b82 4194f32a
......@@ -46,22 +46,12 @@ static int dwmac_generic_probe(struct platform_device *pdev)
plat_dat->unicast_filter_entries = 1;
}
/* Custom initialisation (if needed) */
if (plat_dat->init) {
ret = plat_dat->init(pdev, plat_dat->bsp_priv);
if (ret)
goto err_remove_config_dt;
}
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
ret = stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
if (ret)
goto err_exit;
goto err_remove_config_dt;
return 0;
err_exit:
if (plat_dat->exit)
plat_dat->exit(pdev, plat_dat->bsp_priv);
err_remove_config_dt:
if (pdev->dev.of_node)
stmmac_remove_config_dt(pdev, plat_dat);
......
......@@ -708,7 +708,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
if (ret)
return ret;
plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
if (IS_ERR(plat_dat)) {
dev_err(dev, "dt configuration failed\n");
return PTR_ERR(plat_dat);
......@@ -717,10 +717,8 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
plat_dat->clks_config = ethqos_clks_config;
ethqos = devm_kzalloc(dev, sizeof(*ethqos), GFP_KERNEL);
if (!ethqos) {
ret = -ENOMEM;
goto out_config_dt;
}
if (!ethqos)
return -ENOMEM;
ethqos->phy_mode = device_get_phy_mode(dev);
switch (ethqos->phy_mode) {
......@@ -734,19 +732,15 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
ethqos->configure_func = ethqos_configure_sgmii;
break;
case -ENODEV:
ret = -ENODEV;
goto out_config_dt;
return -ENODEV;
default:
ret = -EINVAL;
goto out_config_dt;
return -EINVAL;
}
ethqos->pdev = pdev;
ethqos->rgmii_base = devm_platform_ioremap_resource_byname(pdev, "rgmii");
if (IS_ERR(ethqos->rgmii_base)) {
ret = PTR_ERR(ethqos->rgmii_base);
goto out_config_dt;
}
if (IS_ERR(ethqos->rgmii_base))
return PTR_ERR(ethqos->rgmii_base);
ethqos->mac_base = stmmac_res.addr;
......@@ -757,24 +751,20 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
ethqos->has_emac_ge_3 = data->has_emac_ge_3;
ethqos->link_clk = devm_clk_get(dev, data->link_clk_name ?: "rgmii");
if (IS_ERR(ethqos->link_clk)) {
ret = PTR_ERR(ethqos->link_clk);
goto out_config_dt;
}
if (IS_ERR(ethqos->link_clk))
return PTR_ERR(ethqos->link_clk);
ret = ethqos_clks_config(ethqos, true);
if (ret)
goto out_config_dt;
return ret;
ret = devm_add_action_or_reset(dev, ethqos_clks_disable, ethqos);
if (ret)
goto out_config_dt;
return ret;
ethqos->serdes_phy = devm_phy_optional_get(dev, "serdes");
if (IS_ERR(ethqos->serdes_phy)) {
ret = PTR_ERR(ethqos->serdes_phy);
goto out_config_dt;
}
if (IS_ERR(ethqos->serdes_phy))
return PTR_ERR(ethqos->serdes_phy);
ethqos->speed = SPEED_1000;
ethqos_update_link_clk(ethqos, SPEED_1000);
......@@ -797,16 +787,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
plat_dat->serdes_powerdown = qcom_ethqos_serdes_powerdown;
}
ret = stmmac_dvr_probe(dev, plat_dat, &stmmac_res);
if (ret)
goto out_config_dt;
return ret;
out_config_dt:
stmmac_remove_config_dt(pdev, plat_dat);
return ret;
return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
}
static const struct of_device_id qcom_ethqos_match[] = {
......@@ -820,7 +801,6 @@ MODULE_DEVICE_TABLE(of, qcom_ethqos_match);
static struct platform_driver qcom_ethqos_driver = {
.probe = qcom_ethqos_probe,
.remove_new = stmmac_pltfr_remove,
.driver = {
.name = "qcom-ethqos",
.pm = &stmmac_pltfr_pm_ops,
......
......@@ -8,6 +8,7 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
......@@ -629,6 +630,39 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
return ret;
}
static void devm_stmmac_remove_config_dt(void *data)
{
struct plat_stmmacenet_data *plat = data;
/* Platform data argument is unused */
stmmac_remove_config_dt(NULL, plat);
}
/**
* devm_stmmac_probe_config_dt
* @pdev: platform_device structure
* @mac: MAC address to use
* Description: Devres variant of stmmac_probe_config_dt(). Does not require
* the user to call stmmac_remove_config_dt() at driver detach.
*/
struct plat_stmmacenet_data *
devm_stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
{
struct plat_stmmacenet_data *plat;
int ret;
plat = stmmac_probe_config_dt(pdev, mac);
if (IS_ERR(plat))
return plat;
ret = devm_add_action_or_reset(&pdev->dev,
devm_stmmac_remove_config_dt, plat);
if (ret)
return ERR_PTR(ret);
return plat;
}
/**
* stmmac_remove_config_dt - undo the effects of stmmac_probe_config_dt()
* @pdev: platform_device structure
......@@ -651,12 +685,19 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
return ERR_PTR(-EINVAL);
}
struct plat_stmmacenet_data *
devm_stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
{
return ERR_PTR(-EINVAL);
}
void stmmac_remove_config_dt(struct platform_device *pdev,
struct plat_stmmacenet_data *plat)
{
}
#endif /* CONFIG_OF */
EXPORT_SYMBOL_GPL(stmmac_probe_config_dt);
EXPORT_SYMBOL_GPL(devm_stmmac_probe_config_dt);
EXPORT_SYMBOL_GPL(stmmac_remove_config_dt);
int stmmac_get_platform_resources(struct platform_device *pdev,
......@@ -701,6 +742,114 @@ int stmmac_get_platform_resources(struct platform_device *pdev,
}
EXPORT_SYMBOL_GPL(stmmac_get_platform_resources);
/**
* stmmac_pltfr_init
* @pdev: pointer to the platform device
* @plat: driver data platform structure
* Description: Call the platform's init callback (if any) and propagate
* the return value.
*/
int stmmac_pltfr_init(struct platform_device *pdev,
struct plat_stmmacenet_data *plat)
{
int ret = 0;
if (plat->init)
ret = plat->init(pdev, plat->bsp_priv);
return ret;
}
EXPORT_SYMBOL_GPL(stmmac_pltfr_init);
/**
* stmmac_pltfr_exit
* @pdev: pointer to the platform device
* @plat: driver data platform structure
* Description: Call the platform's exit callback (if any).
*/
void stmmac_pltfr_exit(struct platform_device *pdev,
struct plat_stmmacenet_data *plat)
{
if (plat->exit)
plat->exit(pdev, plat->bsp_priv);
}
EXPORT_SYMBOL_GPL(stmmac_pltfr_exit);
/**
* stmmac_pltfr_probe
* @pdev: platform device pointer
* @plat: driver data platform structure
* @res: stmmac resources structure
* Description: This calls the platform's init() callback and probes the
* stmmac driver.
*/
int stmmac_pltfr_probe(struct platform_device *pdev,
struct plat_stmmacenet_data *plat,
struct stmmac_resources *res)
{
int ret;
ret = stmmac_pltfr_init(pdev, plat);
if (ret)
return ret;
ret = stmmac_dvr_probe(&pdev->dev, plat, res);
if (ret) {
stmmac_pltfr_exit(pdev, plat);
return ret;
}
return ret;
}
EXPORT_SYMBOL_GPL(stmmac_pltfr_probe);
static void devm_stmmac_pltfr_remove(void *data)
{
struct platform_device *pdev = data;
stmmac_pltfr_remove_no_dt(pdev);
}
/**
* devm_stmmac_pltfr_probe
* @pdev: pointer to the platform device
* @plat: driver data platform structure
* @res: stmmac resources
* Description: Devres variant of stmmac_pltfr_probe(). Allows users to skip
* calling stmmac_pltfr_remove() on driver detach.
*/
int devm_stmmac_pltfr_probe(struct platform_device *pdev,
struct plat_stmmacenet_data *plat,
struct stmmac_resources *res)
{
int ret;
ret = stmmac_pltfr_probe(pdev, plat, res);
if (ret)
return ret;
return devm_add_action_or_reset(&pdev->dev, devm_stmmac_pltfr_remove,
pdev);
}
EXPORT_SYMBOL_GPL(devm_stmmac_pltfr_probe);
/**
* stmmac_pltfr_remove_no_dt
* @pdev: pointer to the platform device
* Description: This undoes the effects of stmmac_pltfr_probe() by removing the
* driver and calling the platform's exit() callback.
*/
void stmmac_pltfr_remove_no_dt(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct stmmac_priv *priv = netdev_priv(ndev);
struct plat_stmmacenet_data *plat = priv->plat;
stmmac_dvr_remove(&pdev->dev);
stmmac_pltfr_exit(pdev, plat);
}
EXPORT_SYMBOL_GPL(stmmac_pltfr_remove_no_dt);
/**
* stmmac_pltfr_remove
* @pdev: platform device pointer
......@@ -713,11 +862,7 @@ void stmmac_pltfr_remove(struct platform_device *pdev)
struct stmmac_priv *priv = netdev_priv(ndev);
struct plat_stmmacenet_data *plat = priv->plat;
stmmac_dvr_remove(&pdev->dev);
if (plat->exit)
plat->exit(pdev, plat->bsp_priv);
stmmac_pltfr_remove_no_dt(pdev);
stmmac_remove_config_dt(pdev, plat);
}
EXPORT_SYMBOL_GPL(stmmac_pltfr_remove);
......@@ -737,8 +882,7 @@ static int __maybe_unused stmmac_pltfr_suspend(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
ret = stmmac_suspend(dev);
if (priv->plat->exit)
priv->plat->exit(pdev, priv->plat->bsp_priv);
stmmac_pltfr_exit(pdev, priv->plat);
return ret;
}
......@@ -755,9 +899,11 @@ static int __maybe_unused stmmac_pltfr_resume(struct device *dev)
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
struct platform_device *pdev = to_platform_device(dev);
int ret;
if (priv->plat->init)
priv->plat->init(pdev, priv->plat->bsp_priv);
ret = stmmac_pltfr_init(pdev, priv->plat->bsp_priv);
if (ret)
return ret;
return stmmac_resume(dev);
}
......
......@@ -13,12 +13,26 @@
struct plat_stmmacenet_data *
stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac);
struct plat_stmmacenet_data *
devm_stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac);
void stmmac_remove_config_dt(struct platform_device *pdev,
struct plat_stmmacenet_data *plat);
int stmmac_get_platform_resources(struct platform_device *pdev,
struct stmmac_resources *stmmac_res);
int stmmac_pltfr_init(struct platform_device *pdev,
struct plat_stmmacenet_data *plat);
void stmmac_pltfr_exit(struct platform_device *pdev,
struct plat_stmmacenet_data *plat);
int stmmac_pltfr_probe(struct platform_device *pdev,
struct plat_stmmacenet_data *plat,
struct stmmac_resources *res);
int devm_stmmac_pltfr_probe(struct platform_device *pdev,
struct plat_stmmacenet_data *plat,
struct stmmac_resources *res);
void stmmac_pltfr_remove_no_dt(struct platform_device *pdev);
void stmmac_pltfr_remove(struct platform_device *pdev);
extern const struct dev_pm_ops stmmac_pltfr_pm_ops;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment