18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OMAP3/OMAP4 smartreflex device file 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Thara Gopinath <thara@ti.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based originally on code from smartreflex.c 88c2ecf20Sopenharmony_ci * Copyright (C) 2010 Texas Instruments, Inc. 98c2ecf20Sopenharmony_ci * Thara Gopinath <thara@ti.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 128c2ecf20Sopenharmony_ci * Kalle Jokiniemi 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Copyright (C) 2007 Texas Instruments, Inc. 158c2ecf20Sopenharmony_ci * Lesly A M <x0080970@ti.com> 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci#include <linux/power/smartreflex.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/err.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/io.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "soc.h" 248c2ecf20Sopenharmony_ci#include "omap_device.h" 258c2ecf20Sopenharmony_ci#include "voltage.h" 268c2ecf20Sopenharmony_ci#include "control.h" 278c2ecf20Sopenharmony_ci#include "pm.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic bool sr_enable_on_init; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Read EFUSE values from control registers for OMAP3430 */ 328c2ecf20Sopenharmony_cistatic void __init sr_set_nvalues(struct omap_volt_data *volt_data, 338c2ecf20Sopenharmony_ci struct omap_sr_data *sr_data) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct omap_sr_nvalue_table *nvalue_table; 368c2ecf20Sopenharmony_ci int i, j, count = 0; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci sr_data->nvalue_count = 0; 398c2ecf20Sopenharmony_ci sr_data->nvalue_table = NULL; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci while (volt_data[count].volt_nominal) 428c2ecf20Sopenharmony_ci count++; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci nvalue_table = kcalloc(count, sizeof(*nvalue_table), GFP_KERNEL); 458c2ecf20Sopenharmony_ci if (!nvalue_table) 468c2ecf20Sopenharmony_ci return; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci for (i = 0, j = 0; i < count; i++) { 498c2ecf20Sopenharmony_ci u32 v; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* 528c2ecf20Sopenharmony_ci * In OMAP4 the efuse registers are 24 bit aligned. 538c2ecf20Sopenharmony_ci * A readl_relaxed will fail for non-32 bit aligned address 548c2ecf20Sopenharmony_ci * and hence the 8-bit read and shift. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci if (cpu_is_omap44xx()) { 578c2ecf20Sopenharmony_ci u16 offset = volt_data[i].sr_efuse_offs; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci v = omap_ctrl_readb(offset) | 608c2ecf20Sopenharmony_ci omap_ctrl_readb(offset + 1) << 8 | 618c2ecf20Sopenharmony_ci omap_ctrl_readb(offset + 2) << 16; 628c2ecf20Sopenharmony_ci } else { 638c2ecf20Sopenharmony_ci v = omap_ctrl_readl(volt_data[i].sr_efuse_offs); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* 678c2ecf20Sopenharmony_ci * Many OMAP SoCs don't have the eFuse values set. 688c2ecf20Sopenharmony_ci * For example, pretty much all OMAP3xxx before 698c2ecf20Sopenharmony_ci * ES3.something. 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * XXX There needs to be some way for board files or 728c2ecf20Sopenharmony_ci * userspace to add these in. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci if (v == 0) 758c2ecf20Sopenharmony_ci continue; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci nvalue_table[j].nvalue = v; 788c2ecf20Sopenharmony_ci nvalue_table[j].efuse_offs = volt_data[i].sr_efuse_offs; 798c2ecf20Sopenharmony_ci nvalue_table[j].errminlimit = volt_data[i].sr_errminlimit; 808c2ecf20Sopenharmony_ci nvalue_table[j].volt_nominal = volt_data[i].volt_nominal; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci j++; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci sr_data->nvalue_table = nvalue_table; 868c2ecf20Sopenharmony_ci sr_data->nvalue_count = j; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ciextern struct omap_sr_data omap_sr_pdata[]; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int __init sr_init_by_name(const char *name, const char *voltdm) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct omap_sr_data *sr_data = NULL; 948c2ecf20Sopenharmony_ci struct omap_volt_data *volt_data; 958c2ecf20Sopenharmony_ci static int i; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (!strncmp(name, "smartreflex_mpu_iva", 20) || 988c2ecf20Sopenharmony_ci !strncmp(name, "smartreflex_mpu", 16)) 998c2ecf20Sopenharmony_ci sr_data = &omap_sr_pdata[OMAP_SR_MPU]; 1008c2ecf20Sopenharmony_ci else if (!strncmp(name, "smartreflex_core", 17)) 1018c2ecf20Sopenharmony_ci sr_data = &omap_sr_pdata[OMAP_SR_CORE]; 1028c2ecf20Sopenharmony_ci else if (!strncmp(name, "smartreflex_iva", 16)) 1038c2ecf20Sopenharmony_ci sr_data = &omap_sr_pdata[OMAP_SR_IVA]; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (!sr_data) { 1068c2ecf20Sopenharmony_ci pr_err("%s: Unknown instance %s\n", __func__, name); 1078c2ecf20Sopenharmony_ci return -EINVAL; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci sr_data->name = name; 1118c2ecf20Sopenharmony_ci if (cpu_is_omap343x()) 1128c2ecf20Sopenharmony_ci sr_data->ip_type = 1; 1138c2ecf20Sopenharmony_ci else 1148c2ecf20Sopenharmony_ci sr_data->ip_type = 2; 1158c2ecf20Sopenharmony_ci sr_data->senn_mod = 0x1; 1168c2ecf20Sopenharmony_ci sr_data->senp_mod = 0x1; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (cpu_is_omap34xx() || cpu_is_omap44xx()) { 1198c2ecf20Sopenharmony_ci sr_data->err_weight = OMAP3430_SR_ERRWEIGHT; 1208c2ecf20Sopenharmony_ci sr_data->err_maxlimit = OMAP3430_SR_ERRMAXLIMIT; 1218c2ecf20Sopenharmony_ci sr_data->accum_data = OMAP3430_SR_ACCUMDATA; 1228c2ecf20Sopenharmony_ci if (!(strcmp(sr_data->name, "smartreflex_mpu"))) { 1238c2ecf20Sopenharmony_ci sr_data->senn_avgweight = OMAP3430_SR1_SENNAVGWEIGHT; 1248c2ecf20Sopenharmony_ci sr_data->senp_avgweight = OMAP3430_SR1_SENPAVGWEIGHT; 1258c2ecf20Sopenharmony_ci } else { 1268c2ecf20Sopenharmony_ci sr_data->senn_avgweight = OMAP3430_SR2_SENNAVGWEIGHT; 1278c2ecf20Sopenharmony_ci sr_data->senp_avgweight = OMAP3430_SR2_SENPAVGWEIGHT; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci sr_data->voltdm = voltdm_lookup(voltdm); 1328c2ecf20Sopenharmony_ci if (!sr_data->voltdm) { 1338c2ecf20Sopenharmony_ci pr_err("%s: Unable to get voltage domain pointer for VDD %s\n", 1348c2ecf20Sopenharmony_ci __func__, voltdm); 1358c2ecf20Sopenharmony_ci goto exit; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci omap_voltage_get_volttable(sr_data->voltdm, &volt_data); 1398c2ecf20Sopenharmony_ci if (!volt_data) { 1408c2ecf20Sopenharmony_ci pr_err("%s: No Voltage table registered for VDD%d\n", 1418c2ecf20Sopenharmony_ci __func__, i + 1); 1428c2ecf20Sopenharmony_ci goto exit; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci sr_set_nvalues(volt_data, sr_data); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci sr_data->enable_on_init = sr_enable_on_init; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ciexit: 1508c2ecf20Sopenharmony_ci i++; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int __init sr_dev_init(struct omap_hwmod *oh, void *user) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct omap_smartreflex_dev_attr *sr_dev_attr; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci sr_dev_attr = (struct omap_smartreflex_dev_attr *)oh->dev_attr; 1608c2ecf20Sopenharmony_ci if (!sr_dev_attr || !sr_dev_attr->sensor_voltdm_name) { 1618c2ecf20Sopenharmony_ci pr_err("%s: No voltage domain specified for %s. Cannot initialize\n", 1628c2ecf20Sopenharmony_ci __func__, oh->name); 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return sr_init_by_name(oh->name, sr_dev_attr->sensor_voltdm_name); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* 1708c2ecf20Sopenharmony_ci * API to be called from board files to enable smartreflex 1718c2ecf20Sopenharmony_ci * autocompensation at init. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_civoid __init omap_enable_smartreflex_on_init(void) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci sr_enable_on_init = true; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic const char * const omap4_sr_instances[] = { 1798c2ecf20Sopenharmony_ci "mpu", 1808c2ecf20Sopenharmony_ci "iva", 1818c2ecf20Sopenharmony_ci "core", 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic const char * const dra7_sr_instances[] = { 1858c2ecf20Sopenharmony_ci "mpu", 1868c2ecf20Sopenharmony_ci "core", 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ciint __init omap_devinit_smartreflex(void) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci const char * const *sr_inst = NULL; 1928c2ecf20Sopenharmony_ci int i, nr_sr = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (soc_is_omap44xx()) { 1958c2ecf20Sopenharmony_ci sr_inst = omap4_sr_instances; 1968c2ecf20Sopenharmony_ci nr_sr = ARRAY_SIZE(omap4_sr_instances); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci } else if (soc_is_dra7xx()) { 1998c2ecf20Sopenharmony_ci sr_inst = dra7_sr_instances; 2008c2ecf20Sopenharmony_ci nr_sr = ARRAY_SIZE(dra7_sr_instances); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (nr_sr) { 2048c2ecf20Sopenharmony_ci const char *name, *voltdm; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci for (i = 0; i < nr_sr; i++) { 2078c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "smartreflex_%s", sr_inst[i]); 2088c2ecf20Sopenharmony_ci voltdm = sr_inst[i]; 2098c2ecf20Sopenharmony_ci sr_init_by_name(name, voltdm); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return omap_hwmod_for_each_by_class("smartreflex", sr_dev_init, NULL); 2168c2ecf20Sopenharmony_ci} 217