tegrakernel/kernel/kernel-4.9/drivers/padctrl/padctrl-core.c

384 lines
8.4 KiB
C
Raw Normal View History

2022-02-16 09:13:02 -06:00
/*
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/module.h>
#include <linux/list.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/padctrl/padctrl.h>
static DEFINE_MUTEX(padctrl_dev_list_mutex);
static LIST_HEAD(padctrl_dev_list);
static atomic_t padctrl_no = ATOMIC_INIT(0);
struct padctrl_dev {
struct device dev;
struct list_head list;
struct list_head consumer_list;
struct padctrl_desc *desc;
struct padctrl_config config;
struct mutex mutex;
void *drv_data;
};
struct padctrl {
struct list_head list;
struct padctrl_dev *pad_dev;
int index;
};
static void padctrl_dev_release(struct device *dev)
{
struct padctrl_dev *pdev = dev_get_drvdata(dev);
kfree(pdev);
}
static struct class padctrl_class = {
.name = "padctrl",
.dev_release = padctrl_dev_release,
};
static struct padctrl *of_pad_get(struct device *dev, struct device_node *np,
const char *name)
{
struct of_phandle_args npspec;
struct padctrl *pad;
struct padctrl_dev *pad_dev;
int index = 0;
int cindex = -1;
int ret;
if (name)
index = of_property_match_string(np, "pad-names", name);
ret = of_parse_phandle_with_args(np, "pad-controllers",
"#padcontroller-cells", index, &npspec);
if (ret < 0) {
dev_err(dev, "Not found padcontroller handles: %d\n", ret);
return ERR_PTR(ret);
}
list_for_each_entry(pad_dev, &padctrl_dev_list, list) {
if (pad_dev->dev.of_node == npspec.np) {
cindex = npspec.args_count ? npspec.args[0] : 0;
goto out;
}
}
return ERR_PTR(-ENODEV);
out:
pad = kzalloc(sizeof(*pad), GFP_KERNEL);
if (!pad)
return ERR_PTR(-ENOMEM);
pad->pad_dev = pad_dev;
pad->index = cindex;
INIT_LIST_HEAD(&pad->list);
mutex_lock(&pad_dev->mutex);
list_add(&pad->list, &pad_dev->consumer_list);
mutex_unlock(&pad_dev->mutex);
return pad;
}
struct padctrl *padctrl_get(struct device *dev, const char *name)
{
struct padctrl *pad;
if (!dev || !dev->of_node) {
pr_err("%s: not enough information provided\n", __func__);
return ERR_PTR(-ENODEV);
}
mutex_lock(&padctrl_dev_list_mutex);
pad = of_pad_get(dev, dev->of_node, name);
mutex_unlock(&padctrl_dev_list_mutex);
return pad;
}
EXPORT_SYMBOL(padctrl_get);
void padctrl_put(struct padctrl *pctrl)
{
if (pctrl) {
mutex_lock(&pctrl->pad_dev->mutex);
list_del(&pctrl->list);
mutex_unlock(&pctrl->pad_dev->mutex);
kfree(pctrl);
}
}
EXPORT_SYMBOL(padctrl_put);
static void devm_padctrl_release(struct device *dev, void *res)
{
padctrl_put(*(struct padctrl **)res);
}
struct padctrl *devm_padctrl_get(struct device *dev, const char *name)
{
struct padctrl **ptr, *padctrl;
ptr = devres_alloc(devm_padctrl_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
padctrl = padctrl_get(dev, name);
if (!IS_ERR(padctrl)) {
*ptr = padctrl;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return padctrl;
}
EXPORT_SYMBOL(devm_padctrl_get);
struct padctrl *devm_padctrl_get_from_node(struct device *dev,
struct device_node *np,
const char *name)
{
struct padctrl **ptr, *padctrl;
if (!dev || !np) {
pr_err("%s: not enough information provided\n", __func__);
return ERR_PTR(-ENODEV);
}
ptr = devres_alloc(devm_padctrl_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
mutex_lock(&padctrl_dev_list_mutex);
padctrl = of_pad_get(dev, np, name);
mutex_unlock(&padctrl_dev_list_mutex);
if (IS_ERR(padctrl)) {
devres_free(ptr);
return padctrl;
}
*ptr = padctrl;
devres_add(dev, ptr);
return padctrl;
}
EXPORT_SYMBOL(devm_padctrl_get_from_node);
int padctrl_set_voltage(struct padctrl *pad, u32 voltage)
{
struct padctrl_dev *pad_dev;
int ret = -EINVAL;
if (!pad || !pad->pad_dev)
return -EINVAL;
pad_dev = pad->pad_dev;
mutex_lock(&pad_dev->mutex);
if (pad_dev->desc->ops->set_voltage)
ret = pad_dev->desc->ops->set_voltage(pad->pad_dev,
pad->index, voltage);
mutex_unlock(&pad_dev->mutex);
return ret;
}
EXPORT_SYMBOL(padctrl_set_voltage);
int padctrl_get_voltage(struct padctrl *pad, u32 *voltage)
{
struct padctrl_dev *pad_dev;
int ret = -EINVAL;
if (!pad || !pad->pad_dev)
return -EINVAL;
pad_dev = pad->pad_dev;
mutex_lock(&pad_dev->mutex);
if (pad_dev->desc->ops->get_voltage)
ret = pad_dev->desc->ops->get_voltage(pad->pad_dev,
pad->index, voltage);
mutex_unlock(&pad_dev->mutex);
return ret;
}
EXPORT_SYMBOL(padctrl_get_voltage);
int padctrl_power_enable(struct padctrl *pad)
{
struct padctrl_dev *pad_dev;
int ret = -EINVAL;
if (!pad || !pad->pad_dev)
return -EINVAL;
pad_dev = pad->pad_dev;
mutex_lock(&pad_dev->mutex);
if (pad_dev->desc->ops->power_enable)
ret = pad_dev->desc->ops->power_enable(pad->pad_dev,
pad->index);
mutex_unlock(&pad_dev->mutex);
return ret;
}
EXPORT_SYMBOL(padctrl_power_enable);
int padctrl_power_disable(struct padctrl *pad)
{
struct padctrl_dev *pad_dev;
int ret = -EINVAL;
if (!pad || !pad->pad_dev)
return -EINVAL;
pad_dev = pad->pad_dev;
mutex_lock(&pad_dev->mutex);
if (pad_dev->desc->ops->power_disable)
ret = pad_dev->desc->ops->power_disable(pad->pad_dev,
pad->index);
mutex_unlock(&pad_dev->mutex);
return ret;
}
EXPORT_SYMBOL(padctrl_power_disable);
struct padctrl_dev *padctrl_register(struct device *dev,
struct padctrl_desc *desc, struct padctrl_config *config)
{
struct padctrl_dev *pad_dev;
int ret;
if (!desc || !config)
return ERR_PTR(-EINVAL);
WARN_ON(!desc->ops->set_voltage || !desc->ops->get_voltage);
pad_dev = kzalloc(sizeof(*pad_dev), GFP_KERNEL);
if (!pad_dev)
return ERR_PTR(-ENOMEM);
mutex_init(&pad_dev->mutex);
INIT_LIST_HEAD(&pad_dev->consumer_list);
INIT_LIST_HEAD(&pad_dev->list);
pad_dev->desc = desc;
/* register with sysfs */
pad_dev->dev.class = &padctrl_class;
pad_dev->dev.of_node = config->of_node;
pad_dev->dev.parent = dev;
dev_set_name(&pad_dev->dev, "padctrl.%d",
atomic_inc_return(&padctrl_no) - 1);
ret = device_register(&pad_dev->dev);
if (ret) {
put_device(&pad_dev->dev);
goto clean;
}
dev_set_drvdata(&pad_dev->dev, pad_dev);
mutex_lock(&padctrl_dev_list_mutex);
list_add(&pad_dev->list, &padctrl_dev_list);
mutex_unlock(&padctrl_dev_list_mutex);
dev_info(&pad_dev->dev, "Pad control driver %s registered\n",
desc->name);
return pad_dev;
clean:
kfree(pad_dev);
return ERR_PTR(ret);
}
void padctrl_unregister(struct padctrl_dev *pad_dev)
{
if (pad_dev) {
mutex_lock(&padctrl_dev_list_mutex);
list_del(&pad_dev->list);
mutex_unlock(&padctrl_dev_list_mutex);
kfree(pad_dev);
}
}
EXPORT_SYMBOL(padctrl_unregister);
void padctrl_set_drvdata(struct padctrl_dev *pad_dev, void *drv_data)
{
pad_dev->drv_data = drv_data;
}
EXPORT_SYMBOL(padctrl_set_drvdata);
void *padctrl_get_drvdata(struct padctrl_dev *pad_dev)
{
return pad_dev->drv_data;
}
EXPORT_SYMBOL(padctrl_get_drvdata);
static void devm_padctrl_dev_release(struct device *dev, void *res)
{
padctrl_unregister(*(struct padctrl_dev **)res);
}
struct padctrl_dev *devm_padctrl_register(struct device *dev,
struct padctrl_desc *desc, struct padctrl_config *config)
{
struct padctrl_dev **ptr, *pad_dev;
ptr = devres_alloc(devm_padctrl_dev_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
pad_dev = padctrl_register(dev, desc, config);
if (!IS_ERR(pad_dev)) {
*ptr = pad_dev;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return pad_dev;
}
EXPORT_SYMBOL(devm_padctrl_register);
static int devm_padctrl_match(struct device *dev, void *res, void *data)
{
struct padctrl_dev **r = res;
if (!r || !*r) {
WARN_ON(!r || !*r);
return 0;
}
return *r == data;
}
void devm_padctrl_unregister(struct device *dev, struct padctrl_dev *pad_dev)
{
int ret;
ret = devres_release(dev, devm_padctrl_dev_release,
devm_padctrl_match, pad_dev);
if (ret != 0)
WARN_ON(ret);
}
EXPORT_SYMBOL(devm_padctrl_unregister);
static int __init padctrl_init(void)
{
int ret;
ret = class_register(&padctrl_class);
if (ret)
pr_err("ERROR: Padcontrol class create failed: %d\n", ret);
return ret;
}
core_initcall(padctrl_init);