blob: fac08d9dcca471fb2ddc852ffe0e3330a4638b7d [file] [log] [blame]
/*
* Google LWIS Regulator 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 "-reg: " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
#include "lwis_regulator.h"
struct lwis_regulator_list *lwis_regulator_list_alloc(int num_regs)
{
struct lwis_regulator_list *list;
if (num_regs < 0) {
return ERR_PTR(-EINVAL);
}
list = kmalloc(sizeof(struct lwis_regulator_list), GFP_KERNEL);
if (!list) {
return ERR_PTR(-ENOMEM);
}
list->reg = kzalloc(num_regs * sizeof(struct lwis_regulator), GFP_KERNEL);
if (!list->reg) {
kfree(list);
return ERR_PTR(-ENOMEM);
}
list->count = num_regs;
return list;
}
void lwis_regulator_list_free(struct lwis_regulator_list *list)
{
if (!list) {
return;
}
if (list->reg) {
kfree(list->reg);
}
kfree(list);
}
int lwis_regulator_get(struct lwis_regulator_list *list, char *name, int voltage,
struct device *dev)
{
struct regulator *reg;
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->reg[i].reg == NULL) {
index = i;
} else if (!strcmp(list->reg[i].name, name)) {
pr_info("Regulator %s already allocated\n", name);
return i;
}
}
/* No empty slot */
if (index < 0) {
pr_err("No empty slots in the lwis_regulator struct\n");
return -ENOMEM;
}
/* Make sure regulator exists */
reg = devm_regulator_get(dev, name);
if (IS_ERR(reg)) {
return PTR_ERR(reg);
}
list->reg[index].reg = reg;
strlcpy(list->reg[index].name, name, LWIS_MAX_NAME_STRING_LEN);
list->reg[index].voltage = voltage;
return index;
}
int lwis_regulator_put_by_idx(struct lwis_regulator_list *list, int index)
{
if (!list || index < 0 || index >= list->count) {
return -EINVAL;
}
if (IS_ERR_OR_NULL(list->reg[index].reg)) {
return -EINVAL;
}
devm_regulator_put(list->reg[index].reg);
memset(list->reg + index, 0, sizeof(struct lwis_regulator));
return 0;
}
int lwis_regulator_put_by_name(struct lwis_regulator_list *list, char *name)
{
int i;
if (!list) {
return -EINVAL;
}
/* Find entry by name */
for (i = 0; i < list->count; ++i) {
if (!strcmp(list->reg[i].name, name)) {
if (IS_ERR_OR_NULL(list->reg[i].reg)) {
return -EINVAL;
}
devm_regulator_put(list->reg[i].reg);
memset(list->reg + i, 0, sizeof(struct lwis_regulator));
return 0;
}
}
pr_err("Regulator %s not found\n", name);
return -EINVAL;
}
int lwis_regulator_put_all(struct lwis_regulator_list *list)
{
int i;
int ret;
if (!list) {
return -EINVAL;
}
for (i = 0; i < list->count; ++i) {
ret = lwis_regulator_put_by_idx(list, i);
}
return ret;
}
int lwis_regulator_enable_by_idx(struct lwis_regulator_list *list, int index)
{
int ret = 0;
struct lwis_regulator *lwis_reg;
if (!list) {
return -EINVAL;
}
lwis_reg = &list->reg[index];
if (lwis_reg->voltage > 0) {
ret = regulator_set_voltage(lwis_reg->reg, lwis_reg->voltage, lwis_reg->voltage);
if (ret) {
pr_err("Failed to set regulator %s voltage to %d\n", lwis_reg->name,
lwis_reg->voltage);
return ret;
}
}
return regulator_enable(list->reg[index].reg);
}
int lwis_regulator_enable_by_name(struct lwis_regulator_list *list, char *name)
{
int i;
if (!list) {
return -EINVAL;
}
for (i = 0; i < list->count; ++i) {
if (!strcmp(list->reg[i].name, name)) {
return lwis_regulator_enable_by_idx(list, i);
}
}
/* No entry found */
pr_err("Regulator %s not found\n", name);
return -ENOENT;
}
int lwis_regulator_enable_all(struct lwis_regulator_list *list)
{
int i;
int ret;
for (i = 0; i < list->count; ++i) {
ret = lwis_regulator_enable_by_idx(list, i);
if (ret) {
pr_err("Error enabling regulator %s\n", list->reg[i].name);
return ret;
}
}
return 0;
}
int lwis_regulator_disable_by_idx(struct lwis_regulator_list *list, int index)
{
if (!list) {
return -EINVAL;
}
return regulator_disable(list->reg[index].reg);
}
int lwis_regulator_disable_by_name(struct lwis_regulator_list *list, char *name)
{
int i;
if (!list) {
return -EINVAL;
}
for (i = 0; i < list->count; ++i) {
if (!strcmp(list->reg[i].name, name)) {
return regulator_disable(list->reg[i].reg);
}
}
/* No entry found */
pr_err("Regulator %s not found\n", name);
return -ENOENT;
}
int lwis_regulator_disable_all(struct lwis_regulator_list *list)
{
int i;
int ret;
for (i = 0; i < list->count; ++i) {
ret = lwis_regulator_disable_by_idx(list, i);
if (ret) {
pr_err("Error disabling regulator %s\n", list->reg[i].name);
return ret;
}
}
return 0;
}
void lwis_regulator_print(struct lwis_regulator_list *list)
{
int i;
for (i = 0; i < list->count; ++i) {
pr_info("%s: reg: %s voltage: %d\n", __func__, list->reg[i].name,
list->reg[i].voltage);
}
}