blob: 6e4830aaf23d65b22bded186cc85207819206b9f [file] [log] [blame]
/*
* 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 <linux/kernel.h>
#include <linux/slab.h>
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(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);
}
}