| /* |
| * 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); |
| } |
| } |