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