1// SPDX-License-Identifier: GPL-2.0-only 2// 3// Framework for Ethernet Power Sourcing Equipment 4// 5// Copyright (c) 2022 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> 6// 7 8#include <linux/device.h> 9#include <linux/of.h> 10#include <linux/pse-pd/pse.h> 11 12static DEFINE_MUTEX(pse_list_mutex); 13static LIST_HEAD(pse_controller_list); 14 15/** 16 * struct pse_control - a PSE control 17 * @pcdev: a pointer to the PSE controller device 18 * this PSE control belongs to 19 * @list: list entry for the pcdev's PSE controller list 20 * @id: ID of the PSE line in the PSE controller device 21 * @refcnt: Number of gets of this pse_control 22 */ 23struct pse_control { 24 struct pse_controller_dev *pcdev; 25 struct list_head list; 26 unsigned int id; 27 struct kref refcnt; 28}; 29 30/** 31 * of_pse_zero_xlate - dummy function for controllers with one only control 32 * @pcdev: a pointer to the PSE controller device 33 * @pse_spec: PSE line specifier as found in the device tree 34 * 35 * This static translation function is used by default if of_xlate in 36 * :c:type:`pse_controller_dev` is not set. It is useful for all PSE 37 * controllers with #pse-cells = <0>. 38 */ 39static int of_pse_zero_xlate(struct pse_controller_dev *pcdev, 40 const struct of_phandle_args *pse_spec) 41{ 42 return 0; 43} 44 45/** 46 * of_pse_simple_xlate - translate pse_spec to the PSE line number 47 * @pcdev: a pointer to the PSE controller device 48 * @pse_spec: PSE line specifier as found in the device tree 49 * 50 * This static translation function is used by default if of_xlate in 51 * :c:type:`pse_controller_dev` is not set. It is useful for all PSE 52 * controllers with 1:1 mapping, where PSE lines can be indexed by number 53 * without gaps. 54 */ 55static int of_pse_simple_xlate(struct pse_controller_dev *pcdev, 56 const struct of_phandle_args *pse_spec) 57{ 58 if (pse_spec->args[0] >= pcdev->nr_lines) 59 return -EINVAL; 60 61 return pse_spec->args[0]; 62} 63 64/** 65 * pse_controller_register - register a PSE controller device 66 * @pcdev: a pointer to the initialized PSE controller device 67 */ 68int pse_controller_register(struct pse_controller_dev *pcdev) 69{ 70 if (!pcdev->of_xlate) { 71 if (pcdev->of_pse_n_cells == 0) 72 pcdev->of_xlate = of_pse_zero_xlate; 73 else if (pcdev->of_pse_n_cells == 1) 74 pcdev->of_xlate = of_pse_simple_xlate; 75 } 76 77 mutex_init(&pcdev->lock); 78 INIT_LIST_HEAD(&pcdev->pse_control_head); 79 80 mutex_lock(&pse_list_mutex); 81 list_add(&pcdev->list, &pse_controller_list); 82 mutex_unlock(&pse_list_mutex); 83 84 return 0; 85} 86EXPORT_SYMBOL_GPL(pse_controller_register); 87 88/** 89 * pse_controller_unregister - unregister a PSE controller device 90 * @pcdev: a pointer to the PSE controller device 91 */ 92void pse_controller_unregister(struct pse_controller_dev *pcdev) 93{ 94 mutex_lock(&pse_list_mutex); 95 list_del(&pcdev->list); 96 mutex_unlock(&pse_list_mutex); 97} 98EXPORT_SYMBOL_GPL(pse_controller_unregister); 99 100static void devm_pse_controller_release(struct device *dev, void *res) 101{ 102 pse_controller_unregister(*(struct pse_controller_dev **)res); 103} 104 105/** 106 * devm_pse_controller_register - resource managed pse_controller_register() 107 * @dev: device that is registering this PSE controller 108 * @pcdev: a pointer to the initialized PSE controller device 109 * 110 * Managed pse_controller_register(). For PSE controllers registered by 111 * this function, pse_controller_unregister() is automatically called on 112 * driver detach. See pse_controller_register() for more information. 113 */ 114int devm_pse_controller_register(struct device *dev, 115 struct pse_controller_dev *pcdev) 116{ 117 struct pse_controller_dev **pcdevp; 118 int ret; 119 120 pcdevp = devres_alloc(devm_pse_controller_release, sizeof(*pcdevp), 121 GFP_KERNEL); 122 if (!pcdevp) 123 return -ENOMEM; 124 125 ret = pse_controller_register(pcdev); 126 if (ret) { 127 devres_free(pcdevp); 128 return ret; 129 } 130 131 *pcdevp = pcdev; 132 devres_add(dev, pcdevp); 133 134 return 0; 135} 136EXPORT_SYMBOL_GPL(devm_pse_controller_register); 137 138/* PSE control section */ 139 140static void __pse_control_release(struct kref *kref) 141{ 142 struct pse_control *psec = container_of(kref, struct pse_control, 143 refcnt); 144 145 lockdep_assert_held(&pse_list_mutex); 146 147 module_put(psec->pcdev->owner); 148 149 list_del(&psec->list); 150 kfree(psec); 151} 152 153static void __pse_control_put_internal(struct pse_control *psec) 154{ 155 lockdep_assert_held(&pse_list_mutex); 156 157 kref_put(&psec->refcnt, __pse_control_release); 158} 159 160/** 161 * pse_control_put - free the PSE control 162 * @psec: PSE control pointer 163 */ 164void pse_control_put(struct pse_control *psec) 165{ 166 if (IS_ERR_OR_NULL(psec)) 167 return; 168 169 mutex_lock(&pse_list_mutex); 170 __pse_control_put_internal(psec); 171 mutex_unlock(&pse_list_mutex); 172} 173EXPORT_SYMBOL_GPL(pse_control_put); 174 175static struct pse_control * 176pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index) 177{ 178 struct pse_control *psec; 179 180 lockdep_assert_held(&pse_list_mutex); 181 182 list_for_each_entry(psec, &pcdev->pse_control_head, list) { 183 if (psec->id == index) { 184 kref_get(&psec->refcnt); 185 return psec; 186 } 187 } 188 189 psec = kzalloc(sizeof(*psec), GFP_KERNEL); 190 if (!psec) 191 return ERR_PTR(-ENOMEM); 192 193 if (!try_module_get(pcdev->owner)) { 194 kfree(psec); 195 return ERR_PTR(-ENODEV); 196 } 197 198 psec->pcdev = pcdev; 199 list_add(&psec->list, &pcdev->pse_control_head); 200 psec->id = index; 201 kref_init(&psec->refcnt); 202 203 return psec; 204} 205 206struct pse_control * 207of_pse_control_get(struct device_node *node) 208{ 209 struct pse_controller_dev *r, *pcdev; 210 struct of_phandle_args args; 211 struct pse_control *psec; 212 int psec_id; 213 int ret; 214 215 if (!node) 216 return ERR_PTR(-EINVAL); 217 218 ret = of_parse_phandle_with_args(node, "pses", "#pse-cells", 0, &args); 219 if (ret) 220 return ERR_PTR(ret); 221 222 mutex_lock(&pse_list_mutex); 223 pcdev = NULL; 224 list_for_each_entry(r, &pse_controller_list, list) { 225 if (args.np == r->dev->of_node) { 226 pcdev = r; 227 break; 228 } 229 } 230 231 if (!pcdev) { 232 psec = ERR_PTR(-EPROBE_DEFER); 233 goto out; 234 } 235 236 if (WARN_ON(args.args_count != pcdev->of_pse_n_cells)) { 237 psec = ERR_PTR(-EINVAL); 238 goto out; 239 } 240 241 psec_id = pcdev->of_xlate(pcdev, &args); 242 if (psec_id < 0) { 243 psec = ERR_PTR(psec_id); 244 goto out; 245 } 246 247 /* pse_list_mutex also protects the pcdev's pse_control list */ 248 psec = pse_control_get_internal(pcdev, psec_id); 249 250out: 251 mutex_unlock(&pse_list_mutex); 252 of_node_put(args.np); 253 254 return psec; 255} 256EXPORT_SYMBOL_GPL(of_pse_control_get); 257 258/** 259 * pse_ethtool_get_status - get status of PSE control 260 * @psec: PSE control pointer 261 * @extack: extack for reporting useful error messages 262 * @status: struct to store PSE status 263 */ 264int pse_ethtool_get_status(struct pse_control *psec, 265 struct netlink_ext_ack *extack, 266 struct pse_control_status *status) 267{ 268 const struct pse_controller_ops *ops; 269 int err; 270 271 ops = psec->pcdev->ops; 272 273 if (!ops->ethtool_get_status) { 274 NL_SET_ERR_MSG(extack, 275 "PSE driver does not support status report"); 276 return -EOPNOTSUPP; 277 } 278 279 mutex_lock(&psec->pcdev->lock); 280 err = ops->ethtool_get_status(psec->pcdev, psec->id, extack, status); 281 mutex_unlock(&psec->pcdev->lock); 282 283 return err; 284} 285EXPORT_SYMBOL_GPL(pse_ethtool_get_status); 286 287/** 288 * pse_ethtool_set_config - set PSE control configuration 289 * @psec: PSE control pointer 290 * @extack: extack for reporting useful error messages 291 * @config: Configuration of the test to run 292 */ 293int pse_ethtool_set_config(struct pse_control *psec, 294 struct netlink_ext_ack *extack, 295 const struct pse_control_config *config) 296{ 297 const struct pse_controller_ops *ops; 298 int err; 299 300 ops = psec->pcdev->ops; 301 302 if (!ops->ethtool_set_config) { 303 NL_SET_ERR_MSG(extack, 304 "PSE driver does not configuration"); 305 return -EOPNOTSUPP; 306 } 307 308 mutex_lock(&psec->pcdev->lock); 309 err = ops->ethtool_set_config(psec->pcdev, psec->id, extack, config); 310 mutex_unlock(&psec->pcdev->lock); 311 312 return err; 313} 314EXPORT_SYMBOL_GPL(pse_ethtool_set_config); 315