code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP Voltage Controller (VC) interface
 *
 * Copyright (C) 2011 Texas Instruments, Inc.
 */
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/bug.h>
#include <linux/io.h>

#include <asm/div64.h>

#include "iomap.h"
#include "soc.h"
#include "voltage.h"
#include "vc.h"
#include "prm-regbits-34xx.h"
#include "prm-regbits-44xx.h"
#include "prm44xx.h"
#include "pm.h"
#include "scrm44xx.h"
#include "control.h"

#define OMAP4430_VDD_IVA_I2C_DISABLE		BIT(14)
#define OMAP4430_VDD_MPU_I2C_DISABLE		BIT(13)
#define OMAP4430_VDD_CORE_I2C_DISABLE		BIT(12)
#define OMAP4430_VDD_IVA_PRESENCE		BIT(9)
#define OMAP4430_VDD_MPU_PRESENCE		BIT(8)
#define OMAP4430_AUTO_CTRL_VDD_IVA(x)		((x) << 4)
#define OMAP4430_AUTO_CTRL_VDD_MPU(x)		((x) << 2)
#define OMAP4430_AUTO_CTRL_VDD_CORE(x)		((x) << 0)
#define OMAP4430_AUTO_CTRL_VDD_RET		2

#define OMAP4430_VDD_I2C_DISABLE_MASK	\
	(OMAP4430_VDD_IVA_I2C_DISABLE | \
	 OMAP4430_VDD_MPU_I2C_DISABLE | \
	 OMAP4430_VDD_CORE_I2C_DISABLE)

#define OMAP4_VDD_DEFAULT_VAL	\
	(OMAP4430_VDD_I2C_DISABLE_MASK | \
	 OMAP4430_VDD_IVA_PRESENCE | OMAP4430_VDD_MPU_PRESENCE | \
	 OMAP4430_AUTO_CTRL_VDD_IVA(OMAP4430_AUTO_CTRL_VDD_RET) | \
	 OMAP4430_AUTO_CTRL_VDD_MPU(OMAP4430_AUTO_CTRL_VDD_RET) | \
	 OMAP4430_AUTO_CTRL_VDD_CORE(OMAP4430_AUTO_CTRL_VDD_RET))

#define OMAP4_VDD_RET_VAL	\
	(OMAP4_VDD_DEFAULT_VAL & ~OMAP4430_VDD_I2C_DISABLE_MASK)

/**
 * struct omap_vc_channel_cfg - describe the cfg_channel bitfield
 * @sa: bit for slave address
 * @rav: bit for voltage configuration register
 * @rac: bit for command configuration register
 * @racen: enable bit for RAC
 * @cmd: bit for command value set selection
 *
 * Channel configuration bits, common for OMAP3+
 * OMAP3 register: PRM_VC_CH_CONF
 * OMAP4 register: PRM_VC_CFG_CHANNEL
 * OMAP5 register: PRM_VC_SMPS_<voltdm>_CONFIG
 */
struct omap_vc_channel_cfg {
	u8 sa;
	u8 rav;
	u8 rac;
	u8 racen;
	u8 cmd;
};

static struct omap_vc_channel_cfg vc_default_channel_cfg = {
	.sa    = BIT(0),
	.rav   = BIT(1),
	.rac   = BIT(2),
	.racen = BIT(3),
	.cmd   = BIT(4),
};

/*
 * On OMAP3+, all VC channels have the above default bitfield
 * configuration, except the OMAP4 MPU channel.  This appears
 * to be a freak accident as every other VC channel has the
 * default configuration, thus creating a mutant channel config.
 */
static struct omap_vc_channel_cfg vc_mutant_channel_cfg = {
	.sa    = BIT(0),
	.rav   = BIT(2),
	.rac   = BIT(3),
	.racen = BIT(4),
	.cmd   = BIT(1),
};

static struct omap_vc_channel_cfg *vc_cfg_bits;

/* Default I2C trace length on pcb, 6.3cm. Used for capacitance calculations. */
static u32 sr_i2c_pcb_length = 63;
#define CFG_CHANNEL_MASK 0x1f

/**
 * omap_vc_config_channel - configure VC channel to PMIC mappings
 * @voltdm: pointer to voltagdomain defining the desired VC channel
 *
 * Configures the VC channel to PMIC mappings for the following
 * PMIC settings
 * - i2c slave address (SA)
 * - voltage configuration address (RAV)
 * - command configuration address (RAC) and enable bit (RACEN)
 * - command values for ON, ONLP, RET and OFF (CMD)
 *
 * This function currently only allows flexible configuration of the
 * non-default channel.  Starting with OMAP4, there are more than 2
 * channels, with one defined as the default (on OMAP4, it's MPU.)
 * Only the non-default channel can be configured.
 */
static int omap_vc_config_channel(struct voltagedomain *voltdm)
{
	struct omap_vc_channel *vc = voltdm->vc;

	/*
	 * For default channel, the only configurable bit is RACEN.
	 * All others must stay at zero (see function comment above.)
	 */
	if (vc->flags & OMAP_VC_CHANNEL_DEFAULT)
		vc->cfg_channel &= vc_cfg_bits->racen;

	voltdm->rmw(CFG_CHANNEL_MASK << vc->cfg_channel_sa_shift,
		    vc->cfg_channel << vc->cfg_channel_sa_shift,
		    vc->cfg_channel_reg);

	return 0;
}

/* Voltage scale and accessory APIs */
int omap_vc_pre_scale(struct voltagedomain *voltdm,
		      unsigned long target_volt,
		      u8 *target_vsel, u8 *current_vsel)
{
	struct omap_vc_channel *vc = voltdm->vc;
	u32 vc_cmdval;

	/* Check if sufficient pmic info is available for this vdd */
	if (!voltdm->pmic) {
		pr_err("%s: Insufficient pmic info to scale the vdd_%s\n",
			__func__, voltdm->name);
		return -EINVAL;
	}

	if (!voltdm->pmic->uv_to_vsel) {
		pr_err("%s: PMIC function to convert voltage in uV to vsel not registered. Hence unable to scale voltage for vdd_%s\n",
		       __func__, voltdm->name);
		return -ENODATA;
	}

	if (!voltdm->read || !voltdm->write) {
		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
			__func__, voltdm->name);
		return -EINVAL;
	}

	*target_vsel = voltdm->pmic->uv_to_vsel(target_volt);
	*current_vsel = voltdm->pmic->uv_to_vsel(voltdm->nominal_volt);

	/* Setting the ON voltage to the new target voltage */
	vc_cmdval = voltdm->read(vc->cmdval_reg);
	vc_cmdval &= ~vc->common->cmd_on_mask;
	vc_cmdval |= (*target_vsel << vc->common->cmd_on_shift);
	voltdm->write(vc_cmdval, vc->cmdval_reg);

	voltdm->vc_param->on = target_volt;

	omap_vp_update_errorgain(voltdm, target_volt);

	return 0;
}

void omap_vc_post_scale(struct voltagedomain *voltdm,
			unsigned long target_volt,
			u8 target_vsel, u8 current_vsel)
{
	u32 smps_steps = 0, smps_delay = 0;

	smps_steps = abs(target_vsel - current_vsel);
	/* SMPS slew rate / step size. 2us added as buffer. */
	smps_delay = ((smps_steps * voltdm->pmic->step_size) /
			voltdm->pmic->slew_rate) + 2;
	udelay(smps_delay);
}

/* vc_bypass_scale - VC bypass method of voltage scaling */
int omap_vc_bypass_scale(struct voltagedomain *voltdm,
			 unsigned long target_volt)
{
	struct omap_vc_channel *vc = voltdm->vc;
	u32 loop_cnt = 0, retries_cnt = 0;
	u32 vc_valid, vc_bypass_val_reg, vc_bypass_value;
	u8 target_vsel, current_vsel;
	int ret;

	ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, &current_vsel);
	if (ret)
		return ret;

	vc_valid = vc->common->valid;
	vc_bypass_val_reg = vc->common->bypass_val_reg;
	vc_bypass_value = (target_vsel << vc->common->data_shift) |
		(vc->volt_reg_addr << vc->common->regaddr_shift) |
		(vc->i2c_slave_addr << vc->common->slaveaddr_shift);

	voltdm->write(vc_bypass_value, vc_bypass_val_reg);
	voltdm->write(vc_bypass_value | vc_valid, vc_bypass_val_reg);

	vc_bypass_value = voltdm->read(vc_bypass_val_reg);
	/*
	 * Loop till the bypass command is acknowledged from the SMPS.
	 * NOTE: This is legacy code. The loop count and retry count needs
	 * to be revisited.
	 */
	while (!(vc_bypass_value & vc_valid)) {
		loop_cnt++;

		if (retries_cnt > 10) {
			pr_warn("%s: Retry count exceeded\n", __func__);
			return -ETIMEDOUT;
		}

		if (loop_cnt > 50) {
			retries_cnt++;
			loop_cnt = 0;
			udelay(10);
		}
		vc_bypass_value = voltdm->read(vc_bypass_val_reg);
	}

	omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel);
	return 0;
}

/* Convert microsecond value to number of 32kHz clock cycles */
static inline u32 omap_usec_to_32k(u32 usec)
{
	return DIV_ROUND_UP_ULL(32768ULL * (u64)usec, 1000000ULL);
}

struct omap3_vc_timings {
	u32 voltsetup1;
	u32 voltsetup2;
};

struct omap3_vc {
	struct voltagedomain *vd;
	u32 voltctrl;
	u32 voltsetup1;
	u32 voltsetup2;
	struct omap3_vc_timings timings[2];
};
static struct omap3_vc vc;

void omap3_vc_set_pmic_signaling(int core_next_state)
{
	struct voltagedomain *vd = vc.vd;
	struct omap3_vc_timings *c = vc.timings;
	u32 voltctrl, voltsetup1, voltsetup2;

	voltctrl = vc.voltctrl;
	voltsetup1 = vc.voltsetup1;
	voltsetup2 = vc.voltsetup2;

	switch (core_next_state) {
	case PWRDM_POWER_OFF:
		voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_RET |
			      OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP);
		voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_OFF;
		if (voltctrl & OMAP3430_PRM_VOLTCTRL_SEL_OFF)
			voltsetup2 = c->voltsetup2;
		else
			voltsetup1 = c->voltsetup1;
		break;
	case PWRDM_POWER_RET:
	default:
		c++;
		voltctrl &= ~(OMAP3430_PRM_VOLTCTRL_AUTO_OFF |
			      OMAP3430_PRM_VOLTCTRL_AUTO_SLEEP);
		voltctrl |= OMAP3430_PRM_VOLTCTRL_AUTO_RET;
		voltsetup1 = c->voltsetup1;
		break;
	}

	if (voltctrl != vc.voltctrl) {
		vd->write(voltctrl, OMAP3_PRM_VOLTCTRL_OFFSET);
		vc.voltctrl = voltctrl;
	}
	if (voltsetup1 != vc.voltsetup1) {
		vd->write(c->voltsetup1,
			  OMAP3_PRM_VOLTSETUP1_OFFSET);
		vc.voltsetup1 = voltsetup1;
	}
	if (voltsetup2 != vc.voltsetup2) {
		vd->write(c->voltsetup2,
			  OMAP3_PRM_VOLTSETUP2_OFFSET);
		vc.voltsetup2 = voltsetup2;
	}
}

void omap4_vc_set_pmic_signaling(int core_next_state)
{
	struct voltagedomain *vd = vc.vd;
	u32 val;

	if (!vd)
		return;

	switch (core_next_state) {
	case PWRDM_POWER_RET:
		val = OMAP4_VDD_RET_VAL;
		break;
	default:
		val = OMAP4_VDD_DEFAULT_VAL;
		break;
	}

	vd->write(val, OMAP4_PRM_VOLTCTRL_OFFSET);
}

/*
 * Configure signal polarity for sys_clkreq and sys_off_mode pins
 * as the default values are wrong and can cause the system to hang
 * if any twl4030 scripts are loaded.
 */
static void __init omap3_vc_init_pmic_signaling(struct voltagedomain *voltdm)
{
	u32 val;

	if (vc.vd)
		return;

	vc.vd = voltdm;

	val = voltdm->read(OMAP3_PRM_POLCTRL_OFFSET);
	if (!(val & OMAP3430_PRM_POLCTRL_CLKREQ_POL) ||
	    (val & OMAP3430_PRM_POLCTRL_OFFMODE_POL)) {
		val |= OMAP3430_PRM_POLCTRL_CLKREQ_POL;
		val &= ~OMAP3430_PRM_POLCTRL_OFFMODE_POL;
		pr_debug("PM: fixing sys_clkreq and sys_off_mode polarity to 0x%x\n",
			 val);
		voltdm->write(val, OMAP3_PRM_POLCTRL_OFFSET);
	}

	/*
	 * By default let's use I2C4 signaling for retention idle
	 * and sys_off_mode pin signaling for off idle. This way we
	 * have sys_clk_req pin go down for retention and both
	 * sys_clk_req and sys_off_mode pins will go down for off
	 * idle. And we can also scale voltages to zero for off-idle.
	 * Note that no actual voltage scaling during off-idle will
	 * happen unless the board specific twl4030 PMIC scripts are
	 * loaded. See also omap_vc_i2c_init for comments regarding
	 * erratum i531.
	 */
	val = voltdm->read(OMAP3_PRM_VOLTCTRL_OFFSET);
	if (!(val & OMAP3430_PRM_VOLTCTRL_SEL_OFF)) {
		val |= OMAP3430_PRM_VOLTCTRL_SEL_OFF;
		pr_debug("PM: setting voltctrl sys_off_mode signaling to 0x%x\n",
			 val);
		voltdm->write(val, OMAP3_PRM_VOLTCTRL_OFFSET);
	}
	vc.voltctrl = val;

	omap3_vc_set_pmic_signaling(PWRDM_POWER_ON);
}

static void omap3_init_voltsetup1(struct voltagedomain *voltdm,
				  struct omap3_vc_timings *c, u32 idle)
{
	unsigned long val;

	val = (voltdm->vc_param->on - idle) / voltdm->pmic->slew_rate;
	val *= voltdm->sys_clk.rate / 8 / 1000000 + 1;
	val <<= __ffs(voltdm->vfsm->voltsetup_mask);
	c->voltsetup1 &= ~voltdm->vfsm->voltsetup_mask;
	c->voltsetup1 |= val;
}

/**
 * omap3_set_i2c_timings - sets i2c sleep timings for a channel
 * @voltdm: channel to configure
 * @off_mode: select whether retention or off mode values used
 *
 * Calculates and sets up voltage controller to use I2C based
 * voltage scaling for sleep modes. This can be used for either off mode
 * or retention. Off mode has additionally an option to use sys_off_mode
 * pad, which uses a global signal to program the whole power IC to
 * off-mode.
 *
 * Note that pmic is not controlling the voltage scaling during
 * retention signaled over I2C4, so we can keep voltsetup2 as 0.
 * And the oscillator is not shut off over I2C4, so no need to
 * set clksetup.
 */
static void omap3_set_i2c_timings(struct voltagedomain *voltdm)
{
	struct omap3_vc_timings *c = vc.timings;

	/* Configure PRWDM_POWER_OFF over I2C4 */
	omap3_init_voltsetup1(voltdm, c, voltdm->vc_param->off);
	c++;
	/* Configure PRWDM_POWER_RET over I2C4 */
	omap3_init_voltsetup1(voltdm, c, voltdm->vc_param->ret);
}

/**
 * omap3_set_off_timings - sets off-mode timings for a channel
 * @voltdm: channel to configure
 *
 * Calculates and sets up off-mode timings for a channel. Off-mode
 * can use either I2C based voltage scaling, or alternatively
 * sys_off_mode pad can be used to send a global command to power IC.n,
 * sys_off_mode has the additional benefit that voltages can be
 * scaled to zero volt level with TWL4030 / TWL5030, I2C can only
 * scale to 600mV.
 *
 * Note that omap is not controlling the voltage scaling during
 * off idle signaled by sys_off_mode, so we can keep voltsetup1
 * as 0.
 */
static void omap3_set_off_timings(struct voltagedomain *voltdm)
{
	struct omap3_vc_timings *c = vc.timings;
	u32 tstart, tshut, clksetup, voltoffset;

	if (c->voltsetup2)
		return;

	omap_pm_get_oscillator(&tstart, &tshut);
	if (tstart == ULONG_MAX) {
		pr_debug("PM: oscillator start-up time not initialized, using 10ms\n");
		clksetup = omap_usec_to_32k(10000);
	} else {
		clksetup = omap_usec_to_32k(tstart);
	}

	/*
	 * For twl4030 errata 27, we need to allow minimum ~488.32 us wait to
	 * switch from HFCLKIN to internal oscillator. That means timings
	 * have voltoffset fixed to 0xa in rounded up 32 KiHz cycles. And
	 * that means we can calculate the value based on the oscillator
	 * start-up time since voltoffset2 = clksetup - voltoffset.
	 */
	voltoffset = omap_usec_to_32k(488);
	c->voltsetup2 = clksetup - voltoffset;
	voltdm->write(clksetup, OMAP3_PRM_CLKSETUP_OFFSET);
	voltdm->write(voltoffset, OMAP3_PRM_VOLTOFFSET_OFFSET);
}

static void __init omap3_vc_init_channel(struct voltagedomain *voltdm)
{
	omap3_vc_init_pmic_signaling(voltdm);
	omap3_set_off_timings(voltdm);
	omap3_set_i2c_timings(voltdm);
}

/**
 * omap4_calc_volt_ramp - calculates voltage ramping delays on omap4
 * @voltdm: channel to calculate values for
 * @voltage_diff: voltage difference in microvolts
 *
 * Calculates voltage ramp prescaler + counter values for a voltage
 * difference on omap4. Returns a field value suitable for writing to
 * VOLTSETUP register for a channel in following format:
 * bits[8:9] prescaler ... bits[0:5] counter. See OMAP4 TRM for reference.
 */
static u32 omap4_calc_volt_ramp(struct voltagedomain *voltdm, u32 voltage_diff)
{
	u32 prescaler;
	u32 cycles;
	u32 time;

	time = voltage_diff / voltdm->pmic->slew_rate;

	cycles = voltdm->sys_clk.rate / 1000 * time / 1000;

	cycles /= 64;
	prescaler = 0;

	/* shift to next prescaler until no overflow */

	/* scale for div 256 = 64 * 4 */
	if (cycles > 63) {
		cycles /= 4;
		prescaler++;
	}

	/* scale for div 512 = 256 * 2 */
	if (cycles > 63) {
		cycles /= 2;
		prescaler++;
	}

	/* scale for div 2048 = 512 * 4 */
	if (cycles > 63) {
		cycles /= 4;
		prescaler++;
	}

	/* check for overflow => invalid ramp time */
	if (cycles > 63) {
		pr_warn("%s: invalid setuptime for vdd_%s\n", __func__,
			voltdm->name);
		return 0;
	}

	cycles++;

	return (prescaler << OMAP4430_RAMP_UP_PRESCAL_SHIFT) |
		(cycles << OMAP4430_RAMP_UP_COUNT_SHIFT);
}

/**
 * omap4_usec_to_val_scrm - convert microsecond value to SCRM module bitfield
 * @usec: microseconds
 * @shift: number of bits to shift left
 * @mask: bitfield mask
 *
 * Converts microsecond value to OMAP4 SCRM bitfield. Bitfield is
 * shifted to requested position, and checked agains the mask value.
 * If larger, forced to the max value of the field (i.e. the mask itself.)
 * Returns the SCRM bitfield value.
 */
static u32 omap4_usec_to_val_scrm(u32 usec, int shift, u32 mask)
{
	u32 val;

	val = omap_usec_to_32k(usec) << shift;

	/* Check for overflow, if yes, force to max value */
	if (val > mask)
		val = mask;

	return val;
}

/**
 * omap4_set_timings - set voltage ramp timings for a channel
 * @voltdm: channel to configure
 * @off_mode: whether off-mode values are used
 *
 * Calculates and sets the voltage ramp up / down values for a channel.
 */
static void omap4_set_timings(struct voltagedomain *voltdm, bool off_mode)
{
	u32 val;
	u32 ramp;
	int offset;
	u32 tstart, tshut;

	if (off_mode) {
		ramp = omap4_calc_volt_ramp(voltdm,
			voltdm->vc_param->on - voltdm->vc_param->off);
		offset = voltdm->vfsm->voltsetup_off_reg;
	} else {
		ramp = omap4_calc_volt_ramp(voltdm,
			voltdm->vc_param->on - voltdm->vc_param->ret);
		offset = voltdm->vfsm->voltsetup_reg;
	}

	if (!ramp)
		return;

	val = voltdm->read(offset);

	val |= ramp << OMAP4430_RAMP_DOWN_COUNT_SHIFT;

	val |= ramp << OMAP4430_RAMP_UP_COUNT_SHIFT;

	voltdm->write(val, offset);

	omap_pm_get_oscillator(&tstart, &tshut);

	val = omap4_usec_to_val_scrm(tstart, OMAP4_SETUPTIME_SHIFT,
		OMAP4_SETUPTIME_MASK);
	val |= omap4_usec_to_val_scrm(tshut, OMAP4_DOWNTIME_SHIFT,
		OMAP4_DOWNTIME_MASK);

	writel_relaxed(val, OMAP4_SCRM_CLKSETUPTIME);
}

static void __init omap4_vc_init_pmic_signaling(struct voltagedomain *voltdm)
{
	if (vc.vd)
		return;

	vc.vd = voltdm;
	voltdm->write(OMAP4_VDD_DEFAULT_VAL, OMAP4_PRM_VOLTCTRL_OFFSET);
}

/* OMAP4 specific voltage init functions */
static void __init omap4_vc_init_channel(struct voltagedomain *voltdm)
{
	omap4_vc_init_pmic_signaling(voltdm);
	omap4_set_timings(voltdm, true);
	omap4_set_timings(voltdm, false);
}

struct i2c_init_data {
	u8 loadbits;
	u8 load;
	u8 hsscll_38_4;
	u8 hsscll_26;
	u8 hsscll_19_2;
	u8 hsscll_16_8;
	u8 hsscll_12;
};

static const struct i2c_init_data omap4_i2c_timing_data[] __initconst = {
	{
		.load = 50,
		.loadbits = 0x3,
		.hsscll_38_4 = 13,
		.hsscll_26 = 11,
		.hsscll_19_2 = 9,
		.hsscll_16_8 = 9,
		.hsscll_12 = 8,
	},
	{
		.load = 25,
		.loadbits = 0x2,
		.hsscll_38_4 = 13,
		.hsscll_26 = 11,
		.hsscll_19_2 = 9,
		.hsscll_16_8 = 9,
		.hsscll_12 = 8,
	},
	{
		.load = 12,
		.loadbits = 0x1,
		.hsscll_38_4 = 11,
		.hsscll_26 = 10,
		.hsscll_19_2 = 9,
		.hsscll_16_8 = 9,
		.hsscll_12 = 8,
	},
	{
		.load = 0,
		.loadbits = 0x0,
		.hsscll_38_4 = 12,
		.hsscll_26 = 10,
		.hsscll_19_2 = 9,
		.hsscll_16_8 = 8,
		.hsscll_12 = 8,
	},
};

/**
 * omap4_vc_i2c_timing_init - sets up board I2C timing parameters
 * @voltdm: voltagedomain pointer to get data from
 *
 * Use PMIC + board supplied settings for calculating the total I2C
 * channel capacitance and set the timing parameters based on this.
 * Pre-calculated values are provided in data tables, as it is not
 * too straightforward to calculate these runtime.
 */
static void __init omap4_vc_i2c_timing_init(struct voltagedomain *voltdm)
{
	u32 capacitance;
	u32 val;
	u16 hsscll;
	const struct i2c_init_data *i2c_data;

	if (!voltdm->pmic->i2c_high_speed) {
		pr_info("%s: using bootloader low-speed timings\n", __func__);
		return;
	}

	/* PCB trace capacitance, 0.125pF / mm => mm / 8 */
	capacitance = DIV_ROUND_UP(sr_i2c_pcb_length, 8);

	/* OMAP pad capacitance */
	capacitance += 4;

	/* PMIC pad capacitance */
	capacitance += voltdm->pmic->i2c_pad_load;

	/* Search for capacitance match in the table */
	i2c_data = omap4_i2c_timing_data;

	while (i2c_data->load > capacitance)
		i2c_data++;

	/* Select proper values based on sysclk frequency */
	switch (voltdm->sys_clk.rate) {
	case 38400000:
		hsscll = i2c_data->hsscll_38_4;
		break;
	case 26000000:
		hsscll = i2c_data->hsscll_26;
		break;
	case 19200000:
		hsscll = i2c_data->hsscll_19_2;
		break;
	case 16800000:
		hsscll = i2c_data->hsscll_16_8;
		break;
	case 12000000:
		hsscll = i2c_data->hsscll_12;
		break;
	default:
		pr_warn("%s: unsupported sysclk rate: %d!\n", __func__,
			voltdm->sys_clk.rate);
		return;
	}

	/* Loadbits define pull setup for the I2C channels */
	val = i2c_data->loadbits << 25 | i2c_data->loadbits << 29;

	/* Write to SYSCTRL_PADCONF_WKUP_CTRL_I2C_2 to setup I2C pull */
	writel_relaxed(val, OMAP2_L4_IO_ADDRESS(OMAP4_CTRL_MODULE_PAD_WKUP +
				OMAP4_CTRL_MODULE_PAD_WKUP_CONTROL_I2C_2));

	/* HSSCLH can always be zero */
	val = hsscll << OMAP4430_HSSCLL_SHIFT;
	val |= (0x28 << OMAP4430_SCLL_SHIFT | 0x2c << OMAP4430_SCLH_SHIFT);

	/* Write setup times to I2C config register */
	voltdm->write(val, OMAP4_PRM_VC_CFG_I2C_CLK_OFFSET);
}

/**
 * omap_vc_i2c_init - initialize I2C interface to PMIC
 * @voltdm: voltage domain containing VC data
 *
 * Use PMIC supplied settings for I2C high-speed mode and
 * master code (if set) and program the VC I2C configuration
 * register.
 *
 * The VC I2C configuration is common to all VC channels,
 * so this function only configures I2C for the first VC
 * channel registers.  All other VC channels will use the
 * same configuration.
 */
static void __init omap_vc_i2c_init(struct voltagedomain *voltdm)
{
	struct omap_vc_channel *vc = voltdm->vc;
	static bool initialized;
	static bool i2c_high_speed;
	u8 mcode;

	if (initialized) {
		if (voltdm->pmic->i2c_high_speed != i2c_high_speed)
			pr_warn("%s: I2C config for vdd_%s does not match other channels (%u).\n",
				__func__, voltdm->name, i2c_high_speed);
		return;
	}

	/*
	 * Note that for omap3 OMAP3430_SREN_MASK clears SREN to work around
	 * erratum i531 "Extra Power Consumed When Repeated Start Operation
	 * Mode Is Enabled on I2C Interface Dedicated for Smart Reflex (I2C4)".
	 * Otherwise I2C4 eventually leads into about 23mW extra power being
	 * consumed even during off idle using VMODE.
	 */
	i2c_high_speed = voltdm->pmic->i2c_high_speed;
	if (i2c_high_speed)
		voltdm->rmw(vc->common->i2c_cfg_clear_mask,
			    vc->common->i2c_cfg_hsen_mask,
			    vc->common->i2c_cfg_reg);

	mcode = voltdm->pmic->i2c_mcode;
	if (mcode)
		voltdm->rmw(vc->common->i2c_mcode_mask,
			    mcode << __ffs(vc->common->i2c_mcode_mask),
			    vc->common->i2c_cfg_reg);

	if (cpu_is_omap44xx())
		omap4_vc_i2c_timing_init(voltdm);

	initialized = true;
}

/**
 * omap_vc_calc_vsel - calculate vsel value for a channel
 * @voltdm: channel to calculate value for
 * @uvolt: microvolt value to convert to vsel
 *
 * Converts a microvolt value to vsel value for the used PMIC.
 * This checks whether the microvolt value is out of bounds, and
 * adjusts the value accordingly. If unsupported value detected,
 * warning is thrown.
 */
static u8 omap_vc_calc_vsel(struct voltagedomain *voltdm, u32 uvolt)
{
	if (voltdm->pmic->vddmin > uvolt)
		uvolt = voltdm->pmic->vddmin;
	if (voltdm->pmic->vddmax < uvolt) {
		WARN(1, "%s: voltage not supported by pmic: %u vs max %u\n",
			__func__, uvolt, voltdm->pmic->vddmax);
		/* Lets try maximum value anyway */
		uvolt = voltdm->pmic->vddmax;
	}

	return voltdm->pmic->uv_to_vsel(uvolt);
}

#ifdef CONFIG_PM
/**
 * omap_pm_setup_sr_i2c_pcb_length - set length of SR I2C traces on PCB
 * @mm: length of the PCB trace in millimetres
 *
 * Sets the PCB trace length for the I2C channel. By default uses 63mm.
 * This is needed for properly calculating the capacitance value for
 * the PCB trace, and for setting the SR I2C channel timing parameters.
 */
void __init omap_pm_setup_sr_i2c_pcb_length(u32 mm)
{
	sr_i2c_pcb_length = mm;
}
#endif

void __init omap_vc_init_channel(struct voltagedomain *voltdm)
{
	struct omap_vc_channel *vc = voltdm->vc;
	u8 on_vsel, onlp_vsel, ret_vsel, off_vsel;
	u32 val;

	if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) {
		pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name);
		return;
	}

	if (!voltdm->read || !voltdm->write) {
		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
			__func__, voltdm->name);
		return;
	}

	vc->cfg_channel = 0;
	if (vc->flags & OMAP_VC_CHANNEL_CFG_MUTANT)
		vc_cfg_bits = &vc_mutant_channel_cfg;
	else
		vc_cfg_bits = &vc_default_channel_cfg;

	/* get PMIC/board specific settings */
	vc->i2c_slave_addr = voltdm->pmic->i2c_slave_addr;
	vc->volt_reg_addr = voltdm->pmic->volt_reg_addr;
	vc->cmd_reg_addr = voltdm->pmic->cmd_reg_addr;

	/* Configure the i2c slave address for this VC */
	voltdm->rmw(vc->smps_sa_mask,
		    vc->i2c_slave_addr << __ffs(vc->smps_sa_mask),
		    vc->smps_sa_reg);
	vc->cfg_channel |= vc_cfg_bits->sa;

	/*
	 * Configure the PMIC register addresses.
	 */
	voltdm->rmw(vc->smps_volra_mask,
		    vc->volt_reg_addr << __ffs(vc->smps_volra_mask),
		    vc->smps_volra_reg);
	vc->cfg_channel |= vc_cfg_bits->rav;

	if (vc->cmd_reg_addr) {
		voltdm->rmw(vc->smps_cmdra_mask,
			    vc->cmd_reg_addr << __ffs(vc->smps_cmdra_mask),
			    vc->smps_cmdra_reg);
		vc->cfg_channel |= vc_cfg_bits->rac;
	}

	if (vc->cmd_reg_addr == vc->volt_reg_addr)
		vc->cfg_channel |= vc_cfg_bits->racen;

	/* Set up the on, inactive, retention and off voltage */
	on_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->on);
	onlp_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->onlp);
	ret_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->ret);
	off_vsel = omap_vc_calc_vsel(voltdm, voltdm->vc_param->off);

	val = ((on_vsel << vc->common->cmd_on_shift) |
	       (onlp_vsel << vc->common->cmd_onlp_shift) |
	       (ret_vsel << vc->common->cmd_ret_shift) |
	       (off_vsel << vc->common->cmd_off_shift));
	voltdm->write(val, vc->cmdval_reg);
	vc->cfg_channel |= vc_cfg_bits->cmd;

	/* Channel configuration */
	omap_vc_config_channel(voltdm);

	omap_vc_i2c_init(voltdm);

	if (cpu_is_omap34xx())
		omap3_vc_init_channel(voltdm);
	else if (cpu_is_omap44xx())
		omap4_vc_init_channel(voltdm);
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * OMAP3/4 Voltage Controller (VC) structure and macro definitions
 *
 * Copyright (C) 2007, 2010 Texas Instruments, Inc.
 * Rajendra Nayak <rnayak@ti.com>
 * Lesly A M <x0080970@ti.com>
 * Thara Gopinath <thara@ti.com>
 *
 * Copyright (C) 2008, 2011 Nokia Corporation
 * Kalle Jokiniemi
 * Paul Walmsley
 */
#ifndef __ARCH_ARM_MACH_OMAP2_VC_H
#define __ARCH_ARM_MACH_OMAP2_VC_H

#include <linux/kernel.h>

struct voltagedomain;

/**
 * struct omap_vc_common - per-VC register/bitfield data
 * @cmd_on_mask: ON bitmask in PRM_VC_CMD_VAL* register
 * @valid: VALID bitmask in PRM_VC_BYPASS_VAL register
 * @bypass_val_reg: Offset of PRM_VC_BYPASS_VAL reg from PRM start
 * @data_shift: DATA field shift in PRM_VC_BYPASS_VAL register
 * @slaveaddr_shift: SLAVEADDR field shift in PRM_VC_BYPASS_VAL register
 * @regaddr_shift: REGADDR field shift in PRM_VC_BYPASS_VAL register
 * @cmd_on_shift: ON field shift in PRM_VC_CMD_VAL_* register
 * @cmd_onlp_shift: ONLP field shift in PRM_VC_CMD_VAL_* register
 * @cmd_ret_shift: RET field shift in PRM_VC_CMD_VAL_* register
 * @cmd_off_shift: OFF field shift in PRM_VC_CMD_VAL_* register
 * @i2c_cfg_reg: I2C configuration register offset
 * @i2c_cfg_clear_mask: high-speed mode bit clear mask in I2C config register
 * @i2c_cfg_hsen_mask: high-speed mode bit field mask in I2C config register
 * @i2c_mcode_mask: MCODE field mask for I2C config register
 *
 * XXX One of cmd_on_mask and cmd_on_shift are not needed
 * XXX VALID should probably be a shift, not a mask
 */
struct omap_vc_common {
	u32 cmd_on_mask;
	u32 valid;
	u8 bypass_val_reg;
	u8 data_shift;
	u8 slaveaddr_shift;
	u8 regaddr_shift;
	u8 cmd_on_shift;
	u8 cmd_onlp_shift;
	u8 cmd_ret_shift;
	u8 cmd_off_shift;
	u8 i2c_cfg_reg;
	u8 i2c_cfg_clear_mask;
	u8 i2c_cfg_hsen_mask;
	u8 i2c_mcode_mask;
};

/* omap_vc_channel.flags values */
#define OMAP_VC_CHANNEL_DEFAULT BIT(0)
#define OMAP_VC_CHANNEL_CFG_MUTANT BIT(1)

/**
 * struct omap_vc_channel - VC per-instance data
 * @i2c_slave_addr: I2C slave address of PMIC for this VC channel
 * @volt_reg_addr: voltage configuration register address
 * @cmd_reg_addr: command configuration register address
 * @setup_time: setup time (in sys_clk cycles) of regulator for this channel
 * @cfg_channel: current value of VC channel configuration register
 * @i2c_high_speed: whether or not to use I2C high-speed mode
 *
 * @common: pointer to VC common data for this platform
 * @smps_sa_mask: i2c slave address bitmask in the PRM_VC_SMPS_SA register
 * @smps_volra_mask: VOLRA* bitmask in the PRM_VC_VOL_RA register
 * @smps_cmdra_mask: CMDRA* bitmask in the PRM_VC_CMD_RA register
 * @cmdval_reg: register for on/ret/off voltage level values for this channel
 * @smps_sa_reg: Offset of PRM_VC_SMPS_SA reg from PRM start
 * @smps_volra_reg: Offset of PRM_VC_SMPS_VOL_RA reg from PRM start
 * @smps_cmdra_reg: Offset of PRM_VC_SMPS_CMD_RA reg from PRM start
 * @cfg_channel_reg: VC channel configuration register
 * @cfg_channel_sa_shift: bit shift for slave address cfg_channel register
 * @flags: VC channel-specific flags (optional)
 */
struct omap_vc_channel {
	/* channel state */
	u16 i2c_slave_addr;
	u16 volt_reg_addr;
	u16 cmd_reg_addr;
	u8 cfg_channel;
	bool i2c_high_speed;

	/* register access data */
	const struct omap_vc_common *common;
	u32 smps_sa_mask;
	u32 smps_volra_mask;
	u32 smps_cmdra_mask;
	u8 cmdval_reg;
	u8 smps_sa_reg;
	u8 smps_volra_reg;
	u8 smps_cmdra_reg;
	u8 cfg_channel_reg;
	u8 cfg_channel_sa_shift;
	u8 flags;
};

extern struct omap_vc_channel omap3_vc_mpu;
extern struct omap_vc_channel omap3_vc_core;

extern struct omap_vc_channel omap4_vc_mpu;
extern struct omap_vc_channel omap4_vc_iva;
extern struct omap_vc_channel omap4_vc_core;

extern struct omap_vc_param omap3_mpu_vc_data;
extern struct omap_vc_param omap3_core_vc_data;

extern struct omap_vc_param omap4_mpu_vc_data;
extern struct omap_vc_param omap4_iva_vc_data;
extern struct omap_vc_param omap4_core_vc_data;

void omap3_vc_set_pmic_signaling(int core_next_state);
void omap4_vc_set_pmic_signaling(int core_next_state);

void omap_vc_init_channel(struct voltagedomain *voltdm);
int omap_vc_pre_scale(struct voltagedomain *voltdm,
		      unsigned long target_volt,
		      u8 *target_vsel, u8 *current_vsel);
void omap_vc_post_scale(struct voltagedomain *voltdm,
			unsigned long target_volt,
			u8 target_vsel, u8 current_vsel);
int omap_vc_bypass_scale(struct voltagedomain *voltdm,
			 unsigned long target_volt);

#endif

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP3 Voltage Controller (VC) data
 *
 * Copyright (C) 2007, 2010 Texas Instruments, Inc.
 * Rajendra Nayak <rnayak@ti.com>
 * Lesly A M <x0080970@ti.com>
 * Thara Gopinath <thara@ti.com>
 *
 * Copyright (C) 2008, 2011 Nokia Corporation
 * Kalle Jokiniemi
 * Paul Walmsley
 */
#include <linux/io.h>
#include <linux/err.h>
#include <linux/init.h>

#include "common.h"

#include "prm-regbits-34xx.h"
#include "voltage.h"

#include "vc.h"

/*
 * VC data common to 34xx/36xx chips
 * XXX This stuff presumably belongs in the vc3xxx.c or vc.c file.
 */
static struct omap_vc_common omap3_vc_common = {
	.bypass_val_reg	 = OMAP3_PRM_VC_BYPASS_VAL_OFFSET,
	.data_shift	 = OMAP3430_DATA_SHIFT,
	.slaveaddr_shift = OMAP3430_SLAVEADDR_SHIFT,
	.regaddr_shift	 = OMAP3430_REGADDR_SHIFT,
	.valid		 = OMAP3430_VALID_MASK,
	.cmd_on_shift	 = OMAP3430_VC_CMD_ON_SHIFT,
	.cmd_on_mask	 = OMAP3430_VC_CMD_ON_MASK,
	.cmd_onlp_shift	 = OMAP3430_VC_CMD_ONLP_SHIFT,
	.cmd_ret_shift	 = OMAP3430_VC_CMD_RET_SHIFT,
	.cmd_off_shift	 = OMAP3430_VC_CMD_OFF_SHIFT,
	.i2c_cfg_clear_mask = OMAP3430_SREN_MASK | OMAP3430_HSEN_MASK,
	.i2c_cfg_hsen_mask = OMAP3430_HSEN_MASK,
	.i2c_cfg_reg	 = OMAP3_PRM_VC_I2C_CFG_OFFSET,
	.i2c_mcode_mask	 = OMAP3430_MCODE_MASK,
};

struct omap_vc_channel omap3_vc_mpu = {
	.flags = OMAP_VC_CHANNEL_DEFAULT,
	.common = &omap3_vc_common,
	.smps_sa_reg	 = OMAP3_PRM_VC_SMPS_SA_OFFSET,
	.smps_volra_reg	 = OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET,
	.smps_cmdra_reg	 = OMAP3_PRM_VC_SMPS_CMD_RA_OFFSET,
	.cfg_channel_reg = OMAP3_PRM_VC_CH_CONF_OFFSET,
	.cmdval_reg = OMAP3_PRM_VC_CMD_VAL_0_OFFSET,
	.smps_sa_mask = OMAP3430_PRM_VC_SMPS_SA_SA0_MASK,
	.smps_volra_mask = OMAP3430_VOLRA0_MASK,
	.smps_cmdra_mask = OMAP3430_CMDRA0_MASK,
	.cfg_channel_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA0_SHIFT,
};

struct omap_vc_channel omap3_vc_core = {
	.common = &omap3_vc_common,
	.smps_sa_reg	 = OMAP3_PRM_VC_SMPS_SA_OFFSET,
	.smps_volra_reg	 = OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET,
	.smps_cmdra_reg	 = OMAP3_PRM_VC_SMPS_CMD_RA_OFFSET,
	.cfg_channel_reg = OMAP3_PRM_VC_CH_CONF_OFFSET,
	.cmdval_reg = OMAP3_PRM_VC_CMD_VAL_1_OFFSET,
	.smps_sa_mask = OMAP3430_PRM_VC_SMPS_SA_SA1_MASK,
	.smps_volra_mask = OMAP3430_VOLRA1_MASK,
	.smps_cmdra_mask = OMAP3430_CMDRA1_MASK,
	.cfg_channel_sa_shift = OMAP3430_PRM_VC_SMPS_SA_SA1_SHIFT,
};

/*
 * Voltage levels for different operating modes: on, sleep, retention and off
 */
#define OMAP3_ON_VOLTAGE_UV		1200000
#define OMAP3_ONLP_VOLTAGE_UV		1000000
#define OMAP3_RET_VOLTAGE_UV		975000
#define OMAP3_OFF_VOLTAGE_UV		600000

struct omap_vc_param omap3_mpu_vc_data = {
	.on		= OMAP3_ON_VOLTAGE_UV,
	.onlp		= OMAP3_ONLP_VOLTAGE_UV,
	.ret		= OMAP3_RET_VOLTAGE_UV,
	.off		= OMAP3_OFF_VOLTAGE_UV,
};

struct omap_vc_param omap3_core_vc_data = {
	.on		= OMAP3_ON_VOLTAGE_UV,
	.onlp		= OMAP3_ONLP_VOLTAGE_UV,
	.ret		= OMAP3_RET_VOLTAGE_UV,
	.off		= OMAP3_OFF_VOLTAGE_UV,
};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP4 Voltage Controller (VC) data
 *
 * Copyright (C) 2007, 2010 Texas Instruments, Inc.
 * Rajendra Nayak <rnayak@ti.com>
 * Lesly A M <x0080970@ti.com>
 * Thara Gopinath <thara@ti.com>
 *
 * Copyright (C) 2008, 2011 Nokia Corporation
 * Kalle Jokiniemi
 * Paul Walmsley
 */
#include <linux/io.h>
#include <linux/err.h>
#include <linux/init.h>

#include "common.h"

#include "prm44xx.h"
#include "prm-regbits-44xx.h"
#include "voltage.h"

#include "vc.h"

/*
 * VC data common to 44xx chips
 * XXX This stuff presumably belongs in the vc3xxx.c or vc.c file.
 */
static const struct omap_vc_common omap4_vc_common = {
	.bypass_val_reg = OMAP4_PRM_VC_VAL_BYPASS_OFFSET,
	.data_shift = OMAP4430_DATA_SHIFT,
	.slaveaddr_shift = OMAP4430_SLAVEADDR_SHIFT,
	.regaddr_shift = OMAP4430_REGADDR_SHIFT,
	.valid = OMAP4430_VALID_MASK,
	.cmd_on_shift = OMAP4430_ON_SHIFT,
	.cmd_on_mask = OMAP4430_ON_MASK,
	.cmd_onlp_shift = OMAP4430_ONLP_SHIFT,
	.cmd_ret_shift = OMAP4430_RET_SHIFT,
	.cmd_off_shift = OMAP4430_OFF_SHIFT,
	.i2c_cfg_reg = OMAP4_PRM_VC_CFG_I2C_MODE_OFFSET,
	.i2c_cfg_clear_mask = OMAP4430_SRMODEEN_MASK | OMAP4430_HSMODEEN_MASK,
	.i2c_cfg_hsen_mask = OMAP4430_HSMODEEN_MASK,
	.i2c_mcode_mask	 = OMAP4430_HSMCODE_MASK,
};

/* VC instance data for each controllable voltage line */
struct omap_vc_channel omap4_vc_mpu = {
	.flags = OMAP_VC_CHANNEL_DEFAULT | OMAP_VC_CHANNEL_CFG_MUTANT,
	.common = &omap4_vc_common,
	.smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET,
	.smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET,
	.smps_cmdra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_CMD_OFFSET,
	.cfg_channel_reg = OMAP4_PRM_VC_CFG_CHANNEL_OFFSET,
	.cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_MPU_L_OFFSET,
	.smps_sa_mask = OMAP4430_SA_VDD_MPU_L_PRM_VC_SMPS_SA_MASK,
	.smps_volra_mask = OMAP4430_VOLRA_VDD_MPU_L_MASK,
	.smps_cmdra_mask = OMAP4430_CMDRA_VDD_MPU_L_MASK,
	.cfg_channel_sa_shift = OMAP4430_SA_VDD_MPU_L_SHIFT,
};

struct omap_vc_channel omap4_vc_iva = {
	.common = &omap4_vc_common,
	.smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET,
	.smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET,
	.smps_cmdra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_CMD_OFFSET,
	.cfg_channel_reg = OMAP4_PRM_VC_CFG_CHANNEL_OFFSET,
	.cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_IVA_L_OFFSET,
	.smps_sa_mask = OMAP4430_SA_VDD_IVA_L_PRM_VC_SMPS_SA_MASK,
	.smps_volra_mask = OMAP4430_VOLRA_VDD_IVA_L_MASK,
	.smps_cmdra_mask = OMAP4430_CMDRA_VDD_IVA_L_MASK,
	.cfg_channel_sa_shift = OMAP4430_SA_VDD_IVA_L_SHIFT,
};

struct omap_vc_channel omap4_vc_core = {
	.common = &omap4_vc_common,
	.smps_sa_reg = OMAP4_PRM_VC_SMPS_SA_OFFSET,
	.smps_volra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_VOL_OFFSET,
	.smps_cmdra_reg = OMAP4_PRM_VC_VAL_SMPS_RA_CMD_OFFSET,
	.cfg_channel_reg = OMAP4_PRM_VC_CFG_CHANNEL_OFFSET,
	.cmdval_reg = OMAP4_PRM_VC_VAL_CMD_VDD_CORE_L_OFFSET,
	.smps_sa_mask = OMAP4430_SA_VDD_CORE_L_0_6_MASK,
	.smps_volra_mask = OMAP4430_VOLRA_VDD_CORE_L_MASK,
	.smps_cmdra_mask = OMAP4430_CMDRA_VDD_CORE_L_MASK,
	.cfg_channel_sa_shift = OMAP4430_SA_VDD_CORE_L_SHIFT,
};

/*
 * Voltage levels for different operating modes: on, sleep, retention and off
 */
#define OMAP4_ON_VOLTAGE_UV			1375000
#define OMAP4_ONLP_VOLTAGE_UV			1375000
#define OMAP4_RET_VOLTAGE_UV			837500
#define OMAP4_OFF_VOLTAGE_UV			0

struct omap_vc_param omap4_mpu_vc_data = {
	.on			= OMAP4_ON_VOLTAGE_UV,
	.onlp			= OMAP4_ONLP_VOLTAGE_UV,
	.ret			= OMAP4_RET_VOLTAGE_UV,
	.off			= OMAP4_OFF_VOLTAGE_UV,
};

struct omap_vc_param omap4_iva_vc_data = {
	.on			= OMAP4_ON_VOLTAGE_UV,
	.onlp			= OMAP4_ONLP_VOLTAGE_UV,
	.ret			= OMAP4_RET_VOLTAGE_UV,
	.off			= OMAP4_OFF_VOLTAGE_UV,
};

struct omap_vc_param omap4_core_vc_data = {
	.on			= OMAP4_ON_VOLTAGE_UV,
	.onlp			= OMAP4_ONLP_VOLTAGE_UV,
	.ret			= OMAP4_RET_VOLTAGE_UV,
	.off			= OMAP4_OFF_VOLTAGE_UV,
};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP3/OMAP4 Voltage Management Routines
 *
 * Author: Thara Gopinath	<thara@ti.com>
 *
 * Copyright (C) 2007 Texas Instruments, Inc.
 * Rajendra Nayak <rnayak@ti.com>
 * Lesly A M <x0080970@ti.com>
 *
 * Copyright (C) 2008, 2011 Nokia Corporation
 * Kalle Jokiniemi
 * Paul Walmsley
 *
 * Copyright (C) 2010 Texas Instruments, Inc.
 * Thara Gopinath <thara@ti.com>
 */

#include <linux/delay.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/clk.h>

#include "common.h"

#include "prm-regbits-34xx.h"
#include "prm-regbits-44xx.h"
#include "prm44xx.h"
#include "prcm44xx.h"
#include "prminst44xx.h"
#include "control.h"

#include "voltage.h"
#include "powerdomain.h"

#include "vc.h"
#include "vp.h"

static LIST_HEAD(voltdm_list);

/* Public functions */
/**
 * voltdm_get_voltage() - Gets the current non-auto-compensated voltage
 * @voltdm:	pointer to the voltdm for which current voltage info is needed
 *
 * API to get the current non-auto-compensated voltage for a voltage domain.
 * Returns 0 in case of error else returns the current voltage.
 */
unsigned long voltdm_get_voltage(struct voltagedomain *voltdm)
{
	if (!voltdm || IS_ERR(voltdm)) {
		pr_warn("%s: VDD specified does not exist!\n", __func__);
		return 0;
	}

	return voltdm->nominal_volt;
}

/**
 * voltdm_scale() - API to scale voltage of a particular voltage domain.
 * @voltdm: pointer to the voltage domain which is to be scaled.
 * @target_volt: The target voltage of the voltage domain
 *
 * This API should be called by the kernel to do the voltage scaling
 * for a particular voltage domain during DVFS.
 */
int voltdm_scale(struct voltagedomain *voltdm,
		 unsigned long target_volt)
{
	int ret, i;
	unsigned long volt = 0;

	if (!voltdm || IS_ERR(voltdm)) {
		pr_warn("%s: VDD specified does not exist!\n", __func__);
		return -EINVAL;
	}

	if (!voltdm->scale) {
		pr_err("%s: No voltage scale API registered for vdd_%s\n",
			__func__, voltdm->name);
		return -ENODATA;
	}

	if (!voltdm->volt_data) {
		pr_err("%s: No voltage data defined for vdd_%s\n",
			__func__, voltdm->name);
		return -ENODATA;
	}

	/* Adjust voltage to the exact voltage from the OPP table */
	for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) {
		if (voltdm->volt_data[i].volt_nominal >= target_volt) {
			volt = voltdm->volt_data[i].volt_nominal;
			break;
		}
	}

	if (!volt) {
		pr_warn("%s: not scaling. OPP voltage for %lu, not found.\n",
			__func__, target_volt);
		return -EINVAL;
	}

	ret = voltdm->scale(voltdm, volt);
	if (!ret)
		voltdm->nominal_volt = volt;

	return ret;
}

/**
 * voltdm_reset() - Resets the voltage of a particular voltage domain
 *		    to that of the current OPP.
 * @voltdm: pointer to the voltage domain whose voltage is to be reset.
 *
 * This API finds out the correct voltage the voltage domain is supposed
 * to be at and resets the voltage to that level. Should be used especially
 * while disabling any voltage compensation modules.
 */
void voltdm_reset(struct voltagedomain *voltdm)
{
	unsigned long target_volt;

	if (!voltdm || IS_ERR(voltdm)) {
		pr_warn("%s: VDD specified does not exist!\n", __func__);
		return;
	}

	target_volt = voltdm_get_voltage(voltdm);
	if (!target_volt) {
		pr_err("%s: unable to find current voltage for vdd_%s\n",
			__func__, voltdm->name);
		return;
	}

	voltdm_scale(voltdm, target_volt);
}

/**
 * omap_voltage_get_volttable() - API to get the voltage table associated with a
 *				particular voltage domain.
 * @voltdm:	pointer to the VDD for which the voltage table is required
 * @volt_data:	the voltage table for the particular vdd which is to be
 *		populated by this API
 *
 * This API populates the voltage table associated with a VDD into the
 * passed parameter pointer. Returns the count of distinct voltages
 * supported by this vdd.
 *
 */
void omap_voltage_get_volttable(struct voltagedomain *voltdm,
				struct omap_volt_data **volt_data)
{
	if (!voltdm || IS_ERR(voltdm)) {
		pr_warn("%s: VDD specified does not exist!\n", __func__);
		return;
	}

	*volt_data = voltdm->volt_data;
}

/**
 * omap_voltage_get_voltdata() - API to get the voltage table entry for a
 *				particular voltage
 * @voltdm:	pointer to the VDD whose voltage table has to be searched
 * @volt:	the voltage to be searched in the voltage table
 *
 * This API searches through the voltage table for the required voltage
 * domain and tries to find a matching entry for the passed voltage volt.
 * If a matching entry is found volt_data is populated with that entry.
 * This API searches only through the non-compensated voltages int the
 * voltage table.
 * Returns pointer to the voltage table entry corresponding to volt on
 * success. Returns -ENODATA if no voltage table exisits for the passed voltage
 * domain or if there is no matching entry.
 */
struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm,
						 unsigned long volt)
{
	int i;

	if (!voltdm || IS_ERR(voltdm)) {
		pr_warn("%s: VDD specified does not exist!\n", __func__);
		return ERR_PTR(-EINVAL);
	}

	if (!voltdm->volt_data) {
		pr_warn("%s: voltage table does not exist for vdd_%s\n",
			__func__, voltdm->name);
		return ERR_PTR(-ENODATA);
	}

	for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) {
		if (voltdm->volt_data[i].volt_nominal == volt)
			return &voltdm->volt_data[i];
	}

	pr_notice("%s: Unable to match the current voltage with the voltage table for vdd_%s\n",
		  __func__, voltdm->name);

	return ERR_PTR(-ENODATA);
}

/**
 * omap_voltage_register_pmic() - API to register PMIC specific data
 * @voltdm:	pointer to the VDD for which the PMIC specific data is
 *		to be registered
 * @pmic:	the structure containing pmic info
 *
 * This API is to be called by the SOC/PMIC file to specify the
 * pmic specific info as present in omap_voltdm_pmic structure.
 */
int omap_voltage_register_pmic(struct voltagedomain *voltdm,
			       struct omap_voltdm_pmic *pmic)
{
	if (!voltdm || IS_ERR(voltdm)) {
		pr_warn("%s: VDD specified does not exist!\n", __func__);
		return -EINVAL;
	}

	voltdm->pmic = pmic;

	return 0;
}

/**
 * omap_voltage_late_init() - Init the various voltage parameters
 *
 * This API is to be called in the later stages of the
 * system boot to init the voltage controller and
 * voltage processors.
 */
int __init omap_voltage_late_init(void)
{
	struct voltagedomain *voltdm;

	if (list_empty(&voltdm_list)) {
		pr_err("%s: Voltage driver support not added\n",
			__func__);
		return -EINVAL;
	}

	list_for_each_entry(voltdm, &voltdm_list, node) {
		struct clk *sys_ck;

		if (!voltdm->scalable)
			continue;

		sys_ck = clk_get(NULL, voltdm->sys_clk.name);
		if (IS_ERR(sys_ck)) {
			pr_warn("%s: Could not get sys clk.\n", __func__);
			return -EINVAL;
		}
		voltdm->sys_clk.rate = clk_get_rate(sys_ck);
		WARN_ON(!voltdm->sys_clk.rate);
		clk_put(sys_ck);

		if (voltdm->vc) {
			voltdm->scale = omap_vc_bypass_scale;
			omap_vc_init_channel(voltdm);
		}

		if (voltdm->vp) {
			voltdm->scale = omap_vp_forceupdate_scale;
			omap_vp_init(voltdm);
		}
	}

	return 0;
}

static struct voltagedomain *_voltdm_lookup(const char *name)
{
	struct voltagedomain *voltdm, *temp_voltdm;

	voltdm = NULL;

	list_for_each_entry(temp_voltdm, &voltdm_list, node) {
		if (!strcmp(name, temp_voltdm->name)) {
			voltdm = temp_voltdm;
			break;
		}
	}

	return voltdm;
}

static int _voltdm_register(struct voltagedomain *voltdm)
{
	if (!voltdm || !voltdm->name)
		return -EINVAL;

	list_add(&voltdm->node, &voltdm_list);

	pr_debug("voltagedomain: registered %s\n", voltdm->name);

	return 0;
}

/**
 * voltdm_lookup - look up a voltagedomain by name, return a pointer
 * @name: name of voltagedomain
 *
 * Find a registered voltagedomain by its name @name.  Returns a pointer
 * to the struct voltagedomain if found, or NULL otherwise.
 */
struct voltagedomain *voltdm_lookup(const char *name)
{
	struct voltagedomain *voltdm ;

	if (!name)
		return NULL;

	voltdm = _voltdm_lookup(name);

	return voltdm;
}

/**
 * voltdm_init - set up the voltagedomain layer
 * @voltdm_list: array of struct voltagedomain pointers to register
 *
 * Loop through the array of voltagedomains @voltdm_list, registering all
 * that are available on the current CPU. If voltdm_list is supplied
 * and not null, all of the referenced voltagedomains will be
 * registered.  No return value.
 */
void voltdm_init(struct voltagedomain **voltdms)
{
	struct voltagedomain **v;

	if (voltdms) {
		for (v = voltdms; *v; v++)
			_voltdm_register(*v);
	}
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * OMAP Voltage Management Routines
 *
 * Author: Thara Gopinath	<thara@ti.com>
 *
 * Copyright (C) 2009 Texas Instruments, Inc.
 * Thara Gopinath <thara@ti.com>
 */

#ifndef __ARCH_ARM_MACH_OMAP2_VOLTAGE_H
#define __ARCH_ARM_MACH_OMAP2_VOLTAGE_H

#include <linux/err.h>

#include <linux/platform_data/voltage-omap.h>

#include "vc.h"
#include "vp.h"

struct powerdomain;

/*
 * OMAP3 GENERIC setup times. Revisit to see if these needs to be
 * passed from board or PMIC file
 */
#define OMAP3_CLKSETUP		0xff
#define OMAP3_VOLTOFFSET	0xff
#define OMAP3_VOLTSETUP2	0xff

/**
 * struct omap_vfsm_instance - per-voltage manager FSM register/bitfield
 * data
 * @voltsetup_mask: SETUP_TIME* bitmask in the PRM_VOLTSETUP* register
 * @voltsetup_reg: register offset of PRM_VOLTSETUP from PRM base
 * @voltsetup_off_reg: register offset of PRM_VOLTSETUP_OFF from PRM base
 *
 * XXX What about VOLTOFFSET/VOLTCTRL?
 */
struct omap_vfsm_instance {
	u32 voltsetup_mask;
	u8 voltsetup_reg;
	u8 voltsetup_off_reg;
};

/**
 * struct voltagedomain - omap voltage domain global structure.
 * @name: Name of the voltage domain which can be used as a unique identifier.
 * @scalable: Whether or not this voltage domain is scalable
 * @node: list_head linking all voltage domains
 * @vc: pointer to VC channel associated with this voltagedomain
 * @vp: pointer to VP associated with this voltagedomain
 * @read: read a VC/VP register
 * @write: write a VC/VP register
 * @read: read-modify-write a VC/VP register
 * @sys_clk: system clock name/frequency, used for various timing calculations
 * @scale: function used to scale the voltage of the voltagedomain
 * @nominal_volt: current nominal voltage for this voltage domain
 * @volt_data: voltage table having the distinct voltages supported
 *             by the domain and other associated per voltage data.
 */
struct voltagedomain {
	char *name;
	bool scalable;
	struct list_head node;
	struct omap_vc_channel *vc;
	const struct omap_vfsm_instance *vfsm;
	struct omap_vp_instance *vp;
	struct omap_voltdm_pmic *pmic;
	struct omap_vp_param *vp_param;
	struct omap_vc_param *vc_param;

	/* VC/VP register access functions: SoC specific */
	u32 (*read) (u8 offset);
	void (*write) (u32 val, u8 offset);
	u32 (*rmw)(u32 mask, u32 bits, u8 offset);

	union {
		const char *name;
		u32 rate;
	} sys_clk;

	int (*scale) (struct voltagedomain *voltdm,
		      unsigned long target_volt);

	u32 nominal_volt;
	struct omap_volt_data *volt_data;
};

/* Min and max voltages from OMAP perspective */
#define OMAP3430_VP1_VLIMITTO_VDDMIN	850000
#define OMAP3430_VP1_VLIMITTO_VDDMAX	1425000
#define OMAP3430_VP2_VLIMITTO_VDDMIN	900000
#define OMAP3430_VP2_VLIMITTO_VDDMAX	1150000

#define OMAP3630_VP1_VLIMITTO_VDDMIN	900000
#define OMAP3630_VP1_VLIMITTO_VDDMAX	1350000
#define OMAP3630_VP2_VLIMITTO_VDDMIN	900000
#define OMAP3630_VP2_VLIMITTO_VDDMAX	1200000

#define OMAP4_VP_MPU_VLIMITTO_VDDMIN	830000
#define OMAP4_VP_MPU_VLIMITTO_VDDMAX	1410000
#define OMAP4_VP_IVA_VLIMITTO_VDDMIN	830000
#define OMAP4_VP_IVA_VLIMITTO_VDDMAX	1260000
#define OMAP4_VP_CORE_VLIMITTO_VDDMIN	830000
#define OMAP4_VP_CORE_VLIMITTO_VDDMAX	1200000

/**
 * struct omap_voltdm_pmic - PMIC specific data required by voltage driver.
 * @slew_rate:	PMIC slew rate (in uv/us)
 * @step_size:	PMIC voltage step size (in uv)
 * @i2c_slave_addr: I2C slave address of PMIC
 * @volt_reg_addr: voltage configuration register address
 * @cmd_reg_addr: command (on, on-LP, ret, off) configuration register address
 * @i2c_high_speed: whether VC uses I2C high-speed mode to PMIC
 * @i2c_mcode: master code value for I2C high-speed preamble transmission
 * @vsel_to_uv:	PMIC API to convert vsel value to actual voltage in uV.
 * @uv_to_vsel:	PMIC API to convert voltage in uV to vsel value.
 */
struct omap_voltdm_pmic {
	int slew_rate;
	int step_size;
	u16 i2c_slave_addr;
	u16 volt_reg_addr;
	u16 cmd_reg_addr;
	u8 vp_erroroffset;
	u8 vp_vstepmin;
	u8 vp_vstepmax;
	u32 vddmin;
	u32 vddmax;
	u8 vp_timeout_us;
	bool i2c_high_speed;
	u32 i2c_pad_load;
	u8 i2c_mcode;
	unsigned long (*vsel_to_uv) (const u8 vsel);
	u8 (*uv_to_vsel) (unsigned long uV);
};

struct omap_vp_param {
	u32 vddmax;
	u32 vddmin;
};

struct omap_vc_param {
	u32 on;
	u32 onlp;
	u32 ret;
	u32 off;
};

void omap_voltage_get_volttable(struct voltagedomain *voltdm,
		struct omap_volt_data **volt_data);
struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm,
		unsigned long volt);
int omap_voltage_register_pmic(struct voltagedomain *voltdm,
			       struct omap_voltdm_pmic *pmic);
int omap_voltage_late_init(void);

extern void omap2xxx_voltagedomains_init(void);
extern void omap3xxx_voltagedomains_init(void);
extern void omap44xx_voltagedomains_init(void);
extern void omap54xx_voltagedomains_init(void);

struct voltagedomain *voltdm_lookup(const char *name);
void voltdm_init(struct voltagedomain **voltdm_list);
int voltdm_add_pwrdm(struct voltagedomain *voltdm, struct powerdomain *pwrdm);
int voltdm_scale(struct voltagedomain *voltdm, unsigned long target_volt);
void voltdm_reset(struct voltagedomain *voltdm);
unsigned long voltdm_get_voltage(struct voltagedomain *voltdm);
#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP3 voltage domain data
 *
 * Copyright (C) 2011 Texas Instruments, Inc.
 */
#include <linux/kernel.h>
#include <linux/init.h>

#include "voltage.h"

static struct voltagedomain omap2_voltdm_core = {
	.name = "core",
};

static struct voltagedomain omap2_voltdm_wkup = {
	.name = "wakeup",
};

static struct voltagedomain *voltagedomains_omap2[] __initdata = {
	&omap2_voltdm_core,
	&omap2_voltdm_wkup,
	NULL,
};

void __init omap2xxx_voltagedomains_init(void)
{
	voltdm_init(voltagedomains_omap2);
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP3 voltage domain data
 *
 * Copyright (C) 2007, 2010 Texas Instruments, Inc.
 * Rajendra Nayak <rnayak@ti.com>
 * Lesly A M <x0080970@ti.com>
 * Thara Gopinath <thara@ti.com>
 *
 * Copyright (C) 2008, 2011 Nokia Corporation
 * Kalle Jokiniemi
 * Paul Walmsley
 */
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/init.h>

#include "soc.h"
#include "common.h"
#include "prm-regbits-34xx.h"
#include "omap_opp_data.h"
#include "voltage.h"
#include "vc.h"
#include "vp.h"

/*
 * VDD data
 */

/* OMAP3-common voltagedomain data */

static struct voltagedomain omap3_voltdm_wkup = {
	.name = "wakeup",
};

/* 34xx/36xx voltagedomain data */

static const struct omap_vfsm_instance omap3_vdd1_vfsm = {
	.voltsetup_reg = OMAP3_PRM_VOLTSETUP1_OFFSET,
	.voltsetup_mask = OMAP3430_SETUP_TIME1_MASK,
};

static const struct omap_vfsm_instance omap3_vdd2_vfsm = {
	.voltsetup_reg = OMAP3_PRM_VOLTSETUP1_OFFSET,
	.voltsetup_mask = OMAP3430_SETUP_TIME2_MASK,
};

static struct voltagedomain omap3_voltdm_mpu = {
	.name = "mpu_iva",
	.scalable = true,
	.read = omap3_prm_vcvp_read,
	.write = omap3_prm_vcvp_write,
	.rmw = omap3_prm_vcvp_rmw,
	.vc = &omap3_vc_mpu,
	.vfsm = &omap3_vdd1_vfsm,
	.vp = &omap3_vp_mpu,
};

static struct voltagedomain omap3_voltdm_core = {
	.name = "core",
	.scalable = true,
	.read = omap3_prm_vcvp_read,
	.write = omap3_prm_vcvp_write,
	.rmw = omap3_prm_vcvp_rmw,
	.vc = &omap3_vc_core,
	.vfsm = &omap3_vdd2_vfsm,
	.vp = &omap3_vp_core,
};

static struct voltagedomain *voltagedomains_omap3[] __initdata = {
	&omap3_voltdm_mpu,
	&omap3_voltdm_core,
	&omap3_voltdm_wkup,
	NULL,
};

/* AM35xx voltagedomain data */

static struct voltagedomain am35xx_voltdm_mpu = {
	.name = "mpu_iva",
};

static struct voltagedomain am35xx_voltdm_core = {
	.name = "core",
};

static struct voltagedomain *voltagedomains_am35xx[] __initdata = {
	&am35xx_voltdm_mpu,
	&am35xx_voltdm_core,
	&omap3_voltdm_wkup,
	NULL,
};

static const char *const sys_clk_name __initconst = "sys_ck";

void __init omap3xxx_voltagedomains_init(void)
{
	struct voltagedomain *voltdm;
	struct voltagedomain **voltdms;
	int i;

	/*
	 * XXX Will depend on the process, validation, and binning
	 * for the currently-running IC
	 */
#ifdef CONFIG_PM_OPP
	if (cpu_is_omap3630()) {
		omap3_voltdm_mpu.volt_data = omap36xx_vddmpu_volt_data;
		omap3_voltdm_core.volt_data = omap36xx_vddcore_volt_data;
	} else {
		omap3_voltdm_mpu.volt_data = omap34xx_vddmpu_volt_data;
		omap3_voltdm_core.volt_data = omap34xx_vddcore_volt_data;
	}
#endif

	omap3_voltdm_mpu.vp_param = &omap3_mpu_vp_data;
	omap3_voltdm_core.vp_param = &omap3_core_vp_data;
	omap3_voltdm_mpu.vc_param = &omap3_mpu_vc_data;
	omap3_voltdm_core.vc_param = &omap3_core_vc_data;

	if (soc_is_am35xx())
		voltdms = voltagedomains_am35xx;
	else
		voltdms = voltagedomains_omap3;

	for (i = 0; voltdm = voltdms[i], voltdm; i++)
		voltdm->sys_clk.name = sys_clk_name;

	voltdm_init(voltdms);
};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP3/OMAP4 Voltage Management Routines
 *
 * Author: Thara Gopinath	<thara@ti.com>
 *
 * Copyright (C) 2007 Texas Instruments, Inc.
 * Rajendra Nayak <rnayak@ti.com>
 * Lesly A M <x0080970@ti.com>
 *
 * Copyright (C) 2008 Nokia Corporation
 * Kalle Jokiniemi
 *
 * Copyright (C) 2010 Texas Instruments, Inc.
 * Thara Gopinath <thara@ti.com>
 */
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/init.h>

#include "common.h"
#include "soc.h"
#include "prm-regbits-44xx.h"
#include "prm44xx.h"
#include "prcm44xx.h"
#include "prminst44xx.h"
#include "voltage.h"
#include "omap_opp_data.h"
#include "vc.h"
#include "vp.h"

static const struct omap_vfsm_instance omap4_vdd_mpu_vfsm = {
	.voltsetup_reg = OMAP4_PRM_VOLTSETUP_MPU_RET_SLEEP_OFFSET,
	.voltsetup_off_reg = OMAP4_PRM_VOLTSETUP_MPU_OFF_OFFSET,
};

static const struct omap_vfsm_instance omap4_vdd_iva_vfsm = {
	.voltsetup_reg = OMAP4_PRM_VOLTSETUP_IVA_RET_SLEEP_OFFSET,
	.voltsetup_off_reg = OMAP4_PRM_VOLTSETUP_IVA_OFF_OFFSET,
};

static const struct omap_vfsm_instance omap4_vdd_core_vfsm = {
	.voltsetup_reg = OMAP4_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET,
	.voltsetup_off_reg = OMAP4_PRM_VOLTSETUP_CORE_OFF_OFFSET,
};

static struct voltagedomain omap4_voltdm_mpu = {
	.name = "mpu",
	.scalable = true,
	.read = omap4_prm_vcvp_read,
	.write = omap4_prm_vcvp_write,
	.rmw = omap4_prm_vcvp_rmw,
	.vc = &omap4_vc_mpu,
	.vfsm = &omap4_vdd_mpu_vfsm,
	.vp = &omap4_vp_mpu,
};

static struct voltagedomain omap4_voltdm_iva = {
	.name = "iva",
	.scalable = true,
	.read = omap4_prm_vcvp_read,
	.write = omap4_prm_vcvp_write,
	.rmw = omap4_prm_vcvp_rmw,
	.vc = &omap4_vc_iva,
	.vfsm = &omap4_vdd_iva_vfsm,
	.vp = &omap4_vp_iva,
};

static struct voltagedomain omap4_voltdm_core = {
	.name = "core",
	.scalable = true,
	.read = omap4_prm_vcvp_read,
	.write = omap4_prm_vcvp_write,
	.rmw = omap4_prm_vcvp_rmw,
	.vc = &omap4_vc_core,
	.vfsm = &omap4_vdd_core_vfsm,
	.vp = &omap4_vp_core,
};

static struct voltagedomain omap4_voltdm_wkup = {
	.name = "wakeup",
};

static struct voltagedomain *voltagedomains_omap4[] __initdata = {
	&omap4_voltdm_mpu,
	&omap4_voltdm_iva,
	&omap4_voltdm_core,
	&omap4_voltdm_wkup,
	NULL,
};

static const char *const sys_clk_name __initconst = "sys_clkin_ck";

void __init omap44xx_voltagedomains_init(void)
{
	struct voltagedomain *voltdm;
	int i;

	/*
	 * XXX Will depend on the process, validation, and binning
	 * for the currently-running IC
	 */
#ifdef CONFIG_PM_OPP
	if (cpu_is_omap443x()) {
		omap4_voltdm_mpu.volt_data = omap443x_vdd_mpu_volt_data;
		omap4_voltdm_iva.volt_data = omap443x_vdd_iva_volt_data;
		omap4_voltdm_core.volt_data = omap443x_vdd_core_volt_data;
	} else if (cpu_is_omap446x()) {
		omap4_voltdm_mpu.volt_data = omap446x_vdd_mpu_volt_data;
		omap4_voltdm_iva.volt_data = omap446x_vdd_iva_volt_data;
		omap4_voltdm_core.volt_data = omap446x_vdd_core_volt_data;
	}
#endif

	omap4_voltdm_mpu.vp_param = &omap4_mpu_vp_data;
	omap4_voltdm_iva.vp_param = &omap4_iva_vp_data;
	omap4_voltdm_core.vp_param = &omap4_core_vp_data;

	omap4_voltdm_mpu.vc_param = &omap4_mpu_vc_data;
	omap4_voltdm_iva.vc_param = &omap4_iva_vc_data;
	omap4_voltdm_core.vc_param = &omap4_core_vc_data;

	for (i = 0; voltdm = voltagedomains_omap4[i], voltdm; i++)
		voltdm->sys_clk.name = sys_clk_name;

	voltdm_init(voltagedomains_omap4);
};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP5 Voltage Management Routines
 *
 * Based on voltagedomains44xx_data.c
 *
 * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com
 */
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/init.h>

#include "common.h"

#include "prm54xx.h"
#include "voltage.h"
#include "omap_opp_data.h"
#include "vc.h"
#include "vp.h"

static const struct omap_vfsm_instance omap5_vdd_mpu_vfsm = {
	.voltsetup_reg = OMAP54XX_PRM_VOLTSETUP_MPU_RET_SLEEP_OFFSET,
};

static const struct omap_vfsm_instance omap5_vdd_mm_vfsm = {
	.voltsetup_reg = OMAP54XX_PRM_VOLTSETUP_MM_RET_SLEEP_OFFSET,
};

static const struct omap_vfsm_instance omap5_vdd_core_vfsm = {
	.voltsetup_reg = OMAP54XX_PRM_VOLTSETUP_CORE_RET_SLEEP_OFFSET,
};

static struct voltagedomain omap5_voltdm_mpu = {
	.name = "mpu",
	.scalable = true,
	.read = omap4_prm_vcvp_read,
	.write = omap4_prm_vcvp_write,
	.rmw = omap4_prm_vcvp_rmw,
	.vc = &omap4_vc_mpu,
	.vfsm = &omap5_vdd_mpu_vfsm,
	.vp = &omap4_vp_mpu,
};

static struct voltagedomain omap5_voltdm_mm = {
	.name = "mm",
	.scalable = true,
	.read = omap4_prm_vcvp_read,
	.write = omap4_prm_vcvp_write,
	.rmw = omap4_prm_vcvp_rmw,
	.vc = &omap4_vc_iva,
	.vfsm = &omap5_vdd_mm_vfsm,
	.vp = &omap4_vp_iva,
};

static struct voltagedomain omap5_voltdm_core = {
	.name = "core",
	.scalable = true,
	.read = omap4_prm_vcvp_read,
	.write = omap4_prm_vcvp_write,
	.rmw = omap4_prm_vcvp_rmw,
	.vc = &omap4_vc_core,
	.vfsm = &omap5_vdd_core_vfsm,
	.vp = &omap4_vp_core,
};

static struct voltagedomain omap5_voltdm_wkup = {
	.name = "wkup",
};

static struct voltagedomain *voltagedomains_omap5[] __initdata = {
	&omap5_voltdm_mpu,
	&omap5_voltdm_mm,
	&omap5_voltdm_core,
	&omap5_voltdm_wkup,
	NULL,
};

static const char *const sys_clk_name __initconst = "sys_clkin";

void __init omap54xx_voltagedomains_init(void)
{
	struct voltagedomain *voltdm;
	int i;

	for (i = 0; voltdm = voltagedomains_omap5[i], voltdm; i++)
		voltdm->sys_clk.name = sys_clk_name;

	voltdm_init(voltagedomains_omap5);
};


code:
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/init.h>

#include "common.h"

#include "voltage.h"
#include "vp.h"
#include "prm-regbits-34xx.h"
#include "prm-regbits-44xx.h"
#include "prm44xx.h"

static u32 _vp_set_init_voltage(struct voltagedomain *voltdm, u32 volt)
{
	struct omap_vp_instance *vp = voltdm->vp;
	u32 vpconfig;
	char vsel;

	vsel = voltdm->pmic->uv_to_vsel(volt);

	vpconfig = voltdm->read(vp->vpconfig);
	vpconfig &= ~(vp->common->vpconfig_initvoltage_mask |
		      vp->common->vpconfig_forceupdate |
		      vp->common->vpconfig_initvdd);
	vpconfig |= vsel << __ffs(vp->common->vpconfig_initvoltage_mask);
	voltdm->write(vpconfig, vp->vpconfig);

	/* Trigger initVDD value copy to voltage processor */
	voltdm->write((vpconfig | vp->common->vpconfig_initvdd),
		       vp->vpconfig);

	/* Clear initVDD copy trigger bit */
	voltdm->write(vpconfig, vp->vpconfig);

	return vpconfig;
}

/* Generic voltage init functions */
void __init omap_vp_init(struct voltagedomain *voltdm)
{
	struct omap_vp_instance *vp = voltdm->vp;
	u32 val, sys_clk_rate, timeout, waittime;
	u32 vddmin, vddmax, vstepmin, vstepmax;

	if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) {
		pr_err("%s: No PMIC info for vdd_%s\n", __func__, voltdm->name);
		return;
	}

	if (!voltdm->read || !voltdm->write) {
		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
			__func__, voltdm->name);
		return;
	}

	vp->enabled = false;

	/* Divide to avoid overflow */
	sys_clk_rate = voltdm->sys_clk.rate / 1000;

	timeout = (sys_clk_rate * voltdm->pmic->vp_timeout_us) / 1000;
	vddmin = max(voltdm->vp_param->vddmin, voltdm->pmic->vddmin);
	vddmax = min(voltdm->vp_param->vddmax, voltdm->pmic->vddmax);
	vddmin = voltdm->pmic->uv_to_vsel(vddmin);
	vddmax = voltdm->pmic->uv_to_vsel(vddmax);

	waittime = DIV_ROUND_UP(voltdm->pmic->step_size * sys_clk_rate,
				1000 * voltdm->pmic->slew_rate);
	vstepmin = voltdm->pmic->vp_vstepmin;
	vstepmax = voltdm->pmic->vp_vstepmax;

	/*
	 * VP_CONFIG: error gain is not set here, it will be updated
	 * on each scale, based on OPP.
	 */
	val = (voltdm->pmic->vp_erroroffset <<
	       __ffs(voltdm->vp->common->vpconfig_erroroffset_mask)) |
		vp->common->vpconfig_timeouten;
	voltdm->write(val, vp->vpconfig);

	/* VSTEPMIN */
	val = (waittime << vp->common->vstepmin_smpswaittimemin_shift) |
		(vstepmin <<  vp->common->vstepmin_stepmin_shift);
	voltdm->write(val, vp->vstepmin);

	/* VSTEPMAX */
	val = (vstepmax << vp->common->vstepmax_stepmax_shift) |
		(waittime << vp->common->vstepmax_smpswaittimemax_shift);
	voltdm->write(val, vp->vstepmax);

	/* VLIMITTO */
	val = (vddmax << vp->common->vlimitto_vddmax_shift) |
		(vddmin << vp->common->vlimitto_vddmin_shift) |
		(timeout <<  vp->common->vlimitto_timeout_shift);
	voltdm->write(val, vp->vlimitto);
}

int omap_vp_update_errorgain(struct voltagedomain *voltdm,
			     unsigned long target_volt)
{
	struct omap_volt_data *volt_data;

	if (!voltdm->vp)
		return -EINVAL;

	/* Get volt_data corresponding to target_volt */
	volt_data = omap_voltage_get_voltdata(voltdm, target_volt);
	if (IS_ERR(volt_data))
		return -EINVAL;

	/* Setting vp errorgain based on the voltage */
	voltdm->rmw(voltdm->vp->common->vpconfig_errorgain_mask,
		    volt_data->vp_errgain <<
		    __ffs(voltdm->vp->common->vpconfig_errorgain_mask),
		    voltdm->vp->vpconfig);

	return 0;
}

/* VP force update method of voltage scaling */
int omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
			      unsigned long target_volt)
{
	struct omap_vp_instance *vp = voltdm->vp;
	u32 vpconfig;
	u8 target_vsel, current_vsel;
	int ret, timeout = 0;

	ret = omap_vc_pre_scale(voltdm, target_volt, &target_vsel, &current_vsel);
	if (ret)
		return ret;

	/*
	 * Clear all pending TransactionDone interrupt/status. Typical latency
	 * is <3us
	 */
	while (timeout++ < VP_TRANXDONE_TIMEOUT) {
		vp->common->ops->clear_txdone(vp->id);
		if (!vp->common->ops->check_txdone(vp->id))
			break;
		udelay(1);
	}
	if (timeout >= VP_TRANXDONE_TIMEOUT) {
		pr_warn("%s: vdd_%s TRANXDONE timeout exceeded. Voltage change aborted\n",
			__func__, voltdm->name);
		return -ETIMEDOUT;
	}

	vpconfig = _vp_set_init_voltage(voltdm, target_volt);

	/* Force update of voltage */
	voltdm->write(vpconfig | vp->common->vpconfig_forceupdate,
		      voltdm->vp->vpconfig);

	/*
	 * Wait for TransactionDone. Typical latency is <200us.
	 * Depends on SMPSWAITTIMEMIN/MAX and voltage change
	 */
	timeout = 0;
	omap_test_timeout(vp->common->ops->check_txdone(vp->id),
			  VP_TRANXDONE_TIMEOUT, timeout);
	if (timeout >= VP_TRANXDONE_TIMEOUT)
		pr_err("%s: vdd_%s TRANXDONE timeout exceeded. TRANXDONE never got set after the voltage update\n",
		       __func__, voltdm->name);

	omap_vc_post_scale(voltdm, target_volt, target_vsel, current_vsel);

	/*
	 * Disable TransactionDone interrupt , clear all status, clear
	 * control registers
	 */
	timeout = 0;
	while (timeout++ < VP_TRANXDONE_TIMEOUT) {
		vp->common->ops->clear_txdone(vp->id);
		if (!vp->common->ops->check_txdone(vp->id))
			break;
		udelay(1);
	}

	if (timeout >= VP_TRANXDONE_TIMEOUT)
		pr_warn("%s: vdd_%s TRANXDONE timeout exceeded while trying to clear the TRANXDONE status\n",
			__func__, voltdm->name);

	/* Clear force bit */
	voltdm->write(vpconfig, vp->vpconfig);

	return 0;
}

/**
 * omap_vp_enable() - API to enable a particular VP
 * @voltdm:	pointer to the VDD whose VP is to be enabled.
 *
 * This API enables a particular voltage processor. Needed by the smartreflex
 * class drivers.
 */
void omap_vp_enable(struct voltagedomain *voltdm)
{
	struct omap_vp_instance *vp;
	u32 vpconfig, volt;

	if (!voltdm || IS_ERR(voltdm)) {
		pr_warn("%s: VDD specified does not exist!\n", __func__);
		return;
	}

	vp = voltdm->vp;
	if (!voltdm->read || !voltdm->write) {
		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
			__func__, voltdm->name);
		return;
	}

	/* If VP is already enabled, do nothing. Return */
	if (vp->enabled)
		return;

	volt = voltdm_get_voltage(voltdm);
	if (!volt) {
		pr_warn("%s: unable to find current voltage for %s\n",
			__func__, voltdm->name);
		return;
	}

	vpconfig = _vp_set_init_voltage(voltdm, volt);

	/* Enable VP */
	vpconfig |= vp->common->vpconfig_vpenable;
	voltdm->write(vpconfig, vp->vpconfig);

	vp->enabled = true;
}

/**
 * omap_vp_disable() - API to disable a particular VP
 * @voltdm:	pointer to the VDD whose VP is to be disabled.
 *
 * This API disables a particular voltage processor. Needed by the smartreflex
 * class drivers.
 */
void omap_vp_disable(struct voltagedomain *voltdm)
{
	struct omap_vp_instance *vp;
	u32 vpconfig;
	int timeout;

	if (!voltdm || IS_ERR(voltdm)) {
		pr_warn("%s: VDD specified does not exist!\n", __func__);
		return;
	}

	vp = voltdm->vp;
	if (!voltdm->read || !voltdm->write) {
		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
			__func__, voltdm->name);
		return;
	}

	/* If VP is already disabled, do nothing. Return */
	if (!vp->enabled) {
		pr_warn("%s: Trying to disable VP for vdd_%s when it is already disabled\n",
			__func__, voltdm->name);
		return;
	}

	/* Disable VP */
	vpconfig = voltdm->read(vp->vpconfig);
	vpconfig &= ~vp->common->vpconfig_vpenable;
	voltdm->write(vpconfig, vp->vpconfig);

	/*
	 * Wait for VP idle Typical latency is <2us. Maximum latency is ~100us
	 */
	omap_test_timeout((voltdm->read(vp->vstatus)),
			  VP_IDLE_TIMEOUT, timeout);

	if (timeout >= VP_IDLE_TIMEOUT)
		pr_warn("%s: vdd_%s idle timedout\n", __func__, voltdm->name);

	vp->enabled = false;

	return;
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * OMAP3/4 Voltage Processor (VP) structure and macro definitions
 *
 * Copyright (C) 2007, 2010 Texas Instruments, Inc.
 * Rajendra Nayak <rnayak@ti.com>
 * Lesly A M <x0080970@ti.com>
 * Thara Gopinath <thara@ti.com>
 *
 * Copyright (C) 2008, 2011 Nokia Corporation
 * Kalle Jokiniemi
 * Paul Walmsley
 */
#ifndef __ARCH_ARM_MACH_OMAP2_VP_H
#define __ARCH_ARM_MACH_OMAP2_VP_H

#include <linux/kernel.h>

struct voltagedomain;

/* XXX document */
#define VP_IDLE_TIMEOUT		200
#define VP_TRANXDONE_TIMEOUT	300

/**
 * struct omap_vp_ops - per-VP operations
 * @check_txdone: check for VP transaction done
 * @clear_txdone: clear VP transaction done status
 */
struct omap_vp_ops {
	u32 (*check_txdone)(u8 vp_id);
	void (*clear_txdone)(u8 vp_id);
};

/**
 * struct omap_vp_common - register data common to all VDDs
 * @vpconfig_erroroffset_mask: ERROROFFSET bitmask in the PRM_VP*_CONFIG reg
 * @vpconfig_errorgain_mask: ERRORGAIN bitmask in the PRM_VP*_CONFIG reg
 * @vpconfig_initvoltage_mask: INITVOLTAGE bitmask in the PRM_VP*_CONFIG reg
 * @vpconfig_timeouten: TIMEOUT bitmask in the PRM_VP*_CONFIG reg
 * @vpconfig_initvdd: INITVDD bitmask in the PRM_VP*_CONFIG reg
 * @vpconfig_forceupdate: FORCEUPDATE bitmask in the PRM_VP*_CONFIG reg
 * @vpconfig_vpenable: VPENABLE bitmask in the PRM_VP*_CONFIG reg
 * @vpconfig_erroroffset_shift: ERROROFFSET field shift in PRM_VP*_CONFIG reg
 * @vpconfig_errorgain_shift: ERRORGAIN field shift in PRM_VP*_CONFIG reg
 * @vpconfig_initvoltage_shift: INITVOLTAGE field shift in PRM_VP*_CONFIG reg
 * @vstepmin_stepmin_shift: VSTEPMIN field shift in the PRM_VP*_VSTEPMIN reg
 * @vstepmin_smpswaittimemin_shift: SMPSWAITTIMEMIN field shift in PRM_VP*_VSTEPMIN reg
 * @vstepmax_stepmax_shift: VSTEPMAX field shift in the PRM_VP*_VSTEPMAX reg
 * @vstepmax_smpswaittimemax_shift: SMPSWAITTIMEMAX field shift in PRM_VP*_VSTEPMAX reg
 * @vlimitto_vddmin_shift: VDDMIN field shift in PRM_VP*_VLIMITTO reg
 * @vlimitto_vddmax_shift: VDDMAX field shift in PRM_VP*_VLIMITTO reg
 * @vlimitto_timeout_shift: TIMEOUT field shift in PRM_VP*_VLIMITTO reg
 * @vpvoltage_mask: VPVOLTAGE field mask in PRM_VP*_VOLTAGE reg
 */
struct omap_vp_common {
	u32 vpconfig_erroroffset_mask;
	u32 vpconfig_errorgain_mask;
	u32 vpconfig_initvoltage_mask;
	u8 vpconfig_timeouten;
	u8 vpconfig_initvdd;
	u8 vpconfig_forceupdate;
	u8 vpconfig_vpenable;
	u8 vstepmin_stepmin_shift;
	u8 vstepmin_smpswaittimemin_shift;
	u8 vstepmax_stepmax_shift;
	u8 vstepmax_smpswaittimemax_shift;
	u8 vlimitto_vddmin_shift;
	u8 vlimitto_vddmax_shift;
	u8 vlimitto_timeout_shift;
	u8 vpvoltage_mask;

	const struct omap_vp_ops *ops;
};

/**
 * struct omap_vp_instance - VP register offsets (per-VDD)
 * @common: pointer to struct omap_vp_common * for this SoC
 * @vpconfig: PRM_VP*_CONFIG reg offset from PRM start
 * @vstepmin: PRM_VP*_VSTEPMIN reg offset from PRM start
 * @vlimitto: PRM_VP*_VLIMITTO reg offset from PRM start
 * @vstatus: PRM_VP*_VSTATUS reg offset from PRM start
 * @voltage: PRM_VP*_VOLTAGE reg offset from PRM start
 * @id: Unique identifier for VP instance.
 * @enabled: flag to keep track of whether vp is enabled or not
 *
 * XXX vp_common is probably not needed since it is per-SoC
 */
struct omap_vp_instance {
	const struct omap_vp_common *common;
	u8 vpconfig;
	u8 vstepmin;
	u8 vstepmax;
	u8 vlimitto;
	u8 vstatus;
	u8 voltage;
	u8 id;
	bool enabled;
};

extern struct omap_vp_instance omap3_vp_mpu;
extern struct omap_vp_instance omap3_vp_core;

extern struct omap_vp_instance omap4_vp_mpu;
extern struct omap_vp_instance omap4_vp_iva;
extern struct omap_vp_instance omap4_vp_core;

extern struct omap_vp_param omap3_mpu_vp_data;
extern struct omap_vp_param omap3_core_vp_data;

extern struct omap_vp_param omap4_mpu_vp_data;
extern struct omap_vp_param omap4_iva_vp_data;
extern struct omap_vp_param omap4_core_vp_data;

void omap_vp_init(struct voltagedomain *voltdm);
void omap_vp_enable(struct voltagedomain *voltdm);
void omap_vp_disable(struct voltagedomain *voltdm);
int omap_vp_forceupdate_scale(struct voltagedomain *voltdm,
			      unsigned long target_volt);
int omap_vp_update_errorgain(struct voltagedomain *voltdm,
			     unsigned long target_volt);

#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP3 Voltage Processor (VP) data
 *
 * Copyright (C) 2007, 2010 Texas Instruments, Inc.
 * Rajendra Nayak <rnayak@ti.com>
 * Lesly A M <x0080970@ti.com>
 * Thara Gopinath <thara@ti.com>
 *
 * Copyright (C) 2008, 2011 Nokia Corporation
 * Kalle Jokiniemi
 * Paul Walmsley
 */

#include <linux/io.h>
#include <linux/err.h>
#include <linux/init.h>

#include "common.h"

#include "prm-regbits-34xx.h"
#include "voltage.h"

#include "vp.h"
#include "prm2xxx_3xxx.h"

static const struct omap_vp_ops omap3_vp_ops = {
	.check_txdone = omap_prm_vp_check_txdone,
	.clear_txdone = omap_prm_vp_clear_txdone,
};

/*
 * VP data common to 34xx/36xx chips
 * XXX This stuff presumably belongs in the vp3xxx.c or vp.c file.
 */
static const struct omap_vp_common omap3_vp_common = {
	.vpconfig_erroroffset_mask = OMAP3430_ERROROFFSET_MASK,
	.vpconfig_errorgain_mask = OMAP3430_ERRORGAIN_MASK,
	.vpconfig_initvoltage_mask = OMAP3430_INITVOLTAGE_MASK,
	.vpconfig_timeouten = OMAP3430_TIMEOUTEN_MASK,
	.vpconfig_initvdd = OMAP3430_INITVDD_MASK,
	.vpconfig_forceupdate = OMAP3430_FORCEUPDATE_MASK,
	.vpconfig_vpenable = OMAP3430_VPENABLE_MASK,
	.vstepmin_smpswaittimemin_shift = OMAP3430_SMPSWAITTIMEMIN_SHIFT,
	.vstepmax_smpswaittimemax_shift = OMAP3430_SMPSWAITTIMEMAX_SHIFT,
	.vstepmin_stepmin_shift = OMAP3430_VSTEPMIN_SHIFT,
	.vstepmax_stepmax_shift = OMAP3430_VSTEPMAX_SHIFT,
	.vlimitto_vddmin_shift = OMAP3430_VDDMIN_SHIFT,
	.vlimitto_vddmax_shift = OMAP3430_VDDMAX_SHIFT,
	.vlimitto_timeout_shift = OMAP3430_TIMEOUT_SHIFT,
	.vpvoltage_mask = OMAP3430_VPVOLTAGE_MASK,

	.ops = &omap3_vp_ops,
};

struct omap_vp_instance omap3_vp_mpu = {
	.id = OMAP3_VP_VDD_MPU_ID,
	.common = &omap3_vp_common,
	.vpconfig = OMAP3_PRM_VP1_CONFIG_OFFSET,
	.vstepmin = OMAP3_PRM_VP1_VSTEPMIN_OFFSET,
	.vstepmax = OMAP3_PRM_VP1_VSTEPMAX_OFFSET,
	.vlimitto = OMAP3_PRM_VP1_VLIMITTO_OFFSET,
	.vstatus = OMAP3_PRM_VP1_STATUS_OFFSET,
	.voltage = OMAP3_PRM_VP1_VOLTAGE_OFFSET,
};

struct omap_vp_instance omap3_vp_core = {
	.id = OMAP3_VP_VDD_CORE_ID,
	.common = &omap3_vp_common,
	.vpconfig = OMAP3_PRM_VP2_CONFIG_OFFSET,
	.vstepmin = OMAP3_PRM_VP2_VSTEPMIN_OFFSET,
	.vstepmax = OMAP3_PRM_VP2_VSTEPMAX_OFFSET,
	.vlimitto = OMAP3_PRM_VP2_VLIMITTO_OFFSET,
	.vstatus = OMAP3_PRM_VP2_STATUS_OFFSET,
	.voltage = OMAP3_PRM_VP2_VOLTAGE_OFFSET,
};

struct omap_vp_param omap3_mpu_vp_data = {
	.vddmin			= OMAP3430_VP1_VLIMITTO_VDDMIN,
	.vddmax			= OMAP3430_VP1_VLIMITTO_VDDMAX,
};

struct omap_vp_param omap3_core_vp_data = {
	.vddmin			= OMAP3430_VP2_VLIMITTO_VDDMIN,
	.vddmax			= OMAP3430_VP2_VLIMITTO_VDDMAX,
};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP3 Voltage Processor (VP) data
 *
 * Copyright (C) 2007, 2010 Texas Instruments, Inc.
 * Rajendra Nayak <rnayak@ti.com>
 * Lesly A M <x0080970@ti.com>
 * Thara Gopinath <thara@ti.com>
 *
 * Copyright (C) 2008, 2011 Nokia Corporation
 * Kalle Jokiniemi
 * Paul Walmsley
 */

#include <linux/io.h>
#include <linux/err.h>
#include <linux/init.h>

#include "common.h"

#include "prm44xx.h"
#include "prm-regbits-44xx.h"
#include "voltage.h"

#include "vp.h"

static const struct omap_vp_ops omap4_vp_ops = {
	.check_txdone = omap_prm_vp_check_txdone,
	.clear_txdone = omap_prm_vp_clear_txdone,
};

/*
 * VP data common to 44xx chips
 * XXX This stuff presumably belongs in the vp44xx.c or vp.c file.
 */
static const struct omap_vp_common omap4_vp_common = {
	.vpconfig_erroroffset_mask = OMAP4430_ERROROFFSET_MASK,
	.vpconfig_errorgain_mask = OMAP4430_ERRORGAIN_MASK,
	.vpconfig_initvoltage_mask = OMAP4430_INITVOLTAGE_MASK,
	.vpconfig_timeouten = OMAP4430_TIMEOUTEN_MASK,
	.vpconfig_initvdd = OMAP4430_INITVDD_MASK,
	.vpconfig_forceupdate = OMAP4430_FORCEUPDATE_MASK,
	.vpconfig_vpenable = OMAP4430_VPENABLE_MASK,
	.vstepmin_smpswaittimemin_shift = OMAP4430_SMPSWAITTIMEMIN_SHIFT,
	.vstepmax_smpswaittimemax_shift = OMAP4430_SMPSWAITTIMEMAX_SHIFT,
	.vstepmin_stepmin_shift = OMAP4430_VSTEPMIN_SHIFT,
	.vstepmax_stepmax_shift = OMAP4430_VSTEPMAX_SHIFT,
	.vlimitto_vddmin_shift = OMAP4430_VDDMIN_SHIFT,
	.vlimitto_vddmax_shift = OMAP4430_VDDMAX_SHIFT,
	.vlimitto_timeout_shift = OMAP4430_TIMEOUT_SHIFT,
	.vpvoltage_mask = OMAP4430_VPVOLTAGE_MASK,
	.ops = &omap4_vp_ops,
};

struct omap_vp_instance omap4_vp_mpu = {
	.id = OMAP4_VP_VDD_MPU_ID,
	.common = &omap4_vp_common,
	.vpconfig = OMAP4_PRM_VP_MPU_CONFIG_OFFSET,
	.vstepmin = OMAP4_PRM_VP_MPU_VSTEPMIN_OFFSET,
	.vstepmax = OMAP4_PRM_VP_MPU_VSTEPMAX_OFFSET,
	.vlimitto = OMAP4_PRM_VP_MPU_VLIMITTO_OFFSET,
	.vstatus = OMAP4_PRM_VP_MPU_STATUS_OFFSET,
	.voltage = OMAP4_PRM_VP_MPU_VOLTAGE_OFFSET,
};

struct omap_vp_instance omap4_vp_iva = {
	.id = OMAP4_VP_VDD_IVA_ID,
	.common = &omap4_vp_common,
	.vpconfig = OMAP4_PRM_VP_IVA_CONFIG_OFFSET,
	.vstepmin = OMAP4_PRM_VP_IVA_VSTEPMIN_OFFSET,
	.vstepmax = OMAP4_PRM_VP_IVA_VSTEPMAX_OFFSET,
	.vlimitto = OMAP4_PRM_VP_IVA_VLIMITTO_OFFSET,
	.vstatus = OMAP4_PRM_VP_IVA_STATUS_OFFSET,
	.voltage = OMAP4_PRM_VP_IVA_VOLTAGE_OFFSET,
};

struct omap_vp_instance omap4_vp_core = {
	.id = OMAP4_VP_VDD_CORE_ID,
	.common = &omap4_vp_common,
	.vpconfig = OMAP4_PRM_VP_CORE_CONFIG_OFFSET,
	.vstepmin = OMAP4_PRM_VP_CORE_VSTEPMIN_OFFSET,
	.vstepmax = OMAP4_PRM_VP_CORE_VSTEPMAX_OFFSET,
	.vlimitto = OMAP4_PRM_VP_CORE_VLIMITTO_OFFSET,
	.vstatus = OMAP4_PRM_VP_CORE_STATUS_OFFSET,
	.voltage = OMAP4_PRM_VP_CORE_VOLTAGE_OFFSET,
};

struct omap_vp_param omap4_mpu_vp_data = {
	.vddmin			= OMAP4_VP_MPU_VLIMITTO_VDDMIN,
	.vddmax			= OMAP4_VP_MPU_VLIMITTO_VDDMAX,
};

struct omap_vp_param omap4_iva_vp_data = {
	.vddmin			= OMAP4_VP_IVA_VLIMITTO_VDDMIN,
	.vddmax			= OMAP4_VP_IVA_VLIMITTO_VDDMAX,
};

struct omap_vp_param omap4_core_vp_data = {
	.vddmin			= OMAP4_VP_CORE_VLIMITTO_VDDMIN,
	.vddmax			= OMAP4_VP_CORE_VLIMITTO_VDDMAX,
};


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * OMAP2+ MPU WD_TIMER-specific code
 *
 * Copyright (C) 2012 Texas Instruments, Inc.
 */

#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/err.h>

#include <linux/platform_data/omap-wd-timer.h>

#include "omap_hwmod.h"
#include "omap_device.h"
#include "wd_timer.h"
#include "common.h"
#include "prm.h"
#include "soc.h"

/*
 * In order to avoid any assumptions from bootloader regarding WDT
 * settings, WDT module is reset during init. This enables the watchdog
 * timer. Hence it is required to disable the watchdog after the WDT reset
 * during init. Otherwise the system would reboot as per the default
 * watchdog timer registers settings.
 */
#define OMAP_WDT_WPS		0x34
#define OMAP_WDT_SPR		0x48

int omap2_wd_timer_disable(struct omap_hwmod *oh)
{
	void __iomem *base;

	if (!oh) {
		pr_err("%s: Could not look up wdtimer_hwmod\n", __func__);
		return -EINVAL;
	}

	base = omap_hwmod_get_mpu_rt_va(oh);
	if (!base) {
		pr_err("%s: Could not get the base address for %s\n",
				oh->name, __func__);
		return -EINVAL;
	}

	/* sequence required to disable watchdog */
	writel_relaxed(0xAAAA, base + OMAP_WDT_SPR);
	while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10)
		cpu_relax();

	writel_relaxed(0x5555, base + OMAP_WDT_SPR);
	while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10)
		cpu_relax();

	return 0;
}

/**
 * omap2_wdtimer_reset - reset and disable the WDTIMER IP block
 * @oh: struct omap_hwmod *
 *
 * After the WDTIMER IP blocks are reset on OMAP2/3, we must also take
 * care to execute the special watchdog disable sequence.  This is
 * because the watchdog is re-armed upon OCP softreset.  (On OMAP4,
 * this behavior was apparently changed and the watchdog is no longer
 * re-armed after an OCP soft-reset.)  Returns -ETIMEDOUT if the reset
 * did not complete, or 0 upon success.
 *
 * XXX Most of this code should be moved to the omap_hwmod.c layer
 * during a normal merge window.  omap_hwmod_softreset() should be
 * renamed to omap_hwmod_set_ocp_softreset(), and omap_hwmod_softreset()
 * should call the hwmod _ocp_softreset() code.
 */
int omap2_wd_timer_reset(struct omap_hwmod *oh)
{
	int c = 0;

	/* Write to the SOFTRESET bit */
	omap_hwmod_softreset(oh);

	/* Poll on RESETDONE bit */
	omap_test_timeout((omap_hwmod_read(oh,
					   oh->class->sysc->syss_offs)
			   & SYSS_RESETDONE_MASK),
			  MAX_MODULE_SOFTRESET_WAIT, c);

	if (oh->class->sysc->srst_udelay)
		udelay(oh->class->sysc->srst_udelay);

	if (c == MAX_MODULE_SOFTRESET_WAIT)
		pr_warn("%s: %s: softreset failed (waited %d usec)\n",
			__func__, oh->name, MAX_MODULE_SOFTRESET_WAIT);
	else
		pr_debug("%s: %s: softreset in %d usec\n", __func__,
			 oh->name, c);

	return (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT :
		omap2_wd_timer_disable(oh);
}


code:
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * OMAP2+ MPU WD_TIMER-specific function prototypes
 */

#ifndef __ARCH_ARM_MACH_OMAP2_WD_TIMER_H
#define __ARCH_ARM_MACH_OMAP2_WD_TIMER_H

#include "omap_hwmod.h"

extern int omap2_wd_timer_disable(struct omap_hwmod *oh);
extern int omap2_wd_timer_reset(struct omap_hwmod *oh);

#endif


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_ORION5X
	bool "Marvell Orion"
	depends on ARCH_MULTI_V5
	depends on CPU_LITTLE_ENDIAN
	select CPU_FEROCEON
	select GPIOLIB
	select MVEBU_MBUS
	select FORCE_PCI
	select PCI_QUIRKS
	select PHYLIB if NETDEVICES
	select PLAT_ORION_LEGACY
	help
	  Support for the following Marvell Orion 5x series SoCs:
	  Orion-1 (5181), Orion-VoIP (5181L), Orion-NAS (5182),
	  Orion-2 (5281), Orion-1-90 (6183).

if ARCH_ORION5X

config ARCH_ORION5X_DT
	bool "Marvell Orion5x Flattened Device Tree"
	select ORION_CLK
	select ORION_IRQCHIP
	select ORION_TIMER
	select PINCTRL
	select PINCTRL_ORION
	help
	  Say 'Y' here if you want your kernel to support the
	  Marvell Orion5x using flattened device tree.

config MACH_DB88F5281
	bool "Marvell Orion-2 Development Board"
	select I2C_BOARDINFO if I2C
	depends on ATAGS && UNUSED_BOARD_FILES
	help
	  Say 'Y' here if you want your kernel to support the
	  Marvell Orion-2 (88F5281) Development Board

config MACH_RD88F5182
	bool "Marvell Orion-NAS Reference Design"
	select I2C_BOARDINFO if I2C
	depends on ATAGS && UNUSED_BOARD_FILES
	help
	  Say 'Y' here if you want your kernel to support the
	  Marvell Orion-NAS (88F5182) RD2

config MACH_RD88F5182_DT
	bool "Marvell Orion-NAS Reference Design (Flattened Device Tree)"
	select ARCH_ORION5X_DT
	select I2C_BOARDINFO if I2C
	help
	  Say 'Y' here if you want your kernel to support the Marvell
	  Orion-NAS (88F5182) RD2, Flattened Device Tree.

config MACH_KUROBOX_PRO
	bool "KuroBox Pro"
	select I2C_BOARDINFO if I2C
	depends on ATAGS
	help
	  Say 'Y' here if you want your kernel to support the
	  KuroBox Pro platform.

config MACH_DNS323
	bool "D-Link DNS-323"
	select I2C_BOARDINFO if I2C
	depends on ATAGS
	help
	  Say 'Y' here if you want your kernel to support the
	  D-Link DNS-323 platform.

config MACH_TS209
	bool "QNAP TS-109/TS-209"
	depends on ATAGS
	help
	  Say 'Y' here if you want your kernel to support the
	  QNAP TS-109/TS-209 platform.

config MACH_TERASTATION_PRO2
	bool "Buffalo Terastation Pro II/Live"
	depends on ATAGS
	help
	  Say 'Y' here if you want your kernel to support the
	  Buffalo Terastation Pro II/Live platform.

config MACH_LINKSTATION_PRO
	bool "Buffalo Linkstation Pro/Live"
	depends on ATAGS
	select I2C_BOARDINFO if I2C
	help
	  Say 'Y' here if you want your kernel to support the
	  Buffalo Linkstation Pro/Live platform. Both v1 and
	  v2 devices are supported.

config MACH_LINKSTATION_MINI
	bool "Buffalo Linkstation Mini (Flattened Device Tree)"
	select ARCH_ORION5X_DT
	help
	  Say 'Y' here if you want your kernel to support the
	  Buffalo Linkstation Mini (LS-WSGL) platform.

config MACH_LINKSTATION_LS_HGL
	bool "Buffalo Linkstation LS-HGL"
	depends on ATAGS && UNUSED_BOARD_FILES
	select I2C_BOARDINFO if I2C
	help
	  Say 'Y' here if you want your kernel to support the
	  Buffalo Linkstation LS-HGL platform.

config MACH_TS409
	bool "QNAP TS-409"
	depends on ATAGS
	help
	  Say 'Y' here if you want your kernel to support the
	  QNAP TS-409 platform.

config MACH_WRT350N_V2
	bool "Linksys WRT350N v2"
	depends on ATAGS && UNUSED_BOARD_FILES
	help
	  Say 'Y' here if you want your kernel to support the
	  Linksys WRT350N v2 platform.

config MACH_TS78XX
	bool "Technologic Systems TS-78xx"
	depends on ATAGS
	help
	  Say 'Y' here if you want your kernel to support the
	  Technologic Systems TS-78xx platform.

config MACH_MV2120
	bool "HP Media Vault mv2120"
	depends on ATAGS
	help
	  Say 'Y' here if you want your kernel to support the
	  HP Media Vault mv2120 or mv5100.

config MACH_D2NET_DT
	bool "LaCie d2 Network / Big Disk Network (Flattened Device Tree)"
	select ARCH_ORION5X_DT
	help
	  Say 'Y' here if you want your kernel to support the
	  LaCie d2 Network NAS.

config MACH_NET2BIG
	bool "LaCie 2Big Network"
	depends on ATAGS
	select I2C_BOARDINFO if I2C
	help
	  Say 'Y' here if you want your kernel to support the
	  LaCie 2Big Network NAS.

config MACH_MSS2_DT
	bool "Maxtor Shared Storage II (Flattened Device Tree)"
	select ARCH_ORION5X_DT
	help
	  Say 'Y' here if you want your kernel to support the
	  Maxtor Shared Storage II platform.

config MACH_WNR854T
	bool "Netgear WNR854T"
	depends on ATAGS && UNUSED_BOARD_FILES
	help
	  Say 'Y' here if you want your kernel to support the
	  Netgear WNR854T platform.

config MACH_RD88F5181L_GE
	bool "Marvell Orion-VoIP GE Reference Design"
	depends on ATAGS && UNUSED_BOARD_FILES
	help
	  Say 'Y' here if you want your kernel to support the
	  Marvell Orion-VoIP GE (88F5181L) RD.

config MACH_RD88F5181L_FXO
	bool "Marvell Orion-VoIP FXO Reference Design"
	depends on ATAGS && UNUSED_BOARD_FILES
	help
	  Say 'Y' here if you want your kernel to support the
	  Marvell Orion-VoIP FXO (88F5181L) RD.

config MACH_RD88F6183AP_GE
	bool "Marvell Orion-1-90 AP GE Reference Design"
	depends on ATAGS && UNUSED_BOARD_FILES
	help
	  Say 'Y' here if you want your kernel to support the
	  Marvell Orion-1-90 (88F6183) AP GE RD.

endif


code:
# SPDX-License-Identifier: GPL-2.0
ccflags-y := -I$(srctree)/arch/arm/plat-orion/include

obj-y				+= common.o pci.o irq.o mpp.o
obj-$(CONFIG_MACH_DB88F5281)	+= db88f5281-setup.o
obj-$(CONFIG_MACH_RD88F5182)	+= rd88f5182-setup.o
obj-$(CONFIG_MACH_KUROBOX_PRO)	+= kurobox_pro-setup.o
obj-$(CONFIG_MACH_TERASTATION_PRO2)	+= terastation_pro2-setup.o
obj-$(CONFIG_MACH_LINKSTATION_PRO) += kurobox_pro-setup.o
obj-$(CONFIG_MACH_LINKSTATION_LS_HGL) += ls_hgl-setup.o
obj-$(CONFIG_MACH_DNS323)	+= dns323-setup.o
obj-$(CONFIG_MACH_TS209)	+= ts209-setup.o tsx09-common.o
obj-$(CONFIG_MACH_TS409)	+= ts409-setup.o tsx09-common.o
obj-$(CONFIG_MACH_WRT350N_V2)	+= wrt350n-v2-setup.o
obj-$(CONFIG_MACH_TS78XX)	+= ts78xx-setup.o
obj-$(CONFIG_MACH_MV2120)	+= mv2120-setup.o
obj-$(CONFIG_MACH_NET2BIG)	+= net2big-setup.o
obj-$(CONFIG_MACH_WNR854T)	+= wnr854t-setup.o
obj-$(CONFIG_MACH_RD88F5181L_GE)	+= rd88f5181l-ge-setup.o
obj-$(CONFIG_MACH_RD88F5181L_FXO)	+= rd88f5181l-fxo-setup.o
obj-$(CONFIG_MACH_RD88F6183AP_GE)	+= rd88f6183ap-ge-setup.o

obj-$(CONFIG_ARCH_ORION5X_DT)		+= board-dt.o
obj-$(CONFIG_MACH_D2NET_DT)	+= board-d2net.o
obj-$(CONFIG_MACH_MSS2_DT)	+= board-mss2.o
obj-$(CONFIG_MACH_RD88F5182_DT)	+= board-rd88f5182.o


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-orion5x/board-d2net.c
 *
 * LaCie d2Network and Big Disk Network NAS setup
 *
 * Copyright (C) 2009 Simon Guinot <sguinot@lacie.com>
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/leds.h>
#include <linux/gpio.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include <plat/orion-gpio.h>
#include "common.h"
#include "orion5x.h"

/*****************************************************************************
 * LaCie d2 Network Info
 ****************************************************************************/

/*****************************************************************************
 * GPIO LED's
 ****************************************************************************/

/*
 * The blue front LED is wired to the CPLD and can blink in relation with the
 * SATA activity.
 *
 * The following array detail the different LED registers and the combination
 * of their possible values:
 *
 * led_off   | blink_ctrl | SATA active | LED state
 *           |            |             |
 *    1      |     x      |      x      |  off
 *    0      |     0      |      0      |  off
 *    0      |     1      |      0      |  blink (rate 300ms)
 *    0      |     x      |      1      |  on
 *
 * Notes: The blue and the red front LED's can't be on at the same time.
 *        Red LED have priority.
 */

#define D2NET_GPIO_RED_LED		6
#define D2NET_GPIO_BLUE_LED_BLINK_CTRL	16
#define D2NET_GPIO_BLUE_LED_OFF		23

static struct gpio_led d2net_leds[] = {
	{
		.name = "d2net:blue:sata",
		.default_trigger = "default-on",
		.gpio = D2NET_GPIO_BLUE_LED_OFF,
		.active_low = 1,
	},
	{
		.name = "d2net:red:fail",
		.gpio = D2NET_GPIO_RED_LED,
	},
};

static struct gpio_led_platform_data d2net_led_data = {
	.num_leds = ARRAY_SIZE(d2net_leds),
	.leds = d2net_leds,
};

static struct platform_device d2net_gpio_leds = {
	.name           = "leds-gpio",
	.id             = -1,
	.dev            = {
		.platform_data  = &d2net_led_data,
	},
};

static void __init d2net_gpio_leds_init(void)
{
	int err;

	/* Configure register blink_ctrl to allow SATA activity LED blinking. */
	err = gpio_request(D2NET_GPIO_BLUE_LED_BLINK_CTRL, "blue LED blink");
	if (err == 0) {
		err = gpio_direction_output(D2NET_GPIO_BLUE_LED_BLINK_CTRL, 1);
		if (err)
			gpio_free(D2NET_GPIO_BLUE_LED_BLINK_CTRL);
	}
	if (err)
		pr_err("d2net: failed to configure blue LED blink GPIO\n");

	platform_device_register(&d2net_gpio_leds);
}

/*****************************************************************************
 * General Setup
 ****************************************************************************/

void __init d2net_init(void)
{
	d2net_gpio_leds_init();

	pr_notice("d2net: Flash write are not yet supported.\n");
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2012 (C), Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
 *
 * arch/arm/mach-orion5x/board-dt.c
 *
 * Flattened Device Tree board initialization
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/cpu.h>
#include <linux/mbus.h>
#include <linux/clocksource.h>
#include <asm/system_misc.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <plat/irq.h>
#include <plat/time.h>
#include "orion5x.h"
#include "bridge-regs.h"
#include "common.h"

static struct of_dev_auxdata orion5x_auxdata_lookup[] __initdata = {
	OF_DEV_AUXDATA("marvell,orion-spi", 0xf1010600, "orion_spi.0", NULL),
	OF_DEV_AUXDATA("marvell,mv64xxx-i2c", 0xf1011000, "mv64xxx_i2c.0",
		       NULL),
	OF_DEV_AUXDATA("marvell,orion-wdt", 0xf1020300, "orion_wdt", NULL),
	OF_DEV_AUXDATA("marvell,orion-sata", 0xf1080000, "sata_mv.0", NULL),
	OF_DEV_AUXDATA("marvell,orion-crypto", 0xf1090000, "mv_crypto", NULL),
	{},
};

static void __init orion5x_dt_init(void)
{
	char *dev_name;
	u32 dev, rev;

	orion5x_id(&dev, &rev, &dev_name);
	printk(KERN_INFO "Orion ID: %s. TCLK=%d.\n", dev_name, orion5x_tclk);

	BUG_ON(mvebu_mbus_dt_init(false));

	/*
	 * Setup Orion address map
	 */
	orion5x_setup_wins();

	/*
	 * Don't issue "Wait for Interrupt" instruction if we are
	 * running on D0 5281 silicon.
	 */
	if (dev == MV88F5281_DEV_ID && rev == MV88F5281_REV_D0) {
		printk(KERN_INFO "Orion: Applying 5281 D0 WFI workaround.\n");
		cpu_idle_poll_ctrl(true);
	}

	if (of_machine_is_compatible("maxtor,shared-storage-2"))
		mss2_init();

	of_platform_default_populate(NULL, orion5x_auxdata_lookup, NULL);
}

static const char *orion5x_dt_compat[] = {
	"marvell,orion5x",
	NULL,
};

DT_MACHINE_START(ORION5X_DT, "Marvell Orion5x (Flattened Device Tree)")
	/* Maintainer: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> */
	.map_io		= orion5x_map_io,
	.init_machine	= orion5x_dt_init,
	.restart	= orion5x_restart,
	.dt_compat	= orion5x_dt_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Maxtor Shared Storage II Board Setup
 *
 * Maintainer: Sylver Bruneau <sylver.bruneau@googlemail.com>
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include "orion5x.h"
#include "bridge-regs.h"
#include "common.h"

/*****************************************************************************
 * Maxtor Shared Storage II Info
 ****************************************************************************/

/****************************************************************************
 * PCI setup
 ****************************************************************************/
static int __init mss2_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
	int irq;

	/*
	 * Check for devices with hard-wired IRQs.
	 */
	irq = orion5x_pci_map_irq(dev, slot, pin);
	if (irq != -1)
		return irq;

	return -1;
}

static struct hw_pci mss2_pci __initdata = {
	.nr_controllers = 2,
	.setup		= orion5x_pci_sys_setup,
	.scan		= orion5x_pci_sys_scan_bus,
	.map_irq	= mss2_pci_map_irq,
};

static int __init mss2_pci_init(void)
{
	if (machine_is_mss2())
		pci_common_init(&mss2_pci);

	return 0;
}
subsys_initcall(mss2_pci_init);

/*****************************************************************************
 * MSS2 power off method
 ****************************************************************************/
/*
 * On the Maxtor Shared Storage II, the shutdown process is the following :
 * - Userland modifies U-boot env to tell U-boot to go idle at next boot
 * - The board reboots
 * - U-boot starts and go into an idle mode until the user press "power"
 */
static void mss2_power_off(void)
{
	u32 reg;

	/*
	 * Enable and issue soft reset
	 */
	reg = readl(RSTOUTn_MASK);
	reg |= 1 << 2;
	writel(reg, RSTOUTn_MASK);

	reg = readl(CPU_SOFT_RESET);
	reg |= 1;
	writel(reg, CPU_SOFT_RESET);
}

void __init mss2_init(void)
{
	/* register mss2 specific power-off method */
	pm_power_off = mss2_power_off;
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-orion5x/rd88f5182-setup.c
 *
 * Marvell Orion-NAS Reference Design Setup
 *
 * Maintainer: Ronen Shitrit <rshitrit@marvell.com>
 */
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include "common.h"
#include "orion5x.h"

/*****************************************************************************
 * RD-88F5182 Info
 ****************************************************************************/

/*
 * PCI
 */

#define RD88F5182_PCI_SLOT0_OFFS	7
#define RD88F5182_PCI_SLOT0_IRQ_A_PIN	7
#define RD88F5182_PCI_SLOT0_IRQ_B_PIN	6

/*****************************************************************************
 * PCI
 ****************************************************************************/

static void __init rd88f5182_pci_preinit(void)
{
	int pin;

	/*
	 * Configure PCI GPIO IRQ pins
	 */
	pin = RD88F5182_PCI_SLOT0_IRQ_A_PIN;
	if (gpio_request(pin, "PCI IntA") == 0) {
		if (gpio_direction_input(pin) == 0) {
			irq_set_irq_type(gpio_to_irq(pin), IRQ_TYPE_LEVEL_LOW);
		} else {
			printk(KERN_ERR "rd88f5182_pci_preinit failed to "
					"set_irq_type pin %d\n", pin);
			gpio_free(pin);
		}
	} else {
		printk(KERN_ERR "rd88f5182_pci_preinit failed to request gpio %d\n", pin);
	}

	pin = RD88F5182_PCI_SLOT0_IRQ_B_PIN;
	if (gpio_request(pin, "PCI IntB") == 0) {
		if (gpio_direction_input(pin) == 0) {
			irq_set_irq_type(gpio_to_irq(pin), IRQ_TYPE_LEVEL_LOW);
		} else {
			printk(KERN_ERR "rd88f5182_pci_preinit failed to "
					"set_irq_type pin %d\n", pin);
			gpio_free(pin);
		}
	} else {
		printk(KERN_ERR "rd88f5182_pci_preinit failed to gpio_request %d\n", pin);
	}
}

static int __init rd88f5182_pci_map_irq(const struct pci_dev *dev, u8 slot,
	u8 pin)
{
	int irq;

	/*
	 * Check for devices with hard-wired IRQs.
	 */
	irq = orion5x_pci_map_irq(dev, slot, pin);
	if (irq != -1)
		return irq;

	/*
	 * PCI IRQs are connected via GPIOs
	 */
	switch (slot - RD88F5182_PCI_SLOT0_OFFS) {
	case 0:
		if (pin == 1)
			return gpio_to_irq(RD88F5182_PCI_SLOT0_IRQ_A_PIN);
		else
			return gpio_to_irq(RD88F5182_PCI_SLOT0_IRQ_B_PIN);
	default:
		return -1;
	}
}

static struct hw_pci rd88f5182_pci __initdata = {
	.nr_controllers	= 2,
	.preinit	= rd88f5182_pci_preinit,
	.setup		= orion5x_pci_sys_setup,
	.scan		= orion5x_pci_sys_scan_bus,
	.map_irq	= rd88f5182_pci_map_irq,
};

static int __init rd88f5182_pci_init(void)
{
	if (of_machine_is_compatible("marvell,rd-88f5182-nas"))
		pci_common_init(&rd88f5182_pci);

	return 0;
}

subsys_initcall(rd88f5182_pci_init);


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/* Orion CPU Bridge Registers */

#ifndef __ASM_ARCH_BRIDGE_REGS_H
#define __ASM_ARCH_BRIDGE_REGS_H

#include "orion5x.h"

#define CPU_CONF		(ORION5X_BRIDGE_VIRT_BASE + 0x100)

#define CPU_CTRL		(ORION5X_BRIDGE_VIRT_BASE + 0x104)

#define RSTOUTn_MASK		(ORION5X_BRIDGE_VIRT_BASE + 0x108)
#define RSTOUTn_MASK_PHYS	(ORION5X_BRIDGE_PHYS_BASE + 0x108)

#define CPU_SOFT_RESET		(ORION5X_BRIDGE_VIRT_BASE + 0x10c)

#define BRIDGE_CAUSE		(ORION5X_BRIDGE_VIRT_BASE + 0x110)

#define POWER_MNG_CTRL_REG	(ORION5X_BRIDGE_VIRT_BASE + 0x11C)

#define BRIDGE_INT_TIMER1_CLR	(~0x0004)

#define MAIN_IRQ_CAUSE		(ORION5X_BRIDGE_VIRT_BASE + 0x200)

#define MAIN_IRQ_MASK		(ORION5X_BRIDGE_VIRT_BASE + 0x204)

#define TIMER_VIRT_BASE		(ORION5X_BRIDGE_VIRT_BASE + 0x300)
#define TIMER_PHYS_BASE		(ORION5X_BRIDGE_PHYS_BASE + 0x300)
#endif