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