/* * Google LWIS PHY Interface * * Copyright (c) 2018 Google, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define pr_fmt(fmt) KBUILD_MODNAME "-phy: " fmt #include "lwis_phy.h" #include #include struct lwis_phy_list *lwis_phy_list_alloc(int count) { struct lwis_phy_list *list; /* No need to allocate if count is invalid */ if (count <= 0) { return ERR_PTR(-EINVAL); } list = kmalloc(sizeof(struct lwis_phy_list), GFP_KERNEL); if (!list) { pr_err("Failed to allocate PHY list\n"); return ERR_PTR(-ENOMEM); } list->phy = kzalloc(count * sizeof(struct phy), GFP_KERNEL); if (!list->phy) { pr_err("Failed to allocate PHY instances\n"); kfree(list); return ERR_PTR(-ENOMEM); } list->count = count; return list; } void lwis_phy_list_free(struct lwis_phy_list *list) { if (!list) { return; } if (list->phy) { kfree(list->phy); } kfree(list); } int lwis_phy_get(struct lwis_phy_list *list, char *name, struct device *dev) { struct phy *phy; int i; int index = -1; if (!list || !dev) { return -EINVAL; } /* Look for empty slot and duplicate entries */ for (i = 0; i < list->count; ++i) { if (list->phy[i].phy == NULL) { index = i; } else if (!strcmp(list->phy[i].name, name)) { pr_info("PHY %s already allocated\n", name); return i; } } /* No empty slots */ if (index < 0) { pr_err("No empty slots in the lwis_phy struct\n"); return -ENOMEM; } /* Make sure PHY exists */ phy = devm_phy_get(dev, name); if (IS_ERR_OR_NULL(phy)) { pr_err("PHY %s not found\n", name); return PTR_ERR(phy); } list->phy[index].phy = phy; list->phy[index].name = name; return index; } int lwis_phy_put_by_idx(struct lwis_phy_list *list, int index, struct device *dev) { if (!list || index < 0 || index >= list->count) { return -EINVAL; } if (IS_ERR_OR_NULL(list->phy[index].phy)) { return -EINVAL; } devm_phy_put(dev, list->phy[index].phy); memset(list->phy + index, 0, sizeof(struct lwis_phy)); return 0; } int lwis_phy_put_by_name(struct lwis_phy_list *list, char *name, struct device *dev) { int i; if (!dev || !list) { return -EINVAL; } /* Find entry by name */ for (i = 0; i < list->count; ++i) { if (!strcmp(list->phy[i].name, name)) { if (IS_ERR_OR_NULL(list->phy[i].phy)) { return -EINVAL; } devm_phy_put(dev, list->phy[i].phy); memset(list->phy + i, 0, sizeof(struct lwis_phy)); return 0; } } pr_err("PHY %s not found\n", name); return -EINVAL; } int lwis_phy_set_power_by_idx(struct lwis_phy_list *list, int index, bool power_on) { if (!list || index < 0 || index >= list->count) { return -EINVAL; } if (power_on) { return phy_power_on(list->phy[index].phy); } return phy_power_off(list->phy[index].phy); } int lwis_phy_set_power_by_name(struct lwis_phy_list *list, char *name, bool power_on) { int i; if (!list) { return -EINVAL; } /* Find entry by name */ for (i = 0; i < list->count; ++i) { if (!strcmp(list->phy[i].name, name)) { return lwis_phy_set_power_by_idx(list, i, power_on); } } /* Entry not found */ pr_err("PHY %s not found\n", name); return -ENOENT; } int lwis_phy_set_power_all(struct lwis_phy_list *list, bool power_on) { int i; int ret; if (!list) { return -EINVAL; } for (i = 0; i < list->count; ++i) { ret = lwis_phy_set_power_by_idx(list, i, power_on); if (ret) { pr_err("Failed to set PHY power\n"); return ret; } } return 0; } void lwis_phy_print(struct lwis_phy_list *list) { int i; for (i = 0; i < list->count; ++i) { pr_info("%s: PHY: %s\n", __func__, list->phy[i].name); } }