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