162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2016-2017 Texas Instruments Incorporated - https://www.ti.com/
462306a36Sopenharmony_ci *	Nishanth Menon <nm@ti.com>
562306a36Sopenharmony_ci *	Dave Gerlach <d-gerlach@ti.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * TI OPP supply driver that provides override into the regulator control
862306a36Sopenharmony_ci * for generic opp core to handle devices with ABB regulator and/or
962306a36Sopenharmony_ci * SmartReflex Class0.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/clk.h>
1262306a36Sopenharmony_ci#include <linux/cpufreq.h>
1362306a36Sopenharmony_ci#include <linux/device.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/notifier.h>
1762306a36Sopenharmony_ci#include <linux/of_device.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/pm_opp.h>
2162306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/**
2562306a36Sopenharmony_ci * struct ti_opp_supply_optimum_voltage_table - optimized voltage table
2662306a36Sopenharmony_ci * @reference_uv:	reference voltage (usually Nominal voltage)
2762306a36Sopenharmony_ci * @optimized_uv:	Optimized voltage from efuse
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistruct ti_opp_supply_optimum_voltage_table {
3062306a36Sopenharmony_ci	unsigned int reference_uv;
3162306a36Sopenharmony_ci	unsigned int optimized_uv;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/**
3562306a36Sopenharmony_ci * struct ti_opp_supply_data - OMAP specific opp supply data
3662306a36Sopenharmony_ci * @vdd_table:	Optimized voltage mapping table
3762306a36Sopenharmony_ci * @num_vdd_table: number of entries in vdd_table
3862306a36Sopenharmony_ci * @vdd_absolute_max_voltage_uv: absolute maximum voltage in UV for the supply
3962306a36Sopenharmony_ci * @old_supplies: Placeholder for supplies information for old OPP.
4062306a36Sopenharmony_ci * @new_supplies: Placeholder for supplies information for new OPP.
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_cistruct ti_opp_supply_data {
4362306a36Sopenharmony_ci	struct ti_opp_supply_optimum_voltage_table *vdd_table;
4462306a36Sopenharmony_ci	u32 num_vdd_table;
4562306a36Sopenharmony_ci	u32 vdd_absolute_max_voltage_uv;
4662306a36Sopenharmony_ci	struct dev_pm_opp_supply old_supplies[2];
4762306a36Sopenharmony_ci	struct dev_pm_opp_supply new_supplies[2];
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic struct ti_opp_supply_data opp_data;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/**
5362306a36Sopenharmony_ci * struct ti_opp_supply_of_data - device tree match data
5462306a36Sopenharmony_ci * @flags:	specific type of opp supply
5562306a36Sopenharmony_ci * @efuse_voltage_mask: mask required for efuse register representing voltage
5662306a36Sopenharmony_ci * @efuse_voltage_uv: Are the efuse entries in micro-volts? if not, assume
5762306a36Sopenharmony_ci *		milli-volts.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_cistruct ti_opp_supply_of_data {
6062306a36Sopenharmony_ci#define OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE	BIT(1)
6162306a36Sopenharmony_ci#define OPPDM_HAS_NO_ABB			BIT(2)
6262306a36Sopenharmony_ci	const u8 flags;
6362306a36Sopenharmony_ci	const u32 efuse_voltage_mask;
6462306a36Sopenharmony_ci	const bool efuse_voltage_uv;
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/**
6862306a36Sopenharmony_ci * _store_optimized_voltages() - store optimized voltages
6962306a36Sopenharmony_ci * @dev:	ti opp supply device for which we need to store info
7062306a36Sopenharmony_ci * @data:	data specific to the device
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * Picks up efuse based optimized voltages for VDD unique per device and
7362306a36Sopenharmony_ci * stores it in internal data structure for use during transition requests.
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * Return: If successful, 0, else appropriate error value.
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_cistatic int _store_optimized_voltages(struct device *dev,
7862306a36Sopenharmony_ci				     struct ti_opp_supply_data *data)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	void __iomem *base;
8162306a36Sopenharmony_ci	struct property *prop;
8262306a36Sopenharmony_ci	struct resource *res;
8362306a36Sopenharmony_ci	const __be32 *val;
8462306a36Sopenharmony_ci	int proplen, i;
8562306a36Sopenharmony_ci	int ret = 0;
8662306a36Sopenharmony_ci	struct ti_opp_supply_optimum_voltage_table *table;
8762306a36Sopenharmony_ci	const struct ti_opp_supply_of_data *of_data = dev_get_drvdata(dev);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* pick up Efuse based voltages */
9062306a36Sopenharmony_ci	res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 0);
9162306a36Sopenharmony_ci	if (!res) {
9262306a36Sopenharmony_ci		dev_err(dev, "Unable to get IO resource\n");
9362306a36Sopenharmony_ci		ret = -ENODEV;
9462306a36Sopenharmony_ci		goto out_map;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	base = ioremap(res->start, resource_size(res));
9862306a36Sopenharmony_ci	if (!base) {
9962306a36Sopenharmony_ci		dev_err(dev, "Unable to map Efuse registers\n");
10062306a36Sopenharmony_ci		ret = -ENOMEM;
10162306a36Sopenharmony_ci		goto out_map;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* Fetch efuse-settings. */
10562306a36Sopenharmony_ci	prop = of_find_property(dev->of_node, "ti,efuse-settings", NULL);
10662306a36Sopenharmony_ci	if (!prop) {
10762306a36Sopenharmony_ci		dev_err(dev, "No 'ti,efuse-settings' property found\n");
10862306a36Sopenharmony_ci		ret = -EINVAL;
10962306a36Sopenharmony_ci		goto out;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	proplen = prop->length / sizeof(int);
11362306a36Sopenharmony_ci	data->num_vdd_table = proplen / 2;
11462306a36Sopenharmony_ci	/* Verify for corrupted OPP entries in dt */
11562306a36Sopenharmony_ci	if (data->num_vdd_table * 2 * sizeof(int) != prop->length) {
11662306a36Sopenharmony_ci		dev_err(dev, "Invalid 'ti,efuse-settings'\n");
11762306a36Sopenharmony_ci		ret = -EINVAL;
11862306a36Sopenharmony_ci		goto out;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	ret = of_property_read_u32(dev->of_node, "ti,absolute-max-voltage-uv",
12262306a36Sopenharmony_ci				   &data->vdd_absolute_max_voltage_uv);
12362306a36Sopenharmony_ci	if (ret) {
12462306a36Sopenharmony_ci		dev_err(dev, "ti,absolute-max-voltage-uv is missing\n");
12562306a36Sopenharmony_ci		ret = -EINVAL;
12662306a36Sopenharmony_ci		goto out;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	table = kcalloc(data->num_vdd_table, sizeof(*data->vdd_table),
13062306a36Sopenharmony_ci			GFP_KERNEL);
13162306a36Sopenharmony_ci	if (!table) {
13262306a36Sopenharmony_ci		ret = -ENOMEM;
13362306a36Sopenharmony_ci		goto out;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci	data->vdd_table = table;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	val = prop->value;
13862306a36Sopenharmony_ci	for (i = 0; i < data->num_vdd_table; i++, table++) {
13962306a36Sopenharmony_ci		u32 efuse_offset;
14062306a36Sopenharmony_ci		u32 tmp;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		table->reference_uv = be32_to_cpup(val++);
14362306a36Sopenharmony_ci		efuse_offset = be32_to_cpup(val++);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		tmp = readl(base + efuse_offset);
14662306a36Sopenharmony_ci		tmp &= of_data->efuse_voltage_mask;
14762306a36Sopenharmony_ci		tmp >>= __ffs(of_data->efuse_voltage_mask);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		table->optimized_uv = of_data->efuse_voltage_uv ? tmp :
15062306a36Sopenharmony_ci					tmp * 1000;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		dev_dbg(dev, "[%d] efuse=0x%08x volt_table=%d vset=%d\n",
15362306a36Sopenharmony_ci			i, efuse_offset, table->reference_uv,
15462306a36Sopenharmony_ci			table->optimized_uv);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		/*
15762306a36Sopenharmony_ci		 * Some older samples might not have optimized efuse
15862306a36Sopenharmony_ci		 * Use reference voltage for those - just add debug message
15962306a36Sopenharmony_ci		 * for them.
16062306a36Sopenharmony_ci		 */
16162306a36Sopenharmony_ci		if (!table->optimized_uv) {
16262306a36Sopenharmony_ci			dev_dbg(dev, "[%d] efuse=0x%08x volt_table=%d:vset0\n",
16362306a36Sopenharmony_ci				i, efuse_offset, table->reference_uv);
16462306a36Sopenharmony_ci			table->optimized_uv = table->reference_uv;
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ciout:
16862306a36Sopenharmony_ci	iounmap(base);
16962306a36Sopenharmony_ciout_map:
17062306a36Sopenharmony_ci	return ret;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci/**
17462306a36Sopenharmony_ci * _free_optimized_voltages() - free resources for optvoltages
17562306a36Sopenharmony_ci * @dev:	device for which we need to free info
17662306a36Sopenharmony_ci * @data:	data specific to the device
17762306a36Sopenharmony_ci */
17862306a36Sopenharmony_cistatic void _free_optimized_voltages(struct device *dev,
17962306a36Sopenharmony_ci				     struct ti_opp_supply_data *data)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	kfree(data->vdd_table);
18262306a36Sopenharmony_ci	data->vdd_table = NULL;
18362306a36Sopenharmony_ci	data->num_vdd_table = 0;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/**
18762306a36Sopenharmony_ci * _get_optimal_vdd_voltage() - Finds optimal voltage for the supply
18862306a36Sopenharmony_ci * @dev:	device for which we need to find info
18962306a36Sopenharmony_ci * @data:	data specific to the device
19062306a36Sopenharmony_ci * @reference_uv:	reference voltage (OPP voltage) for which we need value
19162306a36Sopenharmony_ci *
19262306a36Sopenharmony_ci * Return: if a match is found, return optimized voltage, else return
19362306a36Sopenharmony_ci * reference_uv, also return reference_uv if no optimization is needed.
19462306a36Sopenharmony_ci */
19562306a36Sopenharmony_cistatic int _get_optimal_vdd_voltage(struct device *dev,
19662306a36Sopenharmony_ci				    struct ti_opp_supply_data *data,
19762306a36Sopenharmony_ci				    int reference_uv)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	int i;
20062306a36Sopenharmony_ci	struct ti_opp_supply_optimum_voltage_table *table;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if (!data->num_vdd_table)
20362306a36Sopenharmony_ci		return reference_uv;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	table = data->vdd_table;
20662306a36Sopenharmony_ci	if (!table)
20762306a36Sopenharmony_ci		return -EINVAL;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/* Find a exact match - this list is usually very small */
21062306a36Sopenharmony_ci	for (i = 0; i < data->num_vdd_table; i++, table++)
21162306a36Sopenharmony_ci		if (table->reference_uv == reference_uv)
21262306a36Sopenharmony_ci			return table->optimized_uv;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* IF things are screwed up, we'd make a mess on console.. ratelimit */
21562306a36Sopenharmony_ci	dev_err_ratelimited(dev, "%s: Failed optimized voltage match for %d\n",
21662306a36Sopenharmony_ci			    __func__, reference_uv);
21762306a36Sopenharmony_ci	return reference_uv;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic int _opp_set_voltage(struct device *dev,
22162306a36Sopenharmony_ci			    struct dev_pm_opp_supply *supply,
22262306a36Sopenharmony_ci			    int new_target_uv, struct regulator *reg,
22362306a36Sopenharmony_ci			    char *reg_name)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	int ret;
22662306a36Sopenharmony_ci	unsigned long vdd_uv, uv_max;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (new_target_uv)
22962306a36Sopenharmony_ci		vdd_uv = new_target_uv;
23062306a36Sopenharmony_ci	else
23162306a36Sopenharmony_ci		vdd_uv = supply->u_volt;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/*
23462306a36Sopenharmony_ci	 * If we do have an absolute max voltage specified, then we should
23562306a36Sopenharmony_ci	 * use that voltage instead to allow for cases where the voltage rails
23662306a36Sopenharmony_ci	 * are ganged (example if we set the max for an opp as 1.12v, and
23762306a36Sopenharmony_ci	 * the absolute max is 1.5v, for another rail to get 1.25v, it cannot
23862306a36Sopenharmony_ci	 * be achieved if the regulator is constrainted to max of 1.12v, even
23962306a36Sopenharmony_ci	 * if it can function at 1.25v
24062306a36Sopenharmony_ci	 */
24162306a36Sopenharmony_ci	if (opp_data.vdd_absolute_max_voltage_uv)
24262306a36Sopenharmony_ci		uv_max = opp_data.vdd_absolute_max_voltage_uv;
24362306a36Sopenharmony_ci	else
24462306a36Sopenharmony_ci		uv_max = supply->u_volt_max;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (vdd_uv > uv_max ||
24762306a36Sopenharmony_ci	    vdd_uv < supply->u_volt_min ||
24862306a36Sopenharmony_ci	    supply->u_volt_min > uv_max) {
24962306a36Sopenharmony_ci		dev_warn(dev,
25062306a36Sopenharmony_ci			 "Invalid range voltages [Min:%lu target:%lu Max:%lu]\n",
25162306a36Sopenharmony_ci			 supply->u_volt_min, vdd_uv, uv_max);
25262306a36Sopenharmony_ci		return -EINVAL;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	dev_dbg(dev, "%s scaling to %luuV[min %luuV max %luuV]\n", reg_name,
25662306a36Sopenharmony_ci		vdd_uv, supply->u_volt_min,
25762306a36Sopenharmony_ci		uv_max);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	ret = regulator_set_voltage_triplet(reg,
26062306a36Sopenharmony_ci					    supply->u_volt_min,
26162306a36Sopenharmony_ci					    vdd_uv,
26262306a36Sopenharmony_ci					    uv_max);
26362306a36Sopenharmony_ci	if (ret) {
26462306a36Sopenharmony_ci		dev_err(dev, "%s failed for %luuV[min %luuV max %luuV]\n",
26562306a36Sopenharmony_ci			reg_name, vdd_uv, supply->u_volt_min,
26662306a36Sopenharmony_ci			uv_max);
26762306a36Sopenharmony_ci		return ret;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return 0;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci/* Do the opp supply transition */
27462306a36Sopenharmony_cistatic int ti_opp_config_regulators(struct device *dev,
27562306a36Sopenharmony_ci			struct dev_pm_opp *old_opp, struct dev_pm_opp *new_opp,
27662306a36Sopenharmony_ci			struct regulator **regulators, unsigned int count)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct dev_pm_opp_supply *old_supply_vdd = &opp_data.old_supplies[0];
27962306a36Sopenharmony_ci	struct dev_pm_opp_supply *old_supply_vbb = &opp_data.old_supplies[1];
28062306a36Sopenharmony_ci	struct dev_pm_opp_supply *new_supply_vdd = &opp_data.new_supplies[0];
28162306a36Sopenharmony_ci	struct dev_pm_opp_supply *new_supply_vbb = &opp_data.new_supplies[1];
28262306a36Sopenharmony_ci	struct regulator *vdd_reg = regulators[0];
28362306a36Sopenharmony_ci	struct regulator *vbb_reg = regulators[1];
28462306a36Sopenharmony_ci	unsigned long old_freq, freq;
28562306a36Sopenharmony_ci	int vdd_uv;
28662306a36Sopenharmony_ci	int ret;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* We must have two regulators here */
28962306a36Sopenharmony_ci	WARN_ON(count != 2);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/* Fetch supplies and freq information from OPP core */
29262306a36Sopenharmony_ci	ret = dev_pm_opp_get_supplies(new_opp, opp_data.new_supplies);
29362306a36Sopenharmony_ci	WARN_ON(ret);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	old_freq = dev_pm_opp_get_freq(old_opp);
29662306a36Sopenharmony_ci	freq = dev_pm_opp_get_freq(new_opp);
29762306a36Sopenharmony_ci	WARN_ON(!old_freq || !freq);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	vdd_uv = _get_optimal_vdd_voltage(dev, &opp_data,
30062306a36Sopenharmony_ci					  new_supply_vdd->u_volt);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (new_supply_vdd->u_volt_min < vdd_uv)
30362306a36Sopenharmony_ci		new_supply_vdd->u_volt_min = vdd_uv;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* Scaling up? Scale voltage before frequency */
30662306a36Sopenharmony_ci	if (freq > old_freq) {
30762306a36Sopenharmony_ci		ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg,
30862306a36Sopenharmony_ci				       "vdd");
30962306a36Sopenharmony_ci		if (ret)
31062306a36Sopenharmony_ci			goto restore_voltage;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb");
31362306a36Sopenharmony_ci		if (ret)
31462306a36Sopenharmony_ci			goto restore_voltage;
31562306a36Sopenharmony_ci	} else {
31662306a36Sopenharmony_ci		ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb");
31762306a36Sopenharmony_ci		if (ret)
31862306a36Sopenharmony_ci			goto restore_voltage;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg,
32162306a36Sopenharmony_ci				       "vdd");
32262306a36Sopenharmony_ci		if (ret)
32362306a36Sopenharmony_ci			goto restore_voltage;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cirestore_voltage:
32962306a36Sopenharmony_ci	/* Fetch old supplies information only if required */
33062306a36Sopenharmony_ci	ret = dev_pm_opp_get_supplies(old_opp, opp_data.old_supplies);
33162306a36Sopenharmony_ci	WARN_ON(ret);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	/* This shouldn't harm even if the voltages weren't updated earlier */
33462306a36Sopenharmony_ci	if (old_supply_vdd->u_volt) {
33562306a36Sopenharmony_ci		ret = _opp_set_voltage(dev, old_supply_vbb, 0, vbb_reg, "vbb");
33662306a36Sopenharmony_ci		if (ret)
33762306a36Sopenharmony_ci			return ret;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		ret = _opp_set_voltage(dev, old_supply_vdd, 0, vdd_reg,
34062306a36Sopenharmony_ci				       "vdd");
34162306a36Sopenharmony_ci		if (ret)
34262306a36Sopenharmony_ci			return ret;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return ret;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic const struct ti_opp_supply_of_data omap_generic_of_data = {
34962306a36Sopenharmony_ci};
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic const struct ti_opp_supply_of_data omap_omap5_of_data = {
35262306a36Sopenharmony_ci	.flags = OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE,
35362306a36Sopenharmony_ci	.efuse_voltage_mask = 0xFFF,
35462306a36Sopenharmony_ci	.efuse_voltage_uv = false,
35562306a36Sopenharmony_ci};
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic const struct ti_opp_supply_of_data omap_omap5core_of_data = {
35862306a36Sopenharmony_ci	.flags = OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE | OPPDM_HAS_NO_ABB,
35962306a36Sopenharmony_ci	.efuse_voltage_mask = 0xFFF,
36062306a36Sopenharmony_ci	.efuse_voltage_uv = false,
36162306a36Sopenharmony_ci};
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic const struct of_device_id ti_opp_supply_of_match[] = {
36462306a36Sopenharmony_ci	{.compatible = "ti,omap-opp-supply", .data = &omap_generic_of_data},
36562306a36Sopenharmony_ci	{.compatible = "ti,omap5-opp-supply", .data = &omap_omap5_of_data},
36662306a36Sopenharmony_ci	{.compatible = "ti,omap5-core-opp-supply",
36762306a36Sopenharmony_ci	 .data = &omap_omap5core_of_data},
36862306a36Sopenharmony_ci	{},
36962306a36Sopenharmony_ci};
37062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ti_opp_supply_of_match);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic int ti_opp_supply_probe(struct platform_device *pdev)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
37562306a36Sopenharmony_ci	struct device *cpu_dev = get_cpu_device(0);
37662306a36Sopenharmony_ci	const struct of_device_id *match;
37762306a36Sopenharmony_ci	const struct ti_opp_supply_of_data *of_data;
37862306a36Sopenharmony_ci	int ret = 0;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	match = of_match_device(ti_opp_supply_of_match, dev);
38162306a36Sopenharmony_ci	if (!match) {
38262306a36Sopenharmony_ci		/* We do not expect this to happen */
38362306a36Sopenharmony_ci		dev_err(dev, "%s: Unable to match device\n", __func__);
38462306a36Sopenharmony_ci		return -ENODEV;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci	if (!match->data) {
38762306a36Sopenharmony_ci		/* Again, unlikely.. but mistakes do happen */
38862306a36Sopenharmony_ci		dev_err(dev, "%s: Bad data in match\n", __func__);
38962306a36Sopenharmony_ci		return -EINVAL;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci	of_data = match->data;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	dev_set_drvdata(dev, (void *)of_data);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* If we need optimized voltage */
39662306a36Sopenharmony_ci	if (of_data->flags & OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE) {
39762306a36Sopenharmony_ci		ret = _store_optimized_voltages(dev, &opp_data);
39862306a36Sopenharmony_ci		if (ret)
39962306a36Sopenharmony_ci			return ret;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	ret = dev_pm_opp_set_config_regulators(cpu_dev, ti_opp_config_regulators);
40362306a36Sopenharmony_ci	if (ret < 0)
40462306a36Sopenharmony_ci		_free_optimized_voltages(dev, &opp_data);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return ret;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic struct platform_driver ti_opp_supply_driver = {
41062306a36Sopenharmony_ci	.probe = ti_opp_supply_probe,
41162306a36Sopenharmony_ci	.driver = {
41262306a36Sopenharmony_ci		   .name = "ti_opp_supply",
41362306a36Sopenharmony_ci		   .of_match_table = of_match_ptr(ti_opp_supply_of_match),
41462306a36Sopenharmony_ci		   },
41562306a36Sopenharmony_ci};
41662306a36Sopenharmony_cimodule_platform_driver(ti_opp_supply_driver);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments OMAP OPP Supply driver");
41962306a36Sopenharmony_ciMODULE_AUTHOR("Texas Instruments Inc.");
42062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
421