/* * Copyright (c) 2013-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 . * * arch/arm/mach-tegra/therm-monitor.c * */ #include #include #include #include #include #include #include "therm-monitor.h" static struct therm_monitor_ldep_data *lc_temp_reg_data; static struct tmon_plat_data tmon_pdata; /* For now assume only one entry */ struct i2c_board_info __initdata tgr_i2c_board_info[1]; /* USB Base address: USB1, USB2, USB3 */ static unsigned int s_tegra_usb_base[] = {0x7D000000, 0x7D004000, 0x7D008000}; /* Fuse USB Calib Value. */ static unsigned int s_fuse_usb_calib_value; static void get_fuse_usb_calib_value(void) { s_fuse_usb_calib_value = readl(IO_TO_VIRT(TEGRA_FUSE_BASE + FUSE_USB_CALIB_0)); } static inline void pg_writel(unsigned long value, unsigned long offset) { writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE) + offset); } static void utmip_temp_dep_update(int curr_rtemp, int utmip_temp_bound) { static int prev_temp; static char initial_update = 1; unsigned int fuse_usb_calib_value = s_fuse_usb_calib_value; int i; char utmip_update_require = 0; /* Extract bits[3:0]. */ fuse_usb_calib_value = fuse_usb_calib_value & 0xF; /* If previous and currentt temperatures falls in the same temperature boundary then no need to update the UTMIP_SPARE_CFG */ if ((prev_temp >= utmip_temp_bound) && (curr_rtemp >= utmip_temp_bound)) utmip_update_require = 0; else if ((prev_temp < utmip_temp_bound) && (curr_rtemp < utmip_temp_bound)) utmip_update_require = 0; else utmip_update_require = 1; /* For Initial call , need to update the UTMIP_SPARE_CFG */ if (initial_update) { utmip_update_require = 1; initial_update = 0; prev_temp = curr_rtemp; } if (utmip_update_require) { prev_temp = curr_rtemp; if (curr_rtemp >= utmip_temp_bound) { fuse_usb_calib_value += 0x1; /* Check if there is a overflow. */ if (fuse_usb_calib_value > UTMIP_XCVR_SETUP_MAX_VAL) fuse_usb_calib_value = UTMIP_XCVR_SETUP_MAX_VAL; } for (i = 0; i < ARRAY_SIZE(s_tegra_usb_base); i++) { unsigned int regval; regval = readl(IO_TO_VIRT(s_tegra_usb_base[i] + UTMIP_SPARE_CFG0)); regval &= ~FUSE_SETUP_SEL; writel(regval, IO_TO_VIRT(s_tegra_usb_base[i] + UTMIP_SPARE_CFG0)); regval = readl(IO_TO_VIRT(s_tegra_usb_base[i] + UTMIP_XCVR_CFG0)); /* If low_to_high, then write 0x2 to HSSLEW_MSB else write 0x8 */ regval &= ~UTMIP_XCVR_HSSLEW_MSB_MSK; regval |= (curr_rtemp >= utmip_temp_bound) ? UTMIP_XCVR_HSSLEW_MSB_HIGH_TEMP_VAL : UTMIP_XCVR_HSSLEW_MSB_LOW_TEMP_VAL; /* write fuse_usb_calib_value to SETUP field of UTMIP_XCVR_CFG0. */ regval &= ~UTMIP_XCVR_SETUP_MSK; regval |= fuse_usb_calib_value; writel(regval, IO_TO_VIRT(s_tegra_usb_base[i] + UTMIP_XCVR_CFG0)); } } } /* Call back function, invoked by driver */ static void ltemp_dependent_reg_update(int curr_ltemp) { int i, j; for (i = 0; lc_temp_reg_data[i].reg_addr != INVALID_ADDR; i++) { for (j = 0; ((j < MAX_NUM_TEMPERAT) && (lc_temp_reg_data[i].temperat[j] != INT_MAX)); j++) { if (curr_ltemp <= lc_temp_reg_data[i].temperat[j]) { if (lc_temp_reg_data[i].previous_val != lc_temp_reg_data[i].value[j]) { pg_writel(lc_temp_reg_data[i].value[j], lc_temp_reg_data[i].reg_addr); lc_temp_reg_data[i].previous_val = lc_temp_reg_data[i].value[j]; } break; } } } } void register_therm_monitor(struct therm_monitor_data *brd_therm_monitor_data) { /* Array which has list of register values with temperature ranges */ lc_temp_reg_data = brd_therm_monitor_data->brd_ltemp_reg_data; /* Thermal monitor operational parameters */ tmon_pdata.delta_temp = brd_therm_monitor_data->delta_temp; tmon_pdata.delta_time = brd_therm_monitor_data->delta_time; tmon_pdata.remote_offset = brd_therm_monitor_data->remote_offset; tmon_pdata.alert_gpio = brd_therm_monitor_data->alert_gpio; /* Local temperature monitoring: Used for pad controls */ if (brd_therm_monitor_data->local_temp_update) tmon_pdata.ltemp_dependent_reg_update = ltemp_dependent_reg_update; /* utmip registers update */ if (brd_therm_monitor_data->utmip_reg_update) { tmon_pdata.utmip_temp_bound = brd_therm_monitor_data->utmip_temp_bound; tmon_pdata.utmip_temp_dep_update = utmip_temp_dep_update; } /* Fill the i2c board info */ strcpy(tgr_i2c_board_info[0].type, brd_therm_monitor_data->i2c_dev_name); tgr_i2c_board_info[0].addr = brd_therm_monitor_data->i2c_dev_addrs; tgr_i2c_board_info[0].platform_data = &tmon_pdata; i2c_register_board_info(brd_therm_monitor_data->i2c_bus_num, tgr_i2c_board_info, 1); get_fuse_usb_calib_value(); }