blob: 52d13848d7bc8628127f88b32a61e65691f99977 [file] [log] [blame]
/*
* arch/arm/mach-tegra/edp.c
*
* Copyright (C) 2011-2013, NVIDIA CORPORATION. All Rights Reserved.
*
* 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.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307, USA
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/edp.h>
#include <mach/edp.h>
#include "fuse.h"
#include "dvfs.h"
#include "clock.h"
#include "cpu-tegra.h"
#define FREQ_STEP 12750000
#define OVERRIDE_DEFAULT 6000
static struct tegra_edp_limits *edp_limits;
static int edp_limits_size;
static unsigned int regulator_cur;
/* Value to subtract from regulator current limit */
static unsigned int edp_reg_override_mA = OVERRIDE_DEFAULT;
static const unsigned int *system_edp_limits;
static struct tegra_system_edp_entry *power_edp_limits;
static int power_edp_limits_size;
/*
* Temperature step size cannot be less than 4C because of hysteresis
* delta
* Code assumes different temperatures for the same speedo_id /
* regulator_cur are adjacent in the table, and higest regulator_cur
* comes first
*/
static char __initdata tegra_edp_vdd_cpu_map[] = {
0x00, 0x2f, 0x17, 0x7d, 0x73, 0x73, 0x73, 0x00,
0x2f, 0x2d, 0x82, 0x78, 0x78, 0x78, 0x00, 0x2f,
0x3c, 0x82, 0x78, 0x78, 0x78, 0x00, 0x2f, 0x4b,
0x82, 0x78, 0x78, 0x78, 0x00, 0x2f, 0x55, 0x82,
0x78, 0x78, 0x78, 0x00, 0x28, 0x17, 0x7d, 0x73,
0x73, 0x73, 0x00, 0x28, 0x2d, 0x82, 0x78, 0x78,
0x78, 0x00, 0x28, 0x3c, 0x82, 0x78, 0x78, 0x78,
0x00, 0x28, 0x4b, 0x82, 0x78, 0x78, 0x73, 0x00,
0x28, 0x55, 0x82, 0x78, 0x78, 0x69, 0x00, 0x23,
0x17, 0x7d, 0x73, 0x73, 0x73, 0x00, 0x23, 0x2d,
0x82, 0x78, 0x78, 0x78, 0x00, 0x23, 0x3c, 0x82,
0x78, 0x78, 0x6e, 0x00, 0x23, 0x4b, 0x82, 0x78,
0x78, 0x64, 0x00, 0x23, 0x55, 0x82, 0x78, 0x6e,
0x5a, 0x00, 0x1e, 0x17, 0x7d, 0x73, 0x73, 0x64,
0x00, 0x1e, 0x2d, 0x82, 0x78, 0x78, 0x69, 0x00,
0x1e, 0x3c, 0x82, 0x78, 0x78, 0x64, 0x00, 0x1e,
0x4b, 0x82, 0x78, 0x6e, 0x5a, 0x00, 0x1e, 0x55,
0x82, 0x78, 0x64, 0x50, 0x00, 0x19, 0x17, 0x7d,
0x73, 0x69, 0x55, 0x00, 0x19, 0x2d, 0x82, 0x78,
0x6e, 0x5a, 0x00, 0x19, 0x3c, 0x82, 0x78, 0x69,
0x55, 0x00, 0x19, 0x4b, 0x82, 0x78, 0x5f, 0x4b,
0x00, 0x19, 0x55, 0x82, 0x73, 0x55, 0x3c, 0x01,
0x2f, 0x17, 0x7d, 0x73, 0x73, 0x73, 0x01, 0x2f,
0x2d, 0x82, 0x78, 0x78, 0x78, 0x01, 0x2f, 0x3c,
0x82, 0x78, 0x78, 0x78, 0x01, 0x2f, 0x4b, 0x82,
0x78, 0x78, 0x78, 0x01, 0x2f, 0x55, 0x82, 0x78,
0x78, 0x78, 0x01, 0x28, 0x17, 0x7d, 0x73, 0x73,
0x73, 0x01, 0x28, 0x2d, 0x82, 0x78, 0x78, 0x78,
0x01, 0x28, 0x3c, 0x82, 0x78, 0x78, 0x78, 0x01,
0x28, 0x4b, 0x82, 0x78, 0x78, 0x73, 0x01, 0x28,
0x55, 0x82, 0x78, 0x78, 0x69, 0x01, 0x23, 0x17,
0x7d, 0x73, 0x73, 0x73, 0x01, 0x23, 0x2d, 0x82,
0x78, 0x78, 0x78, 0x01, 0x23, 0x3c, 0x82, 0x78,
0x78, 0x6e, 0x01, 0x23, 0x4b, 0x82, 0x78, 0x78,
0x64, 0x01, 0x23, 0x55, 0x82, 0x78, 0x6e, 0x5a,
0x01, 0x1e, 0x17, 0x7d, 0x73, 0x73, 0x64, 0x01,
0x1e, 0x2d, 0x82, 0x78, 0x78, 0x69, 0x01, 0x1e,
0x3c, 0x82, 0x78, 0x78, 0x64, 0x01, 0x1e, 0x4b,
0x82, 0x78, 0x6e, 0x5a, 0x01, 0x1e, 0x55, 0x82,
0x78, 0x64, 0x50, 0x01, 0x19, 0x17, 0x7d, 0x73,
0x69, 0x55, 0x01, 0x19, 0x2d, 0x82, 0x78, 0x6e,
0x5a, 0x01, 0x19, 0x3c, 0x82, 0x78, 0x69, 0x55,
0x01, 0x19, 0x4b, 0x82, 0x78, 0x5f, 0x4b, 0x01,
0x19, 0x55, 0x82, 0x73, 0x55, 0x3c, 0x02, 0x3d,
0x17, 0x87, 0x7d, 0x7d, 0x7d, 0x02, 0x3d, 0x2d,
0x8c, 0x82, 0x82, 0x82, 0x02, 0x3d, 0x3c, 0x8c,
0x82, 0x82, 0x82, 0x02, 0x3d, 0x4b, 0x8c, 0x82,
0x82, 0x82, 0x02, 0x3d, 0x55, 0x8c, 0x82, 0x82,
0x82, 0x02, 0x32, 0x17, 0x87, 0x7d, 0x7d, 0x7d,
0x02, 0x32, 0x2d, 0x8c, 0x82, 0x82, 0x82, 0x02,
0x32, 0x3c, 0x8c, 0x82, 0x82, 0x82, 0x02, 0x32,
0x4b, 0x8c, 0x82, 0x82, 0x78, 0x02, 0x32, 0x55,
0x8c, 0x82, 0x82, 0x6e, 0x02, 0x28, 0x17, 0x87,
0x7d, 0x7d, 0x73, 0x02, 0x28, 0x2d, 0x8c, 0x82,
0x82, 0x78, 0x02, 0x28, 0x3c, 0x8c, 0x82, 0x82,
0x73, 0x02, 0x28, 0x4b, 0x8c, 0x82, 0x78, 0x69,
0x02, 0x28, 0x55, 0x8c, 0x82, 0x6e, 0x5a, 0x02,
0x23, 0x17, 0x87, 0x7d, 0x7d, 0x69, 0x02, 0x23,
0x2d, 0x8c, 0x82, 0x82, 0x6e, 0x02, 0x23, 0x3c,
0x8c, 0x82, 0x78, 0x69, 0x02, 0x23, 0x4b, 0x8c,
0x82, 0x6e, 0x5a, 0x02, 0x23, 0x55, 0x8c, 0x82,
0x64, 0x50, 0x03, 0x3d, 0x17, 0x87, 0x7d, 0x7d,
0x7d, 0x03, 0x3d, 0x2d, 0x8c, 0x82, 0x82, 0x82,
0x03, 0x3d, 0x3c, 0x8c, 0x82, 0x82, 0x82, 0x03,
0x3d, 0x4b, 0x8c, 0x82, 0x82, 0x82, 0x03, 0x3d,
0x55, 0x8c, 0x82, 0x82, 0x82, 0x03, 0x32, 0x17,
0x87, 0x7d, 0x7d, 0x7d, 0x03, 0x32, 0x2d, 0x8c,
0x82, 0x82, 0x82, 0x03, 0x32, 0x3c, 0x8c, 0x82,
0x82, 0x82, 0x03, 0x32, 0x4b, 0x8c, 0x82, 0x82,
0x78, 0x03, 0x32, 0x55, 0x8c, 0x82, 0x82, 0x6e,
0x03, 0x28, 0x17, 0x87, 0x7d, 0x7d, 0x73, 0x03,
0x28, 0x2d, 0x8c, 0x82, 0x82, 0x78, 0x03, 0x28,
0x3c, 0x8c, 0x82, 0x82, 0x73, 0x03, 0x28, 0x4b,
0x8c, 0x82, 0x78, 0x69, 0x03, 0x28, 0x55, 0x8c,
0x82, 0x6e, 0x5a, 0x03, 0x23, 0x17, 0x87, 0x7d,
0x7d, 0x69, 0x03, 0x23, 0x2d, 0x8c, 0x82, 0x82,
0x6e, 0x03, 0x23, 0x3c, 0x8c, 0x82, 0x78, 0x69,
0x03, 0x23, 0x4b, 0x8c, 0x82, 0x6e, 0x5a, 0x03,
0x23, 0x55, 0x8c, 0x82, 0x64, 0x50, 0x04, 0x32,
0x17, 0x91, 0x87, 0x87, 0x87, 0x04, 0x32, 0x2d,
0x96, 0x8c, 0x8c, 0x8c, 0x04, 0x32, 0x3c, 0x96,
0x8c, 0x8c, 0x8c, 0x04, 0x32, 0x46, 0x96, 0x8c,
0x8c, 0x8c, 0x04, 0x32, 0x4b, 0x82, 0x78, 0x78,
0x78, 0x04, 0x32, 0x55, 0x82, 0x78, 0x78, 0x78,
0x04, 0x2f, 0x17, 0x91, 0x87, 0x87, 0x87, 0x04,
0x2f, 0x2d, 0x96, 0x8c, 0x8c, 0x8c, 0x04, 0x2f,
0x3c, 0x96, 0x8c, 0x8c, 0x8c, 0x04, 0x2f, 0x46,
0x96, 0x8c, 0x8c, 0x82, 0x04, 0x2f, 0x4b, 0x82,
0x78, 0x78, 0x78, 0x04, 0x2f, 0x55, 0x82, 0x78,
0x78, 0x78, 0x04, 0x28, 0x17, 0x91, 0x87, 0x87,
0x87, 0x04, 0x28, 0x2d, 0x96, 0x8c, 0x8c, 0x82,
0x04, 0x28, 0x3c, 0x96, 0x8c, 0x8c, 0x82, 0x04,
0x28, 0x46, 0x96, 0x8c, 0x8c, 0x78, 0x04, 0x28,
0x4b, 0x82, 0x78, 0x78, 0x78, 0x04, 0x28, 0x55,
0x82, 0x78, 0x78, 0x6e, 0x04, 0x23, 0x17, 0x91,
0x87, 0x87, 0x73, 0x04, 0x23, 0x2d, 0x96, 0x8c,
0x8c, 0x78, 0x04, 0x23, 0x3c, 0x96, 0x8c, 0x82,
0x78, 0x04, 0x23, 0x46, 0x96, 0x8c, 0x82, 0x6e,
0x04, 0x23, 0x4b, 0x82, 0x78, 0x78, 0x6e, 0x04,
0x23, 0x55, 0x82, 0x78, 0x78, 0x64, 0x04, 0x1e,
0x17, 0x91, 0x87, 0x7d, 0x69, 0x04, 0x1e, 0x2d,
0x96, 0x8c, 0x82, 0x6e, 0x04, 0x1e, 0x3c, 0x96,
0x8c, 0x78, 0x64, 0x04, 0x1e, 0x46, 0x96, 0x8c,
0x78, 0x5a, 0x04, 0x1e, 0x4b, 0x82, 0x78, 0x78,
0x5a, 0x04, 0x1e, 0x55, 0x82, 0x78, 0x64, 0x50,
0x04, 0x19, 0x17, 0x91, 0x87, 0x69, 0x55, 0x04,
0x19, 0x2d, 0x96, 0x8c, 0x6e, 0x5a, 0x04, 0x19,
0x3c, 0x96, 0x82, 0x6e, 0x55, 0x04, 0x19, 0x46,
0x96, 0x82, 0x64, 0x50, 0x04, 0x19, 0x4b, 0x82,
0x78, 0x64, 0x50, 0x04, 0x19, 0x55, 0x82, 0x78,
0x55, 0x3c, 0x05, 0x64, 0x17, 0xa5, 0x9b, 0x9b,
0x9b, 0x05, 0x64, 0x2d, 0xaa, 0xa0, 0xa0, 0xa0,
0x05, 0x64, 0x3c, 0xaa, 0xa0, 0xa0, 0xa0, 0x05,
0x64, 0x46, 0xaa, 0xa0, 0xa0, 0xa0, 0x05, 0x64,
0x4b, 0x8c, 0x82, 0x82, 0x82, 0x05, 0x64, 0x55,
0x8c, 0x82, 0x82, 0x82, 0x05, 0x50, 0x17, 0xa5,
0x9b, 0x9b, 0x9b, 0x05, 0x50, 0x2d, 0xaa, 0xa0,
0xa0, 0xa0, 0x05, 0x50, 0x3c, 0xaa, 0xa0, 0xa0,
0x96, 0x05, 0x50, 0x46, 0xaa, 0xa0, 0xa0, 0x96,
0x05, 0x50, 0x4b, 0x8c, 0x82, 0x82, 0x82, 0x05,
0x50, 0x55, 0x8c, 0x82, 0x82, 0x82, 0x05, 0x3c,
0x17, 0xa5, 0x9b, 0x9b, 0x87, 0x05, 0x3c, 0x2d,
0xaa, 0xa0, 0xa0, 0x8c, 0x05, 0x3c, 0x3c, 0xaa,
0xa0, 0x96, 0x82, 0x05, 0x3c, 0x46, 0xaa, 0xa0,
0x96, 0x78, 0x05, 0x3c, 0x4b, 0x8c, 0x82, 0x82,
0x78, 0x05, 0x3c, 0x55, 0x8c, 0x82, 0x82, 0x6e,
0x05, 0x28, 0x17, 0xa5, 0x91, 0x7d, 0x69, 0x05,
0x28, 0x2d, 0xaa, 0x96, 0x82, 0x6e, 0x05, 0x28,
0x3c, 0xaa, 0x96, 0x78, 0x64, 0x05, 0x28, 0x46,
0xaa, 0x8c, 0x6e, 0x5a, 0x05, 0x28, 0x4b, 0x8c,
0x82, 0x6e, 0x5a, 0x05, 0x28, 0x55, 0x8c, 0x82,
0x64, 0x50, 0x06, 0x3d, 0x17, 0xa5, 0x9b, 0x7d,
0x7d, 0x06, 0x3d, 0x2d, 0xaa, 0xa0, 0x82, 0x82,
0x06, 0x3d, 0x3c, 0xaa, 0xa0, 0x82, 0x82, 0x06,
0x3d, 0x46, 0xaa, 0xa0, 0x82, 0x82, 0x06, 0x3d,
0x4b, 0x8c, 0x82, 0x82, 0x82, 0x06, 0x3d, 0x55,
0x8c, 0x82, 0x82, 0x82, 0x06, 0x32, 0x17, 0xa5,
0x9b, 0x7d, 0x7d, 0x06, 0x32, 0x2d, 0xaa, 0xa0,
0x82, 0x82, 0x06, 0x32, 0x3c, 0xaa, 0xa0, 0x82,
0x82, 0x06, 0x32, 0x46, 0xaa, 0xa0, 0x82, 0x78,
0x06, 0x32, 0x4b, 0x8c, 0x82, 0x82, 0x78, 0x06,
0x32, 0x55, 0x8c, 0x82, 0x82, 0x6e, 0x06, 0x28,
0x17, 0xa5, 0x9b, 0x7d, 0x73, 0x06, 0x28, 0x2d,
0xaa, 0xa0, 0x82, 0x78, 0x06, 0x28, 0x3c, 0xaa,
0x96, 0x82, 0x73, 0x06, 0x28, 0x46, 0xaa, 0x96,
0x78, 0x69, 0x06, 0x28, 0x4b, 0x8c, 0x82, 0x78,
0x69, 0x06, 0x28, 0x55, 0x8c, 0x82, 0x6e, 0x5a,
0x06, 0x23, 0x17, 0xa5, 0x91, 0x7d, 0x69, 0x06,
0x23, 0x2d, 0xaa, 0x96, 0x82, 0x6e, 0x06, 0x23,
0x3c, 0xaa, 0x96, 0x78, 0x69, 0x06, 0x23, 0x46,
0xaa, 0x8c, 0x6e, 0x5a, 0x06, 0x23, 0x4b, 0x8c,
0x82, 0x6e, 0x5a, 0x06, 0x23, 0x55, 0x8c, 0x82,
0x64, 0x50, 0x07, 0x3b, 0x17, 0x7d, 0x73, 0x73,
0x73, 0x07, 0x3b, 0x2d, 0x82, 0x78, 0x78, 0x78,
0x07, 0x3b, 0x3c, 0x82, 0x78, 0x78, 0x78, 0x07,
0x3b, 0x4b, 0x82, 0x78, 0x78, 0x78, 0x07, 0x3b,
0x5a, 0x82, 0x78, 0x78, 0x78, 0x07, 0x32, 0x17,
0x7d, 0x73, 0x73, 0x73, 0x07, 0x32, 0x2d, 0x82,
0x78, 0x78, 0x78, 0x07, 0x32, 0x3c, 0x82, 0x78,
0x78, 0x78, 0x07, 0x32, 0x4b, 0x82, 0x78, 0x78,
0x78, 0x07, 0x32, 0x5a, 0x82, 0x78, 0x6e, 0x64,
0x07, 0x28, 0x17, 0x7d, 0x73, 0x73, 0x69, 0x07,
0x28, 0x2d, 0x82, 0x78, 0x78, 0x6e, 0x07, 0x28,
0x3c, 0x82, 0x78, 0x78, 0x64, 0x07, 0x28, 0x4b,
0x82, 0x78, 0x78, 0x64, 0x07, 0x28, 0x5a, 0x82,
0x78, 0x64, 0x50, 0x07, 0x23, 0x17, 0x7d, 0x73,
0x73, 0x5f, 0x07, 0x23, 0x2d, 0x82, 0x78, 0x78,
0x64, 0x07, 0x23, 0x3c, 0x82, 0x78, 0x78, 0x64,
0x07, 0x23, 0x4b, 0x82, 0x78, 0x64, 0x50, 0x07,
0x23, 0x5a, 0x82, 0x78, 0x5a, 0x46, 0x08, 0x3b,
0x17, 0x7d, 0x73, 0x73, 0x73, 0x08, 0x3b, 0x2d,
0x82, 0x78, 0x78, 0x78, 0x08, 0x3b, 0x3c, 0x82,
0x78, 0x78, 0x78, 0x08, 0x3b, 0x4b, 0x82, 0x78,
0x78, 0x78, 0x08, 0x3b, 0x5a, 0x82, 0x78, 0x78,
0x78, 0x08, 0x32, 0x17, 0x7d, 0x73, 0x73, 0x73,
0x08, 0x32, 0x2d, 0x82, 0x78, 0x78, 0x78, 0x08,
0x32, 0x3c, 0x82, 0x78, 0x78, 0x78, 0x08, 0x32,
0x4b, 0x82, 0x78, 0x78, 0x78, 0x08, 0x32, 0x5a,
0x82, 0x78, 0x6e, 0x64, 0x08, 0x28, 0x17, 0x7d,
0x73, 0x73, 0x69, 0x08, 0x28, 0x2d, 0x82, 0x78,
0x78, 0x6e, 0x08, 0x28, 0x3c, 0x82, 0x78, 0x78,
0x64, 0x08, 0x28, 0x4b, 0x82, 0x78, 0x78, 0x64,
0x08, 0x28, 0x5a, 0x82, 0x78, 0x64, 0x50, 0x08,
0x23, 0x17, 0x7d, 0x73, 0x73, 0x5f, 0x08, 0x23,
0x2d, 0x82, 0x78, 0x78, 0x64, 0x08, 0x23, 0x3c,
0x82, 0x78, 0x78, 0x64, 0x08, 0x23, 0x4b, 0x82,
0x78, 0x64, 0x50, 0x08, 0x23, 0x5a, 0x82, 0x78,
0x5a, 0x46, 0x0c, 0x52, 0x17, 0xa5, 0x9b, 0x9b,
0x9b, 0x0c, 0x52, 0x2d, 0xaa, 0xa0, 0xa0, 0xa0,
0x0c, 0x52, 0x3c, 0xaa, 0xa0, 0xa0, 0xa0, 0x0c,
0x52, 0x46, 0xaa, 0xa0, 0xa0, 0xa0, 0x0c, 0x52,
0x4b, 0x8c, 0x82, 0x82, 0x82, 0x0c, 0x52, 0x55,
0x8c, 0x82, 0x82, 0x82, 0x0c, 0x42, 0x17, 0xa5,
0x9b, 0x9b, 0x91, 0x0c, 0x42, 0x2d, 0xaa, 0xa0,
0xa0, 0x96, 0x0c, 0x42, 0x3c, 0xaa, 0xa0, 0xa0,
0x96, 0x0c, 0x42, 0x46, 0xaa, 0xa0, 0xa0, 0x96,
0x0c, 0x42, 0x4b, 0x8c, 0x82, 0x82, 0x82, 0x0c,
0x42, 0x55, 0x8c, 0x82, 0x82, 0x82, 0x0c, 0x3d,
0x17, 0xa5, 0x9b, 0x9b, 0x91, 0x0c, 0x3d, 0x2d,
0xaa, 0xa0, 0xa0, 0x96, 0x0c, 0x3d, 0x3c, 0xaa,
0xa0, 0xa0, 0x8c, 0x0c, 0x3d, 0x46, 0xaa, 0xa0,
0x96, 0x8c, 0x0c, 0x3d, 0x4b, 0x8c, 0x82, 0x82,
0x82, 0x0c, 0x3d, 0x55, 0x8c, 0x82, 0x82, 0x82,
0x0c, 0x32, 0x17, 0xa5, 0x9b, 0x91, 0x87, 0x0c,
0x32, 0x2d, 0xaa, 0xa0, 0x96, 0x8c, 0x0c, 0x32,
0x3c, 0xaa, 0xa0, 0x96, 0x82, 0x0c, 0x32, 0x46,
0xaa, 0xa0, 0x8c, 0x78, 0x0c, 0x32, 0x4b, 0x8c,
0x82, 0x82, 0x78, 0x0c, 0x32, 0x55, 0x8c, 0x82,
0x82, 0x6e, 0x0c, 0x28, 0x17, 0xa5, 0x9b, 0x87,
0x73, 0x0c, 0x28, 0x2d, 0xaa, 0xa0, 0x8c, 0x78,
0x0c, 0x28, 0x3c, 0xaa, 0x96, 0x82, 0x73, 0x0c,
0x28, 0x46, 0xaa, 0x96, 0x78, 0x69, 0x0c, 0x28,
0x4b, 0x8c, 0x82, 0x78, 0x69, 0x0c, 0x28, 0x55,
0x8c, 0x82, 0x6e, 0x5a, 0x0c, 0x23, 0x17, 0xa5,
0x91, 0x7d, 0x69, 0x0c, 0x23, 0x2d, 0xaa, 0x96,
0x82, 0x6e, 0x0c, 0x23, 0x3c, 0xaa, 0x96, 0x78,
0x69, 0x0c, 0x23, 0x46, 0xaa, 0x8c, 0x6e, 0x5a,
0x0c, 0x23, 0x4b, 0x8c, 0x82, 0x6e, 0x5a, 0x0c,
0x23, 0x55, 0x8c, 0x82, 0x64, 0x50, 0x0d, 0x64,
0x17, 0xa5, 0x9b, 0x9b, 0x9b, 0x0d, 0x64, 0x2d,
0xaa, 0xa0, 0xa0, 0xa0, 0x0d, 0x64, 0x3c, 0xaa,
0xa0, 0xa0, 0xa0, 0x0d, 0x64, 0x46, 0xaa, 0xa0,
0xa0, 0xa0, 0x0d, 0x64, 0x4b, 0x8c, 0x82, 0x82,
0x82, 0x0d, 0x64, 0x55, 0x8c, 0x82, 0x82, 0x82,
0x0d, 0x50, 0x17, 0xa5, 0x9b, 0x9b, 0x9b, 0x0d,
0x50, 0x2d, 0xaa, 0xa0, 0xa0, 0xa0, 0x0d, 0x50,
0x3c, 0xaa, 0xa0, 0xa0, 0x96, 0x0d, 0x50, 0x46,
0xaa, 0xa0, 0xa0, 0x96, 0x0d, 0x50, 0x4b, 0x8c,
0x82, 0x82, 0x82, 0x0d, 0x50, 0x55, 0x8c, 0x82,
0x82, 0x82, 0x0d, 0x3c, 0x17, 0xa5, 0x9b, 0x9b,
0x87, 0x0d, 0x3c, 0x2d, 0xaa, 0xa0, 0xa0, 0x8c,
0x0d, 0x3c, 0x3c, 0xaa, 0xa0, 0x96, 0x82, 0x0d,
0x3c, 0x46, 0xaa, 0xa0, 0x96, 0x78, 0x0d, 0x3c,
0x4b, 0x8c, 0x82, 0x82, 0x78, 0x0d, 0x3c, 0x55,
0x8c, 0x82, 0x82, 0x6e, 0x0d, 0x28, 0x17, 0xa5,
0x91, 0x7d, 0x69, 0x0d, 0x28, 0x2d, 0xaa, 0x96,
0x82, 0x6e, 0x0d, 0x28, 0x3c, 0xaa, 0x96, 0x78,
0x64, 0x0d, 0x28, 0x46, 0xaa, 0x8c, 0x6e, 0x5a,
0x0d, 0x28, 0x4b, 0x8c, 0x82, 0x6e, 0x5a, 0x0d,
0x28, 0x55, 0x8c, 0x82, 0x64, 0x50,
};
static struct tegra_system_edp_entry __initdata tegra_system_edp_map[] = {
/* {SKU, power-limit (in 100mW), {freq limits (in 10Mhz)} } */
{ 1, 49, {130, 120, 120, 120} },
{ 1, 44, {130, 120, 120, 110} },
{ 1, 37, {130, 120, 110, 100} },
{ 1, 35, {130, 120, 110, 90} },
{ 1, 29, {130, 120, 100, 80} },
{ 1, 27, {130, 120, 90, 80} },
{ 1, 25, {130, 110, 80, 60} },
{ 1, 21, {130, 100, 80, 40} },
{ 4, 49, {130, 120, 120, 120} },
{ 4, 44, {130, 120, 120, 110} },
{ 4, 37, {130, 120, 110, 100} },
{ 4, 35, {130, 120, 110, 90} },
{ 4, 29, {130, 120, 100, 80} },
{ 4, 27, {130, 120, 90, 80} },
{ 4, 25, {130, 110, 80, 60} },
{ 4, 21, {130, 100, 80, 40} },
};
/*
* "Safe entry" to be used when no match for speedo_id /
* regulator_cur is found; must be the last one
*/
static struct tegra_edp_limits edp_default_limits[] = {
{85, {1000000, 1000000, 1000000, 1000000} },
};
static struct tegra_system_edp_entry power_edp_default_limits[] = {
{0, 20, {1000000, 1000000, 1000000, 1000000} },
};
/* Constants for EDP calculations */
static const int temperatures[] = { /* degree celcius (C) */
23, 40, 50, 60, 70, 74, 78, 82, 86, 90, 94, 98, 102,
};
static const int power_cap_levels[] = { /* milliwatts (mW) */
700, 1700, 2700, 3700, 3800, 3900, 4500, 4600,
4700, 4800, 4900, 5200, 5300, 5400, 5500, 5800,
5900, 6200, 6400, 6500, 6800, 7200, 7500, 8200,
8500, 9200, 9500, 10200, 10500, 11200, 11500, 12200,
12500, 13200, 13500, 14200, 14500, 15200, 15500, 16500,
17500
};
static struct tegra_edp_cpu_leakage_params leakage_params[] = {
{
.cpu_speedo_id = 0, /* A01 CPU */
.dyn_consts_n = { 1091747, 2035205, 2978661, 3922119 },
.leakage_consts_n = { 538991, 752463, 959441, 1150000 },
.leakage_consts_ijk = {
/* i = 0 */
{ { -42746668, -5458429, 164998, -1711, },
{ 178262421, 13375684, -411791, 4590, },
{ -228866784, -10482993, 331248, -4062, },
{ 94301550, 2618719, -85983, 1193, },
},
/* i = 1 */
{ { -256611791, 49677413, -1655785, 14917, },
{ 584675433, -132620939, 4541560, -41812, },
{ -398106336, 115987156, -4102328, 38737, },
{ 68897184, -33030745, 1217839, -11801, },
},
/* i = 2 */
{ { 186324676, -36019083, 1177969, -10669, },
{ -439237936, 98429131, -3276444, 30301, },
{ 315060898, -88635036, 3004777, -28474, },
{ -60854399, 26267188, -907121, 8844, },
},
/* i = 3 */
{ { -35432997, 6154621, -202200, 1830, },
{ 87402153, -16908683, 565152, -5220, },
{ -67775314, 15326770, -521221, 4927, },
{ 15618709, -4576116, 158401, -1538, },
},
},
.volt_temp_cap = { 70, 1300 },
},
{
.cpu_speedo_id = 1, /* A01P+ CPU */
.dyn_consts_n = { 1091747, 2035205, 2978661, 3922119 },
.leakage_consts_n = { 538991, 752463, 959441, 1150000 },
.leakage_consts_ijk = {
/* i = 0 */
{ { -42746668, -5458429, 164998, -1711, },
{ 178262421, 13375684, -411791, 4590, },
{ -228866784, -10482993, 331248, -4062, },
{ 94301550, 2618719, -85983, 1193, },
},
/* i = 1 */
{ { -256611791, 49677413, -1655785, 14917, },
{ 584675433, -132620939, 4541560, -41812, },
{ -398106336, 115987156, -4102328, 38737, },
{ 68897184, -33030745, 1217839, -11801, },
},
/* i = 2 */
{ { 186324676, -36019083, 1177969, -10669, },
{ -439237936, 98429131, -3276444, 30301, },
{ 315060898, -88635036, 3004777, -28474, },
{ -60854399, 26267188, -907121, 8844, },
},
/* i = 3 */
{ { -35432997, 6154621, -202200, 1830, },
{ 87402153, -16908683, 565152, -5220, },
{ -67775314, 15326770, -521221, 4927, },
{ 15618709, -4576116, 158401, -1538, },
},
},
.safety_cap = { 1810500, 1810500, 1606500, 1606500 },
.volt_temp_cap = { 70, 1300 },
},
{
.cpu_speedo_id = 2, /* A01P+ fast CPU */
.dyn_consts_n = { 1091747, 2035205, 2978661, 3922119 },
.leakage_consts_n = { 538991, 752463, 959441, 1150000 },
.leakage_consts_ijk = {
/* i = 0 */
{ { -42746668, -5458429, 164998, -1711, },
{ 178262421, 13375684, -411791, 4590, },
{ -228866784, -10482993, 331248, -4062, },
{ 94301550, 2618719, -85983, 1193, },
},
/* i = 1 */
{ { -256611791, 49677413, -1655785, 14917, },
{ 584675433, -132620939, 4541560, -41812, },
{ -398106336, 115987156, -4102328, 38737, },
{ 68897184, -33030745, 1217839, -11801, },
},
/* i = 2 */
{ { 186324676, -36019083, 1177969, -10669, },
{ -439237936, 98429131, -3276444, 30301, },
{ 315060898, -88635036, 3004777, -28474, },
{ -60854399, 26267188, -907121, 8844, },
},
/* i = 3 */
{ { -35432997, 6154621, -202200, 1830, },
{ 87402153, -16908683, 565152, -5220, },
{ -67775314, 15326770, -521221, 4927, },
{ 15618709, -4576116, 158401, -1538, },
},
},
.safety_cap = { 1912500, 1912500, 1912500, 1912500 },
.volt_temp_cap = { 70, 1300 },
},
};
static struct tegra_edp_freq_voltage_table *freq_voltage_lut_saved;
static unsigned int freq_voltage_lut_size_saved;
static struct tegra_edp_freq_voltage_table *freq_voltage_lut;
static unsigned int freq_voltage_lut_size;
static inline s64 edp_pow(s64 val, int pwr)
{
s64 retval = 1;
while (pwr) {
if (pwr & 1)
retval *= val;
pwr >>= 1;
if (pwr)
val *= val;
}
return retval;
}
/*
* Find the maximum frequency that results in dynamic and leakage current that
* is less than the regulator current limit.
* temp_C - valid or -EINVAL
* power_mW - valid or -1 (infinite) or -EINVAL
*/
static unsigned int edp_calculate_maxf(
struct tegra_edp_cpu_leakage_params *params,
int temp_C, int power_mW,
int iddq_mA,
int n_cores_idx)
{
unsigned int voltage_mV, freq_KHz;
unsigned int cur_effective = regulator_cur - edp_reg_override_mA;
int f, i, j, k;
s64 leakage_mA, dyn_mA, leakage_calc_step;
s64 leakage_mW, dyn_mW;
for (f = freq_voltage_lut_size - 1; f >= 0; f--) {
freq_KHz = freq_voltage_lut[f].freq / 1000;
voltage_mV = freq_voltage_lut[f].voltage_mV;
/* Constrain Volt-Temp. Eg. at Tj >= 70C, no VDD_CPU > 1.24V */
if (temp_C > params->volt_temp_cap.temperature &&
voltage_mV > params->volt_temp_cap.voltage_limit_mV)
continue;
/* Calculate leakage current */
leakage_mA = 0;
for (i = 0; i <= 3; i++) {
for (j = 0; j <= 3; j++) {
for (k = 0; k <= 3; k++) {
leakage_calc_step =
params->leakage_consts_ijk
[i][j][k] * edp_pow(iddq_mA, i);
/* Convert (mA)^i to (A)^i */
leakage_calc_step =
div64_s64(leakage_calc_step,
edp_pow(1000, i));
leakage_calc_step *=
edp_pow(voltage_mV, j);
/* Convert (mV)^i to (V)^i */
leakage_calc_step =
div64_s64(leakage_calc_step,
edp_pow(1000, j));
leakage_calc_step *=
edp_pow(temp_C, k);
/* leakage_consts_ijk was X 100,000 */
leakage_calc_step =
div64_s64(leakage_calc_step,
100000);
leakage_mA += leakage_calc_step;
}
}
}
/* leakage cannot be negative => leakage model has error */
if (leakage_mA <= 0) {
pr_err("VDD_CPU EDP failed: IDDQ too high (%d mA)\n",
iddq_mA);
return -EINVAL;
}
leakage_mA *= params->leakage_consts_n[n_cores_idx];
/* leakage_const_n was pre-multiplied by 1,000,000 */
leakage_mA = div64_s64(leakage_mA, 1000000);
/* Calculate dynamic current */
dyn_mA = voltage_mV * freq_KHz / 1000;
/* Convert mV to V */
dyn_mA = div64_s64(dyn_mA, 1000);
dyn_mA *= params->dyn_consts_n[n_cores_idx];
/* dyn_const_n was pre-multiplied by 1,000,000 */
dyn_mA = div64_s64(dyn_mA, 1000000);
if (power_mW != -1) {
leakage_mW = leakage_mA * voltage_mV;
dyn_mW = dyn_mA * voltage_mV;
if (div64_s64(leakage_mW + dyn_mW, 1000) <= power_mW)
return freq_KHz;
} else if ((leakage_mA + dyn_mA) <= cur_effective) {
return freq_KHz;
}
}
return -EINVAL;
}
static int edp_relate_freq_voltage(struct clk *clk_cpu_g,
unsigned int cpu_speedo_idx,
unsigned int freq_volt_lut_size,
struct tegra_edp_freq_voltage_table *freq_volt_lut)
{
unsigned int i, j, freq;
int voltage_mV;
for (i = 0, j = 0, freq = 0;
i < freq_volt_lut_size;
i++, freq += FREQ_STEP) {
/* Predict voltages */
voltage_mV = tegra_dvfs_predict_millivolts(clk_cpu_g, freq);
if (voltage_mV < 0) {
pr_err("%s: couldn't predict voltage: freq %u; err %d",
__func__, freq, voltage_mV);
return -EINVAL;
}
/* Cache frequency / voltage / voltage constant relationship */
freq_volt_lut[i].freq = freq;
freq_volt_lut[i].voltage_mV = voltage_mV;
}
return 0;
}
unsigned int tegra_edp_find_maxf(int volt)
{
unsigned int i;
for (i = 0; i < freq_voltage_lut_size_saved; i++) {
if (freq_voltage_lut_saved[i].voltage_mV > volt)
break;
}
return freq_voltage_lut[i - 1].freq;
}
static int edp_find_speedo_idx(int cpu_speedo_id, unsigned int *cpu_speedo_idx)
{
int i;
for (i = 0; i < ARRAY_SIZE(leakage_params); i++)
if (cpu_speedo_id == leakage_params[i].cpu_speedo_id) {
*cpu_speedo_idx = i;
return 0;
}
pr_err("%s: couldn't find cpu speedo id %d in freq/voltage LUT\n",
__func__, cpu_speedo_id);
return -EINVAL;
}
static int init_cpu_edp_limits_calculated(void)
{
unsigned int max_nr_cpus = num_possible_cpus();
unsigned int temp_idx, n_cores_idx, pwr_idx;
unsigned int cpu_g_minf, cpu_g_maxf;
unsigned int iddq_mA;
unsigned int cpu_speedo_idx;
unsigned int cap, limit;
struct tegra_edp_limits *edp_calculated_limits;
struct tegra_system_edp_entry *power_edp_calc_limits;
struct tegra_edp_cpu_leakage_params *params;
int ret;
struct clk *clk_cpu_g = tegra_get_clock_by_name("cpu_g");
int cpu_speedo_id = tegra_cpu_speedo_id();
/* Determine all inputs to EDP formula */
iddq_mA = tegra_get_cpu_iddq_value();
ret = edp_find_speedo_idx(cpu_speedo_id, &cpu_speedo_idx);
if (ret)
return ret;
params = &leakage_params[cpu_speedo_idx];
edp_calculated_limits = kmalloc(sizeof(struct tegra_edp_limits)
* ARRAY_SIZE(temperatures), GFP_KERNEL);
BUG_ON(!edp_calculated_limits);
power_edp_calc_limits = kmalloc(sizeof(struct tegra_system_edp_entry)
* ARRAY_SIZE(power_cap_levels), GFP_KERNEL);
BUG_ON(!power_edp_calc_limits);
cpu_g_minf = 0;
cpu_g_maxf = clk_get_max_rate(clk_cpu_g);
freq_voltage_lut_size = (cpu_g_maxf - cpu_g_minf) / FREQ_STEP + 1;
freq_voltage_lut = kmalloc(sizeof(struct tegra_edp_freq_voltage_table)
* freq_voltage_lut_size, GFP_KERNEL);
if (!freq_voltage_lut) {
pr_err("%s: failed alloc mem for freq/voltage LUT\n", __func__);
return -ENOMEM;
}
ret = edp_relate_freq_voltage(clk_cpu_g, cpu_speedo_idx,
freq_voltage_lut_size, freq_voltage_lut);
if (ret) {
kfree(freq_voltage_lut);
return ret;
}
if (freq_voltage_lut_size != freq_voltage_lut_size_saved) {
/* release previous table if present */
kfree(freq_voltage_lut_saved);
/* create table to save */
freq_voltage_lut_saved =
kmalloc(sizeof(struct tegra_edp_freq_voltage_table) *
freq_voltage_lut_size, GFP_KERNEL);
if (!freq_voltage_lut_saved) {
pr_err("%s: failed alloc mem for freq/voltage LUT\n",
__func__);
kfree(freq_voltage_lut);
return -ENOMEM;
}
freq_voltage_lut_size_saved = freq_voltage_lut_size;
}
memcpy(freq_voltage_lut_saved,
freq_voltage_lut,
sizeof(struct tegra_edp_freq_voltage_table) *
freq_voltage_lut_size);
/* Calculate EDP table */
for (n_cores_idx = 0; n_cores_idx < max_nr_cpus; n_cores_idx++) {
for (temp_idx = 0;
temp_idx < ARRAY_SIZE(temperatures); temp_idx++) {
edp_calculated_limits[temp_idx].temperature =
temperatures[temp_idx];
limit = edp_calculate_maxf(params,
temperatures[temp_idx],
-1,
iddq_mA,
n_cores_idx);
if (limit == -EINVAL)
return -EINVAL;
/* apply safety cap if it is specified */
if (n_cores_idx < 4) {
cap = params->safety_cap[n_cores_idx];
if (cap && cap < limit)
limit = cap;
}
edp_calculated_limits[temp_idx].
freq_limits[n_cores_idx] = limit;
}
for (pwr_idx = 0;
pwr_idx < ARRAY_SIZE(power_cap_levels); pwr_idx++) {
power_edp_calc_limits[pwr_idx].power_limit_100mW =
power_cap_levels[pwr_idx] / 100;
limit = edp_calculate_maxf(params,
90,
power_cap_levels[pwr_idx],
iddq_mA,
n_cores_idx);
if (limit == -EINVAL)
return -EINVAL;
power_edp_calc_limits[pwr_idx].
freq_limits[n_cores_idx] = limit;
}
}
/*
* If this is an EDP table update, need to overwrite old table.
* The old table's address must remain valid.
*/
if (edp_limits != edp_default_limits) {
memcpy(edp_limits, edp_calculated_limits,
sizeof(struct tegra_edp_limits)
* ARRAY_SIZE(temperatures));
kfree(edp_calculated_limits);
}
else {
edp_limits = edp_calculated_limits;
edp_limits_size = ARRAY_SIZE(temperatures);
}
if (power_edp_limits != power_edp_default_limits) {
memcpy(power_edp_limits, power_edp_calc_limits,
sizeof(struct tegra_system_edp_entry)
* ARRAY_SIZE(power_cap_levels));
kfree(power_edp_calc_limits);
} else {
power_edp_limits = power_edp_calc_limits;
power_edp_limits_size = ARRAY_SIZE(power_cap_levels);
}
kfree(freq_voltage_lut);
return 0;
}
static int __init init_cpu_edp_limits_lookup(void)
{
int i, j;
struct tegra_edp_limits *e;
struct tegra_edp_vdd_cpu_entry *t;
int tsize;
int cpu_speedo_id = tegra_cpu_speedo_id();
t = (struct tegra_edp_vdd_cpu_entry *)tegra_edp_vdd_cpu_map;
tsize = sizeof(tegra_edp_vdd_cpu_map)
/ sizeof(struct tegra_edp_vdd_cpu_entry);
for (i = 0; i < tsize; i++) {
if (t[i].speedo_id == cpu_speedo_id &&
t[i].regulator_100mA <= regulator_cur / 100)
break;
}
/* No entry found in tegra_edp_vdd_cpu_map */
if (i >= tsize)
return -EINVAL;
/* Find all rows for this entry */
for (j = i + 1; j < tsize; j++) {
if (t[i].speedo_id != t[j].speedo_id ||
t[i].regulator_100mA != t[j].regulator_100mA)
break;
}
edp_limits_size = j - i;
e = kmalloc(sizeof(struct tegra_edp_limits) * edp_limits_size,
GFP_KERNEL);
BUG_ON(!e);
for (j = 0; j < edp_limits_size; j++) {
e[j].temperature = (int)t[i+j].temperature;
e[j].freq_limits[0] = (unsigned int)t[i+j].freq_limits[0]*10000;
e[j].freq_limits[1] = (unsigned int)t[i+j].freq_limits[1]*10000;
e[j].freq_limits[2] = (unsigned int)t[i+j].freq_limits[2]*10000;
e[j].freq_limits[3] = (unsigned int)t[i+j].freq_limits[3]*10000;
}
if (edp_limits != edp_default_limits)
kfree(edp_limits);
edp_limits = e;
return 0;
}
void tegra_recalculate_cpu_edp_limits(void)
{
if (tegra_chip_id != TEGRA11X)
return;
if (init_cpu_edp_limits_calculated() == 0)
return;
/* Revert to default EDP table on error */
edp_limits = edp_default_limits;
edp_limits_size = ARRAY_SIZE(edp_default_limits);
power_edp_limits = power_edp_default_limits;
power_edp_limits_size = ARRAY_SIZE(power_edp_default_limits);
}
/*
* Specify regulator current in mA, e.g. 5000mA
* Use 0 for default
*/
void __init tegra_init_cpu_edp_limits(unsigned int regulator_mA)
{
if (!regulator_mA)
goto end;
regulator_cur = regulator_mA + OVERRIDE_DEFAULT;
switch (tegra_chip_id) {
case TEGRA30:
if (init_cpu_edp_limits_lookup() == 0)
return;
break;
case TEGRA11X:
if (init_cpu_edp_limits_calculated() == 0)
return;
break;
case TEGRA20:
default:
BUG();
break;
}
end:
edp_limits = edp_default_limits;
edp_limits_size = ARRAY_SIZE(edp_default_limits);
power_edp_limits = power_edp_default_limits;
power_edp_limits_size = ARRAY_SIZE(power_edp_default_limits);
}
void __init tegra_init_system_edp_limits(unsigned int power_limit_mW)
{
int cpu_speedo_id = tegra_cpu_speedo_id();
int i;
unsigned int *e;
struct tegra_system_edp_entry *t = tegra_system_edp_map;
int tsize = ARRAY_SIZE(tegra_system_edp_map);
if (!power_limit_mW) {
e = NULL;
goto out;
}
for (i = 0; i < tsize; i++)
if (t[i].speedo_id == cpu_speedo_id)
break;
if (i >= tsize) {
e = NULL;
goto out;
}
do {
if (t[i].power_limit_100mW <= power_limit_mW / 100)
break;
i++;
} while (i < tsize && t[i].speedo_id == cpu_speedo_id);
if (i >= tsize || t[i].speedo_id != cpu_speedo_id)
i--; /* No low enough entry in the table, use best possible */
e = kmalloc(sizeof(unsigned int) * 4, GFP_KERNEL);
BUG_ON(!e);
e[0] = (unsigned int)t[i].freq_limits[0] * 10000;
e[1] = (unsigned int)t[i].freq_limits[1] * 10000;
e[2] = (unsigned int)t[i].freq_limits[2] * 10000;
e[3] = (unsigned int)t[i].freq_limits[3] * 10000;
out:
kfree(system_edp_limits);
system_edp_limits = e;
}
void tegra_get_cpu_edp_limits(const struct tegra_edp_limits **limits, int *size)
{
*limits = edp_limits;
*size = edp_limits_size;
}
void tegra_get_system_edp_limits(const unsigned int **limits)
{
*limits = system_edp_limits;
}
void tegra_platform_edp_init(struct thermal_trip_info *trips,
int *num_trips, int margin)
{
const struct tegra_edp_limits *cpu_edp_limits;
struct thermal_trip_info *trip_state;
int i, cpu_edp_limits_size;
if (!trips || !num_trips)
return;
/* edp capping */
tegra_get_cpu_edp_limits(&cpu_edp_limits, &cpu_edp_limits_size);
if (cpu_edp_limits_size > MAX_THROT_TABLE_SIZE)
BUG();
for (i = 0; i < cpu_edp_limits_size-1; i++) {
trip_state = &trips[*num_trips];
trip_state->cdev_type = "cpu_edp";
trip_state->trip_temp =
(cpu_edp_limits[i].temperature * 1000) - margin;
trip_state->trip_type = THERMAL_TRIP_ACTIVE;
trip_state->upper = trip_state->lower = i + 1;
(*num_trips)++;
if (*num_trips >= THERMAL_MAX_TRIPS)
BUG();
}
}
struct tegra_system_edp_entry *tegra_get_system_edp_entries(int *size)
{
*size = power_edp_limits_size;
return power_edp_limits;
}
#ifdef CONFIG_DEBUG_FS
static int edp_limit_debugfs_show(struct seq_file *s, void *data)
{
seq_printf(s, "%u\n", tegra_get_edp_limit(NULL));
return 0;
}
static inline void edp_show_4core_edp_table(struct seq_file *s, int th_idx)
{
int i;
seq_printf(s, "%6s %10s %10s %10s %10s\n",
" Temp.", "1-core", "2-cores", "3-cores", "4-cores");
for (i = 0; i < edp_limits_size; i++) {
seq_printf(s, "%c%3dC: %10u %10u %10u %10u\n",
i == th_idx ? '>' : ' ',
edp_limits[i].temperature,
edp_limits[i].freq_limits[0],
edp_limits[i].freq_limits[1],
edp_limits[i].freq_limits[2],
edp_limits[i].freq_limits[3]);
}
}
static inline void edp_show_2core_edp_table(struct seq_file *s, int th_idx)
{
int i;
seq_printf(s, "%6s %10s %10s\n",
" Temp.", "1-core", "2-cores");
for (i = 0; i < edp_limits_size; i++) {
seq_printf(s, "%c%3dC: %10u %10u\n",
i == th_idx ? '>' : ' ',
edp_limits[i].temperature,
edp_limits[i].freq_limits[0],
edp_limits[i].freq_limits[1]);
}
}
static inline void edp_show_4core_power_table(struct seq_file *s)
{
int i;
seq_printf(s, "%6s %10s %10s %10s %10s\n",
" Power", "1-core", "2-cores", "3-cores", "4-cores");
for (i = 0; i < power_edp_limits_size; i++) {
seq_printf(s, "%5dmW: %10u %10u %10u %10u\n",
power_edp_limits[i].power_limit_100mW * 100,
power_edp_limits[i].freq_limits[0],
power_edp_limits[i].freq_limits[1],
power_edp_limits[i].freq_limits[2],
power_edp_limits[i].freq_limits[3]);
}
}
static inline void edp_show_2core_power_table(struct seq_file *s)
{
int i;
seq_printf(s, "%6s %10s %10s\n",
" Power", "1-core", "2-cores");
for (i = 0; i < power_edp_limits_size; i++) {
seq_printf(s, "%5dmW: %10u %10u\n",
power_edp_limits[i].power_limit_100mW * 100,
power_edp_limits[i].freq_limits[0],
power_edp_limits[i].freq_limits[1]);
}
}
static inline void edp_show_4core_system_table(struct seq_file *s)
{
seq_printf(s, "%10u %10u %10u %10u\n",
system_edp_limits[0],
system_edp_limits[1],
system_edp_limits[2],
system_edp_limits[3]);
}
static inline void edp_show_2core_system_table(struct seq_file *s)
{
seq_printf(s, "%10u %10u\n",
system_edp_limits[0],
system_edp_limits[1]);
}
static int edp_debugfs_show(struct seq_file *s, void *data)
{
unsigned int max_nr_cpus = num_possible_cpus();
int th_idx;
if (max_nr_cpus != 2 && max_nr_cpus != 4) {
seq_printf(s, "Unsupported number of CPUs\n");
return 0;
}
tegra_get_edp_limit(&th_idx);
seq_printf(s, "-- VDD_CPU %sEDP table (%umA = %umA - %umA) --\n",
edp_limits == edp_default_limits ? "**default** " : "",
regulator_cur - edp_reg_override_mA,
regulator_cur, edp_reg_override_mA);
if (max_nr_cpus == 2)
edp_show_2core_edp_table(s, th_idx);
else if (max_nr_cpus == 4)
edp_show_4core_edp_table(s, th_idx);
seq_printf(s, "-- VDD_CPU %sPower EDP table --\n",
power_edp_limits == power_edp_default_limits ?
"**default** " : "");
if (max_nr_cpus == 2)
edp_show_2core_power_table(s);
else if (max_nr_cpus == 4)
edp_show_4core_power_table(s);
if (system_edp_limits) {
seq_printf(s, "\n-- System EDP table --\n");
if (max_nr_cpus == 2)
edp_show_2core_system_table(s);
else if (max_nr_cpus == 4)
edp_show_4core_system_table(s);
}
return 0;
}
static int edp_reg_override_show(struct seq_file *s, void *data)
{
seq_printf(s, "Limit override: %u mA. Effective limit: %u mA\n",
edp_reg_override_mA, regulator_cur - edp_reg_override_mA);
return 0;
}
static int edp_reg_override_write(struct file *file,
const char __user *userbuf, size_t count, loff_t *ppos)
{
char buf[32], *end;
unsigned int edp_reg_override_mA_temp;
unsigned int edp_reg_override_mA_prev = edp_reg_override_mA;
if (sizeof(buf) <= count)
goto override_err;
if (copy_from_user(buf, userbuf, count))
goto override_err;
/* terminate buffer and trim - white spaces may be appended
* at the end when invoked from shell command line */
buf[count]='\0';
strim(buf);
edp_reg_override_mA_temp = simple_strtoul(buf, &end, 10);
if (*end != '\0')
goto override_err;
if (edp_reg_override_mA_temp >= regulator_cur)
goto override_err;
if (edp_reg_override_mA == edp_reg_override_mA_temp)
return count;
edp_reg_override_mA = edp_reg_override_mA_temp;
if (init_cpu_edp_limits_calculated()) {
/* Revert to previous override value if new value fails */
edp_reg_override_mA = edp_reg_override_mA_prev;
goto override_err;
}
if (tegra_cpu_set_speed_cap(NULL)) {
pr_err("FAILED: Set CPU freq cap with new VDD_CPU EDP table\n");
goto override_out;
}
pr_info("Reinitialized VDD_CPU EDP table with regulator current limit"
" %u mA\n", regulator_cur - edp_reg_override_mA);
return count;
override_err:
pr_err("FAILED: Reinitialize VDD_CPU EDP table with override \"%s\"",
buf);
override_out:
return -EINVAL;
}
static int edp_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, edp_debugfs_show, inode->i_private);
}
static int edp_limit_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, edp_limit_debugfs_show, inode->i_private);
}
static int edp_reg_override_open(struct inode *inode, struct file *file)
{
return single_open(file, edp_reg_override_show, inode->i_private);
}
static const struct file_operations edp_debugfs_fops = {
.open = edp_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations edp_limit_debugfs_fops = {
.open = edp_limit_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations edp_reg_override_debugfs_fops = {
.open = edp_reg_override_open,
.read = seq_read,
.write = edp_reg_override_write,
.llseek = seq_lseek,
.release = single_release,
};
#ifdef CONFIG_EDP_FRAMEWORK
static __init struct dentry *tegra_edp_debugfs_dir(void)
{
return edp_debugfs_dir;
}
#else
static __init struct dentry *tegra_edp_debugfs_dir(void)
{
return debugfs_create_dir("edp", NULL);
}
#endif
static int __init tegra_edp_debugfs_init(void)
{
struct dentry *d_edp;
struct dentry *d_edp_limit;
struct dentry *d_edp_reg_override;
struct dentry *edp_dir;
struct dentry *vdd_cpu_dir;
edp_dir = tegra_edp_debugfs_dir();
if (!edp_dir)
goto edp_dir_err;
vdd_cpu_dir = debugfs_create_dir("vdd_cpu", edp_dir);
if (!vdd_cpu_dir)
goto vdd_cpu_dir_err;
d_edp = debugfs_create_file("edp", S_IRUGO, vdd_cpu_dir, NULL,
&edp_debugfs_fops);
if (!d_edp)
goto edp_err;
d_edp_limit = debugfs_create_file("edp_limit", S_IRUGO, vdd_cpu_dir,
NULL, &edp_limit_debugfs_fops);
if (!d_edp_limit)
goto edp_limit_err;
d_edp_reg_override = debugfs_create_file("edp_reg_override",
S_IRUGO | S_IWUSR, vdd_cpu_dir, NULL,
&edp_reg_override_debugfs_fops);
if (!d_edp_reg_override)
goto edp_reg_override_err;
if (tegra_core_edp_debugfs_init(edp_dir))
goto edp_reg_override_err;
return 0;
edp_reg_override_err:
debugfs_remove(d_edp_limit);
edp_limit_err:
debugfs_remove(d_edp);
edp_err:
debugfs_remove(vdd_cpu_dir);
vdd_cpu_dir_err:
debugfs_remove(edp_dir);
edp_dir_err:
return -ENOMEM;
}
late_initcall(tegra_edp_debugfs_init);
#endif /* CONFIG_DEBUG_FS */