code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/arch/arm/mach-omap1/clock.c
 *
 *  Copyright (C) 2004 - 2005, 2009-2010 Nokia Corporation
 *  Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
 *
 *  Modified to use omap shared clock framework by
 *  Tony Lindgren <tony@atomide.com>
 */
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/soc/ti/omap1-io.h>
#include <linux/spinlock.h>

#include <asm/mach-types.h>

#include "hardware.h"
#include "soc.h"
#include "iomap.h"
#include "clock.h"
#include "opp.h"
#include "sram.h"

__u32 arm_idlect1_mask;
/* provide direct internal access (not via clk API) to some clocks */
struct omap1_clk *api_ck_p, *ck_dpll1_p, *ck_ref_p;

/* protect registeres shared among clk_enable/disable() and clk_set_rate() operations */
static DEFINE_SPINLOCK(arm_ckctl_lock);
static DEFINE_SPINLOCK(arm_idlect2_lock);
static DEFINE_SPINLOCK(mod_conf_ctrl_0_lock);
static DEFINE_SPINLOCK(mod_conf_ctrl_1_lock);
static DEFINE_SPINLOCK(swd_clk_div_ctrl_sel_lock);

/*
 * Omap1 specific clock functions
 */

unsigned long omap1_uart_recalc(struct omap1_clk *clk, unsigned long p_rate)
{
	unsigned int val = __raw_readl(clk->enable_reg);
	return val & 1 << clk->enable_bit ? 48000000 : 12000000;
}

unsigned long omap1_sossi_recalc(struct omap1_clk *clk, unsigned long p_rate)
{
	u32 div = omap_readl(MOD_CONF_CTRL_1);

	div = (div >> 17) & 0x7;
	div++;

	return p_rate / div;
}

static void omap1_clk_allow_idle(struct omap1_clk *clk)
{
	struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk;

	if (!(clk->flags & CLOCK_IDLE_CONTROL))
		return;

	if (iclk->no_idle_count > 0 && !(--iclk->no_idle_count))
		arm_idlect1_mask |= 1 << iclk->idlect_shift;
}

static void omap1_clk_deny_idle(struct omap1_clk *clk)
{
	struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk;

	if (!(clk->flags & CLOCK_IDLE_CONTROL))
		return;

	if (iclk->no_idle_count++ == 0)
		arm_idlect1_mask &= ~(1 << iclk->idlect_shift);
}

static __u16 verify_ckctl_value(__u16 newval)
{
	/* This function checks for following limitations set
	 * by the hardware (all conditions must be true):
	 * DSPMMU_CK == DSP_CK  or  DSPMMU_CK == DSP_CK/2
	 * ARM_CK >= TC_CK
	 * DSP_CK >= TC_CK
	 * DSPMMU_CK >= TC_CK
	 *
	 * In addition following rules are enforced:
	 * LCD_CK <= TC_CK
	 * ARMPER_CK <= TC_CK
	 *
	 * However, maximum frequencies are not checked for!
	 */
	__u8 per_exp;
	__u8 lcd_exp;
	__u8 arm_exp;
	__u8 dsp_exp;
	__u8 tc_exp;
	__u8 dspmmu_exp;

	per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3;
	lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3;
	arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3;
	dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3;
	tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3;
	dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3;

	if (dspmmu_exp < dsp_exp)
		dspmmu_exp = dsp_exp;
	if (dspmmu_exp > dsp_exp+1)
		dspmmu_exp = dsp_exp+1;
	if (tc_exp < arm_exp)
		tc_exp = arm_exp;
	if (tc_exp < dspmmu_exp)
		tc_exp = dspmmu_exp;
	if (tc_exp > lcd_exp)
		lcd_exp = tc_exp;
	if (tc_exp > per_exp)
		per_exp = tc_exp;

	newval &= 0xf000;
	newval |= per_exp << CKCTL_PERDIV_OFFSET;
	newval |= lcd_exp << CKCTL_LCDDIV_OFFSET;
	newval |= arm_exp << CKCTL_ARMDIV_OFFSET;
	newval |= dsp_exp << CKCTL_DSPDIV_OFFSET;
	newval |= tc_exp << CKCTL_TCDIV_OFFSET;
	newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET;

	return newval;
}

static int calc_dsor_exp(unsigned long rate, unsigned long realrate)
{
	/* Note: If target frequency is too low, this function will return 4,
	 * which is invalid value. Caller must check for this value and act
	 * accordingly.
	 *
	 * Note: This function does not check for following limitations set
	 * by the hardware (all conditions must be true):
	 * DSPMMU_CK == DSP_CK  or  DSPMMU_CK == DSP_CK/2
	 * ARM_CK >= TC_CK
	 * DSP_CK >= TC_CK
	 * DSPMMU_CK >= TC_CK
	 */
	unsigned  dsor_exp;

	if (unlikely(realrate == 0))
		return -EIO;

	for (dsor_exp=0; dsor_exp<4; dsor_exp++) {
		if (realrate <= rate)
			break;

		realrate /= 2;
	}

	return dsor_exp;
}

unsigned long omap1_ckctl_recalc(struct omap1_clk *clk, unsigned long p_rate)
{
	/* Calculate divisor encoded as 2-bit exponent */
	int dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset));

	/* update locally maintained rate, required by arm_ck for omap1_show_rates() */
	clk->rate = p_rate / dsor;
	return clk->rate;
}

static int omap1_clk_is_enabled(struct clk_hw *hw)
{
	struct omap1_clk *clk = to_omap1_clk(hw);
	bool api_ck_was_enabled = true;
	__u32 regval32;
	int ret;

	if (!clk->ops)	/* no gate -- always enabled */
		return 1;

	if (clk->ops == &clkops_dspck) {
		api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw);
		if (!api_ck_was_enabled)
			if (api_ck_p->ops->enable(api_ck_p) < 0)
				return 0;
	}

	if (clk->flags & ENABLE_REG_32BIT)
		regval32 = __raw_readl(clk->enable_reg);
	else
		regval32 = __raw_readw(clk->enable_reg);

	ret = regval32 & (1 << clk->enable_bit);

	if (!api_ck_was_enabled)
		api_ck_p->ops->disable(api_ck_p);

	return ret;
}

unsigned long omap1_ckctl_recalc_dsp_domain(struct omap1_clk *clk, unsigned long p_rate)
{
	bool api_ck_was_enabled;
	int dsor;

	/* Calculate divisor encoded as 2-bit exponent
	 *
	 * The clock control bits are in DSP domain,
	 * so api_ck is needed for access.
	 * Note that DSP_CKCTL virt addr = phys addr, so
	 * we must use __raw_readw() instead of omap_readw().
	 */
	api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw);
	if (!api_ck_was_enabled)
		api_ck_p->ops->enable(api_ck_p);
	dsor = 1 << (3 & (__raw_readw(DSP_CKCTL) >> clk->rate_offset));
	if (!api_ck_was_enabled)
		api_ck_p->ops->disable(api_ck_p);

	return p_rate / dsor;
}

/* MPU virtual clock functions */
int omap1_select_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
{
	/* Find the highest supported frequency <= rate and switch to it */
	struct mpu_rate * ptr;
	unsigned long ref_rate;

	ref_rate = ck_ref_p->rate;

	for (ptr = omap1_rate_table; ptr->rate; ptr++) {
		if (!(ptr->flags & cpu_mask))
			continue;

		if (ptr->xtal != ref_rate)
			continue;

		/* Can check only after xtal frequency check */
		if (ptr->rate <= rate)
			break;
	}

	if (!ptr->rate)
		return -EINVAL;

	/*
	 * In most cases we should not need to reprogram DPLL.
	 * Reprogramming the DPLL is tricky, it must be done from SRAM.
	 */
	omap_sram_reprogram_clock(ptr->dpllctl_val, ptr->ckctl_val);

	/* XXX Do we need to recalculate the tree below DPLL1 at this point? */
	ck_dpll1_p->rate = ptr->pll_rate;

	return 0;
}

int omap1_clk_set_rate_dsp_domain(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
{
	int dsor_exp;
	u16 regval;

	dsor_exp = calc_dsor_exp(rate, p_rate);
	if (dsor_exp > 3)
		dsor_exp = -EINVAL;
	if (dsor_exp < 0)
		return dsor_exp;

	regval = __raw_readw(DSP_CKCTL);
	regval &= ~(3 << clk->rate_offset);
	regval |= dsor_exp << clk->rate_offset;
	__raw_writew(regval, DSP_CKCTL);
	clk->rate = p_rate / (1 << dsor_exp);

	return 0;
}

long omap1_clk_round_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate,
				    unsigned long *p_rate)
{
	int dsor_exp = calc_dsor_exp(rate, *p_rate);

	if (dsor_exp < 0)
		return dsor_exp;
	if (dsor_exp > 3)
		dsor_exp = 3;
	return *p_rate / (1 << dsor_exp);
}

int omap1_clk_set_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
{
	unsigned long flags;
	int dsor_exp;
	u16 regval;

	dsor_exp = calc_dsor_exp(rate, p_rate);
	if (dsor_exp > 3)
		dsor_exp = -EINVAL;
	if (dsor_exp < 0)
		return dsor_exp;

	/* protect ARM_CKCTL register from concurrent access via clk_enable/disable() */
	spin_lock_irqsave(&arm_ckctl_lock, flags);

	regval = omap_readw(ARM_CKCTL);
	regval &= ~(3 << clk->rate_offset);
	regval |= dsor_exp << clk->rate_offset;
	regval = verify_ckctl_value(regval);
	omap_writew(regval, ARM_CKCTL);
	clk->rate = p_rate / (1 << dsor_exp);

	spin_unlock_irqrestore(&arm_ckctl_lock, flags);

	return 0;
}

long omap1_round_to_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate)
{
	/* Find the highest supported frequency <= rate */
	struct mpu_rate * ptr;
	long highest_rate;
	unsigned long ref_rate;

	ref_rate = ck_ref_p->rate;

	highest_rate = -EINVAL;

	for (ptr = omap1_rate_table; ptr->rate; ptr++) {
		if (!(ptr->flags & cpu_mask))
			continue;

		if (ptr->xtal != ref_rate)
			continue;

		highest_rate = ptr->rate;

		/* Can check only after xtal frequency check */
		if (ptr->rate <= rate)
			break;
	}

	return highest_rate;
}

static unsigned calc_ext_dsor(unsigned long rate)
{
	unsigned dsor;

	/* MCLK and BCLK divisor selection is not linear:
	 * freq = 96MHz / dsor
	 *
	 * RATIO_SEL range: dsor <-> RATIO_SEL
	 * 0..6: (RATIO_SEL+2) <-> (dsor-2)
	 * 6..48:  (8+(RATIO_SEL-6)*2) <-> ((dsor-8)/2+6)
	 * Minimum dsor is 2 and maximum is 96. Odd divisors starting from 9
	 * can not be used.
	 */
	for (dsor = 2; dsor < 96; ++dsor) {
		if ((dsor & 1) && dsor > 8)
			continue;
		if (rate >= 96000000 / dsor)
			break;
	}
	return dsor;
}

/* XXX Only needed on 1510 */
long omap1_round_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate)
{
	return rate > 24000000 ? 48000000 : 12000000;
}

int omap1_set_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
{
	unsigned long flags;
	unsigned int val;

	if (rate == 12000000)
		val = 0;
	else if (rate == 48000000)
		val = 1 << clk->enable_bit;
	else
		return -EINVAL;

	/* protect MOD_CONF_CTRL_0 register from concurrent access via clk_enable/disable() */
	spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags);

	val |= __raw_readl(clk->enable_reg) & ~(1 << clk->enable_bit);
	__raw_writel(val, clk->enable_reg);

	spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags);

	clk->rate = rate;

	return 0;
}

/* External clock (MCLK & BCLK) functions */
int omap1_set_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
{
	unsigned long flags;
	unsigned dsor;
	__u16 ratio_bits;

	dsor = calc_ext_dsor(rate);
	clk->rate = 96000000 / dsor;
	if (dsor > 8)
		ratio_bits = ((dsor - 8) / 2 + 6) << 2;
	else
		ratio_bits = (dsor - 2) << 2;

	/* protect SWD_CLK_DIV_CTRL_SEL register from concurrent access via clk_enable/disable() */
	spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags);

	ratio_bits |= __raw_readw(clk->enable_reg) & ~0xfd;
	__raw_writew(ratio_bits, clk->enable_reg);

	spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags);

	return 0;
}

static int calc_div_sossi(unsigned long rate, unsigned long p_rate)
{
	int div;

	/* Round towards slower frequency */
	div = (p_rate + rate - 1) / rate;

	return --div;
}

long omap1_round_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate)
{
	int div;

	div = calc_div_sossi(rate, *p_rate);
	if (div < 0)
		div = 0;
	else if (div > 7)
		div = 7;

	return *p_rate / (div + 1);
}

int omap1_set_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate)
{
	unsigned long flags;
	u32 l;
	int div;

	div = calc_div_sossi(rate, p_rate);
	if (div < 0 || div > 7)
		return -EINVAL;

	/* protect MOD_CONF_CTRL_1 register from concurrent access via clk_enable/disable() */
	spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags);

	l = omap_readl(MOD_CONF_CTRL_1);
	l &= ~(7 << 17);
	l |= div << 17;
	omap_writel(l, MOD_CONF_CTRL_1);

	clk->rate = p_rate / (div + 1);

	spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags);

	return 0;
}

long omap1_round_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate)
{
	return 96000000 / calc_ext_dsor(rate);
}

int omap1_init_ext_clk(struct omap1_clk *clk)
{
	unsigned dsor;
	__u16 ratio_bits;

	/* Determine current rate and ensure clock is based on 96MHz APLL */
	ratio_bits = __raw_readw(clk->enable_reg) & ~1;
	__raw_writew(ratio_bits, clk->enable_reg);

	ratio_bits = (ratio_bits & 0xfc) >> 2;
	if (ratio_bits > 6)
		dsor = (ratio_bits - 6) * 2 + 8;
	else
		dsor = ratio_bits + 2;

	clk-> rate = 96000000 / dsor;

	return 0;
}

static int omap1_clk_enable(struct clk_hw *hw)
{
	struct omap1_clk *clk = to_omap1_clk(hw), *parent = to_omap1_clk(clk_hw_get_parent(hw));
	int ret = 0;

	if (parent && clk->flags & CLOCK_NO_IDLE_PARENT)
		omap1_clk_deny_idle(parent);

	if (clk->ops && !(WARN_ON(!clk->ops->enable)))
		ret = clk->ops->enable(clk);

	return ret;
}

static void omap1_clk_disable(struct clk_hw *hw)
{
	struct omap1_clk *clk = to_omap1_clk(hw), *parent = to_omap1_clk(clk_hw_get_parent(hw));

	if (clk->ops && !(WARN_ON(!clk->ops->disable)))
		clk->ops->disable(clk);

	if (likely(parent) && clk->flags & CLOCK_NO_IDLE_PARENT)
		omap1_clk_allow_idle(parent);
}

static int omap1_clk_enable_generic(struct omap1_clk *clk)
{
	unsigned long flags;
	__u16 regval16;
	__u32 regval32;

	if (unlikely(clk->enable_reg == NULL)) {
		printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
		       clk_hw_get_name(&clk->hw));
		return -EINVAL;
	}

	/* protect clk->enable_reg from concurrent access via clk_set_rate() */
	if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL))
		spin_lock_irqsave(&arm_ckctl_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2))
		spin_lock_irqsave(&arm_idlect2_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0))
		spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1))
		spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL))
		spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags);

	if (clk->flags & ENABLE_REG_32BIT) {
		regval32 = __raw_readl(clk->enable_reg);
		regval32 |= (1 << clk->enable_bit);
		__raw_writel(regval32, clk->enable_reg);
	} else {
		regval16 = __raw_readw(clk->enable_reg);
		regval16 |= (1 << clk->enable_bit);
		__raw_writew(regval16, clk->enable_reg);
	}

	if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL))
		spin_unlock_irqrestore(&arm_ckctl_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2))
		spin_unlock_irqrestore(&arm_idlect2_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0))
		spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1))
		spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL))
		spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags);

	return 0;
}

static void omap1_clk_disable_generic(struct omap1_clk *clk)
{
	unsigned long flags;
	__u16 regval16;
	__u32 regval32;

	if (clk->enable_reg == NULL)
		return;

	/* protect clk->enable_reg from concurrent access via clk_set_rate() */
	if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL))
		spin_lock_irqsave(&arm_ckctl_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2))
		spin_lock_irqsave(&arm_idlect2_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0))
		spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1))
		spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL))
		spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags);

	if (clk->flags & ENABLE_REG_32BIT) {
		regval32 = __raw_readl(clk->enable_reg);
		regval32 &= ~(1 << clk->enable_bit);
		__raw_writel(regval32, clk->enable_reg);
	} else {
		regval16 = __raw_readw(clk->enable_reg);
		regval16 &= ~(1 << clk->enable_bit);
		__raw_writew(regval16, clk->enable_reg);
	}

	if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL))
		spin_unlock_irqrestore(&arm_ckctl_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2))
		spin_unlock_irqrestore(&arm_idlect2_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0))
		spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1))
		spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags);
	else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL))
		spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags);
}

const struct clkops clkops_generic = {
	.enable		= omap1_clk_enable_generic,
	.disable	= omap1_clk_disable_generic,
};

static int omap1_clk_enable_dsp_domain(struct omap1_clk *clk)
{
	bool api_ck_was_enabled;
	int retval = 0;

	api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw);
	if (!api_ck_was_enabled)
		retval = api_ck_p->ops->enable(api_ck_p);

	if (!retval) {
		retval = omap1_clk_enable_generic(clk);

		if (!api_ck_was_enabled)
			api_ck_p->ops->disable(api_ck_p);
	}

	return retval;
}

static void omap1_clk_disable_dsp_domain(struct omap1_clk *clk)
{
	bool api_ck_was_enabled;

	api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw);
	if (!api_ck_was_enabled)
		if (api_ck_p->ops->enable(api_ck_p) < 0)
			return;

	omap1_clk_disable_generic(clk);

	if (!api_ck_was_enabled)
		api_ck_p->ops->disable(api_ck_p);
}

const struct clkops clkops_dspck = {
	.enable		= omap1_clk_enable_dsp_domain,
	.disable	= omap1_clk_disable_dsp_domain,
};

/* XXX SYSC register handling does not belong in the clock framework */
static int omap1_clk_enable_uart_functional_16xx(struct omap1_clk *clk)
{
	int ret;
	struct uart_clk *uclk;

	ret = omap1_clk_enable_generic(clk);
	if (ret == 0) {
		/* Set smart idle acknowledgement mode */
		uclk = (struct uart_clk *)clk;
		omap_writeb((omap_readb(uclk->sysc_addr) & ~0x10) | 8,
			    uclk->sysc_addr);
	}

	return ret;
}

/* XXX SYSC register handling does not belong in the clock framework */
static void omap1_clk_disable_uart_functional_16xx(struct omap1_clk *clk)
{
	struct uart_clk *uclk;

	/* Set force idle acknowledgement mode */
	uclk = (struct uart_clk *)clk;
	omap_writeb((omap_readb(uclk->sysc_addr) & ~0x18), uclk->sysc_addr);

	omap1_clk_disable_generic(clk);
}

/* XXX SYSC register handling does not belong in the clock framework */
const struct clkops clkops_uart_16xx = {
	.enable		= omap1_clk_enable_uart_functional_16xx,
	.disable	= omap1_clk_disable_uart_functional_16xx,
};

static unsigned long omap1_clk_recalc_rate(struct clk_hw *hw, unsigned long p_rate)
{
	struct omap1_clk *clk = to_omap1_clk(hw);

	if (clk->recalc)
		return clk->recalc(clk, p_rate);

	return clk->rate;
}

static long omap1_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *p_rate)
{
	struct omap1_clk *clk = to_omap1_clk(hw);

	if (clk->round_rate != NULL)
		return clk->round_rate(clk, rate, p_rate);

	return omap1_clk_recalc_rate(hw, *p_rate);
}

static int omap1_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate)
{
	struct omap1_clk *clk = to_omap1_clk(hw);
	int  ret = -EINVAL;

	if (clk->set_rate)
		ret = clk->set_rate(clk, rate, p_rate);
	return ret;
}

/*
 * Omap1 clock reset and init functions
 */

static int omap1_clk_init_op(struct clk_hw *hw)
{
	struct omap1_clk *clk = to_omap1_clk(hw);

	if (clk->init)
		return clk->init(clk);

	return 0;
}

#ifdef CONFIG_OMAP_RESET_CLOCKS

static void omap1_clk_disable_unused(struct clk_hw *hw)
{
	struct omap1_clk *clk = to_omap1_clk(hw);
	const char *name = clk_hw_get_name(hw);

	/* Clocks in the DSP domain need api_ck. Just assume bootloader
	 * has not enabled any DSP clocks */
	if (clk->enable_reg == DSP_IDLECT2) {
		pr_info("Skipping reset check for DSP domain clock \"%s\"\n", name);
		return;
	}

	pr_info("Disabling unused clock \"%s\"... ", name);
	omap1_clk_disable(hw);
	printk(" done\n");
}

#endif

const struct clk_ops omap1_clk_gate_ops = {
	.enable		= omap1_clk_enable,
	.disable	= omap1_clk_disable,
	.is_enabled	= omap1_clk_is_enabled,
#ifdef CONFIG_OMAP_RESET_CLOCKS
	.disable_unused	= omap1_clk_disable_unused,
#endif
};

const struct clk_ops omap1_clk_rate_ops = {
	.recalc_rate	= omap1_clk_recalc_rate,
	.round_rate	= omap1_clk_round_rate,
	.set_rate	= omap1_clk_set_rate,
	.init		= omap1_clk_init_op,
};

const struct clk_ops omap1_clk_full_ops = {
	.enable		= omap1_clk_enable,
	.disable	= omap1_clk_disable,
	.is_enabled	= omap1_clk_is_enabled,
#ifdef CONFIG_OMAP_RESET_CLOCKS
	.disable_unused	= omap1_clk_disable_unused,
#endif
	.recalc_rate	= omap1_clk_recalc_rate,
	.round_rate	= omap1_clk_round_rate,
	.set_rate	= omap1_clk_set_rate,
	.init		= omap1_clk_init_op,
};

/*
 * OMAP specific clock functions shared between omap1 and omap2
 */

/* Used for clocks that always have same value as the parent clock */
unsigned long followparent_recalc(struct omap1_clk *clk, unsigned long p_rate)
{
	return p_rate;
}

/*
 * Used for clocks that have the same value as the parent clock,
 * divided by some factor
 */
unsigned long omap_fixed_divisor_recalc(struct omap1_clk *clk, unsigned long p_rate)
{
	WARN_ON(!clk->fixed_div);

	return p_rate / clk->fixed_div;
}

/* Propagate rate to children */
void propagate_rate(struct omap1_clk *tclk)
{
	struct clk *clkp;

	/* depend on CCF ability to recalculate new rates across whole clock subtree */
	if (WARN_ON(!(clk_hw_get_flags(&tclk->hw) & CLK_GET_RATE_NOCACHE)))
		return;

	clkp = clk_get_sys(NULL, clk_hw_get_name(&tclk->hw));
	if (WARN_ON(!clkp))
		return;

	clk_get_rate(clkp);
	clk_put(clkp);
}

const struct clk_ops omap1_clk_null_ops = {
};

/*
 * Dummy clock
 *
 * Used for clock aliases that are needed on some OMAPs, but not others
 */
struct omap1_clk dummy_ck __refdata = {
	.hw.init	= CLK_HW_INIT_NO_PARENT("dummy", &omap1_clk_null_ops, 0),
};


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/mach-omap1/clock.h
 *
 *  Copyright (C) 2004 - 2005, 2009 Nokia corporation
 *  Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
 *  Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc
 */

#ifndef __ARCH_ARM_MACH_OMAP1_CLOCK_H
#define __ARCH_ARM_MACH_OMAP1_CLOCK_H

#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>

struct module;
struct omap1_clk;

struct omap_clk {
	u16				cpu;
	struct clk_lookup		lk;
};

#define CLK(dev, con, ck, cp)		\
	{				\
		 .cpu = cp,		\
		.lk = {			\
			.dev_id = dev,	\
			.con_id = con,	\
			.clk_hw = ck,	\
		},			\
	}

/* Platform flags for the clkdev-OMAP integration code */
#define CK_310		(1 << 0)
#define CK_7XX		(1 << 1)	/* 7xx, 850 */
#define CK_1510		(1 << 2)
#define CK_16XX		(1 << 3)	/* 16xx, 17xx, 5912 */
#define CK_1710		(1 << 4)	/* 1710 extra for rate selection */

/**
 * struct clkops - some clock function pointers
 * @enable: fn ptr that enables the current clock in hardware
 * @disable: fn ptr that enables the current clock in hardware
 * @allow_idle: fn ptr that enables autoidle for the current clock in hardware
 */
struct clkops {
	int			(*enable)(struct omap1_clk *clk);
	void			(*disable)(struct omap1_clk *clk);
};

/*
 * struct clk.flags possibilities
 *
 * XXX document the rest of the clock flags here
 */
#define ENABLE_REG_32BIT	(1 << 0)	/* Use 32-bit access */
#define CLOCK_IDLE_CONTROL	(1 << 1)
#define CLOCK_NO_IDLE_PARENT	(1 << 2)

/**
 * struct omap1_clk - OMAP1 struct clk
 * @hw: struct clk_hw for common clock framework integration
 * @ops: struct clkops * for this clock
 * @rate: current clock rate
 * @enable_reg: register to write to enable the clock (see @enable_bit)
 * @recalc: fn ptr that returns the clock's current rate
 * @set_rate: fn ptr that can change the clock's current rate
 * @round_rate: fn ptr that can round the clock's current rate
 * @init: fn ptr to do clock-specific initialization
 * @enable_bit: bitshift to write to enable/disable the clock (see @enable_reg)
 * @fixed_div: when > 0, this clock's rate is its parent's rate / @fixed_div
 * @flags: see "struct clk.flags possibilities" above
 * @rate_offset: bitshift for rate selection bitfield (OMAP1 only)
 */
struct omap1_clk {
	struct clk_hw		hw;
	const struct clkops	*ops;
	unsigned long		rate;
	void __iomem		*enable_reg;
	unsigned long		(*recalc)(struct omap1_clk *clk, unsigned long rate);
	int			(*set_rate)(struct omap1_clk *clk, unsigned long rate,
					    unsigned long p_rate);
	long			(*round_rate)(struct omap1_clk *clk, unsigned long rate,
					      unsigned long *p_rate);
	int			(*init)(struct omap1_clk *clk);
	u8			enable_bit;
	u8			fixed_div;
	u8			flags;
	u8			rate_offset;
};
#define to_omap1_clk(_hw)	container_of(_hw, struct omap1_clk, hw)

void propagate_rate(struct omap1_clk *clk);
unsigned long followparent_recalc(struct omap1_clk *clk, unsigned long p_rate);
unsigned long omap_fixed_divisor_recalc(struct omap1_clk *clk, unsigned long p_rate);

extern struct omap1_clk dummy_ck;

int omap1_clk_init(void);
void omap1_clk_late_init(void);
unsigned long omap1_ckctl_recalc(struct omap1_clk *clk, unsigned long p_rate);
long omap1_round_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate);
int omap1_set_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate);
unsigned long omap1_sossi_recalc(struct omap1_clk *clk, unsigned long p_rate);
unsigned long omap1_ckctl_recalc_dsp_domain(struct omap1_clk *clk, unsigned long p_rate);
int omap1_clk_set_rate_dsp_domain(struct omap1_clk *clk, unsigned long rate,
				  unsigned long p_rate);
long omap1_round_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate);
int omap1_set_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate);
unsigned long omap1_uart_recalc(struct omap1_clk *clk, unsigned long p_rate);
int omap1_set_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate);
long omap1_round_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate);
int omap1_init_ext_clk(struct omap1_clk *clk);
int omap1_select_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate);
long omap1_round_to_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate);
int omap1_clk_set_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate);
long omap1_clk_round_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate,
				    unsigned long *p_rate);

struct uart_clk {
	struct omap1_clk	clk;
	unsigned long		sysc_addr;
};

/* Provide a method for preventing idling some ARM IDLECT clocks */
struct arm_idlect1_clk {
	struct omap1_clk	clk;
	unsigned long		no_idle_count;
	__u8			idlect_shift;
};

/* ARM_CKCTL bit shifts */
#define CKCTL_PERDIV_OFFSET	0
#define CKCTL_LCDDIV_OFFSET	2
#define CKCTL_ARMDIV_OFFSET	4
#define CKCTL_DSPDIV_OFFSET	6
#define CKCTL_TCDIV_OFFSET	8
#define CKCTL_DSPMMUDIV_OFFSET	10
/*#define ARM_TIMXO		12*/
#define EN_DSPCK		13
/*#define ARM_INTHCK_SEL	14*/ /* Divide-by-2 for mpu inth_ck */
/* DSP_CKCTL bit shifts */
#define CKCTL_DSPPERDIV_OFFSET	0

/* ARM_IDLECT2 bit shifts */
#define EN_WDTCK	0
#define EN_XORPCK	1
#define EN_PERCK	2
#define EN_LCDCK	3
#define EN_LBCK		4 /* Not on 1610/1710 */
/*#define EN_HSABCK	5*/
#define EN_APICK	6
#define EN_TIMCK	7
#define DMACK_REQ	8
#define EN_GPIOCK	9 /* Not on 1610/1710 */
/*#define EN_LBFREECK	10*/
#define EN_CKOUT_ARM	11

/* ARM_IDLECT3 bit shifts */
#define EN_OCPI_CK	0
#define EN_TC1_CK	2
#define EN_TC2_CK	4

/* DSP_IDLECT2 bit shifts (0,1,2 are same as for ARM_IDLECT2) */
#define EN_DSPTIMCK	5

/* Various register defines for clock controls scattered around OMAP chip */
#define SDW_MCLK_INV_BIT	2	/* In ULPD_CLKC_CTRL */
#define USB_MCLK_EN_BIT		4	/* In ULPD_CLKC_CTRL */
#define USB_HOST_HHC_UHOST_EN	9	/* In MOD_CONF_CTRL_0 */
#define SWD_ULPD_PLL_CLK_REQ	1	/* In SWD_CLK_DIV_CTRL_SEL */
#define COM_ULPD_PLL_CLK_REQ	1	/* In COM_CLK_DIV_CTRL_SEL */
#define SWD_CLK_DIV_CTRL_SEL	0xfffe0874
#define COM_CLK_DIV_CTRL_SEL	0xfffe0878
#define SOFT_REQ_REG		0xfffe0834
#define SOFT_REQ_REG2		0xfffe0880

extern __u32 arm_idlect1_mask;
extern struct omap1_clk *api_ck_p, *ck_dpll1_p, *ck_ref_p;

extern const struct clkops clkops_dspck;
extern const struct clkops clkops_uart_16xx;
extern const struct clkops clkops_generic;

/* used for passing SoC type to omap1_{select,round_to}_table_rate() */
extern u32 cpu_mask;

extern const struct clk_ops omap1_clk_null_ops;
extern const struct clk_ops omap1_clk_gate_ops;
extern const struct clk_ops omap1_clk_rate_ops;
extern const struct clk_ops omap1_clk_full_ops;

#endif

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/arch/arm/mach-omap1/clock_data.c
 *
 *  Copyright (C) 2004 - 2005, 2009-2010 Nokia Corporation
 *  Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
 *  Based on clocks.h by Tony Lindgren, Gordon McNutt and RidgeRun, Inc
 *
 * To do:
 * - Clocks that are only available on some chips should be marked with the
 *   chips that they are present on.
 */

#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/soc/ti/omap1-io.h>

#include <asm/mach-types.h>  /* for machine_is_* */

#include "soc.h"
#include "hardware.h"
#include "usb.h"   /* for OTG_BASE */
#include "iomap.h"
#include "clock.h"
#include "sram.h"

/* Some ARM_IDLECT1 bit shifts - used in struct arm_idlect1_clk */
#define IDL_CLKOUT_ARM_SHIFT			12
#define IDLTIM_ARM_SHIFT			9
#define IDLAPI_ARM_SHIFT			8
#define IDLIF_ARM_SHIFT				6
#define IDLLB_ARM_SHIFT				4	/* undocumented? */
#define OMAP1510_IDLLCD_ARM_SHIFT		3	/* undocumented? */
#define IDLPER_ARM_SHIFT			2
#define IDLXORP_ARM_SHIFT			1
#define IDLWDT_ARM_SHIFT			0

/* Some MOD_CONF_CTRL_0 bit shifts - used in struct clk.enable_bit */
#define CONF_MOD_UART3_CLK_MODE_R		31
#define CONF_MOD_UART2_CLK_MODE_R		30
#define CONF_MOD_UART1_CLK_MODE_R		29
#define CONF_MOD_MMC_SD_CLK_REQ_R		23
#define CONF_MOD_MCBSP3_AUXON			20

/* Some MOD_CONF_CTRL_1 bit shifts - used in struct clk.enable_bit */
#define CONF_MOD_SOSSI_CLK_EN_R			16

/* Some OTG_SYSCON_2-specific bit fields */
#define OTG_SYSCON_2_UHOST_EN_SHIFT		8

/* Some SOFT_REQ_REG bit fields - used in struct clk.enable_bit */
#define SOFT_MMC2_DPLL_REQ_SHIFT	13
#define SOFT_MMC_DPLL_REQ_SHIFT		12
#define SOFT_UART3_DPLL_REQ_SHIFT	11
#define SOFT_UART2_DPLL_REQ_SHIFT	10
#define SOFT_UART1_DPLL_REQ_SHIFT	9
#define SOFT_USB_OTG_DPLL_REQ_SHIFT	8
#define SOFT_CAM_DPLL_REQ_SHIFT		7
#define SOFT_COM_MCKO_REQ_SHIFT		6
#define SOFT_PERIPH_REQ_SHIFT		5	/* sys_ck gate for UART2 ? */
#define USB_REQ_EN_SHIFT		4
#define SOFT_USB_REQ_SHIFT		3	/* sys_ck gate for USB host? */
#define SOFT_SDW_REQ_SHIFT		2	/* sys_ck gate for Bluetooth? */
#define SOFT_COM_REQ_SHIFT		1	/* sys_ck gate for com proc? */
#define SOFT_DPLL_REQ_SHIFT		0

/*
 * Omap1 clocks
 */

static struct omap1_clk ck_ref = {
	.hw.init	= CLK_HW_INIT_NO_PARENT("ck_ref", &omap1_clk_rate_ops, 0),
	.rate		= 12000000,
};

static struct omap1_clk ck_dpll1 = {
	.hw.init	= CLK_HW_INIT("ck_dpll1", "ck_ref", &omap1_clk_rate_ops,
				      /*
				       * force recursive refresh of rates of the clock
				       * and its children when clk_get_rate() is called
				       */
				      CLK_GET_RATE_NOCACHE),
};

/*
 * FIXME: This clock seems to be necessary but no-one has asked for its
 * activation.  [ FIX: SoSSI, SSR ]
 */
static struct arm_idlect1_clk ck_dpll1out = {
	.clk = {
		.hw.init	= CLK_HW_INIT("ck_dpll1out", "ck_dpll1", &omap1_clk_gate_ops, 0),
		.ops		= &clkops_generic,
		.flags		= CLOCK_IDLE_CONTROL | ENABLE_REG_32BIT,
		.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT2),
		.enable_bit	= EN_CKOUT_ARM,
	},
	.idlect_shift	= IDL_CLKOUT_ARM_SHIFT,
};

static struct omap1_clk sossi_ck = {
	.hw.init	= CLK_HW_INIT("ck_sossi", "ck_dpll1out", &omap1_clk_full_ops, 0),
	.ops		= &clkops_generic,
	.flags		= CLOCK_NO_IDLE_PARENT | ENABLE_REG_32BIT,
	.enable_reg	= OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1),
	.enable_bit	= CONF_MOD_SOSSI_CLK_EN_R,
	.recalc		= &omap1_sossi_recalc,
	.round_rate	= &omap1_round_sossi_rate,
	.set_rate	= &omap1_set_sossi_rate,
};

static struct omap1_clk arm_ck = {
	.hw.init	= CLK_HW_INIT("arm_ck", "ck_dpll1", &omap1_clk_rate_ops, 0),
	.rate_offset	= CKCTL_ARMDIV_OFFSET,
	.recalc		= &omap1_ckctl_recalc,
	.round_rate	= omap1_clk_round_rate_ckctl_arm,
	.set_rate	= omap1_clk_set_rate_ckctl_arm,
};

static struct arm_idlect1_clk armper_ck = {
	.clk = {
		.hw.init	= CLK_HW_INIT("armper_ck", "ck_dpll1", &omap1_clk_full_ops,
					      CLK_IS_CRITICAL),
		.ops		= &clkops_generic,
		.flags		= CLOCK_IDLE_CONTROL,
		.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT2),
		.enable_bit	= EN_PERCK,
		.rate_offset	= CKCTL_PERDIV_OFFSET,
		.recalc		= &omap1_ckctl_recalc,
		.round_rate	= omap1_clk_round_rate_ckctl_arm,
		.set_rate	= omap1_clk_set_rate_ckctl_arm,
	},
	.idlect_shift	= IDLPER_ARM_SHIFT,
};

/*
 * FIXME: This clock seems to be necessary but no-one has asked for its
 * activation.  [ GPIO code for 1510 ]
 */
static struct omap1_clk arm_gpio_ck = {
	.hw.init	= CLK_HW_INIT("ick", "ck_dpll1", &omap1_clk_gate_ops, CLK_IS_CRITICAL),
	.ops		= &clkops_generic,
	.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT2),
	.enable_bit	= EN_GPIOCK,
};

static struct arm_idlect1_clk armxor_ck = {
	.clk = {
		.hw.init	= CLK_HW_INIT("armxor_ck", "ck_ref", &omap1_clk_gate_ops,
					      CLK_IS_CRITICAL),
		.ops		= &clkops_generic,
		.flags		= CLOCK_IDLE_CONTROL,
		.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT2),
		.enable_bit	= EN_XORPCK,
	},
	.idlect_shift	= IDLXORP_ARM_SHIFT,
};

static struct arm_idlect1_clk armtim_ck = {
	.clk = {
		.hw.init	= CLK_HW_INIT("armtim_ck", "ck_ref", &omap1_clk_gate_ops,
					      CLK_IS_CRITICAL),
		.ops		= &clkops_generic,
		.flags		= CLOCK_IDLE_CONTROL,
		.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT2),
		.enable_bit	= EN_TIMCK,
	},
	.idlect_shift	= IDLTIM_ARM_SHIFT,
};

static struct arm_idlect1_clk armwdt_ck = {
	.clk = {
		.hw.init	= CLK_HW_INIT("armwdt_ck", "ck_ref", &omap1_clk_full_ops, 0),
		.ops		= &clkops_generic,
		.flags		= CLOCK_IDLE_CONTROL,
		.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT2),
		.enable_bit	= EN_WDTCK,
		.fixed_div	= 14,
		.recalc		= &omap_fixed_divisor_recalc,
	},
	.idlect_shift	= IDLWDT_ARM_SHIFT,
};

static struct omap1_clk arminth_ck16xx = {
	.hw.init	= CLK_HW_INIT("arminth_ck", "arm_ck", &omap1_clk_null_ops, 0),
	/* Note: On 16xx the frequency can be divided by 2 by programming
	 * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1
	 *
	 * 1510 version is in TC clocks.
	 */
};

static struct omap1_clk dsp_ck = {
	.hw.init	= CLK_HW_INIT("dsp_ck", "ck_dpll1", &omap1_clk_full_ops, 0),
	.ops		= &clkops_generic,
	.enable_reg	= OMAP1_IO_ADDRESS(ARM_CKCTL),
	.enable_bit	= EN_DSPCK,
	.rate_offset	= CKCTL_DSPDIV_OFFSET,
	.recalc		= &omap1_ckctl_recalc,
	.round_rate	= omap1_clk_round_rate_ckctl_arm,
	.set_rate	= omap1_clk_set_rate_ckctl_arm,
};

static struct omap1_clk dspmmu_ck = {
	.hw.init	= CLK_HW_INIT("dspmmu_ck", "ck_dpll1", &omap1_clk_rate_ops, 0),
	.rate_offset	= CKCTL_DSPMMUDIV_OFFSET,
	.recalc		= &omap1_ckctl_recalc,
	.round_rate	= omap1_clk_round_rate_ckctl_arm,
	.set_rate	= omap1_clk_set_rate_ckctl_arm,
};

static struct omap1_clk dspper_ck = {
	.hw.init	= CLK_HW_INIT("dspper_ck", "ck_dpll1", &omap1_clk_full_ops, 0),
	.ops		= &clkops_dspck,
	.enable_reg	= DSP_IDLECT2,
	.enable_bit	= EN_PERCK,
	.rate_offset	= CKCTL_PERDIV_OFFSET,
	.recalc		= &omap1_ckctl_recalc_dsp_domain,
	.round_rate	= omap1_clk_round_rate_ckctl_arm,
	.set_rate	= &omap1_clk_set_rate_dsp_domain,
};

static struct omap1_clk dspxor_ck = {
	.hw.init	= CLK_HW_INIT("dspxor_ck", "ck_ref", &omap1_clk_gate_ops, 0),
	.ops		= &clkops_dspck,
	.enable_reg	= DSP_IDLECT2,
	.enable_bit	= EN_XORPCK,
};

static struct omap1_clk dsptim_ck = {
	.hw.init	= CLK_HW_INIT("dsptim_ck", "ck_ref", &omap1_clk_gate_ops, 0),
	.ops		= &clkops_dspck,
	.enable_reg	= DSP_IDLECT2,
	.enable_bit	= EN_DSPTIMCK,
};

static struct arm_idlect1_clk tc_ck = {
	.clk = {
		.hw.init	= CLK_HW_INIT("tc_ck", "ck_dpll1", &omap1_clk_rate_ops, 0),
		.flags		= CLOCK_IDLE_CONTROL,
		.rate_offset	= CKCTL_TCDIV_OFFSET,
		.recalc		= &omap1_ckctl_recalc,
		.round_rate	= omap1_clk_round_rate_ckctl_arm,
		.set_rate	= omap1_clk_set_rate_ckctl_arm,
	},
	.idlect_shift	= IDLIF_ARM_SHIFT,
};

static struct omap1_clk arminth_ck1510 = {
	.hw.init	= CLK_HW_INIT("arminth_ck", "tc_ck", &omap1_clk_null_ops, 0),
	/* Note: On 1510 the frequency follows TC_CK
	 *
	 * 16xx version is in MPU clocks.
	 */
};

static struct omap1_clk tipb_ck = {
	/* No-idle controlled by "tc_ck" */
	.hw.init	= CLK_HW_INIT("tipb_ck", "tc_ck", &omap1_clk_null_ops, 0),
};

static struct omap1_clk l3_ocpi_ck = {
	/* No-idle controlled by "tc_ck" */
	.hw.init	= CLK_HW_INIT("l3_ocpi_ck", "tc_ck", &omap1_clk_gate_ops, 0),
	.ops		= &clkops_generic,
	.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT3),
	.enable_bit	= EN_OCPI_CK,
};

static struct omap1_clk tc1_ck = {
	.hw.init	= CLK_HW_INIT("tc1_ck", "tc_ck", &omap1_clk_gate_ops, 0),
	.ops		= &clkops_generic,
	.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT3),
	.enable_bit	= EN_TC1_CK,
};

/*
 * FIXME: This clock seems to be necessary but no-one has asked for its
 * activation.  [ pm.c (SRAM), CCP, Camera ]
 */

static struct omap1_clk tc2_ck = {
	.hw.init	= CLK_HW_INIT("tc2_ck", "tc_ck", &omap1_clk_gate_ops, CLK_IS_CRITICAL),
	.ops		= &clkops_generic,
	.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT3),
	.enable_bit	= EN_TC2_CK,
};

static struct omap1_clk dma_ck = {
	/* No-idle controlled by "tc_ck" */
	.hw.init	= CLK_HW_INIT("dma_ck", "tc_ck", &omap1_clk_null_ops, 0),
};

static struct omap1_clk dma_lcdfree_ck = {
	.hw.init	= CLK_HW_INIT("dma_lcdfree_ck", "tc_ck", &omap1_clk_null_ops, 0),
};

static struct arm_idlect1_clk api_ck = {
	.clk = {
		.hw.init	= CLK_HW_INIT("api_ck", "tc_ck", &omap1_clk_gate_ops, 0),
		.ops		= &clkops_generic,
		.flags		= CLOCK_IDLE_CONTROL,
		.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT2),
		.enable_bit	= EN_APICK,
	},
	.idlect_shift	= IDLAPI_ARM_SHIFT,
};

static struct arm_idlect1_clk lb_ck = {
	.clk = {
		.hw.init	= CLK_HW_INIT("lb_ck", "tc_ck", &omap1_clk_gate_ops, 0),
		.ops		= &clkops_generic,
		.flags		= CLOCK_IDLE_CONTROL,
		.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT2),
		.enable_bit	= EN_LBCK,
	},
	.idlect_shift	= IDLLB_ARM_SHIFT,
};

static struct omap1_clk rhea1_ck = {
	.hw.init	= CLK_HW_INIT("rhea1_ck", "tc_ck", &omap1_clk_null_ops, 0),
};

static struct omap1_clk rhea2_ck = {
	.hw.init	= CLK_HW_INIT("rhea2_ck", "tc_ck", &omap1_clk_null_ops, 0),
};

static struct omap1_clk lcd_ck_16xx = {
	.hw.init	= CLK_HW_INIT("lcd_ck", "ck_dpll1", &omap1_clk_full_ops, 0),
	.ops		= &clkops_generic,
	.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT2),
	.enable_bit	= EN_LCDCK,
	.rate_offset	= CKCTL_LCDDIV_OFFSET,
	.recalc		= &omap1_ckctl_recalc,
	.round_rate	= omap1_clk_round_rate_ckctl_arm,
	.set_rate	= omap1_clk_set_rate_ckctl_arm,
};

static struct arm_idlect1_clk lcd_ck_1510 = {
	.clk = {
		.hw.init	= CLK_HW_INIT("lcd_ck", "ck_dpll1", &omap1_clk_full_ops, 0),
		.ops		= &clkops_generic,
		.flags		= CLOCK_IDLE_CONTROL,
		.enable_reg	= OMAP1_IO_ADDRESS(ARM_IDLECT2),
		.enable_bit	= EN_LCDCK,
		.rate_offset	= CKCTL_LCDDIV_OFFSET,
		.recalc		= &omap1_ckctl_recalc,
		.round_rate	= omap1_clk_round_rate_ckctl_arm,
		.set_rate	= omap1_clk_set_rate_ckctl_arm,
	},
	.idlect_shift	= OMAP1510_IDLLCD_ARM_SHIFT,
};

/*
 * XXX The enable_bit here is misused - it simply switches between 12MHz
 * and 48MHz.  Reimplement with clk_mux.
 *
 * XXX does this need SYSC register handling?
 */
static struct omap1_clk uart1_1510 = {
	/* Direct from ULPD, no real parent */
	.hw.init	= CLK_HW_INIT("uart1_ck", "armper_ck", &omap1_clk_full_ops, 0),
	.flags		= ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT,
	.enable_reg	= OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0),
	.enable_bit	= CONF_MOD_UART1_CLK_MODE_R,
	.round_rate	= &omap1_round_uart_rate,
	.set_rate	= &omap1_set_uart_rate,
	.recalc		= &omap1_uart_recalc,
};

/*
 * XXX The enable_bit here is misused - it simply switches between 12MHz
 * and 48MHz.  Reimplement with clk_mux.
 *
 * XXX SYSC register handling does not belong in the clock framework
 */
static struct uart_clk uart1_16xx = {
	.clk	= {
		.ops		= &clkops_uart_16xx,
		/* Direct from ULPD, no real parent */
		.hw.init	= CLK_HW_INIT("uart1_ck", "armper_ck", &omap1_clk_full_ops, 0),
		.rate		= 48000000,
		.flags		= ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT,
		.enable_reg	= OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0),
		.enable_bit	= CONF_MOD_UART1_CLK_MODE_R,
	},
	.sysc_addr	= 0xfffb0054,
};

/*
 * XXX The enable_bit here is misused - it simply switches between 12MHz
 * and 48MHz.  Reimplement with clk_mux.
 *
 * XXX does this need SYSC register handling?
 */
static struct omap1_clk uart2_ck = {
	/* Direct from ULPD, no real parent */
	.hw.init	= CLK_HW_INIT("uart2_ck", "armper_ck", &omap1_clk_full_ops, 0),
	.flags		= ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT,
	.enable_reg	= OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0),
	.enable_bit	= CONF_MOD_UART2_CLK_MODE_R,
	.round_rate	= &omap1_round_uart_rate,
	.set_rate	= &omap1_set_uart_rate,
	.recalc		= &omap1_uart_recalc,
};

/*
 * XXX The enable_bit here is misused - it simply switches between 12MHz
 * and 48MHz.  Reimplement with clk_mux.
 *
 * XXX does this need SYSC register handling?
 */
static struct omap1_clk uart3_1510 = {
	/* Direct from ULPD, no real parent */
	.hw.init	= CLK_HW_INIT("uart3_ck", "armper_ck", &omap1_clk_full_ops, 0),
	.flags		= ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT,
	.enable_reg	= OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0),
	.enable_bit	= CONF_MOD_UART3_CLK_MODE_R,
	.round_rate	= &omap1_round_uart_rate,
	.set_rate	= &omap1_set_uart_rate,
	.recalc		= &omap1_uart_recalc,
};

/*
 * XXX The enable_bit here is misused - it simply switches between 12MHz
 * and 48MHz.  Reimplement with clk_mux.
 *
 * XXX SYSC register handling does not belong in the clock framework
 */
static struct uart_clk uart3_16xx = {
	.clk	= {
		.ops		= &clkops_uart_16xx,
		/* Direct from ULPD, no real parent */
		.hw.init	= CLK_HW_INIT("uart3_ck", "armper_ck", &omap1_clk_full_ops, 0),
		.rate		= 48000000,
		.flags		= ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT,
		.enable_reg	= OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0),
		.enable_bit	= CONF_MOD_UART3_CLK_MODE_R,
	},
	.sysc_addr	= 0xfffb9854,
};

static struct omap1_clk usb_clko = {	/* 6 MHz output on W4_USB_CLKO */
	.ops		= &clkops_generic,
	/* Direct from ULPD, no parent */
	.hw.init	= CLK_HW_INIT_NO_PARENT("usb_clko", &omap1_clk_full_ops, 0),
	.rate		= 6000000,
	.flags		= ENABLE_REG_32BIT,
	.enable_reg	= OMAP1_IO_ADDRESS(ULPD_CLOCK_CTRL),
	.enable_bit	= USB_MCLK_EN_BIT,
};

static struct omap1_clk usb_hhc_ck1510 = {
	.ops		= &clkops_generic,
	/* Direct from ULPD, no parent */
	.hw.init	= CLK_HW_INIT_NO_PARENT("usb_hhc_ck", &omap1_clk_full_ops, 0),
	.rate		= 48000000, /* Actually 2 clocks, 12MHz and 48MHz */
	.flags		= ENABLE_REG_32BIT,
	.enable_reg	= OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0),
	.enable_bit	= USB_HOST_HHC_UHOST_EN,
};

static struct omap1_clk usb_hhc_ck16xx = {
	.ops		= &clkops_generic,
	/* Direct from ULPD, no parent */
	.hw.init	= CLK_HW_INIT_NO_PARENT("usb_hhc_ck", &omap1_clk_full_ops, 0),
	.rate		= 48000000,
	/* OTG_SYSCON_2.OTG_PADEN == 0 (not 1510-compatible) */
	.flags		= ENABLE_REG_32BIT,
	.enable_reg	= OMAP1_IO_ADDRESS(OTG_BASE + 0x08), /* OTG_SYSCON_2 */
	.enable_bit	= OTG_SYSCON_2_UHOST_EN_SHIFT
};

static struct omap1_clk usb_dc_ck = {
	.ops		= &clkops_generic,
	/* Direct from ULPD, no parent */
	.hw.init	= CLK_HW_INIT_NO_PARENT("usb_dc_ck", &omap1_clk_full_ops, 0),
	.rate		= 48000000,
	.enable_reg	= OMAP1_IO_ADDRESS(SOFT_REQ_REG),
	.enable_bit	= SOFT_USB_OTG_DPLL_REQ_SHIFT,
};

static struct omap1_clk uart1_7xx = {
	.ops		= &clkops_generic,
	/* Direct from ULPD, no parent */
	.hw.init	= CLK_HW_INIT_NO_PARENT("uart1_ck", &omap1_clk_full_ops, 0),
	.rate		= 12000000,
	.enable_reg	= OMAP1_IO_ADDRESS(SOFT_REQ_REG),
	.enable_bit	= 9,
};

static struct omap1_clk uart2_7xx = {
	.ops		= &clkops_generic,
	/* Direct from ULPD, no parent */
	.hw.init	= CLK_HW_INIT_NO_PARENT("uart2_ck", &omap1_clk_full_ops, 0),
	.rate		= 12000000,
	.enable_reg	= OMAP1_IO_ADDRESS(SOFT_REQ_REG),
	.enable_bit	= 11,
};

static struct omap1_clk mclk_1510 = {
	.ops		= &clkops_generic,
	/* Direct from ULPD, no parent. May be enabled by ext hardware. */
	.hw.init	= CLK_HW_INIT_NO_PARENT("mclk", &omap1_clk_full_ops, 0),
	.rate		= 12000000,
	.enable_reg	= OMAP1_IO_ADDRESS(SOFT_REQ_REG),
	.enable_bit	= SOFT_COM_MCKO_REQ_SHIFT,
};

static struct omap1_clk mclk_16xx = {
	.ops		= &clkops_generic,
	/* Direct from ULPD, no parent. May be enabled by ext hardware. */
	.hw.init	= CLK_HW_INIT_NO_PARENT("mclk", &omap1_clk_full_ops, 0),
	.enable_reg	= OMAP1_IO_ADDRESS(COM_CLK_DIV_CTRL_SEL),
	.enable_bit	= COM_ULPD_PLL_CLK_REQ,
	.set_rate	= &omap1_set_ext_clk_rate,
	.round_rate	= &omap1_round_ext_clk_rate,
	.init		= &omap1_init_ext_clk,
};

static struct omap1_clk bclk_1510 = {
	/* Direct from ULPD, no parent. May be enabled by ext hardware. */
	.hw.init	= CLK_HW_INIT_NO_PARENT("bclk", &omap1_clk_rate_ops, 0),
	.rate		= 12000000,
};

static struct omap1_clk bclk_16xx = {
	.ops		= &clkops_generic,
	/* Direct from ULPD, no parent. May be enabled by ext hardware. */
	.hw.init	= CLK_HW_INIT_NO_PARENT("bclk", &omap1_clk_full_ops, 0),
	.enable_reg	= OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL),
	.enable_bit	= SWD_ULPD_PLL_CLK_REQ,
	.set_rate	= &omap1_set_ext_clk_rate,
	.round_rate	= &omap1_round_ext_clk_rate,
	.init		= &omap1_init_ext_clk,
};

static struct omap1_clk mmc1_ck = {
	.ops		= &clkops_generic,
	/* Functional clock is direct from ULPD, interface clock is ARMPER */
	.hw.init	= CLK_HW_INIT("mmc1_ck", "armper_ck", &omap1_clk_full_ops, 0),
	.rate		= 48000000,
	.flags		= ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT,
	.enable_reg	= OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0),
	.enable_bit	= CONF_MOD_MMC_SD_CLK_REQ_R,
};

/*
 * XXX MOD_CONF_CTRL_0 bit 20 is defined in the 1510 TRM as
 * CONF_MOD_MCBSP3_AUXON ??
 */
static struct omap1_clk mmc2_ck = {
	.ops		= &clkops_generic,
	/* Functional clock is direct from ULPD, interface clock is ARMPER */
	.hw.init	= CLK_HW_INIT("mmc2_ck", "armper_ck", &omap1_clk_full_ops, 0),
	.rate		= 48000000,
	.flags		= ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT,
	.enable_reg	= OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0),
	.enable_bit	= 20,
};

static struct omap1_clk mmc3_ck = {
	.ops		= &clkops_generic,
	/* Functional clock is direct from ULPD, interface clock is ARMPER */
	.hw.init	= CLK_HW_INIT("mmc3_ck", "armper_ck", &omap1_clk_full_ops, 0),
	.rate		= 48000000,
	.flags		= ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT,
	.enable_reg	= OMAP1_IO_ADDRESS(SOFT_REQ_REG),
	.enable_bit	= SOFT_MMC_DPLL_REQ_SHIFT,
};

static struct omap1_clk virtual_ck_mpu = {
	/* Is smarter alias for arm_ck */
	.hw.init	= CLK_HW_INIT("mpu", "arm_ck", &omap1_clk_rate_ops, 0),
	.recalc		= &followparent_recalc,
	.set_rate	= &omap1_select_table_rate,
	.round_rate	= &omap1_round_to_table_rate,
};

/* virtual functional clock domain for I2C. Just for making sure that ARMXOR_CK
remains active during MPU idle whenever this is enabled */
static struct omap1_clk i2c_fck = {
	.hw.init	= CLK_HW_INIT("i2c_fck", "armxor_ck", &omap1_clk_gate_ops, 0),
	.flags		= CLOCK_NO_IDLE_PARENT,
};

static struct omap1_clk i2c_ick = {
	.hw.init	= CLK_HW_INIT("i2c_ick", "armper_ck", &omap1_clk_gate_ops, 0),
	.flags		= CLOCK_NO_IDLE_PARENT,
};

/*
 * clkdev integration
 */

static struct omap_clk omap_clks[] = {
	/* non-ULPD clocks */
	CLK(NULL,	"ck_ref",	&ck_ref.hw,	CK_16XX | CK_1510 | CK_310 | CK_7XX),
	CLK(NULL,	"ck_dpll1",	&ck_dpll1.hw,	CK_16XX | CK_1510 | CK_310 | CK_7XX),
	/* CK_GEN1 clocks */
	CLK(NULL,	"ck_dpll1out",	&ck_dpll1out.clk.hw, CK_16XX),
	CLK(NULL,	"ck_sossi",	&sossi_ck.hw,	CK_16XX),
	CLK(NULL,	"arm_ck",	&arm_ck.hw,	CK_16XX | CK_1510 | CK_310),
	CLK(NULL,	"armper_ck",	&armper_ck.clk.hw, CK_16XX | CK_1510 | CK_310),
	CLK("omap_gpio.0", "ick",	&arm_gpio_ck.hw, CK_1510 | CK_310),
	CLK(NULL,	"armxor_ck",	&armxor_ck.clk.hw, CK_16XX | CK_1510 | CK_310 | CK_7XX),
	CLK(NULL,	"armtim_ck",	&armtim_ck.clk.hw, CK_16XX | CK_1510 | CK_310),
	CLK("omap_wdt",	"fck",		&armwdt_ck.clk.hw, CK_16XX | CK_1510 | CK_310),
	CLK("omap_wdt",	"ick",		&armper_ck.clk.hw, CK_16XX),
	CLK("omap_wdt", "ick",		&dummy_ck.hw,	CK_1510 | CK_310),
	CLK(NULL,	"arminth_ck",	&arminth_ck1510.hw, CK_1510 | CK_310),
	CLK(NULL,	"arminth_ck",	&arminth_ck16xx.hw, CK_16XX),
	/* CK_GEN2 clocks */
	CLK(NULL,	"dsp_ck",	&dsp_ck.hw,	CK_16XX | CK_1510 | CK_310),
	CLK(NULL,	"dspmmu_ck",	&dspmmu_ck.hw,	CK_16XX | CK_1510 | CK_310),
	CLK(NULL,	"dspper_ck",	&dspper_ck.hw,	CK_16XX | CK_1510 | CK_310),
	CLK(NULL,	"dspxor_ck",	&dspxor_ck.hw,	CK_16XX | CK_1510 | CK_310),
	CLK(NULL,	"dsptim_ck",	&dsptim_ck.hw,	CK_16XX | CK_1510 | CK_310),
	/* CK_GEN3 clocks */
	CLK(NULL,	"tc_ck",	&tc_ck.clk.hw,	CK_16XX | CK_1510 | CK_310 | CK_7XX),
	CLK(NULL,	"tipb_ck",	&tipb_ck.hw,	CK_1510 | CK_310),
	CLK(NULL,	"l3_ocpi_ck",	&l3_ocpi_ck.hw,	CK_16XX | CK_7XX),
	CLK(NULL,	"tc1_ck",	&tc1_ck.hw,	CK_16XX),
	CLK(NULL,	"tc2_ck",	&tc2_ck.hw,	CK_16XX),
	CLK(NULL,	"dma_ck",	&dma_ck.hw,	CK_16XX | CK_1510 | CK_310),
	CLK(NULL,	"dma_lcdfree_ck", &dma_lcdfree_ck.hw, CK_16XX),
	CLK(NULL,	"api_ck",	&api_ck.clk.hw,	CK_16XX | CK_1510 | CK_310 | CK_7XX),
	CLK(NULL,	"lb_ck",	&lb_ck.clk.hw,	CK_1510 | CK_310),
	CLK(NULL,	"rhea1_ck",	&rhea1_ck.hw,	CK_16XX),
	CLK(NULL,	"rhea2_ck",	&rhea2_ck.hw,	CK_16XX),
	CLK(NULL,	"lcd_ck",	&lcd_ck_16xx.hw, CK_16XX | CK_7XX),
	CLK(NULL,	"lcd_ck",	&lcd_ck_1510.clk.hw, CK_1510 | CK_310),
	/* ULPD clocks */
	CLK(NULL,	"uart1_ck",	&uart1_1510.hw,	CK_1510 | CK_310),
	CLK(NULL,	"uart1_ck",	&uart1_16xx.clk.hw, CK_16XX),
	CLK(NULL,	"uart1_ck",	&uart1_7xx.hw,	CK_7XX),
	CLK(NULL,	"uart2_ck",	&uart2_ck.hw,	CK_16XX | CK_1510 | CK_310),
	CLK(NULL,	"uart2_ck",	&uart2_7xx.hw,	CK_7XX),
	CLK(NULL,	"uart3_ck",	&uart3_1510.hw,	CK_1510 | CK_310),
	CLK(NULL,	"uart3_ck",	&uart3_16xx.clk.hw, CK_16XX),
	CLK(NULL,	"usb_clko",	&usb_clko.hw,	CK_16XX | CK_1510 | CK_310),
	CLK(NULL,	"usb_hhc_ck",	&usb_hhc_ck1510.hw, CK_1510 | CK_310),
	CLK(NULL,	"usb_hhc_ck",	&usb_hhc_ck16xx.hw, CK_16XX),
	CLK(NULL,	"usb_dc_ck",	&usb_dc_ck.hw,	CK_16XX | CK_7XX),
	CLK(NULL,	"mclk",		&mclk_1510.hw,	CK_1510 | CK_310),
	CLK(NULL,	"mclk",		&mclk_16xx.hw,	CK_16XX),
	CLK(NULL,	"bclk",		&bclk_1510.hw,	CK_1510 | CK_310),
	CLK(NULL,	"bclk",		&bclk_16xx.hw,	CK_16XX),
	CLK("mmci-omap.0", "fck",	&mmc1_ck.hw,	CK_16XX | CK_1510 | CK_310),
	CLK("mmci-omap.0", "fck",	&mmc3_ck.hw,	CK_7XX),
	CLK("mmci-omap.0", "ick",	&armper_ck.clk.hw, CK_16XX | CK_1510 | CK_310 | CK_7XX),
	CLK("mmci-omap.1", "fck",	&mmc2_ck.hw,	CK_16XX),
	CLK("mmci-omap.1", "ick",	&armper_ck.clk.hw, CK_16XX),
	/* Virtual clocks */
	CLK(NULL,	"mpu",		&virtual_ck_mpu.hw, CK_16XX | CK_1510 | CK_310),
	CLK("omap_i2c.1", "fck",	&i2c_fck.hw,	CK_16XX | CK_1510 | CK_310 | CK_7XX),
	CLK("omap_i2c.1", "ick",	&i2c_ick.hw,	CK_16XX),
	CLK("omap_i2c.1", "ick",	&dummy_ck.hw,	CK_1510 | CK_310 | CK_7XX),
	CLK("omap1_spi100k.1", "fck",	&dummy_ck.hw,	CK_7XX),
	CLK("omap1_spi100k.1", "ick",	&dummy_ck.hw,	CK_7XX),
	CLK("omap1_spi100k.2", "fck",	&dummy_ck.hw,	CK_7XX),
	CLK("omap1_spi100k.2", "ick",	&dummy_ck.hw,	CK_7XX),
	CLK("omap_uwire", "fck",	&armxor_ck.clk.hw, CK_16XX | CK_1510 | CK_310),
	CLK("omap-mcbsp.1", "ick",	&dspper_ck.hw,	CK_16XX),
	CLK("omap-mcbsp.1", "ick",	&dummy_ck.hw,	CK_1510 | CK_310),
	CLK("omap-mcbsp.2", "ick",	&armper_ck.clk.hw, CK_16XX),
	CLK("omap-mcbsp.2", "ick",	&dummy_ck.hw,	CK_1510 | CK_310),
	CLK("omap-mcbsp.3", "ick",	&dspper_ck.hw,	CK_16XX),
	CLK("omap-mcbsp.3", "ick",	&dummy_ck.hw,	CK_1510 | CK_310),
	CLK("omap-mcbsp.1", "fck",	&dspxor_ck.hw,	CK_16XX | CK_1510 | CK_310),
	CLK("omap-mcbsp.2", "fck",	&armper_ck.clk.hw, CK_16XX | CK_1510 | CK_310),
	CLK("omap-mcbsp.3", "fck",	&dspxor_ck.hw,	CK_16XX | CK_1510 | CK_310),
};

/*
 * init
 */

static void __init omap1_show_rates(void)
{
	pr_notice("Clocking rate (xtal/DPLL1/MPU): %ld.%01ld/%ld.%01ld/%ld.%01ld MHz\n",
		  ck_ref.rate / 1000000, (ck_ref.rate / 100000) % 10,
		  ck_dpll1.rate / 1000000, (ck_dpll1.rate / 100000) % 10,
		  arm_ck.rate / 1000000, (arm_ck.rate / 100000) % 10);
}

u32 cpu_mask;

int __init omap1_clk_init(void)
{
	struct omap_clk *c;
	u32 reg;

#ifdef CONFIG_DEBUG_LL
	/* Make sure UART clocks are enabled early */
	if (cpu_is_omap16xx())
		omap_writel(omap_readl(MOD_CONF_CTRL_0) |
			    CONF_MOD_UART1_CLK_MODE_R |
			    CONF_MOD_UART3_CLK_MODE_R, MOD_CONF_CTRL_0);
#endif

	/* USB_REQ_EN will be disabled later if necessary (usb_dc_ck) */
	reg = omap_readw(SOFT_REQ_REG) & (1 << 4);
	omap_writew(reg, SOFT_REQ_REG);
	if (!cpu_is_omap15xx())
		omap_writew(0, SOFT_REQ_REG2);

	/* By default all idlect1 clocks are allowed to idle */
	arm_idlect1_mask = ~0;

	cpu_mask = 0;
	if (cpu_is_omap1710())
		cpu_mask |= CK_1710;
	if (cpu_is_omap16xx())
		cpu_mask |= CK_16XX;
	if (cpu_is_omap1510())
		cpu_mask |= CK_1510;
	if (cpu_is_omap7xx())
		cpu_mask |= CK_7XX;
	if (cpu_is_omap310())
		cpu_mask |= CK_310;

	/* Pointers to these clocks are needed by code in clock.c */
	api_ck_p = &api_ck.clk;
	ck_dpll1_p = &ck_dpll1;
	ck_ref_p = &ck_ref;

	if (cpu_is_omap7xx())
		ck_ref.rate = 13000000;

	pr_info("Clocks: ARM_SYSST: 0x%04x DPLL_CTL: 0x%04x ARM_CKCTL: 0x%04x\n",
		omap_readw(ARM_SYSST), omap_readw(DPLL_CTL),
		omap_readw(ARM_CKCTL));

	/* We want to be in synchronous scalable mode */
	omap_writew(0x1000, ARM_SYSST);

	/*
	 * Initially use the values set by bootloader. Determine PLL rate and
	 * recalculate dependent clocks as if kernel had changed PLL or
	 * divisors. See also omap1_clk_late_init() that can reprogram dpll1
	 * after the SRAM is initialized.
	 */
	{
		unsigned pll_ctl_val = omap_readw(DPLL_CTL);

		ck_dpll1.rate = ck_ref.rate; /* Base xtal rate */
		if (pll_ctl_val & 0x10) {
			/* PLL enabled, apply multiplier and divisor */
			if (pll_ctl_val & 0xf80)
				ck_dpll1.rate *= (pll_ctl_val & 0xf80) >> 7;
			ck_dpll1.rate /= ((pll_ctl_val & 0x60) >> 5) + 1;
		} else {
			/* PLL disabled, apply bypass divisor */
			switch (pll_ctl_val & 0xc) {
			case 0:
				break;
			case 0x4:
				ck_dpll1.rate /= 2;
				break;
			default:
				ck_dpll1.rate /= 4;
				break;
			}
		}
	}

	if (machine_is_omap_perseus2() || machine_is_omap_fsample()) {
		/* Select slicer output as OMAP input clock */
		omap_writew(omap_readw(OMAP7XX_PCC_UPLD_CTRL) & ~0x1,
				OMAP7XX_PCC_UPLD_CTRL);
	}

	/* Amstrad Delta wants BCLK high when inactive */
	if (machine_is_ams_delta())
		omap_writel(omap_readl(ULPD_CLOCK_CTRL) |
				(1 << SDW_MCLK_INV_BIT),
				ULPD_CLOCK_CTRL);

	/* Turn off DSP and ARM_TIMXO. Make sure ARM_INTHCK is not divided */
	/* (on 730, bit 13 must not be cleared) */
	if (cpu_is_omap7xx())
		omap_writew(omap_readw(ARM_CKCTL) & 0x2fff, ARM_CKCTL);
	else
		omap_writew(omap_readw(ARM_CKCTL) & 0x0fff, ARM_CKCTL);

	/* Put DSP/MPUI into reset until needed */
	omap_writew(0, ARM_RSTCT1);
	omap_writew(1, ARM_RSTCT2);
	omap_writew(0x400, ARM_IDLECT1);

	/*
	 * According to OMAP5910 Erratum SYS_DMA_1, bit DMACK_REQ (bit 8)
	 * of the ARM_IDLECT2 register must be set to zero. The power-on
	 * default value of this bit is one.
	 */
	omap_writew(0x0000, ARM_IDLECT2);	/* Turn LCD clock off also */

	for (c = omap_clks; c < omap_clks + ARRAY_SIZE(omap_clks); c++) {
		if (!(c->cpu & cpu_mask))
			continue;

		if (c->lk.clk_hw->init) { /* NULL if provider already registered */
			const struct clk_init_data *init = c->lk.clk_hw->init;
			const char *name = c->lk.clk_hw->init->name;
			int err;

			err = clk_hw_register(NULL, c->lk.clk_hw);
			if (err < 0) {
				pr_err("failed to register clock \"%s\"! (%d)\n", name, err);
				/* may be tried again, restore init data */
				c->lk.clk_hw->init = init;
				continue;
			}
		}

		clk_hw_register_clkdev(c->lk.clk_hw, c->lk.con_id, c->lk.dev_id);
	}

	omap1_show_rates();

	return 0;
}

#define OMAP1_DPLL1_SANE_VALUE	60000000

void __init omap1_clk_late_init(void)
{
	unsigned long rate = ck_dpll1.rate;

	/* Find the highest supported frequency and enable it */
	if (omap1_select_table_rate(&virtual_ck_mpu, ~0, arm_ck.rate)) {
		pr_err("System frequencies not set, using default. Check your config.\n");
		/*
		 * Reprogramming the DPLL is tricky, it must be done from SRAM.
		 */
		omap_sram_reprogram_clock(0x2290, 0x0005);
		ck_dpll1.rate = OMAP1_DPLL1_SANE_VALUE;
	}
	propagate_rate(&ck_dpll1);
	omap1_show_rates();
	loops_per_jiffy = cpufreq_scale(loops_per_jiffy, rate, ck_dpll1.rate);
}


code:
/*
 *
 * Header for code common to all OMAP1 machines.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the  GNU General Public License along
 * with this program; if not, write  to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef __ARCH_ARM_MACH_OMAP1_COMMON_H
#define __ARCH_ARM_MACH_OMAP1_COMMON_H

#include <linux/platform_data/i2c-omap.h>
#include <linux/reboot.h>

#include <asm/exception.h>

#include "irqs.h"
#include "soc.h"
#include "i2c.h"

#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
void omap7xx_map_io(void);
#else
static inline void omap7xx_map_io(void)
{
}
#endif

#ifdef CONFIG_ARCH_OMAP15XX
void omap1510_fpga_init_irq(void);
void omap15xx_map_io(void);
#else
static inline void omap1510_fpga_init_irq(void)
{
}
static inline void omap15xx_map_io(void)
{
}
#endif

#ifdef CONFIG_ARCH_OMAP16XX
void omap16xx_map_io(void);
#else
static inline void omap16xx_map_io(void)
{
}
#endif

#ifdef CONFIG_OMAP_SERIAL_WAKE
int omap_serial_wakeup_init(void);
#else
static inline int omap_serial_wakeup_init(void)
{
	return 0;
}
#endif

void omap1_init_early(void);
void omap1_init_irq(void);
void __exception_irq_entry omap1_handle_irq(struct pt_regs *regs);
void omap1_init_late(void);
void omap1_restart(enum reboot_mode, const char *);

extern void __init omap_check_revision(void);

struct nand_chip;
extern void omap1_nand_cmd_ctl(struct nand_chip *this, int cmd,
			       unsigned int ctrl);

extern void omap1_timer_init(void);
#ifdef CONFIG_OMAP_32K_TIMER
extern int omap_32k_timer_init(void);
#else
static inline int __init omap_32k_timer_init(void)
{
	return -ENODEV;
}
#endif

#ifdef CONFIG_ARCH_OMAP16XX
extern int ocpi_enable(void);
#else
static inline int ocpi_enable(void) { return 0; }
#endif

extern u32 omap1_get_reset_sources(void);

#endif /* __ARCH_ARM_MACH_OMAP1_COMMON_H */


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * linux/arch/arm/mach-omap1/devices.c
 *
 * OMAP1 platform device setup/initialization
 */

#include <linux/dma-mapping.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>

#include <linux/platform_data/omap-wd-timer.h>
#include <linux/soc/ti/omap1-io.h>

#include <asm/mach/map.h>

#include "tc.h"
#include "mux.h"

#include "omap7xx.h"
#include "hardware.h"
#include "common.h"
#include "clock.h"
#include "mmc.h"
#include "sram.h"

#if IS_ENABLED(CONFIG_RTC_DRV_OMAP)

#define	OMAP_RTC_BASE		0xfffb4800

static struct resource rtc_resources[] = {
	{
		.start		= OMAP_RTC_BASE,
		.end		= OMAP_RTC_BASE + 0x5f,
		.flags		= IORESOURCE_MEM,
	},
	{
		.start		= INT_RTC_TIMER,
		.flags		= IORESOURCE_IRQ,
	},
	{
		.start		= INT_RTC_ALARM,
		.flags		= IORESOURCE_IRQ,
	},
};

static struct platform_device omap_rtc_device = {
	.name           = "omap_rtc",
	.id             = -1,
	.num_resources	= ARRAY_SIZE(rtc_resources),
	.resource	= rtc_resources,
};

static void omap_init_rtc(void)
{
	(void) platform_device_register(&omap_rtc_device);
}
#else
static inline void omap_init_rtc(void) {}
#endif

static inline void omap_init_mbox(void) { }

/*-------------------------------------------------------------------------*/

#if IS_ENABLED(CONFIG_MMC_OMAP)

static inline void omap1_mmc_mux(struct omap_mmc_platform_data *mmc_controller,
			int controller_nr)
{
	if (controller_nr == 0) {
		if (cpu_is_omap7xx()) {
			omap_cfg_reg(MMC_7XX_CMD);
			omap_cfg_reg(MMC_7XX_CLK);
			omap_cfg_reg(MMC_7XX_DAT0);
		} else {
			omap_cfg_reg(MMC_CMD);
			omap_cfg_reg(MMC_CLK);
			omap_cfg_reg(MMC_DAT0);
		}

		if (cpu_is_omap1710()) {
			omap_cfg_reg(M15_1710_MMC_CLKI);
			omap_cfg_reg(P19_1710_MMC_CMDDIR);
			omap_cfg_reg(P20_1710_MMC_DATDIR0);
		}
		if (mmc_controller->slots[0].wires == 4 && !cpu_is_omap7xx()) {
			omap_cfg_reg(MMC_DAT1);
			/* NOTE: DAT2 can be on W10 (here) or M15 */
			if (!mmc_controller->slots[0].nomux)
				omap_cfg_reg(MMC_DAT2);
			omap_cfg_reg(MMC_DAT3);
		}
	}

	/* Block 2 is on newer chips, and has many pinout options */
	if (cpu_is_omap16xx() && controller_nr == 1) {
		if (!mmc_controller->slots[1].nomux) {
			omap_cfg_reg(Y8_1610_MMC2_CMD);
			omap_cfg_reg(Y10_1610_MMC2_CLK);
			omap_cfg_reg(R18_1610_MMC2_CLKIN);
			omap_cfg_reg(W8_1610_MMC2_DAT0);
			if (mmc_controller->slots[1].wires == 4) {
				omap_cfg_reg(V8_1610_MMC2_DAT1);
				omap_cfg_reg(W15_1610_MMC2_DAT2);
				omap_cfg_reg(R10_1610_MMC2_DAT3);
			}

			/* These are needed for the level shifter */
			omap_cfg_reg(V9_1610_MMC2_CMDDIR);
			omap_cfg_reg(V5_1610_MMC2_DATDIR0);
			omap_cfg_reg(W19_1610_MMC2_DATDIR1);
		}

		/* Feedback clock must be set on OMAP-1710 MMC2 */
		if (cpu_is_omap1710())
			omap_writel(omap_readl(MOD_CONF_CTRL_1) | (1 << 24),
					MOD_CONF_CTRL_1);
	}
}

#define OMAP_MMC_NR_RES		4

/*
 * Register MMC devices.
 */
static int __init omap_mmc_add(const char *name, int id, unsigned long base,
				unsigned long size, unsigned int irq,
				unsigned rx_req, unsigned tx_req,
				struct omap_mmc_platform_data *data)
{
	struct platform_device *pdev;
	struct resource res[OMAP_MMC_NR_RES];
	int ret;

	pdev = platform_device_alloc(name, id);
	if (!pdev)
		return -ENOMEM;

	memset(res, 0, OMAP_MMC_NR_RES * sizeof(struct resource));
	res[0].start = base;
	res[0].end = base + size - 1;
	res[0].flags = IORESOURCE_MEM;
	res[1].start = res[1].end = irq;
	res[1].flags = IORESOURCE_IRQ;
	res[2].start = rx_req;
	res[2].name = "rx";
	res[2].flags = IORESOURCE_DMA;
	res[3].start = tx_req;
	res[3].name = "tx";
	res[3].flags = IORESOURCE_DMA;

	if (cpu_is_omap7xx())
		data->slots[0].features = MMC_OMAP7XX;
	if (cpu_is_omap15xx())
		data->slots[0].features = MMC_OMAP15XX;
	if (cpu_is_omap16xx())
		data->slots[0].features = MMC_OMAP16XX;

	ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
	if (ret == 0)
		ret = platform_device_add_data(pdev, data, sizeof(*data));
	if (ret)
		goto fail;

	ret = platform_device_add(pdev);
	if (ret)
		goto fail;

	/* return device handle to board setup code */
	data->dev = &pdev->dev;
	return 0;

fail:
	platform_device_put(pdev);
	return ret;
}

void __init omap1_init_mmc(struct omap_mmc_platform_data **mmc_data,
			int nr_controllers)
{
	int i;

	for (i = 0; i < nr_controllers; i++) {
		unsigned long base, size;
		unsigned rx_req, tx_req;
		unsigned int irq = 0;

		if (!mmc_data[i])
			continue;

		omap1_mmc_mux(mmc_data[i], i);

		switch (i) {
		case 0:
			base = OMAP1_MMC1_BASE;
			irq = INT_MMC;
			rx_req = 22;
			tx_req = 21;
			break;
		case 1:
			if (!cpu_is_omap16xx())
				return;
			base = OMAP1_MMC2_BASE;
			irq = INT_1610_MMC2;
			rx_req = 55;
			tx_req = 54;
			break;
		default:
			continue;
		}
		size = OMAP1_MMC_SIZE;

		omap_mmc_add("mmci-omap", i, base, size, irq,
				rx_req, tx_req, mmc_data[i]);
	}
}

#endif

/*-------------------------------------------------------------------------*/

/* OMAP7xx SPI support */
#if IS_ENABLED(CONFIG_SPI_OMAP_100K)

struct platform_device omap_spi1 = {
	.name           = "omap1_spi100k",
	.id             = 1,
};

struct platform_device omap_spi2 = {
	.name           = "omap1_spi100k",
	.id             = 2,
};

static void omap_init_spi100k(void)
{
	if (!cpu_is_omap7xx())
		return;

	omap_spi1.dev.platform_data = ioremap(OMAP7XX_SPI1_BASE, 0x7ff);
	if (omap_spi1.dev.platform_data)
		platform_device_register(&omap_spi1);

	omap_spi2.dev.platform_data = ioremap(OMAP7XX_SPI2_BASE, 0x7ff);
	if (omap_spi2.dev.platform_data)
		platform_device_register(&omap_spi2);
}

#else
static inline void omap_init_spi100k(void)
{
}
#endif

/*-------------------------------------------------------------------------*/

static inline void omap_init_sti(void) {}

/* Numbering for the SPI-capable controllers when used for SPI:
 * spi		= 1
 * uwire	= 2
 * mmc1..2	= 3..4
 * mcbsp1..3	= 5..7
 */

#if IS_ENABLED(CONFIG_SPI_OMAP_UWIRE)

#define	OMAP_UWIRE_BASE		0xfffb3000

static struct resource uwire_resources[] = {
	{
		.start		= OMAP_UWIRE_BASE,
		.end		= OMAP_UWIRE_BASE + 0x20,
		.flags		= IORESOURCE_MEM,
	},
};

static struct platform_device omap_uwire_device = {
	.name	   = "omap_uwire",
	.id	     = -1,
	.num_resources	= ARRAY_SIZE(uwire_resources),
	.resource	= uwire_resources,
};

static void omap_init_uwire(void)
{
	/* FIXME define and use a boot tag; not all boards will be hooking
	 * up devices to the microwire controller, and multi-board configs
	 * mean that CONFIG_SPI_OMAP_UWIRE may be configured anyway...
	 */

	/* board-specific code must configure chipselects (only a few
	 * are normally used) and SCLK/SDI/SDO (each has two choices).
	 */
	(void) platform_device_register(&omap_uwire_device);
}
#else
static inline void omap_init_uwire(void) {}
#endif

#define OMAP1_RNG_BASE		0xfffe5000

static struct resource omap1_rng_resources[] = {
	{
		.start		= OMAP1_RNG_BASE,
		.end		= OMAP1_RNG_BASE + 0x4f,
		.flags		= IORESOURCE_MEM,
	},
};

static struct platform_device omap1_rng_device = {
	.name		= "omap_rng",
	.id		= -1,
	.num_resources	= ARRAY_SIZE(omap1_rng_resources),
	.resource	= omap1_rng_resources,
};

static void omap1_init_rng(void)
{
	if (!cpu_is_omap16xx())
		return;

	(void) platform_device_register(&omap1_rng_device);
}

/*-------------------------------------------------------------------------*/

/*
 * This gets called after board-specific INIT_MACHINE, and initializes most
 * on-chip peripherals accessible on this board (except for few like USB):
 *
 *  (a) Does any "standard config" pin muxing needed.  Board-specific
 *	code will have muxed GPIO pins and done "nonstandard" setup;
 *	that code could live in the boot loader.
 *  (b) Populating board-specific platform_data with the data drivers
 *	rely on to handle wiring variations.
 *  (c) Creating platform devices as meaningful on this board and
 *	with this kernel configuration.
 *
 * Claiming GPIOs, and setting their direction and initial values, is the
 * responsibility of the device drivers.  So is responding to probe().
 *
 * Board-specific knowledge like creating devices or pin setup is to be
 * kept out of drivers as much as possible.  In particular, pin setup
 * may be handled by the boot loader, and drivers should expect it will
 * normally have been done by the time they're probed.
 */
static int __init omap1_init_devices(void)
{
	if (!cpu_class_is_omap1())
		return -ENODEV;

	omap1_sram_init();
	omap1_clk_late_init();

	/* please keep these calls, and their implementations above,
	 * in alphabetical order so they're easier to sort through.
	 */

	omap_init_mbox();
	omap_init_rtc();
	omap_init_spi100k();
	omap_init_sti();
	omap_init_uwire();
	omap1_init_rng();

	return 0;
}
arch_initcall(omap1_init_devices);

#if IS_ENABLED(CONFIG_OMAP_WATCHDOG)

static struct resource wdt_resources[] = {
	{
		.start		= 0xfffeb000,
		.end		= 0xfffeb07F,
		.flags		= IORESOURCE_MEM,
	},
};

static struct platform_device omap_wdt_device = {
	.name		= "omap_wdt",
	.id		= -1,
	.num_resources	= ARRAY_SIZE(wdt_resources),
	.resource	= wdt_resources,
};

static int __init omap_init_wdt(void)
{
	struct omap_wd_timer_platform_data pdata;
	int ret;

	if (!cpu_is_omap16xx())
		return -ENODEV;

	pdata.read_reset_sources = omap1_get_reset_sources;

	ret = platform_device_register(&omap_wdt_device);
	if (!ret) {
		ret = platform_device_add_data(&omap_wdt_device, &pdata,
					       sizeof(pdata));
		if (ret)
			platform_device_del(&omap_wdt_device);
	}

	return ret;
}
subsys_initcall(omap_init_wdt);
#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP1/OMAP7xx - specific DMA driver
 *
 * Copyright (C) 2003 - 2008 Nokia Corporation
 * Author: Juha Yrjölä <juha.yrjola@nokia.com>
 * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com>
 * Graphics DMA and LCD DMA graphics tranformations
 * by Imre Deak <imre.deak@nokia.com>
 * OMAP2/3 support Copyright (C) 2004-2007 Texas Instruments, Inc.
 * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc.
 *
 * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/
 * Converted DMA library into platform driver
 *                   - G, Manjunath Kondaiah <manjugk@ti.com>
 */

#include <linux/err.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/omap-dma.h>
#include "tc.h"

#include "soc.h"

#define OMAP1_DMA_BASE			(0xfffed800)

static u32 enable_1510_mode;

static const struct omap_dma_reg reg_map[] = {
	[GCR]		= { 0x0400, 0x00, OMAP_DMA_REG_16BIT },
	[GSCR]		= { 0x0404, 0x00, OMAP_DMA_REG_16BIT },
	[GRST1]		= { 0x0408, 0x00, OMAP_DMA_REG_16BIT },
	[HW_ID]		= { 0x0442, 0x00, OMAP_DMA_REG_16BIT },
	[PCH2_ID]	= { 0x0444, 0x00, OMAP_DMA_REG_16BIT },
	[PCH0_ID]	= { 0x0446, 0x00, OMAP_DMA_REG_16BIT },
	[PCH1_ID]	= { 0x0448, 0x00, OMAP_DMA_REG_16BIT },
	[PCHG_ID]	= { 0x044a, 0x00, OMAP_DMA_REG_16BIT },
	[PCHD_ID]	= { 0x044c, 0x00, OMAP_DMA_REG_16BIT },
	[CAPS_0]	= { 0x044e, 0x00, OMAP_DMA_REG_2X16BIT },
	[CAPS_1]	= { 0x0452, 0x00, OMAP_DMA_REG_2X16BIT },
	[CAPS_2]	= { 0x0456, 0x00, OMAP_DMA_REG_16BIT },
	[CAPS_3]	= { 0x0458, 0x00, OMAP_DMA_REG_16BIT },
	[CAPS_4]	= { 0x045a, 0x00, OMAP_DMA_REG_16BIT },
	[PCH2_SR]	= { 0x0460, 0x00, OMAP_DMA_REG_16BIT },
	[PCH0_SR]	= { 0x0480, 0x00, OMAP_DMA_REG_16BIT },
	[PCH1_SR]	= { 0x0482, 0x00, OMAP_DMA_REG_16BIT },
	[PCHD_SR]	= { 0x04c0, 0x00, OMAP_DMA_REG_16BIT },

	/* Common Registers */
	[CSDP]		= { 0x0000, 0x40, OMAP_DMA_REG_16BIT },
	[CCR]		= { 0x0002, 0x40, OMAP_DMA_REG_16BIT },
	[CICR]		= { 0x0004, 0x40, OMAP_DMA_REG_16BIT },
	[CSR]		= { 0x0006, 0x40, OMAP_DMA_REG_16BIT },
	[CEN]		= { 0x0010, 0x40, OMAP_DMA_REG_16BIT },
	[CFN]		= { 0x0012, 0x40, OMAP_DMA_REG_16BIT },
	[CSFI]		= { 0x0014, 0x40, OMAP_DMA_REG_16BIT },
	[CSEI]		= { 0x0016, 0x40, OMAP_DMA_REG_16BIT },
	[CPC]		= { 0x0018, 0x40, OMAP_DMA_REG_16BIT },	/* 15xx only */
	[CSAC]		= { 0x0018, 0x40, OMAP_DMA_REG_16BIT },
	[CDAC]		= { 0x001a, 0x40, OMAP_DMA_REG_16BIT },
	[CDEI]		= { 0x001c, 0x40, OMAP_DMA_REG_16BIT },
	[CDFI]		= { 0x001e, 0x40, OMAP_DMA_REG_16BIT },
	[CLNK_CTRL]	= { 0x0028, 0x40, OMAP_DMA_REG_16BIT },

	/* Channel specific register offsets */
	[CSSA]		= { 0x0008, 0x40, OMAP_DMA_REG_2X16BIT },
	[CDSA]		= { 0x000c, 0x40, OMAP_DMA_REG_2X16BIT },
	[COLOR]		= { 0x0020, 0x40, OMAP_DMA_REG_2X16BIT },
	[CCR2]		= { 0x0024, 0x40, OMAP_DMA_REG_16BIT },
	[LCH_CTRL]	= { 0x002a, 0x40, OMAP_DMA_REG_16BIT },
};

static struct resource res[] __initdata = {
	[0] = {
		.start	= OMAP1_DMA_BASE,
		.end	= OMAP1_DMA_BASE + SZ_2K - 1,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.name   = "0",
		.start  = INT_DMA_CH0_6,
		.flags  = IORESOURCE_IRQ,
	},
	[2] = {
		.name   = "1",
		.start  = INT_DMA_CH1_7,
		.flags  = IORESOURCE_IRQ,
	},
	[3] = {
		.name   = "2",
		.start  = INT_DMA_CH2_8,
		.flags  = IORESOURCE_IRQ,
	},
	[4] = {
		.name   = "3",
		.start  = INT_DMA_CH3,
		.flags  = IORESOURCE_IRQ,
	},
	[5] = {
		.name   = "4",
		.start  = INT_DMA_CH4,
		.flags  = IORESOURCE_IRQ,
	},
	[6] = {
		.name   = "5",
		.start  = INT_DMA_CH5,
		.flags  = IORESOURCE_IRQ,
	},
	/* Handled in lcd_dma.c */
	[7] = {
		.name   = "6",
		.start  = INT_1610_DMA_CH6,
		.flags  = IORESOURCE_IRQ,
	},
	/* irq's for omap16xx and omap7xx */
	[8] = {
		.name   = "7",
		.start  = INT_1610_DMA_CH7,
		.flags  = IORESOURCE_IRQ,
	},
	[9] = {
		.name   = "8",
		.start  = INT_1610_DMA_CH8,
		.flags  = IORESOURCE_IRQ,
	},
	[10] = {
		.name  = "9",
		.start = INT_1610_DMA_CH9,
		.flags = IORESOURCE_IRQ,
	},
	[11] = {
		.name  = "10",
		.start = INT_1610_DMA_CH10,
		.flags = IORESOURCE_IRQ,
	},
	[12] = {
		.name  = "11",
		.start = INT_1610_DMA_CH11,
		.flags = IORESOURCE_IRQ,
	},
	[13] = {
		.name  = "12",
		.start = INT_1610_DMA_CH12,
		.flags = IORESOURCE_IRQ,
	},
	[14] = {
		.name  = "13",
		.start = INT_1610_DMA_CH13,
		.flags = IORESOURCE_IRQ,
	},
	[15] = {
		.name  = "14",
		.start = INT_1610_DMA_CH14,
		.flags = IORESOURCE_IRQ,
	},
	[16] = {
		.name  = "15",
		.start = INT_1610_DMA_CH15,
		.flags = IORESOURCE_IRQ,
	},
	[17] = {
		.name  = "16",
		.start = INT_DMA_LCD,
		.flags = IORESOURCE_IRQ,
	},
};

static void __iomem *dma_base;
static inline void dma_write(u32 val, int reg, int lch)
{
	void __iomem *addr = dma_base;

	addr += reg_map[reg].offset;
	addr += reg_map[reg].stride * lch;

	__raw_writew(val, addr);
	if (reg_map[reg].type == OMAP_DMA_REG_2X16BIT)
		__raw_writew(val >> 16, addr + 2);
}

static inline u32 dma_read(int reg, int lch)
{
	void __iomem *addr = dma_base;
	uint32_t val;

	addr += reg_map[reg].offset;
	addr += reg_map[reg].stride * lch;

	val = __raw_readw(addr);
	if (reg_map[reg].type == OMAP_DMA_REG_2X16BIT)
		val |= __raw_readw(addr + 2) << 16;

	return val;
}

static void omap1_clear_lch_regs(int lch)
{
	int i;

	for (i = CPC; i <= COLOR; i += 1)
		dma_write(0, i, lch);
}

static void omap1_clear_dma(int lch)
{
	u32 l;

	l = dma_read(CCR, lch);
	l &= ~OMAP_DMA_CCR_EN;
	dma_write(l, CCR, lch);

	/* Clear pending interrupts */
	l = dma_read(CSR, lch);
}

static void omap1_show_dma_caps(void)
{
	if (enable_1510_mode) {
		printk(KERN_INFO "DMA support for OMAP15xx initialized\n");
	} else {
		u16 w;
		printk(KERN_INFO "OMAP DMA hardware version %d\n",
							dma_read(HW_ID, 0));
		printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n",
			dma_read(CAPS_0, 0), dma_read(CAPS_1, 0),
			dma_read(CAPS_2, 0), dma_read(CAPS_3, 0),
			dma_read(CAPS_4, 0));

		/* Disable OMAP 3.0/3.1 compatibility mode. */
		w = dma_read(GSCR, 0);
		w |= 1 << 3;
		dma_write(w, GSCR, 0);
	}
}

static unsigned configure_dma_errata(void)
{
	unsigned errata = 0;

	/*
	 * Erratum 3.2/3.3: sometimes 0 is returned if CSAC/CDAC is
	 * read before the DMA controller finished disabling the channel.
	 */
	if (!cpu_is_omap15xx())
		SET_DMA_ERRATA(DMA_ERRATA_3_3);

	return errata;
}

static const struct platform_device_info omap_dma_dev_info = {
	.name = "omap-dma-engine",
	.id = -1,
	.dma_mask = DMA_BIT_MASK(32),
	.res = res,
	.num_res = 1,
};

/* OMAP730, OMAP850 */
static const struct dma_slave_map omap7xx_sdma_map[] = {
	{ "omap-mcbsp.1", "tx", SDMA_FILTER_PARAM(8) },
	{ "omap-mcbsp.1", "rx", SDMA_FILTER_PARAM(9) },
	{ "omap-mcbsp.2", "tx", SDMA_FILTER_PARAM(10) },
	{ "omap-mcbsp.2", "rx", SDMA_FILTER_PARAM(11) },
	{ "mmci-omap.0", "tx", SDMA_FILTER_PARAM(21) },
	{ "mmci-omap.0", "rx", SDMA_FILTER_PARAM(22) },
	{ "omap_udc", "rx0", SDMA_FILTER_PARAM(26) },
	{ "omap_udc", "rx1", SDMA_FILTER_PARAM(27) },
	{ "omap_udc", "rx2", SDMA_FILTER_PARAM(28) },
	{ "omap_udc", "tx0", SDMA_FILTER_PARAM(29) },
	{ "omap_udc", "tx1", SDMA_FILTER_PARAM(30) },
	{ "omap_udc", "tx2", SDMA_FILTER_PARAM(31) },
};

/* OMAP1510, OMAP1610*/
static const struct dma_slave_map omap1xxx_sdma_map[] = {
	{ "omap-mcbsp.1", "tx", SDMA_FILTER_PARAM(8) },
	{ "omap-mcbsp.1", "rx", SDMA_FILTER_PARAM(9) },
	{ "omap-mcbsp.3", "tx", SDMA_FILTER_PARAM(10) },
	{ "omap-mcbsp.3", "rx", SDMA_FILTER_PARAM(11) },
	{ "omap-mcbsp.2", "tx", SDMA_FILTER_PARAM(16) },
	{ "omap-mcbsp.2", "rx", SDMA_FILTER_PARAM(17) },
	{ "mmci-omap.0", "tx", SDMA_FILTER_PARAM(21) },
	{ "mmci-omap.0", "rx", SDMA_FILTER_PARAM(22) },
	{ "omap_udc", "rx0", SDMA_FILTER_PARAM(26) },
	{ "omap_udc", "rx1", SDMA_FILTER_PARAM(27) },
	{ "omap_udc", "rx2", SDMA_FILTER_PARAM(28) },
	{ "omap_udc", "tx0", SDMA_FILTER_PARAM(29) },
	{ "omap_udc", "tx1", SDMA_FILTER_PARAM(30) },
	{ "omap_udc", "tx2", SDMA_FILTER_PARAM(31) },
	{ "mmci-omap.1", "tx", SDMA_FILTER_PARAM(54) },
	{ "mmci-omap.1", "rx", SDMA_FILTER_PARAM(55) },
};

static struct omap_system_dma_plat_info dma_plat_info __initdata = {
	.reg_map	= reg_map,
	.channel_stride	= 0x40,
	.show_dma_caps	= omap1_show_dma_caps,
	.clear_lch_regs	= omap1_clear_lch_regs,
	.clear_dma	= omap1_clear_dma,
	.dma_write	= dma_write,
	.dma_read	= dma_read,
};

static int __init omap1_system_dma_init(void)
{
	struct omap_system_dma_plat_info	p;
	struct omap_dma_dev_attr		*d;
	struct platform_device			*pdev, *dma_pdev;
	int ret;

	pdev = platform_device_alloc("omap_dma_system", 0);
	if (!pdev) {
		pr_err("%s: Unable to device alloc for dma\n",
			__func__);
		return -ENOMEM;
	}

	dma_base = ioremap(res[0].start, resource_size(&res[0]));
	if (!dma_base) {
		pr_err("%s: Unable to ioremap\n", __func__);
		ret = -ENODEV;
		goto exit_device_put;
	}

	ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
	if (ret) {
		dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n",
			__func__, pdev->name, pdev->id);
		goto exit_iounmap;
	}

	d = kzalloc(sizeof(*d), GFP_KERNEL);
	if (!d) {
		ret = -ENOMEM;
		goto exit_iounmap;
	}

	/* Valid attributes for omap1 plus processors */
	if (cpu_is_omap15xx())
		d->dev_caps = ENABLE_1510_MODE;
	enable_1510_mode = d->dev_caps & ENABLE_1510_MODE;

	if (cpu_is_omap16xx())
		d->dev_caps = ENABLE_16XX_MODE;

	d->dev_caps		|= SRC_PORT;
	d->dev_caps		|= DST_PORT;
	d->dev_caps		|= SRC_INDEX;
	d->dev_caps		|= DST_INDEX;
	d->dev_caps		|= IS_BURST_ONLY4;
	d->dev_caps		|= CLEAR_CSR_ON_READ;
	d->dev_caps		|= IS_WORD_16;

	/* available logical channels */
	if (cpu_is_omap15xx()) {
		d->lch_count = 9;
	} else {
		if (d->dev_caps & ENABLE_1510_MODE)
			d->lch_count = 9;
		else
			d->lch_count = 16;
	}

	p = dma_plat_info;
	p.dma_attr = d;
	p.errata = configure_dma_errata();

	if (cpu_is_omap7xx()) {
		p.slave_map = omap7xx_sdma_map;
		p.slavecnt = ARRAY_SIZE(omap7xx_sdma_map);
	} else {
		p.slave_map = omap1xxx_sdma_map;
		p.slavecnt = ARRAY_SIZE(omap1xxx_sdma_map);
	}

	ret = platform_device_add_data(pdev, &p, sizeof(p));
	if (ret) {
		dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n",
			__func__, pdev->name, pdev->id);
		goto exit_release_d;
	}

	ret = platform_device_add(pdev);
	if (ret) {
		dev_err(&pdev->dev, "%s: Unable to add resources for %s%d\n",
			__func__, pdev->name, pdev->id);
		goto exit_release_d;
	}

	dma_pdev = platform_device_register_full(&omap_dma_dev_info);
	if (IS_ERR(dma_pdev)) {
		ret = PTR_ERR(dma_pdev);
		goto exit_release_pdev;
	}

	return ret;

exit_release_pdev:
	platform_device_del(pdev);
exit_release_d:
	kfree(d);
exit_iounmap:
	iounmap(dma_base);
exit_device_put:
	platform_device_put(pdev);

	return ret;
}
arch_initcall(omap1_system_dma_init);


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * File: arch/arm/plat-omap/fb.c
 *
 * Framebuffer device registration for TI OMAP platforms
 *
 * Copyright (C) 2006 Nokia Corporation
 * Author: Imre Deak <imre.deak@nokia.com>
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/memblock.h>
#include <linux/io.h>
#include <linux/omapfb.h>
#include <linux/dma-mapping.h>
#include <linux/irq.h>

#include <asm/mach/map.h>

#include "irqs.h"

#if IS_ENABLED(CONFIG_FB_OMAP)

static bool omapfb_lcd_configured;
static struct omapfb_platform_data omapfb_config;

static u64 omap_fb_dma_mask = ~(u32)0;

static struct resource omap_fb_resources[] = {
	{
		.name  = "irq",
		.start = INT_LCD_CTRL,
		.flags = IORESOURCE_IRQ,
	},
	{
		.name  = "irq",
		.start = INT_SOSSI_MATCH,
		.flags = IORESOURCE_IRQ,
	},
};

static struct platform_device omap_fb_device = {
	.name		= "omapfb",
	.id		= -1,
	.dev = {
		.dma_mask		= &omap_fb_dma_mask,
		.coherent_dma_mask	= DMA_BIT_MASK(32),
		.platform_data		= &omapfb_config,
	},
	.num_resources = ARRAY_SIZE(omap_fb_resources),
	.resource = omap_fb_resources,
};

void __init omapfb_set_lcd_config(const struct omap_lcd_config *config)
{
	omapfb_config.lcd = *config;
	omapfb_lcd_configured = true;
}

static int __init omap_init_fb(void)
{
	/*
	 * If the board file has not set the lcd config with
	 * omapfb_set_lcd_config(), don't bother registering the omapfb device
	 */
	if (!omapfb_lcd_configured)
		return 0;

	return platform_device_register(&omap_fb_device);
}

arch_initcall(omap_init_fb);

#else

void __init omapfb_set_lcd_config(const struct omap_lcd_config *config)
{
}

#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Flash support for OMAP1
 */

#include <linux/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/soc/ti/omap1-io.h>

#include "tc.h"

#include "flash.h"

void omap1_set_vpp(struct platform_device *pdev, int enable)
{
	u32 l;

	l = omap_readl(EMIFS_CONFIG);
	if (enable)
		l |= OMAP_EMIFS_CONFIG_WP;
	else
		l &= ~OMAP_EMIFS_CONFIG_WP;
	omap_writel(l, EMIFS_CONFIG);
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Flash support for OMAP1
 */

#ifndef __OMAP_FLASH_H
#define __OMAP_FLASH_H

#include <linux/mtd/map.h>

struct platform_device;
extern void omap1_set_vpp(struct platform_device *pdev, int enable);

#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/arch/arm/mach-omap1/fpga.c
 *
 * Interrupt handler for OMAP-1510 Innovator FPGA
 *
 * Copyright (C) 2001 RidgeRun, Inc.
 * Author: Greg Lonnon <glonnon@ridgerun.com>
 *
 * Copyright (C) 2002 MontaVista Software, Inc.
 *
 * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6
 * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen <tony@atomide.com>
 */

#include <linux/types.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/io.h>

#include <asm/irq.h>
#include <asm/mach/irq.h>

#include "hardware.h"
#include "iomap.h"
#include "common.h"
#include "fpga.h"

static void fpga_mask_irq(struct irq_data *d)
{
	unsigned int irq = d->irq - OMAP_FPGA_IRQ_BASE;

	if (irq < 8)
		__raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO)
			      & ~(1 << irq)), OMAP1510_FPGA_IMR_LO);
	else if (irq < 16)
		__raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI)
			      & ~(1 << (irq - 8))), OMAP1510_FPGA_IMR_HI);
	else
		__raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2)
			      & ~(1 << (irq - 16))), INNOVATOR_FPGA_IMR2);
}

static inline u32 get_fpga_unmasked_irqs(void)
{
	return
		((__raw_readb(OMAP1510_FPGA_ISR_LO) &
		  __raw_readb(OMAP1510_FPGA_IMR_LO))) |
		((__raw_readb(OMAP1510_FPGA_ISR_HI) &
		  __raw_readb(OMAP1510_FPGA_IMR_HI)) << 8) |
		((__raw_readb(INNOVATOR_FPGA_ISR2) &
		  __raw_readb(INNOVATOR_FPGA_IMR2)) << 16);
}

static void fpga_ack_irq(struct irq_data *d)
{
	/* Don't need to explicitly ACK FPGA interrupts */
}

static void fpga_unmask_irq(struct irq_data *d)
{
	unsigned int irq = d->irq - OMAP_FPGA_IRQ_BASE;

	if (irq < 8)
		__raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_LO) | (1 << irq)),
		     OMAP1510_FPGA_IMR_LO);
	else if (irq < 16)
		__raw_writeb((__raw_readb(OMAP1510_FPGA_IMR_HI)
			      | (1 << (irq - 8))), OMAP1510_FPGA_IMR_HI);
	else
		__raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2)
			      | (1 << (irq - 16))), INNOVATOR_FPGA_IMR2);
}

static void fpga_mask_ack_irq(struct irq_data *d)
{
	fpga_mask_irq(d);
	fpga_ack_irq(d);
}

static void innovator_fpga_IRQ_demux(struct irq_desc *desc)
{
	u32 stat;
	int fpga_irq;

	stat = get_fpga_unmasked_irqs();

	if (!stat)
		return;

	for (fpga_irq = OMAP_FPGA_IRQ_BASE;
	     (fpga_irq < OMAP_FPGA_IRQ_END) && stat;
	     fpga_irq++, stat >>= 1) {
		if (stat & 1) {
			generic_handle_irq(fpga_irq);
		}
	}
}

static struct irq_chip omap_fpga_irq_ack = {
	.name		= "FPGA-ack",
	.irq_ack	= fpga_mask_ack_irq,
	.irq_mask	= fpga_mask_irq,
	.irq_unmask	= fpga_unmask_irq,
};

static struct irq_chip omap_fpga_irq = {
	.name		= "FPGA",
	.irq_ack	= fpga_ack_irq,
	.irq_mask	= fpga_mask_irq,
	.irq_unmask	= fpga_unmask_irq,
};

/*
 * All of the FPGA interrupt request inputs except for the touchscreen are
 * edge-sensitive; the touchscreen is level-sensitive.  The edge-sensitive
 * interrupts are acknowledged as a side-effect of reading the interrupt
 * status register from the FPGA.  The edge-sensitive interrupt inputs
 * cause a problem with level interrupt requests, such as Ethernet.  The
 * problem occurs when a level interrupt request is asserted while its
 * interrupt input is masked in the FPGA, which results in a missed
 * interrupt.
 *
 * In an attempt to workaround the problem with missed interrupts, the
 * mask_ack routine for all of the FPGA interrupts has been changed from
 * fpga_mask_ack_irq() to fpga_ack_irq() so that the specific FPGA interrupt
 * being serviced is left unmasked.  We can do this because the FPGA cascade
 * interrupt is run with all interrupts masked.
 *
 * Limited testing indicates that this workaround appears to be effective
 * for the smc9194 Ethernet driver used on the Innovator.  It should work
 * on other FPGA interrupts as well, but any drivers that explicitly mask
 * interrupts at the interrupt controller via disable_irq/enable_irq
 * could pose a problem.
 */
void omap1510_fpga_init_irq(void)
{
	int i, res;

	__raw_writeb(0, OMAP1510_FPGA_IMR_LO);
	__raw_writeb(0, OMAP1510_FPGA_IMR_HI);
	__raw_writeb(0, INNOVATOR_FPGA_IMR2);

	for (i = OMAP_FPGA_IRQ_BASE; i < OMAP_FPGA_IRQ_END; i++) {

		if (i == OMAP1510_INT_FPGA_TS) {
			/*
			 * The touchscreen interrupt is level-sensitive, so
			 * we'll use the regular mask_ack routine for it.
			 */
			irq_set_chip(i, &omap_fpga_irq_ack);
		}
		else {
			/*
			 * All FPGA interrupts except the touchscreen are
			 * edge-sensitive, so we won't mask them.
			 */
			irq_set_chip(i, &omap_fpga_irq);
		}

		irq_set_handler(i, handle_edge_irq);
		irq_clear_status_flags(i, IRQ_NOREQUEST);
	}

	/*
	 * The FPGA interrupt line is connected to GPIO13. Claim this pin for
	 * the ARM.
	 *
	 * NOTE: For general GPIO/MPUIO access and interrupts, please see
	 * gpio.[ch]
	 */
	res = gpio_request(13, "FPGA irq");
	if (res) {
		pr_err("%s failed to get gpio\n", __func__);
		return;
	}
	gpio_direction_input(13);
	irq_set_irq_type(gpio_to_irq(13), IRQ_TYPE_EDGE_RISING);
	irq_set_chained_handler(OMAP1510_INT_FPGA, innovator_fpga_IRQ_demux);
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Interrupt handler for OMAP-1510 FPGA
 *
 * Copyright (C) 2001 RidgeRun, Inc.
 * Author: Greg Lonnon <glonnon@ridgerun.com>
 *
 * Copyright (C) 2002 MontaVista Software, Inc.
 *
 * Separated FPGA interrupts from innovator1510.c and cleaned up for 2.6
 * Copyright (C) 2004 Nokia Corporation by Tony Lindrgen <tony@atomide.com>
 */

#ifndef __ASM_ARCH_OMAP_FPGA_H
#define __ASM_ARCH_OMAP_FPGA_H

/*
 * ---------------------------------------------------------------------------
 *  H2/P2 Debug board FPGA
 * ---------------------------------------------------------------------------
 */
/* maps in the FPGA registers and the ETHR registers */
#define H2P2_DBG_FPGA_BASE		0xE8000000		/* VA */
#define H2P2_DBG_FPGA_SIZE		SZ_4K			/* SIZE */
#define H2P2_DBG_FPGA_START		0x04000000		/* PA */

#define H2P2_DBG_FPGA_ETHR_START	(H2P2_DBG_FPGA_START + 0x300)
#define H2P2_DBG_FPGA_FPGA_REV		IOMEM(H2P2_DBG_FPGA_BASE + 0x10)	/* FPGA Revision */
#define H2P2_DBG_FPGA_BOARD_REV		IOMEM(H2P2_DBG_FPGA_BASE + 0x12)	/* Board Revision */
#define H2P2_DBG_FPGA_GPIO		IOMEM(H2P2_DBG_FPGA_BASE + 0x14)	/* GPIO outputs */
#define H2P2_DBG_FPGA_LEDS		IOMEM(H2P2_DBG_FPGA_BASE + 0x16)	/* LEDs outputs */
#define H2P2_DBG_FPGA_MISC_INPUTS	IOMEM(H2P2_DBG_FPGA_BASE + 0x18)	/* Misc inputs */
#define H2P2_DBG_FPGA_LAN_STATUS	IOMEM(H2P2_DBG_FPGA_BASE + 0x1A)	/* LAN Status line */
#define H2P2_DBG_FPGA_LAN_RESET		IOMEM(H2P2_DBG_FPGA_BASE + 0x1C)	/* LAN Reset line */

/* LEDs definition on debug board (16 LEDs, all physically green) */
#define H2P2_DBG_FPGA_LED_GREEN		(1 << 15)
#define H2P2_DBG_FPGA_LED_AMBER		(1 << 14)
#define H2P2_DBG_FPGA_LED_RED		(1 << 13)
#define H2P2_DBG_FPGA_LED_BLUE		(1 << 12)
/*  cpu0 load-meter LEDs */
#define H2P2_DBG_FPGA_LOAD_METER	(1 << 0)	// A bit of fun on our board ...
#define H2P2_DBG_FPGA_LOAD_METER_SIZE	11
#define H2P2_DBG_FPGA_LOAD_METER_MASK	((1 << H2P2_DBG_FPGA_LOAD_METER_SIZE) - 1)

#define H2P2_DBG_FPGA_P2_LED_TIMER		(1 << 0)
#define H2P2_DBG_FPGA_P2_LED_IDLE		(1 << 1)

#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP15xx specific gpio init
 *
 * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/
 *
 * Author:
 *	Charulatha V <charu@ti.com>
 */

#include <linux/gpio.h>
#include <linux/platform_data/gpio-omap.h>
#include <linux/soc/ti/omap1-soc.h>

#include "irqs.h"

#define OMAP1_MPUIO_VBASE		OMAP1_MPUIO_BASE
#define OMAP1510_GPIO_BASE		0xFFFCE000

/* gpio1 */
static struct resource omap15xx_mpu_gpio_resources[] = {
	{
		.start	= OMAP1_MPUIO_VBASE,
		.end	= OMAP1_MPUIO_VBASE + SZ_2K - 1,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= INT_MPUIO,
		.flags	= IORESOURCE_IRQ,
	},
};

static struct omap_gpio_reg_offs omap15xx_mpuio_regs = {
	.revision       = USHRT_MAX,
	.direction	= OMAP_MPUIO_IO_CNTL,
	.datain		= OMAP_MPUIO_INPUT_LATCH,
	.dataout	= OMAP_MPUIO_OUTPUT,
	.irqstatus	= OMAP_MPUIO_GPIO_INT,
	.irqenable	= OMAP_MPUIO_GPIO_MASKIT,
	.irqenable_inv	= true,
	.irqctrl	= OMAP_MPUIO_GPIO_INT_EDGE,
};

static struct omap_gpio_platform_data omap15xx_mpu_gpio_config = {
	.is_mpuio		= true,
	.bank_width		= 16,
	.bank_stride		= 1,
	.regs			= &omap15xx_mpuio_regs,
};

static struct platform_device omap15xx_mpu_gpio = {
	.name           = "omap_gpio",
	.id             = 0,
	.dev            = {
		.platform_data = &omap15xx_mpu_gpio_config,
	},
	.num_resources = ARRAY_SIZE(omap15xx_mpu_gpio_resources),
	.resource = omap15xx_mpu_gpio_resources,
};

/* gpio2 */
static struct resource omap15xx_gpio_resources[] = {
	{
		.start	= OMAP1510_GPIO_BASE,
		.end	= OMAP1510_GPIO_BASE + SZ_2K - 1,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= INT_GPIO_BANK1,
		.flags	= IORESOURCE_IRQ,
	},
};

static struct omap_gpio_reg_offs omap15xx_gpio_regs = {
	.revision	= USHRT_MAX,
	.direction	= OMAP1510_GPIO_DIR_CONTROL,
	.datain		= OMAP1510_GPIO_DATA_INPUT,
	.dataout	= OMAP1510_GPIO_DATA_OUTPUT,
	.irqstatus	= OMAP1510_GPIO_INT_STATUS,
	.irqenable	= OMAP1510_GPIO_INT_MASK,
	.irqenable_inv	= true,
	.irqctrl	= OMAP1510_GPIO_INT_CONTROL,
	.pinctrl	= OMAP1510_GPIO_PIN_CONTROL,
};

static struct omap_gpio_platform_data omap15xx_gpio_config = {
	.bank_width		= 16,
	.regs                   = &omap15xx_gpio_regs,
};

static struct platform_device omap15xx_gpio = {
	.name           = "omap_gpio",
	.id             = 1,
	.dev            = {
		.platform_data = &omap15xx_gpio_config,
	},
	.num_resources = ARRAY_SIZE(omap15xx_gpio_resources),
	.resource = omap15xx_gpio_resources,
};

/*
 * omap15xx_gpio_init needs to be done before
 * machine_init functions access gpio APIs.
 * Hence omap15xx_gpio_init is a postcore_initcall.
 */
static int __init omap15xx_gpio_init(void)
{
	if (!cpu_is_omap15xx())
		return -EINVAL;

	platform_device_register(&omap15xx_mpu_gpio);
	platform_device_register(&omap15xx_gpio);

	return 0;
}
postcore_initcall(omap15xx_gpio_init);


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * OMAP16xx specific gpio init
 *
 * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/
 *
 * Author:
 *	Charulatha V <charu@ti.com>
 */

#include <linux/gpio.h>
#include <linux/platform_data/gpio-omap.h>
#include <linux/soc/ti/omap1-io.h>

#include "hardware.h"
#include "irqs.h"
#include "soc.h"

#define OMAP1610_GPIO1_BASE		0xfffbe400
#define OMAP1610_GPIO2_BASE		0xfffbec00
#define OMAP1610_GPIO3_BASE		0xfffbb400
#define OMAP1610_GPIO4_BASE		0xfffbbc00
#define OMAP1_MPUIO_VBASE		OMAP1_MPUIO_BASE

/* smart idle, enable wakeup */
#define SYSCONFIG_WORD			0x14

/* mpu gpio */
static struct resource omap16xx_mpu_gpio_resources[] = {
	{
		.start	= OMAP1_MPUIO_VBASE,
		.end	= OMAP1_MPUIO_VBASE + SZ_2K - 1,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= INT_MPUIO,
		.flags	= IORESOURCE_IRQ,
	},
};

static struct omap_gpio_reg_offs omap16xx_mpuio_regs = {
	.revision       = USHRT_MAX,
	.direction	= OMAP_MPUIO_IO_CNTL,
	.datain		= OMAP_MPUIO_INPUT_LATCH,
	.dataout	= OMAP_MPUIO_OUTPUT,
	.irqstatus	= OMAP_MPUIO_GPIO_INT,
	.irqenable	= OMAP_MPUIO_GPIO_MASKIT,
	.irqenable_inv	= true,
	.irqctrl	= OMAP_MPUIO_GPIO_INT_EDGE,
};

static struct omap_gpio_platform_data omap16xx_mpu_gpio_config = {
	.is_mpuio		= true,
	.bank_width		= 16,
	.bank_stride		= 1,
	.regs                   = &omap16xx_mpuio_regs,
};

static struct platform_device omap16xx_mpu_gpio = {
	.name           = "omap_gpio",
	.id             = 0,
	.dev            = {
		.platform_data = &omap16xx_mpu_gpio_config,
	},
	.num_resources = ARRAY_SIZE(omap16xx_mpu_gpio_resources),
	.resource = omap16xx_mpu_gpio_resources,
};

/* gpio1 */
static struct resource omap16xx_gpio1_resources[] = {
	{
		.start	= OMAP1610_GPIO1_BASE,
		.end	= OMAP1610_GPIO1_BASE + SZ_2K - 1,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= INT_GPIO_BANK1,
		.flags	= IORESOURCE_IRQ,
	},
};

static struct omap_gpio_reg_offs omap16xx_gpio_regs = {
	.revision       = OMAP1610_GPIO_REVISION,
	.direction	= OMAP1610_GPIO_DIRECTION,
	.set_dataout	= OMAP1610_GPIO_SET_DATAOUT,
	.clr_dataout	= OMAP1610_GPIO_CLEAR_DATAOUT,
	.datain		= OMAP1610_GPIO_DATAIN,
	.dataout	= OMAP1610_GPIO_DATAOUT,
	.irqstatus	= OMAP1610_GPIO_IRQSTATUS1,
	.irqenable	= OMAP1610_GPIO_IRQENABLE1,
	.set_irqenable	= OMAP1610_GPIO_SET_IRQENABLE1,
	.clr_irqenable	= OMAP1610_GPIO_CLEAR_IRQENABLE1,
	.wkup_en	= OMAP1610_GPIO_WAKEUPENABLE,
	.edgectrl1	= OMAP1610_GPIO_EDGE_CTRL1,
	.edgectrl2	= OMAP1610_GPIO_EDGE_CTRL2,
};

static struct omap_gpio_platform_data omap16xx_gpio1_config = {
	.bank_width		= 16,
	.regs                   = &omap16xx_gpio_regs,
};

static struct platform_device omap16xx_gpio1 = {
	.name           = "omap_gpio",
	.id             = 1,
	.dev            = {
		.platform_data = &omap16xx_gpio1_config,
	},
	.num_resources = ARRAY_SIZE(omap16xx_gpio1_resources),
	.resource = omap16xx_gpio1_resources,
};

/* gpio2 */
static struct resource omap16xx_gpio2_resources[] = {
	{
		.start	= OMAP1610_GPIO2_BASE,
		.end	= OMAP1610_GPIO2_BASE + SZ_2K - 1,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= INT_1610_GPIO_BANK2,
		.flags	= IORESOURCE_IRQ,
	},
};

static struct omap_gpio_platform_data omap16xx_gpio2_config = {
	.bank_width		= 16,
	.regs                   = &omap16xx_gpio_regs,
};

static struct platform_device omap16xx_gpio2 = {
	.name           = "omap_gpio",
	.id             = 2,
	.dev            = {
		.platform_data = &omap16xx_gpio2_config,
	},
	.num_resources = ARRAY_SIZE(omap16xx_gpio2_resources),
	.resource = omap16xx_gpio2_resources,
};

/* gpio3 */
static struct resource omap16xx_gpio3_resources[] = {
	{
		.start	= OMAP1610_GPIO3_BASE,
		.end	= OMAP1610_GPIO3_BASE + SZ_2K - 1,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= INT_1610_GPIO_BANK3,
		.flags	= IORESOURCE_IRQ,
	},
};

static struct omap_gpio_platform_data omap16xx_gpio3_config = {
	.bank_width		= 16,
	.regs                   = &omap16xx_gpio_regs,
};

static struct platform_device omap16xx_gpio3 = {
	.name           = "omap_gpio",
	.id             = 3,
	.dev            = {
		.platform_data = &omap16xx_gpio3_config,
	},
	.num_resources = ARRAY_SIZE(omap16xx_gpio3_resources),
	.resource = omap16xx_gpio3_resources,
};

/* gpio4 */
static struct resource omap16xx_gpio4_resources[] = {
	{
		.start	= OMAP1610_GPIO4_BASE,
		.end	= OMAP1610_GPIO4_BASE + SZ_2K - 1,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= INT_1610_GPIO_BANK4,
		.flags	= IORESOURCE_IRQ,
	},
};

static struct omap_gpio_platform_data omap16xx_gpio4_config = {
	.bank_width		= 16,
	.regs                   = &omap16xx_gpio_regs,
};

static struct platform_device omap16xx_gpio4 = {
	.name           = "omap_gpio",
	.id             = 4,
	.dev            = {
		.platform_data = &omap16xx_gpio4_config,
	},
	.num_resources = ARRAY_SIZE(omap16xx_gpio4_resources),
	.resource = omap16xx_gpio4_resources,
};

static struct platform_device *omap16xx_gpio_dev[] __initdata = {
	&omap16xx_mpu_gpio,
	&omap16xx_gpio1,
	&omap16xx_gpio2,
	&omap16xx_gpio3,
	&omap16xx_gpio4,
};

/*
 * omap16xx_gpio_init needs to be done before
 * machine_init functions access gpio APIs.
 * Hence omap16xx_gpio_init is a postcore_initcall.
 */
static int __init omap16xx_gpio_init(void)
{
	int i;
	void __iomem *base;
	struct resource *res;
	struct platform_device *pdev;
	struct omap_gpio_platform_data *pdata;

	if (!cpu_is_omap16xx())
		return -EINVAL;

	/*
	 * Enable system clock for GPIO module.
	 * The CAM_CLK_CTRL *is* really the right place.
	 */
	omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04,
					ULPD_CAM_CLK_CTRL);

	for (i = 0; i < ARRAY_SIZE(omap16xx_gpio_dev); i++) {
		pdev = omap16xx_gpio_dev[i];
		pdata = pdev->dev.platform_data;

		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
		if (unlikely(!res)) {
			dev_err(&pdev->dev, "Invalid mem resource.\n");
			return -ENODEV;
		}

		base = ioremap(res->start, resource_size(res));
		if (unlikely(!base)) {
			dev_err(&pdev->dev, "ioremap failed.\n");
			return -ENOMEM;
		}

		__raw_writel(SYSCONFIG_WORD, base + OMAP1610_GPIO_SYSCONFIG);
		iounmap(base);

		platform_device_register(omap16xx_gpio_dev[i]);
	}

	return 0;
}
postcore_initcall(omap16xx_gpio_init);