162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP3/OMAP4 smartreflex device file 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Thara Gopinath <thara@ti.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based originally on code from smartreflex.c 862306a36Sopenharmony_ci * Copyright (C) 2010 Texas Instruments, Inc. 962306a36Sopenharmony_ci * Thara Gopinath <thara@ti.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 1262306a36Sopenharmony_ci * Kalle Jokiniemi 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Copyright (C) 2007 Texas Instruments, Inc. 1562306a36Sopenharmony_ci * Lesly A M <x0080970@ti.com> 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci#include <linux/power/smartreflex.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/err.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/io.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "soc.h" 2462306a36Sopenharmony_ci#include "omap_device.h" 2562306a36Sopenharmony_ci#include "voltage.h" 2662306a36Sopenharmony_ci#include "control.h" 2762306a36Sopenharmony_ci#include "pm.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Read EFUSE values from control registers for OMAP3430 */ 3062306a36Sopenharmony_cistatic void __init sr_set_nvalues(struct omap_volt_data *volt_data, 3162306a36Sopenharmony_ci struct omap_sr_data *sr_data) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct omap_sr_nvalue_table *nvalue_table; 3462306a36Sopenharmony_ci int i, j, count = 0; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci sr_data->nvalue_count = 0; 3762306a36Sopenharmony_ci sr_data->nvalue_table = NULL; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci while (volt_data[count].volt_nominal) 4062306a36Sopenharmony_ci count++; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci nvalue_table = kcalloc(count, sizeof(*nvalue_table), GFP_KERNEL); 4362306a36Sopenharmony_ci if (!nvalue_table) 4462306a36Sopenharmony_ci return; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci for (i = 0, j = 0; i < count; i++) { 4762306a36Sopenharmony_ci u32 v; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* 5062306a36Sopenharmony_ci * In OMAP4 the efuse registers are 24 bit aligned. 5162306a36Sopenharmony_ci * A readl_relaxed will fail for non-32 bit aligned address 5262306a36Sopenharmony_ci * and hence the 8-bit read and shift. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci if (cpu_is_omap44xx()) { 5562306a36Sopenharmony_ci u16 offset = volt_data[i].sr_efuse_offs; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci v = omap_ctrl_readb(offset) | 5862306a36Sopenharmony_ci omap_ctrl_readb(offset + 1) << 8 | 5962306a36Sopenharmony_ci omap_ctrl_readb(offset + 2) << 16; 6062306a36Sopenharmony_ci } else { 6162306a36Sopenharmony_ci v = omap_ctrl_readl(volt_data[i].sr_efuse_offs); 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* 6562306a36Sopenharmony_ci * Many OMAP SoCs don't have the eFuse values set. 6662306a36Sopenharmony_ci * For example, pretty much all OMAP3xxx before 6762306a36Sopenharmony_ci * ES3.something. 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci * XXX There needs to be some way for board files or 7062306a36Sopenharmony_ci * userspace to add these in. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci if (v == 0) 7362306a36Sopenharmony_ci continue; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci nvalue_table[j].nvalue = v; 7662306a36Sopenharmony_ci nvalue_table[j].efuse_offs = volt_data[i].sr_efuse_offs; 7762306a36Sopenharmony_ci nvalue_table[j].errminlimit = volt_data[i].sr_errminlimit; 7862306a36Sopenharmony_ci nvalue_table[j].volt_nominal = volt_data[i].volt_nominal; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci j++; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci sr_data->nvalue_table = nvalue_table; 8462306a36Sopenharmony_ci sr_data->nvalue_count = j; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciextern struct omap_sr_data omap_sr_pdata[]; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int __init sr_init_by_name(const char *name, const char *voltdm) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct omap_sr_data *sr_data = NULL; 9262306a36Sopenharmony_ci struct omap_volt_data *volt_data; 9362306a36Sopenharmony_ci static int i; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (!strncmp(name, "smartreflex_mpu_iva", 20) || 9662306a36Sopenharmony_ci !strncmp(name, "smartreflex_mpu", 16)) 9762306a36Sopenharmony_ci sr_data = &omap_sr_pdata[OMAP_SR_MPU]; 9862306a36Sopenharmony_ci else if (!strncmp(name, "smartreflex_core", 17)) 9962306a36Sopenharmony_ci sr_data = &omap_sr_pdata[OMAP_SR_CORE]; 10062306a36Sopenharmony_ci else if (!strncmp(name, "smartreflex_iva", 16)) 10162306a36Sopenharmony_ci sr_data = &omap_sr_pdata[OMAP_SR_IVA]; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (!sr_data) { 10462306a36Sopenharmony_ci pr_err("%s: Unknown instance %s\n", __func__, name); 10562306a36Sopenharmony_ci return -EINVAL; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci sr_data->name = name; 10962306a36Sopenharmony_ci if (cpu_is_omap343x()) 11062306a36Sopenharmony_ci sr_data->ip_type = 1; 11162306a36Sopenharmony_ci else 11262306a36Sopenharmony_ci sr_data->ip_type = 2; 11362306a36Sopenharmony_ci sr_data->senn_mod = 0x1; 11462306a36Sopenharmony_ci sr_data->senp_mod = 0x1; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (cpu_is_omap34xx() || cpu_is_omap44xx()) { 11762306a36Sopenharmony_ci sr_data->err_weight = OMAP3430_SR_ERRWEIGHT; 11862306a36Sopenharmony_ci sr_data->err_maxlimit = OMAP3430_SR_ERRMAXLIMIT; 11962306a36Sopenharmony_ci sr_data->accum_data = OMAP3430_SR_ACCUMDATA; 12062306a36Sopenharmony_ci if (!(strcmp(sr_data->name, "smartreflex_mpu"))) { 12162306a36Sopenharmony_ci sr_data->senn_avgweight = OMAP3430_SR1_SENNAVGWEIGHT; 12262306a36Sopenharmony_ci sr_data->senp_avgweight = OMAP3430_SR1_SENPAVGWEIGHT; 12362306a36Sopenharmony_ci } else { 12462306a36Sopenharmony_ci sr_data->senn_avgweight = OMAP3430_SR2_SENNAVGWEIGHT; 12562306a36Sopenharmony_ci sr_data->senp_avgweight = OMAP3430_SR2_SENPAVGWEIGHT; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci sr_data->voltdm = voltdm_lookup(voltdm); 13062306a36Sopenharmony_ci if (!sr_data->voltdm) { 13162306a36Sopenharmony_ci pr_err("%s: Unable to get voltage domain pointer for VDD %s\n", 13262306a36Sopenharmony_ci __func__, voltdm); 13362306a36Sopenharmony_ci goto exit; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci omap_voltage_get_volttable(sr_data->voltdm, &volt_data); 13762306a36Sopenharmony_ci if (!volt_data) { 13862306a36Sopenharmony_ci pr_err("%s: No Voltage table registered for VDD%d\n", 13962306a36Sopenharmony_ci __func__, i + 1); 14062306a36Sopenharmony_ci goto exit; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci sr_set_nvalues(volt_data, sr_data); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ciexit: 14662306a36Sopenharmony_ci i++; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#ifdef CONFIG_OMAP_HWMOD 15262306a36Sopenharmony_cistatic int __init sr_dev_init(struct omap_hwmod *oh, void *user) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct omap_smartreflex_dev_attr *sr_dev_attr; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci sr_dev_attr = (struct omap_smartreflex_dev_attr *)oh->dev_attr; 15762306a36Sopenharmony_ci if (!sr_dev_attr || !sr_dev_attr->sensor_voltdm_name) { 15862306a36Sopenharmony_ci pr_err("%s: No voltage domain specified for %s. Cannot initialize\n", 15962306a36Sopenharmony_ci __func__, oh->name); 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return sr_init_by_name(oh->name, sr_dev_attr->sensor_voltdm_name); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci#else 16662306a36Sopenharmony_cistatic int __init sr_dev_init(struct omap_hwmod *oh, void *user) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci return -EINVAL; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci#endif 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic const char * const omap4_sr_instances[] = { 17362306a36Sopenharmony_ci "mpu", 17462306a36Sopenharmony_ci "iva", 17562306a36Sopenharmony_ci "core", 17662306a36Sopenharmony_ci}; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic const char * const dra7_sr_instances[] = { 17962306a36Sopenharmony_ci "mpu", 18062306a36Sopenharmony_ci "core", 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ciint __init omap_devinit_smartreflex(void) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci const char * const *sr_inst = NULL; 18662306a36Sopenharmony_ci int i, nr_sr = 0; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (soc_is_omap44xx()) { 18962306a36Sopenharmony_ci sr_inst = omap4_sr_instances; 19062306a36Sopenharmony_ci nr_sr = ARRAY_SIZE(omap4_sr_instances); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci } else if (soc_is_dra7xx()) { 19362306a36Sopenharmony_ci sr_inst = dra7_sr_instances; 19462306a36Sopenharmony_ci nr_sr = ARRAY_SIZE(dra7_sr_instances); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (nr_sr) { 19862306a36Sopenharmony_ci const char *name, *voltdm; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci for (i = 0; i < nr_sr; i++) { 20162306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "smartreflex_%s", sr_inst[i]); 20262306a36Sopenharmony_ci voltdm = sr_inst[i]; 20362306a36Sopenharmony_ci sr_init_by_name(name, voltdm); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return omap_hwmod_for_each_by_class("smartreflex", sr_dev_init, NULL); 21062306a36Sopenharmony_ci} 211