/* * mods_tegraprod.c - This file is part of NVIDIA MODS kernel driver. * * Copyright (c) 2017, 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 . */ #include "mods_internal.h" #include #include #include #include #include #include #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 file *fp, 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]) { mods_error_printk( "node name is missing for tegra prod value\n"); return -EINVAL; } if (!iterator->next_name[0] && !iterator->is_leaf) { mods_error_printk("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) { mods_error_printk("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( 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) { mods_error_printk("Read #address-cells failed\n"); return ret; } else if (*count_address_cells == 0) { mods_error_printk("#address-cells cannot be 0\n"); return -EINVAL; } ret = of_property_read_u32(parent_node, "#size-cells", count_size_cells); if (ret < 0) { mods_error_printk("Read #size-cells failed\n"); return ret; } else if (*count_size_cells == 0) { mods_error_printk("#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) { mods_error_printk( "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) { mods_error_printk("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. */ mods_error_printk( "\"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) { mods_error_printk( "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( void __iomem **io_base, __u32 count_io_base ) { int i; if (!io_base) { mods_error_printk("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( 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) { mods_error_printk("Controller device handle is NULL\n"); return -EINVAL; } if (!io_base) { mods_error_printk("IO base address array is NULL\n"); return -EINVAL; } if (!count_io_base) { mods_error_printk("Count of IO base address array is NULL\n"); return -EINVAL; } /* Get registers information */ ret = mods_read_reg_info( 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) { mods_error_printk( "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(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 device_node *dev_node) { struct tegra_prod *prod_list; if (!mods_tegra_prod_dev) { mods_error_printk("tegra prod is not initialized\n"); return NULL; } if (!dev_node) { mods_error_printk("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)) { mods_error_printk("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( 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) { mods_error_printk("Detected NULL pointer for out value."); return -EINVAL; } prod_node = (struct device_node *)tuple->prod_dev_handle; if (!prod_node) { mods_error_printk("Prod device handle is NULL\n"); return -EINVAL; } *tegra_prod_list = mods_tegra_get_prod_list(prod_node); if (!(*tegra_prod_list)) { mods_error_printk( "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) { mods_error_printk("Controller device handle is NULL\n"); return -EINVAL; } return mods_batch_iomap(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 file *fp, 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) { mods_error_printk("Prod device handle is NULL\n"); return -EINVAL; } tegra_prod = mods_tegra_get_prod_list(prod_node); if (!tegra_prod) { mods_error_printk( "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 file *fp, 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(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) mods_error_printk("Set prod failed\n"); mods_batch_iounmap(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 file *fp, 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(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) mods_error_printk("Set boot init prod failed\n"); mods_batch_iounmap(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 file *fp, 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(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) { mods_error_printk("Set prod by name \"%s\" failed\n", tuple->prod_name); } mods_batch_iounmap(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 file *fp, 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(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) { mods_error_printk("Set prod exact by name \"%s\" failed\n", tuple->prod_name); mods_error_printk("index [%x]; offset [%x]; mask [%x]\n", tuple->index, tuple->offset, tuple->mask); } mods_batch_iounmap(ctrl_base, count_ctrl_base); return ret; }