mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-23 01:31:30 +03:00
* Fixed __user and __iomem pointer types. * Added __poll_t for poll() return type if possible. * Fixed error return from poll(). * Fixed init/shutdown in mods_dma. * Declared internal unit functions as static. * Assign NULL to init pointers instead of 0. Bug 3528414 Change-Id: If39a14a429e2cda5d58edb6ffd60fd13693371e6 Signed-off-by: Chris Dragan <kdragan@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2688314 Reviewed-by: svcacv <svcacv@nvidia.com> Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com> Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com> Reviewed-by: Sachin Nikam <snikam@nvidia.com> GVS: Gerrit_Virtual_Submit
771 lines
21 KiB
C
771 lines
21 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* This file is part of NVIDIA MODS kernel driver.
|
|
*
|
|
* Copyright (c) 2017-2022, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* NVIDIA MODS kernel driver 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.
|
|
*
|
|
* NVIDIA MODS kernel driver is distributed in the hope that 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 NVIDIA MODS kernel driver.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "mods_internal.h"
|
|
#include <linux/err.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/tegra_prod.h>
|
|
|
|
|
|
#define MAX_REG_INFO_ENTRY 400
|
|
#define MAX_IO_MAP_ENTRY 200
|
|
|
|
static struct device *mods_tegra_prod_dev;
|
|
|
|
/**
|
|
* mods_tegra_prod_init - Initialize tegra prod module.
|
|
* @misc_dev: pointer to mods device
|
|
*
|
|
* Returns 0 on success, others for error.
|
|
*/
|
|
int mods_tegra_prod_init(const struct miscdevice *misc_dev)
|
|
{
|
|
if (!misc_dev) {
|
|
mods_error_printk("mods device pointer is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mods_tegra_prod_dev = misc_dev->this_device;
|
|
if (!mods_tegra_prod_dev) {
|
|
mods_error_printk("this_device in mods device is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/**
|
|
* esc_mods_tegra_prod_iterate_dt - Find device_node by device name.
|
|
* @MODS_TEGRA_PROD_ITERATOR: Input information, only the following
|
|
* members are used.
|
|
* +device_handle: Last node pointer for current iteration
|
|
* +name: Current node name
|
|
* +next_name: Next node name
|
|
* +index: Index of the device nodes shared the same name
|
|
* (NOTE: index starts from 0)
|
|
* +is_leaf: Is the node with current name a leaf node
|
|
* +next_device_handle: Result node pointer for next iteration
|
|
* Returns 0 on success, others for error or no matching node found.
|
|
*/
|
|
int esc_mods_tegra_prod_iterate_dt(
|
|
struct mods_client *client,
|
|
struct MODS_TEGRA_PROD_ITERATOR *iterator
|
|
)
|
|
{
|
|
struct device_node *dev_node;
|
|
struct device_node *child_node = NULL;
|
|
struct resource res;
|
|
int ret_resource = -1;
|
|
int i;
|
|
|
|
dev_node = (struct device_node *)iterator->device_handle;
|
|
|
|
if (!iterator->name[0]) {
|
|
cl_error("node name is missing for tegra prod value\n");
|
|
return -EINVAL;
|
|
}
|
|
if (!iterator->next_name[0] && !iterator->is_leaf) {
|
|
cl_error("inner node with empty next_name\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Need to search several times since some nodes may share same name */
|
|
for (i = 0; i <= iterator->index; ++i) {
|
|
/* Search from Left to Right in DT */
|
|
dev_node = of_find_node_by_name(dev_node, iterator->name);
|
|
if (!dev_node) {
|
|
cl_error("node %s not found in device tree\n",
|
|
iterator->name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Search from Top to Bottom in DT
|
|
* Check: does this node need to be filtered out
|
|
*/
|
|
if (iterator->is_leaf) {
|
|
/* Leaf node : search for "prod-settings" node */
|
|
child_node = of_get_child_by_name(dev_node,
|
|
"prod-settings");
|
|
ret_resource = of_address_to_resource(
|
|
dev_node, 0, &res);
|
|
} else {
|
|
/* Inner node : search for next device node */
|
|
child_node = of_get_child_by_name(dev_node,
|
|
iterator->next_name);
|
|
ret_resource = -1;
|
|
}
|
|
if (!child_node && ret_resource < 0)
|
|
--i; /* Not a valid device node */
|
|
}
|
|
|
|
/* Return next_device_handler to ioctl caller */
|
|
iterator->next_device_handle = (__u64)dev_node;
|
|
return OK;
|
|
}
|
|
|
|
/**
|
|
* mods_read_reg_info - Read register information (address and size)
|
|
* @dev_node: Device node pointer
|
|
* @reg_info: Register information array
|
|
* @count_reg_cells: (OUT) Pointer to count of cells in "reg"
|
|
* @count_addrsize_pair: (OUT) Pointer to count of each pair in "reg"
|
|
* @count_address_cells: (OUT) Pointer to count of "address" cell in "reg"
|
|
* @count_size_cells: (OUT) Pointer to count of "size" cell in "reg"
|
|
*
|
|
* Return Value :
|
|
* 0 - OK
|
|
* Others - ERROR
|
|
*
|
|
*/
|
|
static int mods_read_reg_info(struct mods_client *client,
|
|
const struct device_node *dev_node,
|
|
__u32 *reg_info,
|
|
__u32 *count_reg_cells,
|
|
__u32 *count_addrsize_pair,
|
|
__u32 *count_address_cells,
|
|
__u32 *count_size_cells)
|
|
{
|
|
struct device_node *parent_node;
|
|
int ret;
|
|
|
|
/* Get parent node to read the format of "reg" property */
|
|
parent_node = dev_node->parent;
|
|
ret = of_property_read_u32(parent_node, "#address-cells",
|
|
count_address_cells);
|
|
if (ret < 0) {
|
|
cl_error("read #address-cells failed\n");
|
|
return ret;
|
|
|
|
} else if (*count_address_cells == 0) {
|
|
cl_error("#address-cells cannot be 0\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = of_property_read_u32(parent_node, "#size-cells",
|
|
count_size_cells);
|
|
if (ret < 0) {
|
|
cl_error("read #size-cells failed\n");
|
|
return ret;
|
|
|
|
} else if (*count_size_cells == 0) {
|
|
cl_error("#size-cells cannot be 0\n");
|
|
return -EINVAL;
|
|
}
|
|
*count_addrsize_pair = *count_address_cells + *count_size_cells;
|
|
|
|
/* Read count of cells in "reg" property */
|
|
ret = of_property_count_u32_elems(dev_node, "reg");
|
|
if (ret < 0) {
|
|
cl_error(
|
|
"unable to get count of cells in \"reg\" of node %s\n",
|
|
dev_node->name);
|
|
return ret;
|
|
}
|
|
|
|
*count_reg_cells = (__u32)ret;
|
|
if (*count_reg_cells == 0) {
|
|
cl_error("no \"reg\" info is available\n");
|
|
return -EINVAL;
|
|
|
|
} else if (*count_reg_cells % *count_addrsize_pair != 0) {
|
|
/*
|
|
* "reg" property may have more than 1 pairs of address
|
|
* and size. The length of each pair is represented by
|
|
* "count_addrsize_pair"; the total length of "reg"
|
|
* property is represented by "count_reg_cells". If
|
|
* "count_reg_cells" is not an integral multiple of
|
|
* "count_addrsize_pair", the read "reg" information is
|
|
* incomplete or incorrect.
|
|
*/
|
|
cl_error("\"reg\" property has invalid length : %d\n",
|
|
*count_reg_cells);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Read register information from "reg" property */
|
|
ret = of_property_read_u32_array(dev_node, "reg", reg_info,
|
|
(size_t)(*count_reg_cells));
|
|
if (ret < 0) {
|
|
cl_error("Unable to read \"reg\" property of node %s\n",
|
|
dev_node->name);
|
|
return ret;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/**
|
|
* mods_batch_iounmap - Unmap all virtual memory mapped by IO address.
|
|
* @io_base: IO address array
|
|
* @count_io_base: Count of IO addresses are mapped
|
|
*
|
|
*/
|
|
static void mods_batch_iounmap(struct mods_client *client,
|
|
void __iomem **io_base,
|
|
__u32 count_io_base)
|
|
{
|
|
int i;
|
|
|
|
if (!io_base) {
|
|
cl_error("IO base address array is NULL\n");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < count_io_base; ++i)
|
|
iounmap(io_base[i]);
|
|
}
|
|
|
|
/**
|
|
* mods_batch_iomap - Get all segments of IO address from a device node,
|
|
* and create a virtual memory mapping.
|
|
* @dev_node: Device node pointer
|
|
* @io_base: (OUT) IO address array
|
|
* @count_io_base: (OUT) Pointer to the count of IO addresses are mapped
|
|
*
|
|
* Return Value :
|
|
* 0 - OK
|
|
* Others - ERROR (NOTE: partially mapped IO memory will be unmapped on error)
|
|
*
|
|
*/
|
|
static int mods_batch_iomap(struct mods_client *client,
|
|
const struct device_node *dev_node,
|
|
void __iomem **io_base,
|
|
__u32 *count_io_base)
|
|
{
|
|
__u32 reg_info[MAX_REG_INFO_ENTRY];
|
|
__u32 count_reg_cells = 0;
|
|
__u32 count_addrsize_pair, count_address_cells, count_size_cells;
|
|
__u64 io_addr_base, io_addr_length;
|
|
void __iomem *io_addr_mapped;
|
|
int ret;
|
|
int i;
|
|
|
|
/* Check arguments */
|
|
if (!dev_node) {
|
|
cl_error("controller device handle is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
if (!io_base) {
|
|
cl_error("IO base address array is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
if (!count_io_base) {
|
|
cl_error("count of IO base address array is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Get registers information */
|
|
ret = mods_read_reg_info(client,
|
|
dev_node,
|
|
reg_info,
|
|
&count_reg_cells,
|
|
&count_addrsize_pair,
|
|
&count_address_cells,
|
|
&count_size_cells);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* IO mapping - using 64-bit physical address */
|
|
*count_io_base = 0;
|
|
for (i = 0; i < count_reg_cells;
|
|
i += count_addrsize_pair, *count_io_base += 1) {
|
|
|
|
/* Compute IO physical address */
|
|
/* Lower 32-bit IO physical address in DTB */
|
|
io_addr_base = reg_info[i + count_address_cells - 1];
|
|
if (count_address_cells > 1) {
|
|
/* Higher 32-bit IO physical address in DTB */
|
|
io_addr_base += ((__u64)reg_info
|
|
[i + count_address_cells - 2]) << 32;
|
|
}
|
|
|
|
/* Compute IO address space length */
|
|
/* Lower 32-bit IO address space length in DTB */
|
|
io_addr_length = reg_info[i + count_addrsize_pair - 1];
|
|
if (count_size_cells > 1) {
|
|
/* Higher 32-bit IO address space length in DTB */
|
|
io_addr_length += ((__u64)reg_info
|
|
[i + count_addrsize_pair - 2]) << 32;
|
|
}
|
|
|
|
/* Map physical address to virtual address space */
|
|
io_addr_mapped = ioremap((resource_size_t)io_addr_base,
|
|
(resource_size_t)io_addr_length);
|
|
if (io_addr_mapped == NULL) {
|
|
cl_error(
|
|
"Unable to map io address 0x%llx, length 0x%llx\n",
|
|
io_addr_base,
|
|
io_addr_length);
|
|
/* Clean :
|
|
* Unmap the IO memory that have already been mapped
|
|
*/
|
|
mods_batch_iounmap(client, io_base, *count_io_base);
|
|
return -ENXIO;
|
|
}
|
|
io_base[*count_io_base] = io_addr_mapped;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
|
|
/**
|
|
* mods_tegra_get_prod_list - Get tegra_prod list by valid device node.
|
|
* @dev_node: Device node.
|
|
*
|
|
* Returns non-NULL on success, NULL on error.
|
|
*/
|
|
static struct tegra_prod *mods_tegra_get_prod_list(struct mods_client *client,
|
|
struct device_node *dev_node)
|
|
{
|
|
struct tegra_prod *prod_list;
|
|
|
|
if (!mods_tegra_prod_dev) {
|
|
cl_error("tegra prod is not initialized\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (!dev_node) {
|
|
cl_error("device node is NULL\n");
|
|
return NULL;
|
|
}
|
|
|
|
prod_list = devm_tegra_prod_get_from_node(mods_tegra_prod_dev,
|
|
dev_node);
|
|
if (IS_ERR(prod_list)) {
|
|
cl_error("failed to get prod_list : %s\n",
|
|
dev_node->name);
|
|
return NULL;
|
|
}
|
|
|
|
return prod_list;
|
|
}
|
|
|
|
/**
|
|
* mods_tegra_get_prod_info - Get tegra_prod_list & mapped IO addresses
|
|
* @MODS_TEGRA_PROD_SET_TUPLE: Input information, only the following
|
|
* members are used.
|
|
* +prod_node_handle: Handle of device node contained prod values.
|
|
* +ctrl_node_handle: Handle of controller device node.
|
|
* @tegra_prod_list: (OUT) Pointer to tegra_prod_list.
|
|
* @ctrl_base: (OUT) Array of mapped IO address.
|
|
* @count_ctrl_base: (OUT) Pointer to count of mapped IO address.
|
|
*
|
|
* Return Value :
|
|
* 0 - OK
|
|
* Others - ERROR
|
|
*
|
|
*/
|
|
static int mods_tegra_get_prod_info(
|
|
struct mods_client *client,
|
|
const struct MODS_TEGRA_PROD_SET_TUPLE *tuple,
|
|
struct tegra_prod **tegra_prod_list,
|
|
void __iomem **ctrl_base,
|
|
__u32 *count_ctrl_base)
|
|
{
|
|
struct device_node *prod_node, *ctrl_node;
|
|
|
|
if (!tegra_prod_list || !ctrl_base || !count_ctrl_base) {
|
|
cl_error("detected NULL pointer for out value.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
prod_node = (struct device_node *)tuple->prod_dev_handle;
|
|
if (!prod_node) {
|
|
cl_error("prod device handle is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
*tegra_prod_list = mods_tegra_get_prod_list(client, prod_node);
|
|
if (!(*tegra_prod_list)) {
|
|
cl_error("failed to get prod_list with prod handle 0x%llx\n",
|
|
tuple->prod_dev_handle);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ctrl_node = (struct device_node *)tuple->ctrl_dev_handle;
|
|
if (!ctrl_node) {
|
|
cl_error("controller device handle is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return mods_batch_iomap(client, ctrl_node, ctrl_base, count_ctrl_base);
|
|
}
|
|
|
|
/**
|
|
* esc_mods_tegra_prod_is_supported - Test the prod is supported by
|
|
* given prod name.
|
|
* @MODS_TEGRA_PROD_IS_SUPPORTED: Input information, only the following
|
|
members are used.
|
|
* +prod_node_handle: Handle of device node contained prod values.
|
|
* +prod_name: Prod name.
|
|
* +is_supported: Result of supporting given prod configuration.
|
|
*
|
|
* Return Value :
|
|
* 0 - OK
|
|
* Others - ERROR
|
|
*
|
|
*/
|
|
int esc_mods_tegra_prod_is_supported(
|
|
struct mods_client *client,
|
|
struct MODS_TEGRA_PROD_IS_SUPPORTED *tuple
|
|
)
|
|
{
|
|
struct device_node *prod_node;
|
|
struct tegra_prod *tegra_prod;
|
|
bool is_supported;
|
|
|
|
prod_node = (struct device_node *)tuple->prod_dev_handle;
|
|
if (!prod_node) {
|
|
cl_error("prod device handle is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
tegra_prod = mods_tegra_get_prod_list(client, prod_node);
|
|
if (!tegra_prod) {
|
|
cl_error("failed to get prod_list with prod handle 0x%llx\n",
|
|
tuple->prod_dev_handle);
|
|
return -EINVAL;
|
|
}
|
|
|
|
is_supported = tegra_prod_by_name_supported(tegra_prod,
|
|
tuple->prod_name);
|
|
tuple->is_supported = is_supported ? 1 : 0;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/**
|
|
* esc_mods_tegra_prod_set_prod_all - Read prod values from prod device node,
|
|
* and set prod values to controller device
|
|
* node.
|
|
* @MODS_TEGRA_PROD_SET_TUPLE: Input information, only the following
|
|
* members are used.
|
|
* +prod_node_handle: Handle of device node contained prod values.
|
|
* +ctrl_node_handle: Handle of controller device node.
|
|
*
|
|
* Return Value :
|
|
* 0 - OK
|
|
* Others - ERROR
|
|
*
|
|
*/
|
|
int esc_mods_tegra_prod_set_prod_all(
|
|
struct mods_client *client,
|
|
struct MODS_TEGRA_PROD_SET_TUPLE *tuple
|
|
)
|
|
{
|
|
int ret;
|
|
struct tegra_prod *tegra_prod_list;
|
|
void __iomem *ctrl_base[MAX_IO_MAP_ENTRY];
|
|
__u32 count_ctrl_base;
|
|
|
|
ret = mods_tegra_get_prod_info(client,
|
|
tuple,
|
|
&tegra_prod_list,
|
|
ctrl_base,
|
|
&count_ctrl_base);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = tegra_prod_set_list(ctrl_base, tegra_prod_list);
|
|
if (ret < 0)
|
|
cl_error("set prod failed\n");
|
|
|
|
mods_batch_iounmap(client, ctrl_base, count_ctrl_base);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* esc_mods_tegra_prod_set_prod_boot - Read prod values from prod device
|
|
* node, and set prod values to
|
|
* controller device node, which are
|
|
* required for boot initialization.
|
|
* @MODS_TEGRA_PROD_SET_TUPLE: Input information, only the following
|
|
* members are used.
|
|
* +prod_node_handle: Handle of device node contained prod values.
|
|
* +ctrl_node_handle: Handle of controller device node.
|
|
*
|
|
* Return Value :
|
|
* 0 - OK
|
|
* Others - ERROR
|
|
*
|
|
*/
|
|
int esc_mods_tegra_prod_set_prod_boot(
|
|
struct mods_client *client,
|
|
struct MODS_TEGRA_PROD_SET_TUPLE *tuple
|
|
)
|
|
{
|
|
int ret;
|
|
struct tegra_prod *tegra_prod_list;
|
|
void __iomem *ctrl_base[MAX_IO_MAP_ENTRY];
|
|
__u32 count_ctrl_base;
|
|
|
|
ret = mods_tegra_get_prod_info(client,
|
|
tuple,
|
|
&tegra_prod_list,
|
|
ctrl_base,
|
|
&count_ctrl_base);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = tegra_prod_set_boot_init(ctrl_base, tegra_prod_list);
|
|
if (ret < 0)
|
|
cl_error("set boot init prod failed\n");
|
|
|
|
mods_batch_iounmap(client, ctrl_base, count_ctrl_base);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* esc_mods_tegra_prod_set_prod_by_name - Read prod values from prod
|
|
* device node, and set prod values
|
|
* to controller device node based
|
|
* on prod name.
|
|
* @MODS_TEGRA_PROD_SET_TUPLE: Input information, only the following
|
|
* members are used.
|
|
* +prod_node_handle: Handle of device node contained prod values.
|
|
* +ctrl_node_handle: Handle of controller device node.
|
|
* +prod_name: Prod name.
|
|
*
|
|
* Return Value :
|
|
* 0 - OK
|
|
* Others - ERROR
|
|
*
|
|
*/
|
|
int esc_mods_tegra_prod_set_prod_by_name(
|
|
struct mods_client *client,
|
|
struct MODS_TEGRA_PROD_SET_TUPLE *tuple
|
|
)
|
|
{
|
|
int ret;
|
|
struct tegra_prod *tegra_prod_list;
|
|
void __iomem *ctrl_base[MAX_IO_MAP_ENTRY];
|
|
__u32 count_ctrl_base;
|
|
|
|
ret = mods_tegra_get_prod_info(client,
|
|
tuple,
|
|
&tegra_prod_list,
|
|
ctrl_base,
|
|
&count_ctrl_base);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = tegra_prod_set_by_name(ctrl_base, tuple->prod_name,
|
|
tegra_prod_list);
|
|
if (ret < 0) {
|
|
cl_error("set prod by name \"%s\" failed\n",
|
|
tuple->prod_name);
|
|
}
|
|
|
|
mods_batch_iounmap(client, ctrl_base, count_ctrl_base);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* esc_mods_tegra_prod_set_prod_exact - Read prod values from prod device
|
|
* node, and set prod values to
|
|
* controller device node based
|
|
* on prod name, index, offset, and mask.
|
|
* @MODS_TEGRA_PROD_SET_TUPLE: Input information, only the following
|
|
members are used.
|
|
* +prod_node_handle: Handle of device node contained prod values.
|
|
* +ctrl_node_handle: Handle of controller device node.
|
|
* +prod_name: Prod name.
|
|
* +index: Index of base address.
|
|
* +offset: Offset of the register.
|
|
* +mask: Mask field on given register.
|
|
*
|
|
* Return Value :
|
|
* 0 - OK
|
|
* Others - ERROR
|
|
*
|
|
*/
|
|
int esc_mods_tegra_prod_set_prod_exact(
|
|
struct mods_client *client,
|
|
struct MODS_TEGRA_PROD_SET_TUPLE *tuple
|
|
)
|
|
{
|
|
int ret;
|
|
struct tegra_prod *tegra_prod_list;
|
|
void __iomem *ctrl_base[MAX_IO_MAP_ENTRY];
|
|
__u32 count_ctrl_base;
|
|
|
|
ret = mods_tegra_get_prod_info(client,
|
|
tuple,
|
|
&tegra_prod_list,
|
|
ctrl_base,
|
|
&count_ctrl_base);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = tegra_prod_set_by_name_partially(ctrl_base, tuple->prod_name,
|
|
tegra_prod_list, tuple->index, tuple->offset,
|
|
tuple->mask);
|
|
if (ret < 0) {
|
|
cl_error("set prod exact by name \"%s\" failed\n",
|
|
tuple->prod_name);
|
|
cl_error("index [%x]; offset [%x]; mask [%x]\n",
|
|
tuple->index, tuple->offset, tuple->mask);
|
|
}
|
|
|
|
mods_batch_iounmap(client, ctrl_base, count_ctrl_base);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
#ifdef MODS_ENABLE_BPMP_MRQ_API
|
|
static int tegra_pcie_bpmp_set_ctrl_state(struct mods_smmu_dev *pcie_dev,
|
|
bool enable)
|
|
{
|
|
struct mrq_uphy_response resp;
|
|
struct tegra_bpmp_message msg;
|
|
struct mrq_uphy_request req;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
memset(&resp, 0, sizeof(resp));
|
|
|
|
req.cmd = CMD_UPHY_PCIE_CONTROLLER_STATE;
|
|
req.controller_state.pcie_controller = pcie_dev->cid;
|
|
req.controller_state.enable = enable;
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.mrq = MRQ_UPHY;
|
|
msg.tx.data = &req;
|
|
msg.tx.size = sizeof(req);
|
|
msg.rx.data = &resp;
|
|
msg.rx.size = sizeof(resp);
|
|
|
|
return tegra_bpmp_transfer(pcie_dev->bpmp, &msg);
|
|
}
|
|
|
|
static int uphy_bpmp_pcie_controller_state_set(int controller, int enable)
|
|
{
|
|
#define MAX_DEV_NAME_LEN 32
|
|
char dev_name[MAX_DEV_NAME_LEN];
|
|
struct mods_smmu_dev *smmu_pdev = NULL;
|
|
int smmudev_idx, n;
|
|
|
|
memset(dev_name, 0, MAX_DEV_NAME_LEN);
|
|
n = snprintf(dev_name, MAX_DEV_NAME_LEN, "mods_pcie%d", controller);
|
|
if (n < 0 || n >= MAX_DEV_NAME_LEN)
|
|
return -EINVAL;
|
|
smmudev_idx = get_mods_smmu_device_index(dev_name);
|
|
if (smmudev_idx >= 0)
|
|
smmu_pdev = get_mods_smmu_device(smmudev_idx);
|
|
if (!smmu_pdev || smmudev_idx < 0) {
|
|
mods_error_printk("smmu device %s is not found\n", dev_name);
|
|
return -ENODEV;
|
|
}
|
|
smmu_pdev->cid = controller;
|
|
return tegra_pcie_bpmp_set_ctrl_state(smmu_pdev, enable);
|
|
}
|
|
#else
|
|
|
|
static int uphy_bpmp_pcie_controller_state_set(int controller, int enable)
|
|
{
|
|
mods_error_printk("bpmp mrq api is not supported\n");
|
|
return -ENODEV;
|
|
}
|
|
#endif
|
|
|
|
int esc_mods_bpmp_set_pcie_state(
|
|
struct mods_client *client,
|
|
struct MODS_SET_PCIE_STATE *p
|
|
)
|
|
{
|
|
|
|
return uphy_bpmp_pcie_controller_state_set(p->controller, p->enable);
|
|
}
|
|
|
|
#ifdef MODS_ENABLE_BPMP_MRQ_API
|
|
static int tegra_pcie_bpmp_set_pll_state(struct mods_smmu_dev *pcie_dev,
|
|
bool enable)
|
|
{
|
|
struct mrq_uphy_response resp;
|
|
struct tegra_bpmp_message msg;
|
|
struct mrq_uphy_request req;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
memset(&resp, 0, sizeof(resp));
|
|
|
|
if (enable) {
|
|
req.cmd = CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT;
|
|
req.ep_ctrlr_pll_init.ep_controller = pcie_dev->cid;
|
|
} else {
|
|
req.cmd = CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF;
|
|
req.ep_ctrlr_pll_off.ep_controller = pcie_dev->cid;
|
|
}
|
|
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.mrq = MRQ_UPHY;
|
|
msg.tx.data = &req;
|
|
msg.tx.size = sizeof(req);
|
|
msg.rx.data = &resp;
|
|
msg.rx.size = sizeof(resp);
|
|
|
|
return tegra_bpmp_transfer(pcie_dev->bpmp, &msg);
|
|
}
|
|
|
|
static int uphy_bpmp_pcie_set_pll_state(int controller, int enable)
|
|
{
|
|
#define MAX_DEV_NAME_LEN 32
|
|
char dev_name[MAX_DEV_NAME_LEN];
|
|
struct mods_smmu_dev *smmu_pdev = NULL;
|
|
int smmudev_idx, n;
|
|
|
|
memset(dev_name, 0, MAX_DEV_NAME_LEN);
|
|
n = snprintf(dev_name, MAX_DEV_NAME_LEN, "mods_pcie%d", controller);
|
|
if (n < 0 || n >= MAX_DEV_NAME_LEN)
|
|
return -EINVAL;
|
|
smmudev_idx = get_mods_smmu_device_index(dev_name);
|
|
if (smmudev_idx >= 0)
|
|
smmu_pdev = get_mods_smmu_device(smmudev_idx);
|
|
if (!smmu_pdev || smmudev_idx < 0) {
|
|
mods_error_printk("smmu device %s is not found\n", dev_name);
|
|
return -ENODEV;
|
|
}
|
|
smmu_pdev->cid = controller;
|
|
return tegra_pcie_bpmp_set_pll_state(smmu_pdev, enable);
|
|
}
|
|
#else
|
|
|
|
static int uphy_bpmp_pcie_set_pll_state(int controller, int enable)
|
|
{
|
|
mods_error_printk("bpmp mrq api is not supported\n");
|
|
return -ENODEV;
|
|
}
|
|
#endif
|
|
|
|
int esc_mods_bpmp_init_pcie_ep_pll(
|
|
struct mods_client *client,
|
|
struct MODS_INIT_PCIE_EP_PLL *p
|
|
)
|
|
{
|
|
return uphy_bpmp_pcie_set_pll_state(p->ep_id, 1);
|
|
}
|