1*66741b4eSOleksij Rempel // SPDX-License-Identifier: GPL-2.0-only 2*66741b4eSOleksij Rempel // 3*66741b4eSOleksij Rempel // Driver for the regulator based Ethernet Power Sourcing Equipment, without 4*66741b4eSOleksij Rempel // auto classification support. 5*66741b4eSOleksij Rempel // 6*66741b4eSOleksij Rempel // Copyright (c) 2022 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> 7*66741b4eSOleksij Rempel // 8*66741b4eSOleksij Rempel 9*66741b4eSOleksij Rempel #include <linux/module.h> 10*66741b4eSOleksij Rempel #include <linux/of.h> 11*66741b4eSOleksij Rempel #include <linux/platform_device.h> 12*66741b4eSOleksij Rempel #include <linux/pse-pd/pse.h> 13*66741b4eSOleksij Rempel #include <linux/regulator/consumer.h> 14*66741b4eSOleksij Rempel 15*66741b4eSOleksij Rempel struct pse_reg_priv { 16*66741b4eSOleksij Rempel struct pse_controller_dev pcdev; 17*66741b4eSOleksij Rempel struct regulator *ps; /*power source */ 18*66741b4eSOleksij Rempel enum ethtool_podl_pse_admin_state admin_state; 19*66741b4eSOleksij Rempel }; 20*66741b4eSOleksij Rempel 21*66741b4eSOleksij Rempel static struct pse_reg_priv *to_pse_reg(struct pse_controller_dev *pcdev) 22*66741b4eSOleksij Rempel { 23*66741b4eSOleksij Rempel return container_of(pcdev, struct pse_reg_priv, pcdev); 24*66741b4eSOleksij Rempel } 25*66741b4eSOleksij Rempel 26*66741b4eSOleksij Rempel static int 27*66741b4eSOleksij Rempel pse_reg_ethtool_set_config(struct pse_controller_dev *pcdev, unsigned long id, 28*66741b4eSOleksij Rempel struct netlink_ext_ack *extack, 29*66741b4eSOleksij Rempel const struct pse_control_config *config) 30*66741b4eSOleksij Rempel { 31*66741b4eSOleksij Rempel struct pse_reg_priv *priv = to_pse_reg(pcdev); 32*66741b4eSOleksij Rempel int ret; 33*66741b4eSOleksij Rempel 34*66741b4eSOleksij Rempel if (priv->admin_state == config->admin_cotrol) 35*66741b4eSOleksij Rempel return 0; 36*66741b4eSOleksij Rempel 37*66741b4eSOleksij Rempel switch (config->admin_cotrol) { 38*66741b4eSOleksij Rempel case ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED: 39*66741b4eSOleksij Rempel ret = regulator_enable(priv->ps); 40*66741b4eSOleksij Rempel break; 41*66741b4eSOleksij Rempel case ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED: 42*66741b4eSOleksij Rempel ret = regulator_disable(priv->ps); 43*66741b4eSOleksij Rempel break; 44*66741b4eSOleksij Rempel default: 45*66741b4eSOleksij Rempel dev_err(pcdev->dev, "Unknown admin state %i\n", 46*66741b4eSOleksij Rempel config->admin_cotrol); 47*66741b4eSOleksij Rempel ret = -ENOTSUPP; 48*66741b4eSOleksij Rempel } 49*66741b4eSOleksij Rempel 50*66741b4eSOleksij Rempel if (ret) 51*66741b4eSOleksij Rempel return ret; 52*66741b4eSOleksij Rempel 53*66741b4eSOleksij Rempel priv->admin_state = config->admin_cotrol; 54*66741b4eSOleksij Rempel 55*66741b4eSOleksij Rempel return 0; 56*66741b4eSOleksij Rempel } 57*66741b4eSOleksij Rempel 58*66741b4eSOleksij Rempel static int 59*66741b4eSOleksij Rempel pse_reg_ethtool_get_status(struct pse_controller_dev *pcdev, unsigned long id, 60*66741b4eSOleksij Rempel struct netlink_ext_ack *extack, 61*66741b4eSOleksij Rempel struct pse_control_status *status) 62*66741b4eSOleksij Rempel { 63*66741b4eSOleksij Rempel struct pse_reg_priv *priv = to_pse_reg(pcdev); 64*66741b4eSOleksij Rempel int ret; 65*66741b4eSOleksij Rempel 66*66741b4eSOleksij Rempel ret = regulator_is_enabled(priv->ps); 67*66741b4eSOleksij Rempel if (ret < 0) 68*66741b4eSOleksij Rempel return ret; 69*66741b4eSOleksij Rempel 70*66741b4eSOleksij Rempel if (!ret) 71*66741b4eSOleksij Rempel status->podl_pw_status = ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED; 72*66741b4eSOleksij Rempel else 73*66741b4eSOleksij Rempel status->podl_pw_status = 74*66741b4eSOleksij Rempel ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING; 75*66741b4eSOleksij Rempel 76*66741b4eSOleksij Rempel status->podl_admin_state = priv->admin_state; 77*66741b4eSOleksij Rempel 78*66741b4eSOleksij Rempel return 0; 79*66741b4eSOleksij Rempel } 80*66741b4eSOleksij Rempel 81*66741b4eSOleksij Rempel static const struct pse_controller_ops pse_reg_ops = { 82*66741b4eSOleksij Rempel .ethtool_get_status = pse_reg_ethtool_get_status, 83*66741b4eSOleksij Rempel .ethtool_set_config = pse_reg_ethtool_set_config, 84*66741b4eSOleksij Rempel }; 85*66741b4eSOleksij Rempel 86*66741b4eSOleksij Rempel static int 87*66741b4eSOleksij Rempel pse_reg_probe(struct platform_device *pdev) 88*66741b4eSOleksij Rempel { 89*66741b4eSOleksij Rempel struct device *dev = &pdev->dev; 90*66741b4eSOleksij Rempel struct pse_reg_priv *priv; 91*66741b4eSOleksij Rempel int ret; 92*66741b4eSOleksij Rempel 93*66741b4eSOleksij Rempel priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 94*66741b4eSOleksij Rempel if (!priv) 95*66741b4eSOleksij Rempel return -ENOMEM; 96*66741b4eSOleksij Rempel 97*66741b4eSOleksij Rempel if (!pdev->dev.of_node) 98*66741b4eSOleksij Rempel return -ENOENT; 99*66741b4eSOleksij Rempel 100*66741b4eSOleksij Rempel priv->ps = devm_regulator_get_exclusive(dev, "pse"); 101*66741b4eSOleksij Rempel if (IS_ERR(priv->ps)) 102*66741b4eSOleksij Rempel return dev_err_probe(dev, PTR_ERR(priv->ps), 103*66741b4eSOleksij Rempel "failed to get PSE regulator.\n"); 104*66741b4eSOleksij Rempel 105*66741b4eSOleksij Rempel platform_set_drvdata(pdev, priv); 106*66741b4eSOleksij Rempel 107*66741b4eSOleksij Rempel ret = regulator_is_enabled(priv->ps); 108*66741b4eSOleksij Rempel if (ret < 0) 109*66741b4eSOleksij Rempel return ret; 110*66741b4eSOleksij Rempel 111*66741b4eSOleksij Rempel if (ret) 112*66741b4eSOleksij Rempel priv->admin_state = ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED; 113*66741b4eSOleksij Rempel else 114*66741b4eSOleksij Rempel priv->admin_state = ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED; 115*66741b4eSOleksij Rempel 116*66741b4eSOleksij Rempel priv->pcdev.owner = THIS_MODULE; 117*66741b4eSOleksij Rempel priv->pcdev.ops = &pse_reg_ops; 118*66741b4eSOleksij Rempel priv->pcdev.dev = dev; 119*66741b4eSOleksij Rempel ret = devm_pse_controller_register(dev, &priv->pcdev); 120*66741b4eSOleksij Rempel if (ret) { 121*66741b4eSOleksij Rempel dev_err(dev, "failed to register PSE controller (%pe)\n", 122*66741b4eSOleksij Rempel ERR_PTR(ret)); 123*66741b4eSOleksij Rempel return ret; 124*66741b4eSOleksij Rempel } 125*66741b4eSOleksij Rempel 126*66741b4eSOleksij Rempel return 0; 127*66741b4eSOleksij Rempel } 128*66741b4eSOleksij Rempel 129*66741b4eSOleksij Rempel static const __maybe_unused struct of_device_id pse_reg_of_match[] = { 130*66741b4eSOleksij Rempel { .compatible = "podl-pse-regulator", }, 131*66741b4eSOleksij Rempel { }, 132*66741b4eSOleksij Rempel }; 133*66741b4eSOleksij Rempel MODULE_DEVICE_TABLE(of, pse_reg_of_match); 134*66741b4eSOleksij Rempel 135*66741b4eSOleksij Rempel static struct platform_driver pse_reg_driver = { 136*66741b4eSOleksij Rempel .probe = pse_reg_probe, 137*66741b4eSOleksij Rempel .driver = { 138*66741b4eSOleksij Rempel .name = "PSE regulator", 139*66741b4eSOleksij Rempel .of_match_table = of_match_ptr(pse_reg_of_match), 140*66741b4eSOleksij Rempel }, 141*66741b4eSOleksij Rempel }; 142*66741b4eSOleksij Rempel module_platform_driver(pse_reg_driver); 143*66741b4eSOleksij Rempel 144*66741b4eSOleksij Rempel MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>"); 145*66741b4eSOleksij Rempel MODULE_DESCRIPTION("regulator based Ethernet Power Sourcing Equipment"); 146*66741b4eSOleksij Rempel MODULE_LICENSE("GPL v2"); 147*66741b4eSOleksij Rempel MODULE_ALIAS("platform:pse-regulator"); 148