NVIDIA: SAUCE: platform: tegra: Add tegra prod as built-in driver
BugLink: https://bugs.launchpad.net/bugs/2080908 This is an integration squash change of below commits. - [INTG: NV INTERNAL] platform: tegra: Add tegra prod as built-in - [INTG: NV INTERNAL] platform: tegra: Add tegra prod framework - [INTG: NV INTERNAL] platform: tegra: Add support for mask with 1s - [INTG: NV INTERNAL] platform: tegra_prod: Use kmemleak_not_leak for allocated memory - [INTG: NV INTERNAL] platform: tegra_prod: Fix prod setting parsing for package/board - [INTG: NV INTERNAL] platform: tegra_prod: Use proper variable name - [INTG: NV INTERNAL] platform: tegra_prod: Use for_each_available_child_of_node() - [INTG: NV INTERNAL] platform: tegra: move struct tegra_prod_list to private - [INTG: NV INTERNAL] platform: tegra_prod: Add APIs to managed allocation of prod_list - [INTG: NV INTERNAL] platform: tegra_prod: Remove unused APIs from public header - [INTG: NV INTERNAL] platform: tegra_prod: Get rid of tegra_prod_release() - [INTG: NV INTERNAL] platform: tegra_prod: Add support for nested prod nodes - [INTG: NV INTERNAL] platform: tegra_prod: Add devm_tegra_prod_get_from_node() - [INTG: NV INTERNAL] platform: tegra_prod: use devm_ for allocation - [INTG: NV INTERNAL] platform: tegra_prod: Do not use kmemleak_not_leak() - [INTG: NV INTERNAL] platform: tegra: Add support to find prod setting - [INTG: NV INTERNAL] platform: tegra: APIs to set prod based on name/index/offset/mask - [INTG: NV INTERNAL] prod: Add support for masked write in partially prod config - [INTG: NV INTERNAL] platform: tegra_prod: Use strcasecmp() for prod name - [INTG: NV INTERNAL] prod: set prod-settings mask to 1-style default - [INTG: NV INTERNAL] platform: tegra_prod: add support to avoid multiple configruation - soc: tegra: Add prod configuration parser - prod: remove package and board specific prod node - platform: tegra: Add config for next generation tegra prod - tegra-prod: Add legacy and next gen prod based on config - tegra-prod: Fix missing last entry - tegra-prod: Fix null pointer error - tegra-prod: Fix null pointer error - tegra: prod: remove new prod settings support http://nvbugs/4165866 http://nvbugs/200416207 http://nvbugs/1758682 http://nvbugs/1745386 http://nvbugs/1768583 http://nvbugs/200217343 http://nvbugs/200215286 http://nvbugs/1807581 http://nvbugs/1865456 http://nvbugs/4097475 http://nvbugs/3389584 http://nvbugs/4638219 http://nvbugs/4283554 http://nvbugs/4765671 http://nvbugs/4754882 Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> Signed-off-by: Petlozu Pravareshwar <petlozup@nvidia.com> Acked-by: Noah Wager <noah.wager@canonical.com> Acked-by: Jacob Martin <jacob.martin@canonical.com> Signed-off-by: Noah Wager <noah.wager@canonical.com>
This commit is contained in:
@@ -12,4 +12,5 @@ obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
|
||||
obj-$(CONFIG_CZNIC_PLATFORMS) += cznic/
|
||||
obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
|
||||
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||
obj-$(CONFIG_ARM64_PLATFORM_DEVICES) += arm64/
|
||||
|
||||
24
drivers/platform/tegra/Kconfig
Normal file
24
drivers/platform/tegra/Kconfig
Normal file
@@ -0,0 +1,24 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Tegra Platform Specific Drivers
|
||||
#
|
||||
|
||||
menuconfig TEGRA_PLATFORM_DEVICES
|
||||
bool "Tegra Platform Specific Device Drivers"
|
||||
default y
|
||||
help
|
||||
Say Y here to get to see options for device drivers of various
|
||||
Tegra platforms. This option itself does not add any kernel code.
|
||||
|
||||
If you say N, all options in this submenu will be skipped and
|
||||
disabled.
|
||||
|
||||
if TEGRA_PLATFORM_DEVICES
|
||||
|
||||
config TEGRA_PROD_LEGACY
|
||||
def_bool y
|
||||
help
|
||||
This config helps client driver to identify whether prod
|
||||
framework is implemented as legacy method or not.
|
||||
|
||||
endif # TEGRA_PLATFORM_DEVICES
|
||||
3
drivers/platform/tegra/Makefile
Normal file
3
drivers/platform/tegra/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_TEGRA_PROD_LEGACY) += tegra_prod.o
|
||||
620
drivers/platform/tegra/tegra_prod.c
Normal file
620
drivers/platform/tegra/tegra_prod.c
Normal file
@@ -0,0 +1,620 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2017, 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/tegra_prod.h>
|
||||
|
||||
#define PROD_TUPLE_NUM (sizeof(struct prod_tuple)/sizeof(u32))
|
||||
|
||||
/* tegra_prod: Tegra Prod list for the given submodule
|
||||
* @n_prod_cells: Number of prod setting cells.
|
||||
*/
|
||||
struct tegra_prod {
|
||||
struct tegra_prod_config *prod_config;
|
||||
int num; /* number of tegra_prod*/
|
||||
int n_prod_cells;
|
||||
};
|
||||
|
||||
struct prod_tuple {
|
||||
u32 index; /* Address base index */
|
||||
u32 addr; /* offset address*/
|
||||
u32 mask; /* mask */
|
||||
u32 val; /* value */
|
||||
};
|
||||
|
||||
struct tegra_prod_config {
|
||||
const char *name;
|
||||
struct prod_tuple *prod_tuple;
|
||||
int count; /* number of prod_tuple*/
|
||||
bool boot_init;
|
||||
};
|
||||
|
||||
static int tegra_prod_get_child_tupple_count(struct device *dev,
|
||||
struct device_node *np,
|
||||
int n_tupple)
|
||||
{
|
||||
struct device_node *child;
|
||||
int count;
|
||||
int total_tupple = 0;
|
||||
|
||||
count = of_property_count_u32_elems(np, "prod");
|
||||
if (count > 0) {
|
||||
if ((count < n_tupple) || (count % n_tupple != 0)) {
|
||||
dev_err(dev, "Node %s has invalid entries\n", np->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
total_tupple = count / n_tupple;
|
||||
}
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
count = tegra_prod_get_child_tupple_count(dev, child, n_tupple);
|
||||
if (count < 0)
|
||||
return count;
|
||||
|
||||
total_tupple += count;
|
||||
}
|
||||
|
||||
return total_tupple;
|
||||
}
|
||||
|
||||
static int tegra_prod_read_prod_data(struct device *dev,
|
||||
struct device_node *np,
|
||||
struct prod_tuple *p_tuple,
|
||||
int n_tupple)
|
||||
{
|
||||
u32 pval;
|
||||
int count;
|
||||
int t_count;
|
||||
int cnt;
|
||||
int index;
|
||||
int ret;
|
||||
|
||||
count = of_property_count_u32_elems(np, "prod");
|
||||
if (count <= 0) {
|
||||
dev_dbg(dev, "Node %s: prod prop not found\n", np->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
t_count = count / n_tupple;
|
||||
for (cnt = 0; cnt < t_count; cnt++, p_tuple++) {
|
||||
index = cnt * n_tupple;
|
||||
|
||||
if (n_tupple == 4) {
|
||||
ret = of_property_read_u32_index(np, "prod", index,
|
||||
&pval);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to parse prod of node %s\n",
|
||||
np->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
p_tuple->index = pval;
|
||||
index++;
|
||||
} else {
|
||||
p_tuple->index = 0;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_index(np, "prod", index, &pval);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to parse address of node %s\n",
|
||||
np->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
p_tuple->addr = pval;
|
||||
index++;
|
||||
|
||||
ret = of_property_read_u32_index(np, "prod", index, &pval);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to parse mask of node %s\n",
|
||||
np->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
p_tuple->mask = pval;
|
||||
index++;
|
||||
|
||||
ret = of_property_read_u32_index(np, "prod", index, &pval);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to parse value of node %s\n",
|
||||
np->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
p_tuple->val = pval;
|
||||
}
|
||||
|
||||
return t_count;
|
||||
}
|
||||
|
||||
static int tegra_prod_read_node_tupple(struct device *dev,
|
||||
struct device_node *np,
|
||||
struct prod_tuple *p_tuple,
|
||||
int n_tupple)
|
||||
{
|
||||
int ret = 0;
|
||||
int sindex;
|
||||
struct device_node *child;
|
||||
|
||||
ret = tegra_prod_read_prod_data(dev, np, p_tuple, n_tupple);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
sindex = ret;
|
||||
p_tuple += ret;
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
ret = tegra_prod_read_node_tupple(dev, child,
|
||||
p_tuple, n_tupple);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
sindex += ret;
|
||||
p_tuple += ret;
|
||||
}
|
||||
|
||||
return sindex;
|
||||
}
|
||||
|
||||
/* Process the tupples and optimise for the register configuration for
|
||||
* Same location.
|
||||
*/
|
||||
static void tegra_prod_optimise_tupple(struct prod_tuple *p_tuple,
|
||||
int n_tupple)
|
||||
{
|
||||
struct prod_tuple *ti, *tj;
|
||||
u32 mask;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < n_tupple; ++i) {
|
||||
ti = p_tuple + i;
|
||||
for (j = i + 1; j < n_tupple; ++j) {
|
||||
tj = p_tuple + j;
|
||||
if (ti->index != tj->index)
|
||||
continue;
|
||||
|
||||
if (ti->addr != tj->addr)
|
||||
continue;
|
||||
|
||||
mask = ti->mask & tj->mask;
|
||||
if (!mask)
|
||||
continue;
|
||||
|
||||
ti->val &= ~mask;
|
||||
ti->mask &= ~mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_prod_parse_dt - Read the prod setting form Device tree.
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @np_prod: Prod setting node.
|
||||
* @tegra_prod: The list of tegra prods.
|
||||
*
|
||||
* Read the prod setting form DT according the prod name in tegra prod list.
|
||||
* prod tuple will be allocated dynamically according to the tuple number of
|
||||
* each prod in DT.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
|
||||
static int tegra_prod_parse_dt(struct device *dev,
|
||||
const struct device_node *np,
|
||||
const struct device_node *np_prod,
|
||||
struct tegra_prod *tegra_prod)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct tegra_prod_config *t_prod;
|
||||
struct prod_tuple *p_tuple;
|
||||
int n_child;
|
||||
int n_tupple = 3;
|
||||
int ret;
|
||||
int count;
|
||||
u32 pval;
|
||||
bool mask_opt;
|
||||
|
||||
if (!tegra_prod || !tegra_prod->prod_config) {
|
||||
dev_err(dev, "Node %s: Invalid tegra prods list.\n", np->name);
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
mask_opt = of_property_read_bool(np_prod, "enable-mask-optimisation");
|
||||
|
||||
ret = of_property_read_u32(np_prod, "#prod-cells", &pval);
|
||||
if (!ret)
|
||||
n_tupple = pval;
|
||||
if ((n_tupple != 3) && (n_tupple != 4)) {
|
||||
dev_err(dev, "Node %s: Prod cells not supported\n", np->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
tegra_prod->n_prod_cells = n_tupple;
|
||||
|
||||
n_child = 0;
|
||||
for_each_available_child_of_node(np_prod, child) {
|
||||
t_prod = &tegra_prod->prod_config[n_child];
|
||||
t_prod->name = child->name;
|
||||
|
||||
count = tegra_prod_get_child_tupple_count(dev, child, n_tupple);
|
||||
if (count < 0) {
|
||||
dev_err(dev, "Node %s: Child has not proper setting\n",
|
||||
child->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
dev_err(dev, "Node %s: prod prop not found\n",
|
||||
child->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
t_prod->count = count;
|
||||
|
||||
t_prod->prod_tuple = devm_kcalloc(dev, t_prod->count,
|
||||
sizeof(*p_tuple), GFP_KERNEL);
|
||||
if (!t_prod->prod_tuple)
|
||||
return -ENOMEM;
|
||||
|
||||
t_prod->boot_init = of_property_read_bool(child,
|
||||
"nvidia,prod-boot-init");
|
||||
|
||||
ret = tegra_prod_read_node_tupple(dev, child,
|
||||
t_prod->prod_tuple, n_tupple);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Node %s: Reading prod setting failed: %d\n",
|
||||
child->name, ret);
|
||||
return ret;
|
||||
}
|
||||
if (t_prod->count != ret) {
|
||||
dev_err(dev, "Node %s: prod read failed: exp %d read %d\n",
|
||||
child->name, t_prod->count, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Optimise the prod configuration */
|
||||
if (mask_opt)
|
||||
tegra_prod_optimise_tupple(t_prod->prod_tuple,
|
||||
t_prod->count);
|
||||
|
||||
|
||||
n_child++;
|
||||
}
|
||||
|
||||
tegra_prod->num = n_child;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_prod_set_tuple - Only set a tuple.
|
||||
* @base: base address of the register.
|
||||
* @prod_tuple: the tuple to set.
|
||||
* @new_mask: Mask override value, 0 means use from tupple.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int tegra_prod_set_tuple(void __iomem **base,
|
||||
struct prod_tuple *prod_tuple,
|
||||
u32 new_mask)
|
||||
{
|
||||
u32 reg;
|
||||
u32 mask = (new_mask) ? new_mask : prod_tuple->mask;
|
||||
|
||||
if (!prod_tuple)
|
||||
return -EINVAL;
|
||||
|
||||
reg = readl(base[prod_tuple->index] + prod_tuple->addr);
|
||||
reg = ((reg & ~mask) | (prod_tuple->val & mask));
|
||||
|
||||
writel(reg, base[prod_tuple->index] + prod_tuple->addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_prod_set - Set one prod setting.
|
||||
* @base: base address of the register.
|
||||
* @tegra_prod: the prod setting to set.
|
||||
*
|
||||
* Set all the tuples in one tegra_prod.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int tegra_prod_set(void __iomem **base,
|
||||
struct tegra_prod_config *tegra_prod)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (!tegra_prod)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < tegra_prod->count; i++) {
|
||||
ret = tegra_prod_set_tuple(base, &tegra_prod->prod_tuple[i], 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tegra_prod_set_list - Set all the prod settings of the list in sequence.
|
||||
* @base: base address of the register.
|
||||
* @tegra_prod: the list of tegra prods.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int tegra_prod_set_list(void __iomem **base,
|
||||
struct tegra_prod *tegra_prod)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (!tegra_prod)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < tegra_prod->num; i++) {
|
||||
ret = tegra_prod_set(base, &tegra_prod->prod_config[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_prod_set_list);
|
||||
|
||||
/**
|
||||
* tegra_prod_set_boot_init - Set all the prod settings of the list in sequence
|
||||
* Which are needed for boot initialisation.
|
||||
* @base: base address of the register.
|
||||
* @tegra_prod: the list of tegra prods.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int tegra_prod_set_boot_init(void __iomem **base,
|
||||
struct tegra_prod *tegra_prod)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
if (!tegra_prod)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < tegra_prod->num; i++) {
|
||||
if (!tegra_prod->prod_config[i].boot_init)
|
||||
continue;
|
||||
ret = tegra_prod_set(base, &tegra_prod->prod_config[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_prod_set_boot_init);
|
||||
|
||||
/**
|
||||
* tegra_prod_set_by_name - Set the prod setting according the name.
|
||||
* @base: base address of the register.
|
||||
* @name: the name of tegra prod need to set.
|
||||
* @tegra_prod: the list of tegra prods.
|
||||
*
|
||||
* Find the tegra prod in the list according to the name. Then set
|
||||
* that tegra prod.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int tegra_prod_set_by_name(void __iomem **base, const char *name,
|
||||
struct tegra_prod *tegra_prod)
|
||||
{
|
||||
int i;
|
||||
struct tegra_prod_config *t_prod;
|
||||
|
||||
if (!tegra_prod)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < tegra_prod->num; i++) {
|
||||
t_prod = &tegra_prod->prod_config[i];
|
||||
if (!strcasecmp(t_prod->name, name))
|
||||
return tegra_prod_set(base, t_prod);
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_prod_set_by_name);
|
||||
|
||||
/**
|
||||
* tegra_prod_set_by_name_partially - Set the prod setting from list partially
|
||||
* under given prod name. The matching is done
|
||||
* qith index, offset and mask.
|
||||
* @base: base address of the register.
|
||||
* @name: the name of tegra prod need to set.
|
||||
* @tegra_prod: the list of tegra prods.
|
||||
* @index: Index of base address.
|
||||
* @offset: Offset of the register.
|
||||
* @mask: Mask field on given register.
|
||||
*
|
||||
* Find the tegra prod in the list according to the name. Then set
|
||||
* that tegra prod which has matching of index, offset and mask.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int tegra_prod_set_by_name_partially(void __iomem **base, const char *name,
|
||||
struct tegra_prod *tegra_prod, u32 index,
|
||||
u32 offset, u32 mask)
|
||||
{
|
||||
struct tegra_prod_config *t_prod;
|
||||
int ret;
|
||||
int i;
|
||||
bool found = false;
|
||||
|
||||
if (!tegra_prod)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < tegra_prod->num; i++) {
|
||||
t_prod = &tegra_prod->prod_config[i];
|
||||
if (!strcasecmp(t_prod->name, name)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < t_prod->count; i++) {
|
||||
struct prod_tuple *ptuple = &t_prod->prod_tuple[i];;
|
||||
|
||||
if ((ptuple->index != index) || (ptuple->addr != offset) ||
|
||||
((ptuple->mask & mask) != mask))
|
||||
continue;
|
||||
|
||||
ret = tegra_prod_set_tuple(base, ptuple, mask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_prod_set_by_name_partially);
|
||||
|
||||
bool tegra_prod_by_name_supported(struct tegra_prod *tegra_prod,
|
||||
const char *name)
|
||||
{
|
||||
int i;
|
||||
struct tegra_prod_config *t_prod;
|
||||
|
||||
if (!tegra_prod)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < tegra_prod->num; i++) {
|
||||
t_prod = &tegra_prod->prod_config[i];
|
||||
if (!t_prod)
|
||||
break;
|
||||
|
||||
if (!strcasecmp(t_prod->name, name))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_prod_by_name_supported);
|
||||
|
||||
/**
|
||||
* tegra_prod_init - Init tegra prod list.
|
||||
# @dev: Device handle.
|
||||
* @np: device node from which the property value is to be read.
|
||||
*
|
||||
* Query all the prod settings under DT node & Init the tegra prod list
|
||||
* automatically.
|
||||
*
|
||||
* Returns 0 on success, -EINVAL for wrong prod number, -ENOMEM if failed
|
||||
* to allocate memory for tegra prod list.
|
||||
*/
|
||||
static struct tegra_prod *tegra_prod_init(struct device *dev,
|
||||
const struct device_node *np)
|
||||
{
|
||||
struct tegra_prod *tegra_prod;
|
||||
struct device_node *np_prod;
|
||||
int prod_num = 0;
|
||||
int ret;
|
||||
|
||||
np_prod = of_get_child_by_name(np, "prod-settings");
|
||||
if (!np_prod)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
/* Check whether child is enabled or not */
|
||||
if (!of_device_is_available(np_prod)) {
|
||||
dev_err(dev, "Node %s: Node is not enabled\n", np_prod->name);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
prod_num = of_get_child_count(np_prod);
|
||||
if (prod_num <= 0) {
|
||||
dev_err(dev, "Node %s: No child node for prod settings\n",
|
||||
np_prod->name);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
tegra_prod = devm_kzalloc(dev, sizeof(*tegra_prod), GFP_KERNEL);
|
||||
if (!tegra_prod)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
tegra_prod->prod_config = devm_kcalloc(dev, prod_num,
|
||||
sizeof(*tegra_prod->prod_config),
|
||||
GFP_KERNEL);
|
||||
if (!tegra_prod->prod_config)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
tegra_prod->num = prod_num;
|
||||
|
||||
ret = tegra_prod_parse_dt(dev, np, np_prod, tegra_prod);
|
||||
if (ret) {
|
||||
dev_err(dev, "Node %s: Failed to read the Prod Setting.\n",
|
||||
np->name);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return tegra_prod;
|
||||
}
|
||||
|
||||
static void devm_tegra_prod_release(struct device *dev, void *res)
|
||||
{
|
||||
}
|
||||
|
||||
struct tegra_prod *devm_tegra_prod_get(struct device *dev)
|
||||
{
|
||||
struct tegra_prod **ptr, *prod_list;
|
||||
|
||||
ptr = devres_alloc(devm_tegra_prod_release, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
prod_list = tegra_prod_init(dev, dev->of_node);
|
||||
if (IS_ERR(prod_list)) {
|
||||
devres_free(ptr);
|
||||
return prod_list;
|
||||
}
|
||||
|
||||
*ptr = prod_list;
|
||||
devres_add(dev, ptr);
|
||||
|
||||
return prod_list;
|
||||
}
|
||||
EXPORT_SYMBOL(devm_tegra_prod_get);
|
||||
|
||||
struct tegra_prod *devm_tegra_prod_get_from_node(struct device *dev,
|
||||
struct device_node *np)
|
||||
{
|
||||
struct tegra_prod **ptr, *prod_list;
|
||||
|
||||
ptr = devres_alloc(devm_tegra_prod_release, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
prod_list = tegra_prod_init(dev, np);
|
||||
if (IS_ERR(prod_list)) {
|
||||
devres_free(ptr);
|
||||
return prod_list;
|
||||
}
|
||||
|
||||
*ptr = prod_list;
|
||||
devres_add(dev, ptr);
|
||||
|
||||
return prod_list;
|
||||
}
|
||||
EXPORT_SYMBOL(devm_tegra_prod_get_from_node);
|
||||
126
include/linux/tegra_prod.h
Normal file
126
include/linux/tegra_prod.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _TEGRA_PRODS_H
|
||||
#define _TEGRA_PRODS_H
|
||||
|
||||
struct tegra_prod;
|
||||
|
||||
/**
|
||||
* tegra_prod_set_list(): Set all prods configurations
|
||||
* @base: List of IO mapped registers.
|
||||
* @tegra_prod: tegra_prod handle which is allocated by devm_tegra_prod_get()
|
||||
* or tegra_prod_get_from_node();
|
||||
*
|
||||
* Configure all the prod configuration listed on prod-setting nodes.
|
||||
*
|
||||
* Returns 0 on success otherwise negive error number for failed case.
|
||||
*/
|
||||
int tegra_prod_set_list(void __iomem **base, struct tegra_prod *tegra_prod);
|
||||
|
||||
/**
|
||||
* tegra_prod_set_boot_init(): Set all prods configurations which has boot init
|
||||
* flag on the prod setting nodes.
|
||||
* @base: List of IO mapped registers.
|
||||
* @tegra_prod: tegra_prod handle which is allocated by devm_tegra_prod_get()
|
||||
* or tegra_prod_get_from_node();
|
||||
*
|
||||
* Configure all the prod configuration listed on prod-setting nodes.
|
||||
*
|
||||
* Returns 0 on success otherwise negive error number for failed case.
|
||||
*/
|
||||
int tegra_prod_set_boot_init(void __iomem **base,
|
||||
struct tegra_prod *tegra_prod);
|
||||
|
||||
/**
|
||||
* tegra_prod_set_by_name(): Set prod configuration with specific prod name.
|
||||
* This is used for conditional prod configurations.
|
||||
* @base: List of IO mapped registers.
|
||||
* @name: Name of conditional prod which need to be configure.
|
||||
* @tegra_prod: tegra_prod handle which is allocated by devm_tegra_prod_get()
|
||||
* or tegra_prod_get_from_node();
|
||||
*
|
||||
* Configure prod configuration with specific prod name for conditional
|
||||
* prod configurations.
|
||||
*
|
||||
* Returns 0 on success otherwise negive error number for failed case.
|
||||
*/
|
||||
int tegra_prod_set_by_name(void __iomem **base, const char *name,
|
||||
struct tegra_prod *tegra_prod);
|
||||
|
||||
/**
|
||||
* tegra_prod_set_by_name_partially - Set the prod setting from list partially
|
||||
* under given prod name. The matching is done
|
||||
* qith index, offset and mask.
|
||||
* @base: base address of the register.
|
||||
* @name: the name of tegra prod need to set.
|
||||
* @tegra_prod: the list of tegra prods.
|
||||
* @index: Index of base address.
|
||||
* @offset: Offset of the register.
|
||||
* @mask: Mask field on given register.
|
||||
*
|
||||
* Find the tegra prod in the list according to the name. Then set
|
||||
* that tegra prod which has matching of index, offset and mask.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int tegra_prod_set_by_name_partially(void __iomem **base, const char *name,
|
||||
struct tegra_prod *tegra_prod, u32 index,
|
||||
u32 offset, u32 mask);
|
||||
|
||||
/**
|
||||
* tegra_prod_by_name_supported - Tell whether tegra prod will be supported by
|
||||
* given name or not.
|
||||
* @tegra_prod: the list of tegra prods.
|
||||
* @name: the name of tegra prod need to set.
|
||||
*
|
||||
* Find the tegra prod in the list according to the name. If it exist then
|
||||
* return true else false.
|
||||
*/
|
||||
bool tegra_prod_by_name_supported(struct tegra_prod *tegra_prod,
|
||||
const char *name);
|
||||
|
||||
/**
|
||||
* devm_tegra_prod_get(): Get the prod handle from the device.
|
||||
* @dev: Device handle on which prod setting nodes are available.
|
||||
*
|
||||
* Parse the prod-setting node of the dev->of_node and keep all prod
|
||||
* setting data in prod handle.
|
||||
* This handle is used for setting prod configurations.
|
||||
*
|
||||
* Returns valid prod_list handle on success or pointer to the error
|
||||
* when it failed.
|
||||
*/
|
||||
struct tegra_prod *devm_tegra_prod_get(struct device *dev);
|
||||
|
||||
/**
|
||||
* devm_tegra_prod_get_from_node(): Get the prod handle from the node.
|
||||
* @dev: Device handle.
|
||||
* @np: Node pointer on which prod setting nodes are available.
|
||||
*
|
||||
* Parse the prod-setting node of the node pointer "np" and keep all prod
|
||||
* setting data in prod handle.
|
||||
* This handle is used for setting prod configurations.
|
||||
*
|
||||
* Returns valid prod_list handle on success or pointer to the error
|
||||
* when it failed.
|
||||
* The allocated resource is released by driver core framework when device
|
||||
* is unbinded and so no need to call any release APIs for the tegra_prod
|
||||
* handle.
|
||||
*/
|
||||
struct tegra_prod *devm_tegra_prod_get_from_node(struct device *dev,
|
||||
struct device_node *np);
|
||||
#endif
|
||||
Reference in New Issue
Block a user