Commit e96c4c63 authored by Tony Lindgren's avatar Tony Lindgren Committed by Russell King

[ARM PATCH] 1781/1: Add TI OMAP support, arch files

Patch from Tony Lindgren

This patch adds the arch files for Texas Instruments OMAP-1510 and 
1610 processors. 

OMAP is an embedded ARM processor with integrated DSP.

OMAP-1610 has hardware support for USB OTG, which might be of interest
to Linux developers. OMAP-1610 could be easily be used as development 
platform to add USB OTG support to Linux.

This patch is an updated version of patch 1769/1 with Russell King's
comments fixed. This patch requires patch 1777/1 applied.

This patch is brought to you by various linux-omap developers.
parent ca927387
#
# Placeholder for OMAP support menu "TI OMAP Implementations"
#
choice
prompt "OMAP Core Type"
depends on ARCH_OMAP
default ARCH_OMAP1510
config ARCH_OMAP1510
bool "OMAP-1510 Based System"
select CPU_ARM925T
select CPU_DCACHE_WRITETHROUGH
config ARCH_OMAP1610
bool "OMAP-1610 Based System"
select CPU_ARM926T
endchoice
choice
prompt "OMAP Board Type"
depends on ARCH_OMAP
default MACH_OMAP_INNOVATOR
config MACH_OMAP_INNOVATOR
bool "TI Innovator"
depends on ARCH_OMAP1510 || ARCH_OMAP1610
help
TI OMAP 1510 or 1610 Innovator board support. Say Y here if you
have such a board.
config MACH_OMAP_GENERIC
bool "Generic OMAP board"
depends on ARCH_OMAP1510 || ARCH_OMAP1610
help
Support for generic OMAP-1510 or 1610 board with no
FPGA. Can be used as template for porting Linux to
custom OMAP boards. Say Y here if you have a custom
board.
endchoice
comment "OMAP Feature Selections"
config MACH_OMAP_H2
bool "TI H2 Support"
depends on ARCH_OMAP1610 && MACH_OMAP_INNOVATOR
help
TI OMAP 1610 H2 board support. Say Y here if you have such
a board.
config OMAP_MUX
bool "OMAP multiplexing support"
depends on ARCH_OMAP
default y
help
Pin multiplexing support for OMAP boards. If your bootloader
sets the multiplexing correctly, say N. Otherwise, or if unsure,
say Y.
config OMAP_MUX_DEBUG
bool "Multiplexing debug output"
depends on OMAP_MUX
default n
help
Makes the multiplexing functions print out a lot of debug info.
This is useful if you want to find out the correct values of the
multiplexing registers.
config OMAP_ARM_195MHZ
bool "OMAP ARM 195 MHz CPU"
depends on ARCH_OMAP730
help
Enable 195MHz clock for OMAP CPU. If unsure, say N.
config OMAP_ARM_192MHZ
bool "OMAP ARM 192 MHz CPU"
depends on ARCH_OMAP1610
help
Enable 192MHz clock for OMAP CPU. If unsure, say N.
config OMAP_ARM_182MHZ
bool "OMAP ARM 182 MHz CPU"
depends on ARCH_OMAP730
help
Enable 182MHz clock for OMAP CPU. If unsure, say N.
config OMAP_ARM_168MHZ
bool "OMAP ARM 168 MHz CPU"
depends on ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_OMAP730
help
Enable 168MHz clock for OMAP CPU. If unsure, say N.
config OMAP_ARM_120MHZ
bool "OMAP ARM 120 MHz CPU"
depends on ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_OMAP730
help
Enable 120MHz clock for OMAP CPU. If unsure, say N.
config OMAP_ARM_60MHZ
bool "OMAP ARM 60 MHz CPU"
depends on ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_OMAP730
default y
help
Enable 60MHz clock for OMAP CPU. If unsure, say Y.
config OMAP_ARM_30MHZ
bool "OMAP ARM 30 MHz CPU"
depends on ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_OMAP730
help
Enable 30MHz clock for OMAP CPU. If unsure, say N.
endmenu
#
# Makefile for the linux kernel.
#
# Common support
obj-y := common.o irq.o dma.o clocks.o mux.o bus.o gpio.o
obj-m :=
obj-n :=
obj- :=
led-y := leds.o
# OCPI interconnect support for 1610
ifeq ($(CONFIG_ARCH_OMAP1610),y)
obj-y += ocpi.o
ifeq ($(CONFIG_OMAP_INNOVATOR),y)
obj-y += innovator1610.o
endif
endif
ifeq ($(CONFIG_ARCH_OMAP1510),y)
ifeq ($(CONFIG_OMAP_INNOVATOR),y)
obj-y += innovator1510.o fpga.o
endif
endif
# Specific board support
obj-$(CONFIG_MACH_OMAP_GENERIC) += omap-generic.o
obj-$(CONFIG_MACH_OMAP_PERSEUS2) += omap-perseus2.o
# LEDs support
led-$(CONFIG_OMAP_INNOVATOR) += leds-innovator.o
led-$(CONFIG_MACH_OMAP_PERSEUS2) += leds-perseus2.o
obj-$(CONFIG_LEDS) += $(led-y)
# kgdb support
obj-$(CONFIG_KGDB_SERIAL) += kgdb-serial.o
/*
* linux/arch/arm/mach-omap/bus.c
*
* Virtual bus for OMAP. Allows better power management, such as managing
* shared clocks, and mapping of bus addresses to Local Bus addresses.
*
* See drivers/usb/host/ohci-omap.c or drivers/video/omap/omapfb.c for
* examples on how to register drivers to this bus.
*
* Copyright (C) 2003 - 2004 Nokia Corporation
* Written by Tony Lindgren <tony@atomide.com>
* Portions of code based on sa1111.c.
*
* 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 program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ptrace.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/arch/bus.h>
static int omap_bus_match(struct device *_dev, struct device_driver *_drv);
static int omap_bus_suspend(struct device *dev, u32 state);
static int omap_bus_resume(struct device *dev);
/*
* OMAP bus definitions
*
* NOTE: Most devices should use TIPB. LBUS does automatic address mapping
* to Local Bus addresses, and should only be used for Local Bus devices.
* We may add new buses later on for power management reasons. Basically
* we want to be able to turn off any bus if it's not used by device
* drivers.
*/
static struct device omap_bus_devices[OMAP_NR_BUSES] = {
{
.bus_id = OMAP_BUS_NAME_TIPB
}, {
.bus_id = OMAP_BUS_NAME_LBUS
},
};
static struct bus_type omap_bus_types[OMAP_NR_BUSES] = {
{
.name = OMAP_BUS_NAME_TIPB,
.match = omap_bus_match,
.suspend = omap_bus_suspend,
.resume = omap_bus_resume,
}, {
.name = OMAP_BUS_NAME_LBUS, /* Local bus on 1510 */
.match = omap_bus_match,
.suspend = omap_bus_suspend,
.resume = omap_bus_resume,
},
};
#ifdef CONFIG_ARCH_OMAP1510
/*
* NOTE: This code _should_ go somewhere else. But let's wait for the
* dma-mapping code to settle down first.
*/
/*
* Test for Local Bus device in order to do address translation between
* dma_handle and Local Bus address.
*/
inline int dmadev_uses_omap_lbus(struct device * dev)
{
return dev->bus == &omap_bus_types[OMAP_BUS_LBUS] ? 1 : 0;
}
/*
* Translate bus address to Local Bus address for dma-mapping
*/
inline int dmadev_to_lbus(dma_addr_t addr)
{
return bus_to_lbus(addr);
}
/*
* Translate Local Bus address to bus address for dma-mapping
*/
inline int lbus_to_dmadev(dma_addr_t addr)
{
return lbus_to_bus(addr);
}
#endif
static int omap_bus_match(struct device *dev, struct device_driver *drv)
{
struct omap_dev *omapdev = OMAP_DEV(dev);
struct omap_driver *omapdrv = OMAP_DRV(drv);
return omapdev->devid == omapdrv->devid;
}
static int omap_bus_suspend(struct device *dev, u32 state)
{
struct omap_dev *omapdev = OMAP_DEV(dev);
struct omap_driver *omapdrv = OMAP_DRV(dev->driver);
int ret = 0;
if (omapdrv && omapdrv->suspend)
ret = omapdrv->suspend(omapdev, state);
return ret;
}
static int omap_bus_resume(struct device *dev)
{
struct omap_dev *omapdev = OMAP_DEV(dev);
struct omap_driver *omapdrv = OMAP_DRV(dev->driver);
int ret = 0;
if (omapdrv && omapdrv->resume)
ret = omapdrv->resume(omapdev);
return ret;
}
static int omap_device_probe(struct device *dev)
{
struct omap_dev *omapdev = OMAP_DEV(dev);
struct omap_driver *omapdrv = OMAP_DRV(dev->driver);
int ret = -ENODEV;
if (omapdrv && omapdrv->probe)
ret = omapdrv->probe(omapdev);
return ret;
}
static int omap_device_remove(struct device *dev)
{
struct omap_dev *omapdev = OMAP_DEV(dev);
struct omap_driver *omapdrv = OMAP_DRV(dev->driver);
int ret = 0;
if (omapdrv && omapdrv->remove)
ret = omapdrv->remove(omapdev);
return ret;
}
int omap_device_register(struct omap_dev *odev)
{
if (!odev)
return -EINVAL;
if (odev->busid < 0 || odev->busid >= OMAP_NR_BUSES) {
printk(KERN_ERR "%s: busid invalid: %s: bus: %i\n",
__FUNCTION__, odev->name, odev->busid);
return -EINVAL;
}
odev->dev.parent = &omap_bus_devices[odev->busid];
odev->dev.bus = &omap_bus_types[odev->busid];
/* This is needed for USB OHCI to work */
if (odev->dma_mask)
odev->dev.dma_mask = odev->dma_mask;
snprintf(odev->dev.bus_id, BUS_ID_SIZE, "%s%u",
odev->name, odev->devid);
printk("Registering OMAP device '%s'. Parent at %s\n",
odev->dev.bus_id, odev->dev.parent->bus_id);
return device_register(&odev->dev);
}
void omap_device_unregister(struct omap_dev *odev)
{
if (odev)
device_unregister(&odev->dev);
}
int omap_driver_register(struct omap_driver *driver)
{
int ret;
if (driver->busid < 0 || driver->busid >= OMAP_NR_BUSES) {
printk(KERN_ERR "%s: busid invalid: bus: %i device: %i\n",
__FUNCTION__, driver->busid, driver->devid);
return -EINVAL;
}
driver->drv.probe = omap_device_probe;
driver->drv.remove = omap_device_remove;
driver->drv.bus = &omap_bus_types[driver->busid];
/*
* driver_register calls bus_add_driver
*/
ret = driver_register(&driver->drv);
return ret;
}
void omap_driver_unregister(struct omap_driver *driver)
{
driver_unregister(&driver->drv);
}
static int __init omap_bus_init(void)
{
int i, ret;
/* Initialize all OMAP virtual buses */
for (i = 0; i < OMAP_NR_BUSES; i++) {
ret = device_register(&omap_bus_devices[i]);
if (ret != 0) {
printk(KERN_ERR "Unable to register bus device %s\n",
omap_bus_devices[i].bus_id);
continue;
}
ret = bus_register(&omap_bus_types[i]);
if (ret != 0) {
printk(KERN_ERR "Unable to register bus %s\n",
omap_bus_types[i].name);
device_unregister(&omap_bus_devices[i]);
}
}
printk("OMAP virtual buses initialized\n");
return ret;
}
static void __exit omap_bus_exit(void)
{
int i;
/* Unregister all OMAP virtual buses */
for (i = 0; i < OMAP_NR_BUSES; i++) {
bus_unregister(&omap_bus_types[i]);
device_unregister(&omap_bus_devices[i]);
}
}
module_init(omap_bus_init);
module_exit(omap_bus_exit);
MODULE_DESCRIPTION("Virtual bus for OMAP");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(omap_bus_types);
EXPORT_SYMBOL(omap_driver_register);
EXPORT_SYMBOL(omap_driver_unregister);
EXPORT_SYMBOL(omap_device_register);
EXPORT_SYMBOL(omap_device_unregister);
#ifdef CONFIG_ARCH_OMAP1510
EXPORT_SYMBOL(dmadev_uses_omap_lbus);
EXPORT_SYMBOL(dmadev_to_lbus);
EXPORT_SYMBOL(lbus_to_dmadev);
#endif
/*
* Clock interface for OMAP
*
* Copyright (C) 2001 RidgeRun, Inc
* Written by Gordon McNutt <gmcnutt@ridgerun.com>
* Updated 2004 for Linux 2.6 by Tony Lindgren <tony@atomide.com>
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/arch/clocks.h>
/* Input clock in MHz */
static unsigned int source_clock = 12;
/*
* We use one spinlock for all clock registers for now. We may want to
* change this to be clock register specific later on. Before we can do
* that, we need to map out the shared clock registers.
*/
static spinlock_t clock_lock = SPIN_LOCK_UNLOCKED;
typedef struct {
char *name;
__u8 flags;
ck_t parent;
volatile __u16 *rate_reg; /* Clock rate register */
volatile __u16 *enbl_reg; /* Enable register */
volatile __u16 *idle_reg; /* Idle register */
volatile __u16 *slct_reg; /* Select register */
__s8 rate_shift; /* Clock rate bit shift */
__s8 enbl_shift; /* Clock enable bit shift */
__s8 idle_shift; /* Clock idle bit shift */
__s8 slct_shift; /* Clock select bit shift */
} ck_info_t;
#define CK_NAME(ck) ck_info_table[ck].name
#define CK_FLAGS(ck) ck_info_table[ck].flags
#define CK_PARENT(ck) ck_info_table[ck].parent
#define CK_RATE_REG(ck) ck_info_table[ck].rate_reg
#define CK_ENABLE_REG(ck) ck_info_table[ck].enbl_reg
#define CK_IDLE_REG(ck) ck_info_table[ck].idle_reg
#define CK_SELECT_REG(ck) ck_info_table[ck].slct_reg
#define CK_RATE_SHIFT(ck) ck_info_table[ck].rate_shift
#define CK_ENABLE_SHIFT(ck) ck_info_table[ck].enbl_shift
#define CK_IDLE_SHIFT(ck) ck_info_table[ck].idle_shift
#define CK_SELECT_SHIFT(ck) ck_info_table[ck].slct_shift
#define CK_CAN_CHANGE_RATE(cl) (CK_FLAGS(ck) & CK_RATEF)
#define CK_CAN_DISABLE(cl) (CK_FLAGS(ck) & CK_ENABLEF)
#define CK_CAN_IDLE(cl) (CK_FLAGS(ck) & CK_IDLEF)
#define CK_CAN_SWITCH(cl) (CK_FLAGS(ck) & CK_SELECTF)
static ck_info_t ck_info_table[] = {
{
.name = "clkin",
.flags = 0,
.parent = OMAP_CLKIN,
}, {
.name = "ck_gen1",
.flags = CK_RATEF | CK_IDLEF,
.rate_reg = CK_DPLL1,
.idle_reg = ARM_IDLECT1,
.idle_shift = IDLDPLL_ARM,
.parent = OMAP_CLKIN,
}, {
.name = "ck_gen2",
.flags = 0,
.parent = OMAP_CK_GEN1,
}, {
.name = "ck_gen3",
.flags = 0,
.parent = OMAP_CK_GEN1,
}, {
.name = "tc_ck",
.flags = CK_RATEF | CK_IDLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.idle_reg = ARM_IDLECT1,
.rate_shift = TCDIV,
.idle_shift = IDLIF_ARM
}, {
.name = "arm_ck",
.flags = CK_IDLEF | CK_RATEF,
.parent = OMAP_CK_GEN1,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[ARMDIV(5:4)] */
.idle_reg = ARM_IDLECT1,
.rate_shift = ARMDIV,
.idle_shift = SETARM_IDLE,
}, {
.name = "mpuper_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN1,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[PERDIV(1:0)] */
.enbl_reg = ARM_IDLECT2,
.idle_reg = ARM_IDLECT1,
.rate_shift = PERDIV,
.enbl_shift = EN_PERCK,
.idle_shift = IDLPER_ARM
}, {
.name = "arm_gpio_ck",
.flags = CK_ENABLEF,
.parent = OMAP_CK_GEN1,
.enbl_reg = ARM_IDLECT2,
.enbl_shift = EN_GPIOCK
}, {
.name = "mpuxor_ck",
.flags = CK_ENABLEF | CK_IDLEF,
.parent = OMAP_CLKIN,
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.idle_shift = IDLXORP_ARM,
.enbl_shift = EN_XORPCK
}, {
.name = "mputim_ck",
.flags = CK_IDLEF | CK_ENABLEF | CK_SELECTF,
.parent = OMAP_CLKIN,
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.slct_reg = ARM_CKCTL,
.idle_shift = IDLTIM_ARM,
.enbl_shift = EN_TIMCK,
.slct_shift = ARM_TIMXO
}, {
.name = "mpuwd_ck",
.flags = CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CLKIN,
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.idle_shift = IDLWDT_ARM,
.enbl_shift = EN_WDTCK,
}, {
.name = "dsp_ck",
.flags = CK_RATEF | CK_ENABLEF,
.parent = OMAP_CK_GEN2,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[DSPDIV(7:6)] */
.enbl_reg = ARM_CKCTL,
.rate_shift = DSPDIV,
.enbl_shift = EN_DSPCK,
}, {
.name = "dspmmu_ck",
.flags = CK_RATEF | CK_ENABLEF,
.parent = OMAP_CK_GEN2,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[DSPMMUDIV(11:10)] */
.enbl_reg = ARM_CKCTL,
.rate_shift = DSPMMUDIV,
.enbl_shift = EN_DSPCK,
}, {
.name = "dma_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.rate_shift = TCDIV,
.idle_shift = IDLIF_ARM,
.enbl_shift = DMACK_REQ
}, {
.name = "api_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.rate_shift = TCDIV,
.idle_shift = IDLAPI_ARM,
.enbl_shift = EN_APICK,
}, {
.name = "hsab_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.rate_shift = TCDIV,
.idle_shift = IDLHSAB_ARM,
.enbl_shift = EN_HSABCK,
}, {
.name = "lbfree_ck",
.flags = CK_RATEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.enbl_reg = ARM_IDLECT2,
.rate_shift = TCDIV,
.enbl_shift = EN_LBFREECK,
}, {
.name = "lb_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[TCDIV(9:8)] */
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.rate_shift = TCDIV,
.idle_shift = IDLLB_ARM,
.enbl_shift = EN_LBCK,
}, {
.name = "lcd_ck",
.flags = CK_RATEF | CK_IDLEF | CK_ENABLEF,
.parent = OMAP_CK_GEN3,
.rate_reg = ARM_CKCTL, /* ARM_CKCTL[LCDDIV(3:2)] */
.idle_reg = ARM_IDLECT1,
.enbl_reg = ARM_IDLECT2,
.rate_shift = LCDDIV,
.idle_shift = IDLLCD_ARM,
.enbl_shift = EN_LCDCK,
},
};
/*****************************************************************************/
#define CK_IN_RANGE(ck) (!((ck < OMAP_CK_MIN) || (ck > OMAP_CK_MAX)))
int ck_auto_unclock = 1;
int ck_debug = 0;
#define CK_MAX_PLL_FREQ OMAP_CK_MAX_RATE
static __u8 ck_valid_table[CK_MAX_PLL_FREQ / 8 + 1];
static __u8 ck_lookup_table[CK_MAX_PLL_FREQ];
int
ck_set_input(ck_t ck, ck_t input)
{
int ret = 0, shift;
volatile __u16 *reg;
unsigned long flags;
if (!CK_IN_RANGE(ck) || !CK_CAN_SWITCH(ck)) {
ret = -EINVAL;
goto exit;
}
reg = CK_SELECT_REG(ck);
shift = CK_SELECT_SHIFT(ck);
spin_lock_irqsave(&clock_lock, flags);
if (input == OMAP_CLKIN) {
*((volatile __u16 *) reg) &= ~(1 << shift);
goto exit;
} else if (input == CK_PARENT(ck)) {
*((volatile __u16 *) reg) |= (1 << shift);
goto exit;
}
ret = -EINVAL;
exit:
spin_unlock_irqrestore(&clock_lock, flags);
return ret;
}
int
ck_get_input(ck_t ck, ck_t * input)
{
int ret = -EINVAL;
unsigned long flags;
if (!CK_IN_RANGE(ck))
goto exit;
ret = 0;
spin_lock_irqsave(&clock_lock, flags);
if (CK_CAN_SWITCH(ck)) {
int shift;
volatile __u16 *reg;
reg = CK_SELECT_REG(ck);
shift = CK_SELECT_SHIFT(ck);
if (*reg & (1 << shift)) {
*input = CK_PARENT(ck);
goto exit;
}
}
*input = OMAP_CLKIN;
exit:
spin_unlock_irqrestore(&clock_lock, flags);
return ret;
}
static int
__ck_set_pll_rate(ck_t ck, int rate)
{
volatile __u16 *pll;
unsigned long flags;
if ((rate < 0) || (rate > CK_MAX_PLL_FREQ))
return -EINVAL;
/* Scan downward for the closest matching frequency */
while (rate && !test_bit(rate, (unsigned long *)&ck_valid_table))
rate--;
if (!rate) {
printk(KERN_ERR "%s: couldn't find a matching rate\n",
__FUNCTION__);
return -EINVAL;
}
spin_lock_irqsave(&clock_lock, flags);
pll = (volatile __u16 *) CK_RATE_REG(ck);
/* Clear the rate bits */
*pll &= ~(0x1f << 5);
/* Set the rate bits */
*pll |= (ck_lookup_table[rate - 1] << 5);
spin_unlock_irqrestore(&clock_lock, flags);
return 0;
}
static int
__ck_set_clkm_rate(ck_t ck, int rate)
{
int shift, prate, div, ret;
volatile __u16 *reg;
unsigned long flags;
spin_lock_irqsave(&clock_lock, flags);
/*
* We can only set this clock's value to a fraction of its
* parent's value. The interface says I'll round down when necessary.
* So first let's get the parent's current rate.
*/
prate = ck_get_rate(CK_PARENT(ck));
/*
* Let's just start with the highest fraction and keep searching
* down through available rates until we find one less than or equal
* to the desired rate.
*/
for (div = 0; div < 4; div++) {
if (prate <= rate)
break;
prate = prate / 2;
}
/*
* Oops. Looks like the caller wants a rate lower than we can support.
*/
if (div == 5) {
printk(KERN_ERR "%s: %d is too low\n",
__FUNCTION__, rate);
ret = -EINVAL;
goto exit;
}
/*
* One more detail: if this clock supports more than one parent, then
* we're going to automatically switch over to the parent which runs
* through the divisor. For omap this is not ambiguous because for all
* such clocks one choice is always OMAP_CLKIN (which doesn't run
* through the divisor) and the other is whatever I encoded as
* CK_PARENT. Note that I wait until we get this far because I don't
* want to switch the input until we're sure this is going to work.
*/
if (CK_CAN_SWITCH(ck))
if ((ret = ck_set_input(ck, CK_PARENT(ck))) < 0) {
BUG();
goto exit;
}
/*
* At last, we can set the divisor. Clear the old rate bits and
* set the new ones.
*/
reg = (volatile __u16 *) CK_RATE_REG(ck);
shift = CK_RATE_SHIFT(ck);
*reg &= ~(3 << shift);
*reg |= (div << shift);
/* And return the new (actual, after rounding down) rate. */
ret = prate;
exit:
spin_unlock_irqrestore(&clock_lock, flags);
return ret;
}
int
ck_set_rate(ck_t ck, int rate)
{
int ret = -EINVAL;
if (!CK_IN_RANGE(ck) || !CK_CAN_CHANGE_RATE(ck))
goto exit;
switch (ck) {
default:
ret = __ck_set_clkm_rate(ck, rate);
break;
case OMAP_CK_GEN1:
ret = __ck_set_pll_rate(ck, rate);
break;
};
exit:
return ret;
}
static int
__ck_get_pll_rate(ck_t ck)
{
int m, d;
__u16 pll = *((volatile __u16 *) CK_RATE_REG(ck));
m = (pll & (0x1f << 7)) >> 7;
m = m ? m : 1;
d = (pll & (3 << 5)) >> 5;
d++;
return ((source_clock * m) / d);
}
static int
__ck_get_clkm_rate(ck_t ck)
{
static int bits2div[] = { 1, 2, 4, 8 };
int in, bits, reg, shift;
reg = *(CK_RATE_REG(ck));
shift = CK_RATE_SHIFT(ck);
in = ck_get_rate(CK_PARENT(ck));
bits = (reg & (3 << shift)) >> shift;
return (in / bits2div[bits]);
}
int
ck_get_rate(ck_t ck)
{
int ret = 0;
ck_t parent;
if (!CK_IN_RANGE(ck)) {
ret = -EINVAL;
goto exit;
}
switch (ck) {
case OMAP_CK_GEN1:
ret = __ck_get_pll_rate(ck);
break;
case OMAP_CLKIN:
ret = source_clock;
break;
case OMAP_MPUXOR_CK:
case OMAP_CK_GEN2:
case OMAP_CK_GEN3:
case OMAP_ARM_GPIO_CK:
ret = ck_get_rate(CK_PARENT(ck));
break;
case OMAP_ARM_CK:
case OMAP_MPUPER_CK:
case OMAP_DSP_CK:
case OMAP_DSPMMU_CK:
case OMAP_LCD_CK:
case OMAP_TC_CK:
case OMAP_DMA_CK:
case OMAP_API_CK:
case OMAP_HSAB_CK:
case OMAP_LBFREE_CK:
case OMAP_LB_CK:
ret = __ck_get_clkm_rate(ck);
break;
case OMAP_MPUTIM_CK:
ck_get_input(ck, &parent);
ret = ck_get_rate(parent);
break;
case OMAP_MPUWD_CK:
/* Note that this evaluates to zero if source_clock is 12MHz. */
ret = source_clock / 14;
break;
default:
ret = -EINVAL;
break;
}
exit:
return ret;
}
int
ck_enable(ck_t ck)
{
volatile __u16 *reg;
int ret = -EINVAL, shift;
unsigned long flags;
if (!CK_IN_RANGE(ck))
goto exit;
if (ck_debug)
printk(KERN_DEBUG "%s: %s\n", __FUNCTION__, CK_NAME(ck));
ret = 0;
if (!CK_CAN_DISABLE(ck))
/* Then it must be on... */
goto exit;
spin_lock_irqsave(&clock_lock, flags);
reg = CK_ENABLE_REG(ck);
shift = CK_ENABLE_SHIFT(ck);
*reg |= (1 << shift);
spin_unlock_irqrestore(&clock_lock, flags);
exit:
return ret;
}
int
ck_disable(ck_t ck)
{
volatile __u16 *reg;
int ret = -EINVAL, shift;
unsigned long flags;
if (!CK_IN_RANGE(ck))
goto exit;
if (ck_debug)
printk(KERN_DEBUG "%s: %s\n", __FUNCTION__, CK_NAME(ck));
if (!CK_CAN_DISABLE(ck))
goto exit;
ret = 0;
if (ck == OMAP_CLKIN)
return -EINVAL;
spin_lock_irqsave(&clock_lock, flags);
reg = CK_ENABLE_REG(ck);
shift = CK_ENABLE_SHIFT(ck);
*reg &= ~(1 << shift);
spin_unlock_irqrestore(&clock_lock, flags);
exit:
return ret;
}
int ck_valid_rate(int rate)
{
return test_bit(rate, (unsigned long *)&ck_valid_table);
}
static void
__ck_make_lookup_table(void)
{
__u8 m, d;
memset(ck_valid_table, 0, sizeof (ck_valid_table));
for (m = 1; m < 32; m++)
for (d = 1; d < 5; d++) {
int rate = ((source_clock * m) / (d));
if (rate > CK_MAX_PLL_FREQ)
continue;
if (test_bit(rate, (unsigned long *)&ck_valid_table))
continue;
set_bit(rate, (unsigned long *)&ck_valid_table);
ck_lookup_table[rate - 1] = (m << 2) | (d - 1);
}
}
int __init
init_ck(void)
{
__ck_make_lookup_table();
/* We want to be in syncronous scalable mode */
*ARM_SYSST = 0x1000;
#if defined(CONFIG_OMAP_ARM_30MHZ)
*ARM_CKCTL = 0x1555;
*DPLL_CTL_REG = 0x2290;
#elif defined(CONFIG_OMAP_ARM_60MHZ)
*ARM_CKCTL = 0x1005;
*DPLL_CTL_REG = 0x2290;
#elif defined(CONFIG_OMAP_ARM_96MHZ)
*ARM_CKCTL = 0x1005;
*DPLL_CTL_REG = 0x2410;
#elif defined(CONFIG_OMAP_ARM_120MHZ)
*ARM_CKCTL = 0x110a;
*DPLL_CTL_REG = 0x2510;
#elif defined(CONFIG_OMAP_ARM_168MHZ)
*ARM_CKCTL = 0x110f;
*DPLL_CTL_REG = 0x2710;
#elif defined(CONFIG_OMAP_ARM_182MHZ) && defined(CONFIG_ARCH_OMAP730)
*ARM_CKCTL = 0x250E;
*DPLL_CTL_REG = 0x2713;
#elif defined(CONFIG_OMAP_ARM_192MHZ) && defined(CONFIG_ARCH_OMAP1610)
*ARM_CKCTL = 0x110f;
if (crystal_type == 2) {
source_clock = 13; /* MHz */
*DPLL_CTL_REG = 0x2510;
} else
*DPLL_CTL_REG = 0x2810;
#elif defined(CONFIG_OMAP_ARM_195MHZ) && defined(CONFIG_ARCH_OMAP730)
*ARM_CKCTL = 0x250E;
*DPLL_CTL_REG = 0x2793;
#else
#error "OMAP MHZ not set, please run make xconfig"
#endif
/* Turn off some other junk the bootloader might have turned on */
*ARM_CKCTL &= 0x0fff; /* Turn off DSP, ARM_INTHCK, ARM_TIMXO */
*ARM_RSTCT1 = 0; /* Put DSP/MPUI into reset until needed */
*ARM_RSTCT2 = 1;
*ARM_IDLECT1 = 0x400;
/*
* 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.
*/
*ARM_IDLECT2 = 0x0000; /* Turn LCD clock off also */
/*
* Only enable those clocks we will need, let the drivers
* enable other clocks as necessary
*/
ck_enable(OMAP_MPUPER_CK);
ck_enable(OMAP_ARM_GPIO_CK);
ck_enable(OMAP_MPUXOR_CK);
//ck_set_rate(OMAP_MPUTIM_CK, OMAP_CLKIN);
ck_enable(OMAP_MPUTIM_CK);
start_mputimer1(0xffffffff);
return 0;
}
EXPORT_SYMBOL(ck_get_rate);
EXPORT_SYMBOL(ck_set_rate);
EXPORT_SYMBOL(ck_enable);
EXPORT_SYMBOL(ck_disable);
/*
* linux/arch/arm/mach-omap/common.c
*
* Code common to all OMAP machines.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <asm/hardware.h>
#include <asm/system.h>
#include <asm/pgtable.h>
#include <asm/mach/map.h>
#include <asm/arch/clocks.h>
#include <asm/io.h>
#include "common.h"
/*
* Common OMAP I/O mapping
*
* The machine specific code may provide the extra mapping besides the
* default mapping provided here.
*/
static struct map_desc standard_io_desc[] __initdata = {
{ IO_BASE, IO_START, IO_SIZE, MT_DEVICE },
{ OMAP_DSP_BASE, OMAP_DSP_START, OMAP_DSP_SIZE, MT_DEVICE },
{ OMAP_DSPREG_BASE, OMAP_DSPREG_START, OMAP_DSPREG_SIZE, MT_DEVICE },
{ OMAP_SRAM_BASE, OMAP_SRAM_START, OMAP_SRAM_SIZE, MT_DEVICE }
};
static int initialized = 0;
static void __init _omap_map_io(void)
{
initialized = 1;
iotable_init(standard_io_desc, ARRAY_SIZE(standard_io_desc));
/* REVISIT: Refer to OMAP5910 Errata, Advisory SYS_1: "Timeout Abort
* on a Posted Write in the TIPB Bridge".
*/
__raw_writew(0x0, MPU_PUBLIC_TIPB_CNTL_REG);
__raw_writew(0x0, MPU_PRIVATE_TIPB_CNTL_REG);
/* Must init clocks early to assure that timer interrupt works
*/
init_ck();
}
/*
* This should only get called from board specific init
*/
void omap_map_io(void)
{
if (!initialized)
_omap_map_io();
}
EXPORT_SYMBOL(omap_map_io);
/*
* linux/arch/arm/mach-omap/common.h
*/
extern void omap_map_io(void);
/*
* linux/arch/arm/omap/dma.c
*
* Copyright (C) 2003 Nokia Corporation
* Author: Juha Yrjl <juha.yrjola@nokia.com>
*
* Support functions for the OMAP internal DMA channels.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/dma.h>
#include <asm/io.h>
#define OMAP_DMA_ACTIVE 0x01
#define OMAP_DMA_CCR_EN (1 << 7)
#define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec)
static int enable_1510_mode = 0;
struct omap_dma_lch {
int dev_id;
u16 saved_csr;
u16 enabled_irqs;
const char *dev_name;
void (* callback)(int lch, u16 ch_status, void *data);
void *data;
long flags;
};
static int dma_chan_count;
static spinlock_t dma_chan_lock;
static struct omap_dma_lch dma_chan[OMAP_LOGICAL_DMA_CH_COUNT];
const static u8 dma_irq[OMAP_LOGICAL_DMA_CH_COUNT] = {
INT_DMA_CH0_6, INT_DMA_CH1_7, INT_DMA_CH2_8, INT_DMA_CH3,
INT_DMA_CH4, INT_DMA_CH5, INT_1610_DMA_CH6, INT_1610_DMA_CH7,
INT_1610_DMA_CH8, INT_1610_DMA_CH9, INT_1610_DMA_CH10,
INT_1610_DMA_CH11, INT_1610_DMA_CH12, INT_1610_DMA_CH13,
INT_1610_DMA_CH14, INT_1610_DMA_CH15, INT_DMA_LCD
};
static inline int get_gdma_dev(int req)
{
u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4;
int shift = ((req - 1) % 5) * 6;
return ((__raw_readl(reg) >> shift) & 0x3f) + 1;
}
static inline void set_gdma_dev(int req, int dev)
{
u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4;
int shift = ((req - 1) % 5) * 6;
u32 l;
l = __raw_readl(reg);
l &= ~(0x3f << shift);
l |= (dev - 1) << shift;
__raw_writel(l, reg);
}
static void clear_lch_regs(int lch)
{
int i;
u32 lch_base = OMAP_DMA_BASE + lch * 0x40;
for (i = 0; i < 0x2c; i += 2)
__raw_writew(0, lch_base + i);
}
void omap_set_dma_transfer_params(int lch, int data_type, int elem_count,
int frame_count, int sync_mode)
{
u16 w;
w = __raw_readw(OMAP_DMA_CSDP_REG(lch));
w &= ~0x03;
w |= data_type;
__raw_writew(w, OMAP_DMA_CSDP_REG(lch));
w = __raw_readw(OMAP_DMA_CCR_REG(lch));
w &= ~(1 << 5);
if (sync_mode == OMAP_DMA_SYNC_FRAME)
w |= 1 << 5;
__raw_writew(w, OMAP_DMA_CCR_REG(lch));
w = __raw_readw(OMAP_DMA_CCR2_REG(lch));
w &= ~(1 << 2);
if (sync_mode == OMAP_DMA_SYNC_BLOCK)
w |= 1 << 2;
__raw_writew(w, OMAP_DMA_CCR2_REG(lch));
__raw_writew(elem_count, OMAP_DMA_CEN_REG(lch));
__raw_writew(frame_count, OMAP_DMA_CFN_REG(lch));
}
void omap_set_dma_src_params(int lch, int src_port, int src_amode,
unsigned long src_start)
{
u16 w;
w = __raw_readw(OMAP_DMA_CSDP_REG(lch));
w &= ~(0x1f << 2);
w |= src_port << 2;
__raw_writew(w, OMAP_DMA_CSDP_REG(lch));
w = __raw_readw(OMAP_DMA_CCR_REG(lch));
w &= ~(0x03 << 12);
w |= src_amode << 12;
__raw_writew(w, OMAP_DMA_CCR_REG(lch));
__raw_writew(src_start >> 16, OMAP_DMA_CSSA_U_REG(lch));
__raw_writew(src_start, OMAP_DMA_CSSA_L_REG(lch));
}
void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode,
unsigned long dest_start)
{
u16 w;
w = __raw_readw(OMAP_DMA_CSDP_REG(lch));
w &= ~(0x1f << 9);
w |= dest_port << 9;
__raw_writew(w, OMAP_DMA_CSDP_REG(lch));
w = __raw_readw(OMAP_DMA_CCR_REG(lch));
w &= ~(0x03 << 14);
w |= dest_amode << 14;
__raw_writew(w, OMAP_DMA_CCR_REG(lch));
__raw_writew(dest_start >> 16, OMAP_DMA_CDSA_U_REG(lch));
__raw_writew(dest_start, OMAP_DMA_CDSA_L_REG(lch));
}
void omap_start_dma(int lch)
{
u16 w;
/* Read CSR to make sure it's cleared. */
w = __raw_readw(OMAP_DMA_CSR_REG(lch));
/* Enable some nice interrupts. */
__raw_writew(dma_chan[lch].enabled_irqs, OMAP_DMA_CICR_REG(lch));
w = __raw_readw(OMAP_DMA_CCR_REG(lch));
w |= OMAP_DMA_CCR_EN;
__raw_writew(w, OMAP_DMA_CCR_REG(lch));
dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
}
void omap_stop_dma(int lch)
{
u16 w;
/* Disable all interrupts on the channel */
__raw_writew(0, OMAP_DMA_CICR_REG(lch));
w = __raw_readw(OMAP_DMA_CCR_REG(lch));
w &= ~OMAP_DMA_CCR_EN;
__raw_writew(w, OMAP_DMA_CCR_REG(lch));
dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE;
}
void omap_enable_dma_irq(int lch, u16 bits)
{
dma_chan[lch].enabled_irqs |= bits;
}
void omap_disable_dma_irq(int lch, u16 bits)
{
dma_chan[lch].enabled_irqs &= ~bits;
}
static int dma_handle_ch(int ch)
{
u16 csr;
if (enable_1510_mode && ch >= 6) {
csr = dma_chan[ch].saved_csr;
dma_chan[ch].saved_csr = 0;
} else
csr = __raw_readw(OMAP_DMA_CSR_REG(ch));
if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) {
dma_chan[ch + 6].saved_csr = csr >> 7;
csr &= 0x7f;
}
if (!csr)
return 0;
if (unlikely(dma_chan[ch].dev_id == -1)) {
printk(KERN_WARNING "Spurious interrupt from DMA channel %d (CSR %04x)\n",
ch, csr);
return 0;
}
if (unlikely(csr & OMAP_DMA_TOUT_IRQ))
printk(KERN_WARNING "DMA timeout with device %d\n", dma_chan[ch].dev_id);
if (unlikely(csr & OMAP_DMA_DROP_IRQ))
printk(KERN_WARNING "DMA synchronization event drop occurred with device %d\n",
dma_chan[ch].dev_id);
if (likely(csr & OMAP_DMA_BLOCK_IRQ))
dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE;
if (likely(dma_chan[ch].callback != NULL))
dma_chan[ch].callback(ch, csr, dma_chan[ch].data);
return 1;
}
static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
int ch = ((int) dev_id) - 1;
int handled = 0;
for (;;) {
int handled_now = 0;
handled_now += dma_handle_ch(ch);
if (enable_1510_mode && dma_chan[ch + 6].saved_csr)
handled_now += dma_handle_ch(ch + 6);
if (!handled_now)
break;
handled += handled_now;
}
return handled ? IRQ_HANDLED : IRQ_NONE;
}
int omap_request_dma(int dev_id, const char *dev_name,
void (* callback)(int lch, u16 ch_status, void *data),
void *data, int *dma_ch_out)
{
int ch, free_ch = -1;
unsigned long flags;
struct omap_dma_lch *chan;
spin_lock_irqsave(&dma_chan_lock, flags);
for (ch = 0; ch < dma_chan_count; ch++) {
if (free_ch == -1 && dma_chan[ch].dev_id == -1) {
free_ch = ch;
if (dev_id == 0)
break;
}
if (dev_id != 0 && dma_chan[ch].dev_id == dev_id) {
spin_unlock_irqrestore(&dma_chan_lock, flags);
return -EAGAIN;
}
}
if (free_ch == -1) {
spin_unlock_irqrestore(&dma_chan_lock, flags);
return -EBUSY;
}
chan = dma_chan + free_ch;
chan->dev_id = dev_id;
clear_lch_regs(free_ch);
spin_unlock_irqrestore(&dma_chan_lock, flags);
chan->dev_id = dev_id;
chan->dev_name = dev_name;
chan->callback = callback;
chan->data = data;
chan->enabled_irqs = OMAP_DMA_TOUT_IRQ | OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ;
if (cpu_is_omap1610()) {
/* If the sync device is set, configure it dynamically. */
if (dev_id != 0) {
set_gdma_dev(free_ch + 1, dev_id);
dev_id = free_ch + 1;
}
/* Disable the 1510 compatibility mode and set the sync device
* id. */
__raw_writew(dev_id | (1 << 10), OMAP_DMA_CCR_REG(free_ch));
} else {
__raw_writew(dev_id, OMAP_DMA_CCR_REG(free_ch));
}
*dma_ch_out = free_ch;
return 0;
}
void omap_free_dma(int ch)
{
unsigned long flags;
spin_lock_irqsave(&dma_chan_lock, flags);
if (dma_chan[ch].dev_id == -1) {
printk("omap_dma: trying to free nonallocated DMA channel %d\n", ch);
spin_unlock_irqrestore(&dma_chan_lock, flags);
return;
}
dma_chan[ch].dev_id = -1;
spin_unlock_irqrestore(&dma_chan_lock, flags);
/* Disable all DMA interrupts for the channel. */
__raw_writew(0, OMAP_DMA_CICR_REG(ch));
/* Make sure the DMA transfer is stopped. */
__raw_writew(0, OMAP_DMA_CCR_REG(ch));
}
int omap_dma_in_1510_mode(void)
{
return enable_1510_mode;
}
static struct lcd_dma_info {
spinlock_t lock;
int reserved;
void (* callback)(u16 status, void *data);
void *cb_data;
unsigned long addr, size;
int rotate, data_type, xres, yres;
} lcd_dma;
void omap_set_lcd_dma_b1(unsigned long addr, u16 fb_xres, u16 fb_yres,
int data_type)
{
lcd_dma.addr = addr;
lcd_dma.data_type = data_type;
lcd_dma.xres = fb_xres;
lcd_dma.yres = fb_yres;
}
static void set_b1_regs(void)
{
unsigned long top, bottom;
int es;
u16 w, en, fn;
s16 ei;
s32 fi;
u32 l;
switch (lcd_dma.data_type) {
case OMAP_DMA_DATA_TYPE_S8:
es = 1;
break;
case OMAP_DMA_DATA_TYPE_S16:
es = 2;
break;
case OMAP_DMA_DATA_TYPE_S32:
es = 4;
break;
default:
BUG();
return;
}
if (lcd_dma.rotate == 0) {
top = lcd_dma.addr;
bottom = lcd_dma.addr + (lcd_dma.xres * lcd_dma.yres - 1) * es;
/* 1510 DMA requires the bottom address to be 2 more than the
* actual last memory access location. */
if (omap_dma_in_1510_mode() &&
lcd_dma.data_type == OMAP_DMA_DATA_TYPE_S32)
bottom += 2;
en = lcd_dma.xres;
fn = lcd_dma.yres;
ei = 0;
fi = 0;
} else {
top = lcd_dma.addr + (lcd_dma.xres - 1) * es;
bottom = lcd_dma.addr + (lcd_dma.yres - 1) * lcd_dma.xres * es;
en = lcd_dma.yres;
fn = lcd_dma.xres;
ei = (lcd_dma.xres - 1) * es + 1;
fi = -(lcd_dma.xres * (lcd_dma.yres - 1) + 2) * 2 + 1;
}
if (omap_dma_in_1510_mode()) {
__raw_writew(top >> 16, OMAP1510_DMA_LCD_TOP_F1_U);
__raw_writew(top, OMAP1510_DMA_LCD_TOP_F1_L);
__raw_writew(bottom >> 16, OMAP1510_DMA_LCD_BOT_F1_U);
__raw_writew(bottom, OMAP1510_DMA_LCD_BOT_F1_L);
return;
}
/* 1610 regs */
__raw_writew(top >> 16, OMAP1610_DMA_LCD_TOP_B1_U);
__raw_writew(top, OMAP1610_DMA_LCD_TOP_B1_L);
__raw_writew(bottom >> 16, OMAP1610_DMA_LCD_BOT_B1_U);
__raw_writew(bottom, OMAP1610_DMA_LCD_BOT_B1_L);
__raw_writew(en, OMAP1610_DMA_LCD_SRC_EN_B1);
__raw_writew(fn, OMAP1610_DMA_LCD_SRC_FN_B1);
w = __raw_readw(OMAP1610_DMA_LCD_CSDP);
w &= ~0x03;
w |= lcd_dma.data_type;
__raw_writew(w, OMAP1610_DMA_LCD_CSDP);
if (!lcd_dma.rotate)
return;
/* Rotation stuff */
l = __raw_readw(OMAP1610_DMA_LCD_CSDP);
/* Disable burst access */
l &= ~(0x03 << 7);
__raw_writew(l, OMAP1610_DMA_LCD_CSDP);
l = __raw_readw(OMAP1610_DMA_LCD_CCR);
/* Set the double-indexed addressing mode */
l |= (0x03 << 12);
__raw_writew(l, OMAP1610_DMA_LCD_CCR);
__raw_writew(ei, OMAP1610_DMA_LCD_SRC_EI_B1);
__raw_writew(fi >> 16, OMAP1610_DMA_LCD_SRC_FI_B1_U);
__raw_writew(fi, OMAP1610_DMA_LCD_SRC_FI_B1_L);
}
void omap_set_lcd_dma_b1_rotation(int rotate)
{
if (omap_dma_in_1510_mode()) {
printk(KERN_ERR "DMA rotation is not supported in 1510 mode\n");
BUG();
return;
}
lcd_dma.rotate = rotate;
}
int omap_request_lcd_dma(void (* callback)(u16 status, void *data),
void *data)
{
spin_lock_irq(&lcd_dma.lock);
if (lcd_dma.reserved) {
spin_unlock_irq(&lcd_dma.lock);
printk(KERN_ERR "LCD DMA channel already reserved\n");
BUG();
return -EBUSY;
}
lcd_dma.reserved = 1;
spin_unlock_irq(&lcd_dma.lock);
lcd_dma.callback = callback;
lcd_dma.cb_data = data;
return 0;
}
void omap_free_lcd_dma(void)
{
spin_lock(&lcd_dma.lock);
if (!lcd_dma.reserved) {
spin_unlock(&lcd_dma.lock);
printk(KERN_ERR "LCD DMA is not reserved\n");
BUG();
return;
}
if (!enable_1510_mode)
__raw_writew(__raw_readw(OMAP1610_DMA_LCD_CCR) & ~1, OMAP1610_DMA_LCD_CCR);
lcd_dma.reserved = 0;
spin_unlock(&lcd_dma.lock);
}
void omap_start_lcd_dma(void)
{
if (!enable_1510_mode) {
/* Set some reasonable defaults */
__raw_writew(0x9102, OMAP1610_DMA_LCD_CSDP);
__raw_writew(0x0004, OMAP1610_DMA_LCD_LCH_CTRL);
__raw_writew(0x5740, OMAP1610_DMA_LCD_CCR);
}
set_b1_regs();
if (!enable_1510_mode)
__raw_writew(__raw_readw(OMAP1610_DMA_LCD_CCR) | 1, OMAP1610_DMA_LCD_CCR);
}
void omap_stop_lcd_dma(void)
{
if (!enable_1510_mode)
__raw_writew(__raw_readw(OMAP1610_DMA_LCD_CCR) & ~1, OMAP1610_DMA_LCD_CCR);
}
static int __init omap_init_dma(void)
{
int ch, r;
if (cpu_is_omap1510()) {
printk(KERN_INFO "DMA support for OMAP1510 initialized\n");
dma_chan_count = 9;
enable_1510_mode = 1;
} else if (cpu_is_omap1610()) {
printk(KERN_INFO "OMAP DMA hardware version %d\n",
__raw_readw(OMAP_DMA_HW_ID_REG));
printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n",
(__raw_readw(OMAP_DMA_CAPS_0_U_REG) << 16) | __raw_readw(OMAP_DMA_CAPS_0_L_REG),
(__raw_readw(OMAP_DMA_CAPS_1_U_REG) << 16) | __raw_readw(OMAP_DMA_CAPS_1_L_REG),
__raw_readw(OMAP_DMA_CAPS_2_REG), __raw_readw(OMAP_DMA_CAPS_3_REG),
__raw_readw(OMAP_DMA_CAPS_4_REG));
if (!enable_1510_mode) {
u16 w;
/* Disable OMAP 3.0/3.1 compatibility mode. */
w = __raw_readw(OMAP_DMA_GSCR_REG);
w |= 1 << 3;
__raw_writew(w, OMAP_DMA_GSCR_REG);
dma_chan_count = OMAP_LOGICAL_DMA_CH_COUNT;
} else
dma_chan_count = 9;
} else {
dma_chan_count = 0;
return 0;
}
memset(&lcd_dma, 0, sizeof(lcd_dma));
spin_lock_init(&lcd_dma.lock);
spin_lock_init(&dma_chan_lock);
memset(&dma_chan, 0, sizeof(dma_chan));
for (ch = 0; ch < dma_chan_count; ch++) {
dma_chan[ch].dev_id = -1;
if (ch >= 6 && enable_1510_mode)
continue;
/* request_irq() doesn't like dev_id (ie. ch) being zero,
* so we have to kludge around this. */
r = request_irq(dma_irq[ch], dma_irq_handler, 0, "DMA",
(void *) (ch + 1));
if (r != 0) {
int i;
printk(KERN_ERR "unable to request IRQ %d for DMA (error %d)\n",
dma_irq[ch], r);
for (i = 0; i < ch; i++)
free_irq(dma_irq[i], (void *) (i + 1));
return r;
}
}
return 0;
}
arch_initcall(omap_init_dma);
EXPORT_SYMBOL(omap_request_dma);
EXPORT_SYMBOL(omap_free_dma);
EXPORT_SYMBOL(omap_start_dma);
EXPORT_SYMBOL(omap_stop_dma);
EXPORT_SYMBOL(omap_set_dma_transfer_params);
EXPORT_SYMBOL(omap_set_dma_src_params);
EXPORT_SYMBOL(omap_set_dma_dest_params);
EXPORT_SYMBOL(omap_request_lcd_dma);
EXPORT_SYMBOL(omap_free_lcd_dma);
EXPORT_SYMBOL(omap_start_lcd_dma);
EXPORT_SYMBOL(omap_stop_lcd_dma);
EXPORT_SYMBOL(omap_set_lcd_dma_b1);
EXPORT_SYMBOL(omap_set_lcd_dma_b1_rotation);
/*
* linux/arch/arm/mach-omap/fpga.c
*
* 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>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/arch/fpga.h>
#include <asm/arch/gpio.h>
unsigned char fpga_read(int reg)
{
return __raw_readb(reg);
}
void fpga_write(unsigned char val, int reg)
{
__raw_writeb(val, reg);
}
static void fpga_mask_irq(unsigned int irq)
{
irq -= IH_FPGA_BASE;
if (irq < 8)
__raw_writeb((__raw_readb(OMAP1510P1_FPGA_IMR_LO)
& ~(1 << irq)), OMAP1510P1_FPGA_IMR_LO);
else if (irq < 16)
__raw_writeb((__raw_readb(OMAP1510P1_FPGA_IMR_HI)
& ~(1 << (irq - 8))), OMAP1510P1_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(OMAP1510P1_FPGA_ISR_LO) &
__raw_readb(OMAP1510P1_FPGA_IMR_LO))) |
((__raw_readb(OMAP1510P1_FPGA_ISR_HI) &
__raw_readb(OMAP1510P1_FPGA_IMR_HI)) << 8) |
((__raw_readb(INNOVATOR_FPGA_ISR2) &
__raw_readb(INNOVATOR_FPGA_IMR2)) << 16);
}
static void fpga_ack_irq(unsigned int irq)
{
/* Don't need to explicitly ACK FPGA interrupts */
}
static void fpga_unmask_irq(unsigned int irq)
{
irq -= IH_FPGA_BASE;
if (irq < 8)
__raw_writeb((__raw_readb(OMAP1510P1_FPGA_IMR_LO) | (1 << irq)),
OMAP1510P1_FPGA_IMR_LO);
else if (irq < 16)
__raw_writeb((__raw_readb(OMAP1510P1_FPGA_IMR_HI)
| (1 << (irq - 8))), OMAP1510P1_FPGA_IMR_HI);
else
__raw_writeb((__raw_readb(INNOVATOR_FPGA_IMR2)
| (1 << (irq - 16))), INNOVATOR_FPGA_IMR2);
}
static void fpga_mask_ack_irq(unsigned int irq)
{
fpga_mask_irq(irq);
fpga_ack_irq(irq);
}
void innovator_fpga_IRQ_demux(unsigned int irq, struct irqdesc *desc,
struct pt_regs *regs)
{
struct irqdesc *d;
u32 stat;
int fpga_irq;
/*
* Acknowledge the parent IRQ.
*/
desc->chip->ack(irq);
for (;;) {
stat = get_fpga_unmasked_irqs();
if (!stat) {
break;
}
for (fpga_irq = IH_FPGA_BASE;
(fpga_irq < (IH_FPGA_BASE + NR_FPGA_IRQS)) && stat;
fpga_irq++, stat >>= 1) {
if (stat & 1) {
d = irq_desc + fpga_irq;
d->handle(fpga_irq, d, regs);
desc->chip->unmask(irq);
}
}
}
}
static struct irqchip omap_fpga_irq_ack = {
.ack = fpga_mask_ack_irq,
.mask = fpga_mask_irq,
.unmask = fpga_unmask_irq,
};
static struct irqchip omap_fpga_irq = {
.ack = fpga_ack_irq,
.mask = fpga_mask_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 installed with the SA_INTERRUPT flag, which leaves all
* interrupts masked at the CPU while an FPGA interrupt handler executes.
*
* 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 fpga_init_irq(void)
{
int i;
__raw_writeb(0, OMAP1510P1_FPGA_IMR_LO);
__raw_writeb(0, OMAP1510P1_FPGA_IMR_HI);
__raw_writeb(0, INNOVATOR_FPGA_IMR2);
for (i = IH_FPGA_BASE; i < (IH_FPGA_BASE + NR_FPGA_IRQS); i++) {
if (i == INT_FPGA_TS) {
/*
* The touchscreen interrupt is level-sensitive, so
* we'll use the regular mask_ack routine for it.
*/
set_irq_chip(i, &omap_fpga_irq_ack);
}
else {
/*
* All FPGA interrupts except the touchscreen are
* edge-sensitive, so we won't mask them.
*/
set_irq_chip(i, &omap_fpga_irq);
}
set_irq_handler(i, do_level_IRQ);
set_irq_flags(i, IRQF_VALID);
}
/*
* 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]
*/
omap_request_gpio(13);
omap_set_gpio_direction(13, 1);
omap_set_gpio_edge_ctrl(13, OMAP_GPIO_RISING_EDGE);
set_irq_chained_handler(INT_FPGA, innovator_fpga_IRQ_demux);
}
EXPORT_SYMBOL(fpga_init_irq);
EXPORT_SYMBOL(fpga_read);
EXPORT_SYMBOL(fpga_write);
/*
* linux/arch/arm/mach-omap/gpio.c
*
* Support functions for OMAP GPIO
*
* Copyright (C) 2003 Nokia Corporation
* Written by Juha Yrjl <juha.yrjola@nokia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/arch/irqs.h>
#include <asm/arch/gpio.h>
#include <asm/arch/pm.h>
#include <asm/mach/irq.h>
#include <asm/io.h>
/*
* OMAP1510 GPIO registers
*/
#define OMAP1510_GPIO_BASE 0xfffce000
#define OMAP1510_GPIO_DATA_INPUT 0x00
#define OMAP1510_GPIO_DATA_OUTPUT 0x04
#define OMAP1510_GPIO_DIR_CONTROL 0x08
#define OMAP1510_GPIO_INT_CONTROL 0x0c
#define OMAP1510_GPIO_INT_MASK 0x10
#define OMAP1510_GPIO_INT_STATUS 0x14
#define OMAP1510_GPIO_PIN_CONTROL 0x18
#define OMAP1510_IH_GPIO_BASE 64
/*
* OMAP1610 specific GPIO registers
*/
#define OMAP1610_GPIO1_BASE 0xfffbe400
#define OMAP1610_GPIO2_BASE 0xfffbec00
#define OMAP1610_GPIO3_BASE 0xfffbb400
#define OMAP1610_GPIO4_BASE 0xfffbbc00
#define OMAP1610_GPIO_REVISION 0x0000
#define OMAP1610_GPIO_SYSCONFIG 0x0010
#define OMAP1610_GPIO_SYSSTATUS 0x0014
#define OMAP1610_GPIO_IRQSTATUS1 0x0018
#define OMAP1610_GPIO_IRQENABLE1 0x001c
#define OMAP1610_GPIO_DATAIN 0x002c
#define OMAP1610_GPIO_DATAOUT 0x0030
#define OMAP1610_GPIO_DIRECTION 0x0034
#define OMAP1610_GPIO_EDGE_CTRL1 0x0038
#define OMAP1610_GPIO_EDGE_CTRL2 0x003c
#define OMAP1610_GPIO_CLEAR_IRQENABLE1 0x009c
#define OMAP1610_GPIO_CLEAR_DATAOUT 0x00b0
#define OMAP1610_GPIO_SET_IRQENABLE1 0x00dc
#define OMAP1610_GPIO_SET_DATAOUT 0x00f0
/*
* OMAP730 specific GPIO registers
*/
#define OMAP730_GPIO1_BASE 0xfffbc000
#define OMAP730_GPIO2_BASE 0xfffbc800
#define OMAP730_GPIO3_BASE 0xfffbd000
#define OMAP730_GPIO4_BASE 0xfffbd800
#define OMAP730_GPIO5_BASE 0xfffbe000
#define OMAP730_GPIO6_BASE 0xfffbe800
#define OMAP730_GPIO_DATA_INPUT 0x00
#define OMAP730_GPIO_DATA_OUTPUT 0x04
#define OMAP730_GPIO_DIR_CONTROL 0x08
#define OMAP730_GPIO_INT_CONTROL 0x0c
#define OMAP730_GPIO_INT_MASK 0x10
#define OMAP730_GPIO_INT_STATUS 0x14
#define OMAP_MPUIO_MASK (~OMAP_MAX_GPIO_LINES & 0xff)
struct gpio_bank {
u32 base;
u16 irq;
u16 virtual_irq_start;
u8 method;
u32 reserved_map;
spinlock_t lock;
};
#define METHOD_MPUIO 0
#define METHOD_GPIO_1510 1
#define METHOD_GPIO_1610 2
#define METHOD_GPIO_730 3
#ifdef CONFIG_ARCH_OMAP1610
static struct gpio_bank gpio_bank_1610[5] = {
{ OMAP_MPUIO_BASE, INT_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO},
{ OMAP1610_GPIO1_BASE, INT_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_1610 },
{ OMAP1610_GPIO2_BASE, INT_1610_GPIO_BANK2, IH_GPIO_BASE + 16, METHOD_GPIO_1610 },
{ OMAP1610_GPIO3_BASE, INT_1610_GPIO_BANK3, IH_GPIO_BASE + 32, METHOD_GPIO_1610 },
{ OMAP1610_GPIO4_BASE, INT_1610_GPIO_BANK4, IH_GPIO_BASE + 48, METHOD_GPIO_1610 },
};
#endif
#ifdef CONFIG_ARCH_OMAP1510
static struct gpio_bank gpio_bank_1510[2] = {
{ OMAP_MPUIO_BASE, INT_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO },
{ OMAP1510_GPIO_BASE, INT_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_1510 }
};
#endif
#ifdef CONFIG_ARCH_OMAP730
static struct gpio_bank gpio_bank_730[7] = {
{ OMAP_MPUIO_BASE, INT_MPUIO, IH_MPUIO_BASE, METHOD_MPUIO },
{ OMAP730_GPIO1_BASE, INT_GPIO_BANK1, IH_GPIO_BASE, METHOD_GPIO_730 },
{ OMAP730_GPIO2_BASE, INT_730_GPIO_BANK2, IH_GPIO_BASE + 32, METHOD_GPIO_730 },
{ OMAP730_GPIO3_BASE, INT_730_GPIO_BANK3, IH_GPIO_BASE + 64, METHOD_GPIO_730 },
{ OMAP730_GPIO4_BASE, INT_730_GPIO_BANK4, IH_GPIO_BASE + 96, METHOD_GPIO_730 },
{ OMAP730_GPIO5_BASE, INT_730_GPIO_BANK5, IH_GPIO_BASE + 128, METHOD_GPIO_730 },
{ OMAP730_GPIO6_BASE, INT_730_GPIO_BANK6, IH_GPIO_BASE + 160, METHOD_GPIO_730 },
};
#endif
static struct gpio_bank *gpio_bank;
static int gpio_bank_count;
static inline struct gpio_bank *get_gpio_bank(int gpio)
{
#ifdef CONFIG_ARCH_OMAP1510
if (cpu_is_omap1510()) {
if (OMAP_GPIO_IS_MPUIO(gpio))
return &gpio_bank[0];
return &gpio_bank[1];
}
#endif
#ifdef CONFIG_ARCH_OMAP1610
if (cpu_is_omap1610()) {
if (OMAP_GPIO_IS_MPUIO(gpio))
return &gpio_bank[0];
return &gpio_bank[1 + (gpio >> 4)];
}
#endif
#ifdef CONFIG_ARCH_OMAP730
if (cpu_is_omap730()) {
if (OMAP_GPIO_IS_MPUIO(gpio))
return &gpio_bank[0];
return &gpio_bank[1 + (gpio >> 5)];
}
#endif
}
static inline int get_gpio_index(int gpio)
{
if (cpu_is_omap730())
return gpio & 0x1f;
else
return gpio & 0x0f;
}
static inline int gpio_valid(int gpio)
{
if (gpio < 0)
return -1;
if (OMAP_GPIO_IS_MPUIO(gpio)) {
if ((gpio & OMAP_MPUIO_MASK) > 16)
return -1;
return 0;
}
#ifdef CONFIG_ARCH_OMAP1510
if (cpu_is_omap1510() && gpio < 16)
return 0;
#endif
#ifdef CONFIG_ARCH_OMAP1610
if (cpu_is_omap1610() && gpio < 64)
return 0;
#endif
#ifdef CONFIG_ARCH_OMAP1610
if (cpu_is_omap730() && gpio < 192)
return 0;
#endif
return -1;
}
static int check_gpio(int gpio)
{
if (unlikely(gpio_valid(gpio)) < 0) {
printk(KERN_ERR "omap-gpio: invalid GPIO %d\n", gpio);
dump_stack();
return -1;
}
return 0;
}
static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
{
u32 reg = bank->base;
u32 l;
switch (bank->method) {
case METHOD_MPUIO:
reg += OMAP_MPUIO_IO_CNTL;
break;
case METHOD_GPIO_1510:
reg += OMAP1510_GPIO_DIR_CONTROL;
break;
case METHOD_GPIO_1610:
reg += OMAP1610_GPIO_DIRECTION;
break;
case METHOD_GPIO_730:
reg += OMAP730_GPIO_DIR_CONTROL;
break;
}
l = __raw_readl(reg);
if (is_input)
l |= 1 << gpio;
else
l &= ~(1 << gpio);
__raw_writel(l, reg);
}
void omap_set_gpio_direction(int gpio, int is_input)
{
struct gpio_bank *bank;
if (check_gpio(gpio) < 0)
return;
bank = get_gpio_bank(gpio);
spin_lock(&bank->lock);
_set_gpio_direction(bank, get_gpio_index(gpio), is_input);
spin_unlock(&bank->lock);
}
static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable)
{
u32 reg = bank->base;
u32 l = 0;
switch (bank->method) {
case METHOD_MPUIO:
reg += OMAP_MPUIO_OUTPUT_REG;
l = __raw_readl(reg);
if (enable)
l |= 1 << gpio;
else
l &= ~(1 << gpio);
break;
case METHOD_GPIO_1510:
reg += OMAP1510_GPIO_DATA_OUTPUT;
l = __raw_readl(reg);
if (enable)
l |= 1 << gpio;
else
l &= ~(1 << gpio);
break;
case METHOD_GPIO_1610:
if (enable)
reg += OMAP1610_GPIO_SET_DATAOUT;
else
reg += OMAP1610_GPIO_CLEAR_DATAOUT;
l = 1 << gpio;
break;
case METHOD_GPIO_730:
reg += OMAP730_GPIO_DATA_OUTPUT;
l = __raw_readl(reg);
if (enable)
l |= 1 << gpio;
else
l &= ~(1 << gpio);
break;
default:
BUG();
return;
}
__raw_writel(l, reg);
}
void omap_set_gpio_dataout(int gpio, int enable)
{
struct gpio_bank *bank;
if (check_gpio(gpio) < 0)
return;
bank = get_gpio_bank(gpio);
spin_lock(&bank->lock);
_set_gpio_dataout(bank, get_gpio_index(gpio), enable);
spin_unlock(&bank->lock);
}
int omap_get_gpio_datain(int gpio)
{
struct gpio_bank *bank;
u32 reg;
if (check_gpio(gpio) < 0)
return -1;
bank = get_gpio_bank(gpio);
reg = bank->base;
switch (bank->method) {
case METHOD_MPUIO:
reg += OMAP_MPUIO_INPUT_LATCH;
break;
case METHOD_GPIO_1510:
reg += OMAP1510_GPIO_DATA_INPUT;
break;
case METHOD_GPIO_1610:
reg += OMAP1610_GPIO_DATAIN;
break;
default:
BUG();
return -1;
}
return (__raw_readl(reg) & (1 << get_gpio_index(gpio))) != 0;
}
static void _set_gpio_edge_ctrl(struct gpio_bank *bank, int gpio, int edge)
{
u32 reg = bank->base;
u32 l;
switch (bank->method) {
case METHOD_MPUIO:
reg += OMAP_MPUIO_GPIO_INT_EDGE_REG;
l = __raw_readl(reg);
if (edge == OMAP_GPIO_RISING_EDGE)
l |= 1 << gpio;
else
l &= ~(1 << gpio);
__raw_writel(l, reg);
break;
case METHOD_GPIO_1510:
reg += OMAP1510_GPIO_INT_CONTROL;
l = __raw_readl(reg);
if (edge == OMAP_GPIO_RISING_EDGE)
l |= 1 << gpio;
else
l &= ~(1 << gpio);
__raw_writel(l, reg);
break;
case METHOD_GPIO_1610:
edge &= 0x03;
if (gpio & 0x08)
reg += OMAP1610_GPIO_EDGE_CTRL2;
else
reg += OMAP1610_GPIO_EDGE_CTRL1;
gpio &= 0x07;
l = __raw_readl(reg);
l &= ~(3 << (gpio << 1));
l |= edge << (gpio << 1);
__raw_writel(l, reg);
break;
case METHOD_GPIO_730:
reg += OMAP730_GPIO_INT_CONTROL;
l = __raw_readl(reg);
if (edge == OMAP_GPIO_RISING_EDGE)
l |= 1 << gpio;
else
l &= ~(1 << gpio);
__raw_writel(l, reg);
break;
default:
BUG();
return;
}
}
void omap_set_gpio_edge_ctrl(int gpio, int edge)
{
struct gpio_bank *bank;
if (check_gpio(gpio) < 0)
return;
bank = get_gpio_bank(gpio);
spin_lock(&bank->lock);
_set_gpio_edge_ctrl(bank, get_gpio_index(gpio), edge);
spin_unlock(&bank->lock);
}
static int _get_gpio_edge_ctrl(struct gpio_bank *bank, int gpio)
{
u32 reg = bank->base, l;
switch (bank->method) {
case METHOD_MPUIO:
l = __raw_readl(reg + OMAP_MPUIO_GPIO_INT_EDGE_REG);
return (l & (1 << gpio)) ?
OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE;
case METHOD_GPIO_1510:
l = __raw_readl(reg + OMAP1510_GPIO_INT_CONTROL);
return (l & (1 << gpio)) ?
OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE;
case METHOD_GPIO_1610:
if (gpio & 0x08)
reg += OMAP1610_GPIO_EDGE_CTRL2;
else
reg += OMAP1610_GPIO_EDGE_CTRL1;
return (__raw_readl(reg) >> ((gpio & 0x07) << 1)) & 0x03;
case METHOD_GPIO_730:
l = __raw_readl(reg + OMAP730_GPIO_INT_CONTROL);
return (l & (1 << gpio)) ?
OMAP_GPIO_RISING_EDGE : OMAP_GPIO_FALLING_EDGE;
default:
BUG();
return -1;
}
}
static void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio)
{
u32 reg = bank->base;
switch (bank->method) {
case METHOD_MPUIO:
/* MPUIO irqstatus cannot be cleared one bit at a time,
* so do nothing here */
return;
case METHOD_GPIO_1510:
reg += OMAP1510_GPIO_INT_STATUS;
break;
case METHOD_GPIO_1610:
reg += OMAP1610_GPIO_IRQSTATUS1;
break;
case METHOD_GPIO_730:
reg += OMAP730_GPIO_INT_STATUS;
break;
default:
BUG();
return;
}
__raw_writel(1 << get_gpio_index(gpio), reg);
}
static void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable)
{
u32 reg = bank->base;
u32 l;
switch (bank->method) {
case METHOD_MPUIO:
reg += OMAP_MPUIO_GPIO_MASKIT;
l = __raw_readl(reg);
if (enable)
l &= ~(1 << gpio);
else
l |= 1 << gpio;
break;
case METHOD_GPIO_1510:
reg += OMAP1510_GPIO_INT_MASK;
l = __raw_readl(reg);
if (enable)
l &= ~(1 << gpio);
else
l |= 1 << gpio;
break;
case METHOD_GPIO_1610:
if (enable) {
reg += OMAP1610_GPIO_SET_IRQENABLE1;
_clear_gpio_irqstatus(bank, gpio);
} else
reg += OMAP1610_GPIO_CLEAR_IRQENABLE1;
l = 1 << gpio;
break;
case METHOD_GPIO_730:
reg += OMAP730_GPIO_INT_MASK;
l = __raw_readl(reg);
if (enable)
l &= ~(1 << gpio);
else
l |= 1 << gpio;
break;
default:
BUG();
return;
}
__raw_writel(l, reg);
}
int omap_request_gpio(int gpio)
{
struct gpio_bank *bank;
if (check_gpio(gpio) < 0)
return -EINVAL;
bank = get_gpio_bank(gpio);
spin_lock(&bank->lock);
if (unlikely(bank->reserved_map & (1 << get_gpio_index(gpio)))) {
printk(KERN_ERR "omap-gpio: GPIO %d is already reserved!\n", gpio);
dump_stack();
spin_unlock(&bank->lock);
return -1;
}
bank->reserved_map |= (1 << get_gpio_index(gpio));
#ifdef CONFIG_ARCH_OMAP1510
if (bank->method == METHOD_GPIO_1510) {
u32 reg;
/* Claim the pin for the ARM */
reg = bank->base + OMAP1510_GPIO_PIN_CONTROL;
__raw_writel(__raw_readl(reg) | (1 << get_gpio_index(gpio)), reg);
}
#endif
spin_unlock(&bank->lock);
return 0;
}
void omap_free_gpio(int gpio)
{
struct gpio_bank *bank;
if (check_gpio(gpio) < 0)
return;
bank = get_gpio_bank(gpio);
spin_lock(&bank->lock);
if (unlikely(!(bank->reserved_map & (1 << get_gpio_index(gpio))))) {
printk(KERN_ERR "omap-gpio: GPIO %d wasn't reserved!\n", gpio);
dump_stack();
spin_unlock(&bank->lock);
return;
}
bank->reserved_map &= ~(1 << get_gpio_index(gpio));
_set_gpio_direction(bank, get_gpio_index(gpio), 1);
_set_gpio_irqenable(bank, get_gpio_index(gpio), 0);
spin_unlock(&bank->lock);
}
static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc,
struct pt_regs *regs)
{
u32 isr_reg = 0;
struct gpio_bank *bank = (struct gpio_bank *) desc->data;
/*
* Acknowledge the parent IRQ.
*/
desc->chip->ack(irq);
/* Since the level 1 GPIO interrupt cascade (IRQ14) is configured as
* edge-sensitive, we need to unmask it here in order to avoid missing
* any additional GPIO interrupts that might occur after the last time
* we check for pending GPIO interrupts here.
* We are relying on the fact that this interrupt handler was installed
* with the SA_INTERRUPT flag so that interrupts are disabled at the
* CPU while it is executing.
*/
desc->chip->unmask(irq);
if (bank->method == METHOD_MPUIO)
isr_reg = bank->base + OMAP_MPUIO_GPIO_INT;
#ifdef CONFIG_ARCH_OMAP1510
if (bank->method == METHOD_GPIO_1510)
isr_reg = bank->base + OMAP1510_GPIO_INT_STATUS;
#endif
#ifdef CONFIG_ARCH_OMAP1610
if (bank->method == METHOD_GPIO_1610)
isr_reg = bank->base + OMAP1610_GPIO_IRQSTATUS1;
#endif
#ifdef CONFIG_ARCH_OMAP730
if (bank->method == METHOD_GPIO_730)
isr_reg = bank->base + OMAP730_GPIO_INT_STATUS;
#endif
for (;;) {
u32 isr = __raw_readl(isr_reg);
unsigned int gpio_irq;
if (!isr)
break;
gpio_irq = bank->virtual_irq_start;
for (; isr != 0; isr >>= 1, gpio_irq++) {
if (isr & 1) {
struct irqdesc *d = irq_desc + gpio_irq;
d->handle(gpio_irq, d, regs);
}
}
}
}
static void gpio_ack_irq(unsigned int irq)
{
unsigned int gpio = irq - IH_GPIO_BASE;
struct gpio_bank *bank = get_gpio_bank(gpio);
#ifdef CONFIG_ARCH_OMAP1510
if (bank->method == METHOD_GPIO_1510)
__raw_writew(1 << gpio, bank->base + OMAP1510_GPIO_INT_STATUS);
#endif
#ifdef CONFIG_ARCH_OMAP1610
if (bank->method == METHOD_GPIO_1610)
__raw_writew(1 << gpio, bank->base + OMAP1610_GPIO_IRQSTATUS1);
#endif
#ifdef CONFIG_ARCH_OMAP730
if (bank->method == METHOD_GPIO_730)
__raw_writel(1 << gpio, bank->base + OMAP730_GPIO_INT_STATUS);
#endif
}
static void gpio_mask_irq(unsigned int irq)
{
unsigned int gpio = irq - IH_GPIO_BASE;
struct gpio_bank *bank = get_gpio_bank(gpio);
_set_gpio_irqenable(bank, get_gpio_index(gpio), 0);
}
static void gpio_unmask_irq(unsigned int irq)
{
unsigned int gpio = irq - IH_GPIO_BASE;
struct gpio_bank *bank = get_gpio_bank(gpio);
if (_get_gpio_edge_ctrl(bank, get_gpio_index(gpio)) == OMAP_GPIO_NO_EDGE) {
printk(KERN_ERR "OMAP GPIO %d: trying to enable GPIO IRQ while no edge is set\n",
gpio);
_set_gpio_edge_ctrl(bank, get_gpio_index(gpio), OMAP_GPIO_RISING_EDGE);
}
_set_gpio_irqenable(bank, get_gpio_index(gpio), 1);
}
static void mpuio_ack_irq(unsigned int irq)
{
/* The ISR is reset automatically, so do nothing here. */
}
static void mpuio_mask_irq(unsigned int irq)
{
unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE);
struct gpio_bank *bank = get_gpio_bank(gpio);
_set_gpio_irqenable(bank, gpio, 0);
}
static void mpuio_unmask_irq(unsigned int irq)
{
unsigned int gpio = OMAP_MPUIO(irq - IH_MPUIO_BASE);
struct gpio_bank *bank = get_gpio_bank(gpio);
_set_gpio_irqenable(bank, gpio, 1);
}
static struct irqchip gpio_irq_chip = {
.ack = gpio_ack_irq,
.mask = gpio_mask_irq,
.unmask = gpio_unmask_irq,
};
static struct irqchip mpuio_irq_chip = {
.ack = mpuio_ack_irq,
.mask = mpuio_mask_irq,
.unmask = mpuio_unmask_irq
};
static int initialized = 0;
static int __init _omap_gpio_init(void)
{
int i;
struct gpio_bank *bank;
initialized = 1;
#ifdef CONFIG_ARCH_OMAP1510
if (cpu_is_omap1510()) {
printk(KERN_INFO "OMAP1510 GPIO hardware\n");
gpio_bank_count = 2;
gpio_bank = gpio_bank_1510;
}
#endif
#ifdef CONFIG_ARCH_OMAP1610
if (cpu_is_omap1610()) {
int rev;
gpio_bank_count = 5;
gpio_bank = gpio_bank_1610;
rev = __raw_readw(gpio_bank[1].base + OMAP1610_GPIO_REVISION);
printk(KERN_INFO "OMAP GPIO hardware version %d.%d\n",
(rev >> 4) & 0x0f, rev & 0x0f);
}
#endif
#ifdef CONFIG_ARCH_OMAP730
if (cpu_is_omap730()) {
printk(KERN_INFO "OMAP730 GPIO hardware\n");
gpio_bank_count = 7;
gpio_bank = gpio_bank_730;
}
#endif
for (i = 0; i < gpio_bank_count; i++) {
int j, gpio_count = 16;
bank = &gpio_bank[i];
bank->reserved_map = 0;
spin_lock_init(&bank->lock);
if (bank->method == METHOD_MPUIO) {
__raw_writew(0xFFFF, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_MASKIT);
}
#ifdef CONFIG_ARCH_OMAP1510
if (bank->method == METHOD_GPIO_1510) {
__raw_writew(0xffff, bank->base + OMAP1510_GPIO_INT_MASK);
__raw_writew(0x0000, bank->base + OMAP1510_GPIO_INT_STATUS);
}
#endif
#ifdef CONFIG_ARCH_OMAP1610
if (bank->method == METHOD_GPIO_1610) {
__raw_writew(0x0000, bank->base + OMAP1610_GPIO_IRQENABLE1);
__raw_writew(0xffff, bank->base + OMAP1610_GPIO_IRQSTATUS1);
}
#endif
#ifdef CONFIG_ARCH_OMAP730
if (bank->method == METHOD_GPIO_730) {
__raw_writel(0xffffffff, bank->base + OMAP730_GPIO_INT_MASK);
__raw_writel(0x00000000, bank->base + OMAP730_GPIO_INT_STATUS);
gpio_count = 32; /* 730 has 32-bit GPIOs */
}
#endif
for (j = bank->virtual_irq_start;
j < bank->virtual_irq_start + gpio_count; j++) {
if (bank->method == METHOD_MPUIO)
set_irq_chip(j, &mpuio_irq_chip);
else
set_irq_chip(j, &gpio_irq_chip);
set_irq_handler(j, do_level_IRQ);
set_irq_flags(j, IRQF_VALID);
}
set_irq_chained_handler(bank->irq, gpio_irq_handler);
set_irq_data(bank->irq, bank);
}
/* Enable system clock for GPIO module.
* The CAM_CLK_CTRL_REG *is* really the right place. */
if (cpu_is_omap1610())
__raw_writel(__raw_readl(ULPD_CAM_CLK_CTRL_REG) | 0x04, ULPD_CAM_CLK_CTRL_REG);
return 0;
}
/*
* This may get called early from board specific init
*/
int omap_gpio_init(void)
{
if (!initialized)
return _omap_gpio_init();
else
return 0;
}
EXPORT_SYMBOL(omap_gpio_init);
EXPORT_SYMBOL(omap_request_gpio);
EXPORT_SYMBOL(omap_free_gpio);
arch_initcall(omap_gpio_init);
/*
* linux/arch/arm/mach-omap/innovator1510.c
*
* Board specific inits for OMAP-1510 Innovator
*
* 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>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <asm/hardware.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/arch/clocks.h>
#include <asm/arch/gpio.h>
#include <asm/arch/fpga.h>
#include "common.h"
extern int omap_gpio_init(void);
void innovator_init_irq(void)
{
omap_init_irq();
omap_gpio_init();
fpga_init_irq();
}
static struct resource smc91x_resources[] = {
[0] = {
.start = OMAP1510P1_FPGA_ETHR_START, /* Physical */
.end = OMAP1510P1_FPGA_ETHR_START + 16,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = INT_ETHER,
.end = INT_ETHER,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device smc91x_device = {
.name = "smc91x",
.id = 0,
.num_resources = ARRAY_SIZE(smc91x_resources),
.resource = smc91x_resources,
};
static struct platform_device *devices[] __initdata = {
&smc91x_device,
};
static void __init innovator_init(void)
{
if (!machine_is_innovator())
return;
(void) platform_add_devices(devices, ARRAY_SIZE(devices));
}
/* Only FPGA needs to be mapped here. All others are done with ioremap */
static struct map_desc innovator_io_desc[] __initdata = {
{ OMAP1510P1_FPGA_BASE, OMAP1510P1_FPGA_START, OMAP1510P1_FPGA_SIZE,
MT_DEVICE },
};
static void __init innovator_map_io(void)
{
omap_map_io();
iotable_init(innovator_io_desc, ARRAY_SIZE(innovator_io_desc));
/* Dump the Innovator FPGA rev early - useful info for support. */
printk("Innovator FPGA Rev %d.%d Board Rev %d\n",
fpga_read(OMAP1510P1_FPGA_REV_HIGH),
fpga_read(OMAP1510P1_FPGA_REV_LOW),
fpga_read(OMAP1510P1_FPGA_BOARD_REV));
}
MACHINE_START(INNOVATOR, "TI-Innovator/OMAP1510")
MAINTAINER("MontaVista Software, Inc.")
BOOT_MEM(0x10000000, 0xe0000000, 0xe0000000)
BOOT_PARAMS(0x10000100)
MAPIO(innovator_map_io)
INITIRQ(innovator_init_irq)
INIT_MACHINE(innovator_init)
MACHINE_END
/*
* linux/arch/arm/mach-omap/innovator1610.c
*
* This file contains Innovator-specific code.
*
* Copyright (C) 2002 MontaVista Software, Inc.
*
* Copyright (C) 2001 RidgeRun, Inc.
* Author: Greg Lonnon <glonnon@ridgerun.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <asm/setup.h>
#include <asm/page.h>
#include <asm/hardware.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/arch/irqs.h>
#include "common.h"
void
innovator_init_irq(void)
{
omap_init_irq();
}
static struct resource smc91x_resources[] = {
[0] = {
.start = OMAP1610_ETHR_START, /* Physical */
.end = OMAP1610_ETHR_START + SZ_4K,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 0, /* Really GPIO 0 */
.end = 0,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device smc91x_device = {
.name = "smc91x",
.id = 0,
.num_resources = ARRAY_SIZE(smc91x_resources),
.resource = smc91x_resources,
};
static struct platform_device *devices[] __initdata = {
&smc91x_device,
};
static void __init innovator_init(void)
{
if (!machine_is_innovator())
return;
(void) platform_add_devices(devices, ARRAY_SIZE(devices));
}
static struct map_desc innovator_io_desc[] __initdata = {
{ OMAP1610_ETHR_BASE, OMAP1610_ETHR_START, OMAP1610_ETHR_SIZE,MT_DEVICE },
{ OMAP1610_NOR_FLASH_BASE, OMAP1610_NOR_FLASH_START, OMAP1610_NOR_FLASH_SIZE,
MT_DEVICE },
};
static void __init innovator_map_io(void)
{
omap_map_io();
iotable_init(innovator_io_desc, ARRAY_SIZE(innovator_io_desc));
}
MACHINE_START(INNOVATOR, "TI-Innovator/OMAP1610")
MAINTAINER("MontaVista Software, Inc.")
BOOT_MEM(0x10000000, 0xe0000000, 0xe0000000)
BOOT_PARAMS(0x10000100)
MAPIO(innovator_map_io)
INITIRQ(innovator_init_irq)
INIT_MACHINE(innovator_init)
MACHINE_END
/*
* linux/arch/arm/mach-omap/irq.c
*
* Interrupt handler for OMAP-1510 and 1610
*
* Copyright (C) 2001 RidgeRun, Inc.
* Author: Greg Lonnon <glonnon@ridgerun.com>
*
* Modified for OMAP-1610 by Tony Lindgren <tony.lindgren@nokia.com>
* GPIO interrupt handler moved to gpio.c for OMAP-1610 by Juha Yrjola
*
* 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.
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/arch/gpio.h>
#include <asm/io.h>
#define NUM_IRQS IH_BOARD_BASE
static void mask_irq(unsigned int irq);
static void unmask_irq(unsigned int irq);
static void ack_irq(unsigned int irq);
static inline void
write_ih(int level, int reg, u32 value)
{
if (cpu_is_omap1510()) {
__raw_writel(value,
(IO_ADDRESS((level ? OMAP_IH2_BASE : OMAP_IH1_BASE) +
(reg))));
} else {
if (level) {
__raw_writel(value,
IO_ADDRESS(OMAP_IH2_BASE + ((level - 1) << 8) +
reg));
} else {
__raw_writel(value, IO_ADDRESS(OMAP_IH1_BASE + reg));
}
}
}
static inline u32
read_ih(int level, int reg)
{
if (cpu_is_omap1510()) {
return __raw_readl((IO_ADDRESS((level ? OMAP_IH2_BASE : OMAP_IH1_BASE)
+ (reg))));
} else {
if (level) {
return __raw_readl(IO_ADDRESS(OMAP_IH2_BASE +
((level - 1) << 8) + reg));
} else {
return __raw_readl(IO_ADDRESS(OMAP_IH1_BASE + reg));
}
}
}
static inline int
get_level(int irq)
{
if (cpu_is_omap1510()) {
return (((irq) < IH2_BASE) ? 0 : 1);
} else {
if (irq < IH2_BASE)
return 0;
else {
return (irq >> 5);
}
}
}
static inline int
get_irq_num(int irq)
{
if (cpu_is_omap1510()) {
return (((irq) < IH2_BASE) ? irq : irq - IH2_BASE);
} else {
return irq & 0x1f;
}
}
static void
mask_irq(unsigned int irq)
{
int level = get_level(irq);
int irq_num = get_irq_num(irq);
u32 mask = read_ih(level, IRQ_MIR) | (1 << irq_num);
write_ih(level, IRQ_MIR, mask);
}
static void
ack_irq(unsigned int irq)
{
int level = get_level(irq);
if (level > 1)
level = 1;
do {
write_ih(level, IRQ_CONTROL_REG, 0x1);
/*
* REVISIT: So says the TRM:
* if (level) write_ih(0, ITR, 0);
*/
} while (level--);
}
void
unmask_irq(unsigned int irq)
{
int level = get_level(irq);
int irq_num = get_irq_num(irq);
u32 mask = read_ih(level, IRQ_MIR) & ~(1 << irq_num);
write_ih(level, IRQ_MIR, mask);
}
static void
mask_ack_irq(unsigned int irq)
{
mask_irq(irq);
ack_irq(irq);
}
static struct irqchip omap_normal_irq = {
.ack = mask_ack_irq,
.mask = mask_irq,
.unmask = unmask_irq,
};
static void
irq_priority(int irq, int fiq, int priority, int trigger)
{
int level, irq_num;
unsigned long reg_value, reg_addr;
level = get_level(irq);
irq_num = get_irq_num(irq);
/* FIQ is only available on level 0 interrupts */
fiq = level ? 0 : (fiq & 0x1);
reg_value = (fiq) | ((priority & 0x1f) << 2) |
((trigger & 0x1) << 1);
reg_addr = (IRQ_ILR0 + irq_num * 0x4);
write_ih(level, reg_addr, reg_value);
}
void __init
omap_init_irq(void)
{
int i, irq_count, irq_bank_count = 0;
uint *trigger;
if (cpu_is_omap1510()) {
static uint trigger_1510[2] = {
0xb3febfff, 0xffbfffed
};
irq_bank_count = 2;
irq_count = 64;
trigger = trigger_1510;
}
if (cpu_is_omap1610()) {
static uint trigger_1610[5] = {
0xb3fefe8f, 0xfffff7ff, 0xffffffff
};
irq_bank_count = 5;
irq_count = 160;
trigger = trigger_1610;
}
if (cpu_is_omap730()) {
static uint trigger_730[] = {
0xb3f8e22f, 0xfdb9c1f2, 0x800040f3
};
irq_bank_count = 3;
irq_count = 96;
trigger = trigger_730;
}
for (i = 0; i < irq_bank_count; i++) {
/* Mask and clear all interrupts */
write_ih(i, IRQ_MIR, ~0x0);
write_ih(i, IRQ_ITR, 0x0);
}
/* Clear any pending interrupts */
write_ih(1, IRQ_CONTROL_REG, 3);
write_ih(0, IRQ_CONTROL_REG, 3);
for (i = 0; i < irq_count; i++) {
set_irq_chip(i, &omap_normal_irq);
set_irq_handler(i, do_level_IRQ);
set_irq_flags(i, IRQF_VALID);
irq_priority(i, 0, 0, trigger[get_level(i)] >> get_irq_num(i) & 1);
}
unmask_irq(INT_IH2_IRQ);
}
/*
* linux/arch/arm/mach-omap/leds-innovator.c
*/
#include <linux/config.h>
#include <linux/init.h>
#include <asm/hardware.h>
#include <asm/leds.h>
#include <asm/system.h>
#include "leds.h"
#define LED_STATE_ENABLED 1
#define LED_STATE_CLAIMED 2
static unsigned int led_state;
static unsigned int hw_led_state;
void innovator_leds_event(led_event_t evt)
{
unsigned long flags;
local_irq_save(flags);
switch (evt) {
case led_start:
hw_led_state = 0;
led_state = LED_STATE_ENABLED;
break;
case led_stop:
led_state &= ~LED_STATE_ENABLED;
hw_led_state = 0;
break;
case led_claim:
led_state |= LED_STATE_CLAIMED;
hw_led_state = 0;
break;
case led_release:
led_state &= ~LED_STATE_CLAIMED;
hw_led_state = 0;
break;
#ifdef CONFIG_LEDS_TIMER
case led_timer:
if (!(led_state & LED_STATE_CLAIMED))
hw_led_state ^= 0;
break;
#endif
#ifdef CONFIG_LEDS_CPU
case led_idle_start:
if (!(led_state & LED_STATE_CLAIMED))
hw_led_state |= 0;
break;
case led_idle_end:
if (!(led_state & LED_STATE_CLAIMED))
hw_led_state &= ~0;
break;
#endif
case led_halted:
break;
case led_green_on:
if (led_state & LED_STATE_CLAIMED)
hw_led_state &= ~0;
break;
case led_green_off:
if (led_state & LED_STATE_CLAIMED)
hw_led_state |= 0;
break;
case led_amber_on:
break;
case led_amber_off:
break;
case led_red_on:
if (led_state & LED_STATE_CLAIMED)
hw_led_state &= ~0;
break;
case led_red_off:
if (led_state & LED_STATE_CLAIMED)
hw_led_state |= 0;
break;
default:
break;
}
if (led_state & LED_STATE_ENABLED)
;
local_irq_restore(flags);
}
/*
* linux/arch/arm/mach-omap/leds-perseus2.c
*
* Copyright 2003 by Texas Instruments Incorporated
*
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/kernel_stat.h>
#include <linux/sched.h>
#include <linux/version.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/leds.h>
#include <asm/system.h>
#include <asm/arch/omap-perseus2.h>
#include "leds.h"
void perseus2_leds_event(led_event_t evt)
{
unsigned long flags;
static unsigned long hw_led_state = 0;
local_irq_save(flags);
switch (evt) {
case led_start:
hw_led_state |= OMAP730_FPGA_LED_STARTSTOP;
break;
case led_stop:
hw_led_state &= ~OMAP730_FPGA_LED_STARTSTOP;
break;
case led_claim:
hw_led_state |= OMAP730_FPGA_LED_CLAIMRELEASE;
break;
case led_release:
hw_led_state &= ~OMAP730_FPGA_LED_CLAIMRELEASE;
break;
#ifdef CONFIG_LEDS_TIMER
case led_timer:
/*
* Toggle Timer LED
*/
if (hw_led_state & OMAP730_FPGA_LED_TIMER)
hw_led_state &= ~OMAP730_FPGA_LED_TIMER;
else
hw_led_state |= OMAP730_FPGA_LED_TIMER;
break;
#endif
#ifdef CONFIG_LEDS_CPU
case led_idle_start:
hw_led_state |= OMAP730_FPGA_LED_IDLE;
break;
case led_idle_end:
hw_led_state &= ~OMAP730_FPGA_LED_IDLE;
break;
#endif
case led_halted:
if (hw_led_state & OMAP730_FPGA_LED_HALTED)
hw_led_state &= ~OMAP730_FPGA_LED_HALTED;
else
hw_led_state |= OMAP730_FPGA_LED_HALTED;
break;
case led_green_on:
break;
case led_green_off:
break;
case led_amber_on:
break;
case led_amber_off:
break;
case led_red_on:
break;
case led_red_off:
break;
default:
break;
}
/*
* Actually burn the LEDs
*/
__raw_writew(~hw_led_state & 0xffff, OMAP730_FPGA_LEDS);
local_irq_restore(flags);
}
/*
* linux/arch/arm/mach-omap/leds.c
*
* OMAP LEDs dispatcher
*/
#include <linux/init.h>
#include <asm/leds.h>
#include <asm/mach-types.h>
#include "leds.h"
static int __init
omap1510_leds_init(void)
{
if (machine_is_innovator())
leds_event = innovator_leds_event;
leds_event(led_start);
return 0;
}
__initcall(omap1510_leds_init);
extern void innovator_leds_event(led_event_t evt);
extern void perseus2_leds_event(led_event_t evt);
/*
* linux/arch/arm/mach-omap/mux.c
*
* Utility to set the Omap MUX and PULL_DWN registers from a table in mux.h
*
* Copyright (C) 2003 Nokia Corporation
*
* Written by Tony Lindgren <tony.lindgren@nokia.com>
*
* 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 program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/system.h>
#include <asm/io.h>
#include <linux/spinlock.h>
#define __MUX_C__
#include <asm/arch/mux.h>
static spinlock_t mux_spin_lock = SPIN_LOCK_UNLOCKED;
/*
* Sets the Omap MUX and PULL_DWN registers based on the table
*/
int omap_cfg_reg(const reg_cfg_t reg_cfg)
{
#ifdef CONFIG_OMAP_MUX
unsigned long flags;
reg_cfg_set *cfg;
unsigned int reg_orig = 0, reg = 0, pu_pd_orig = 0, pu_pd = 0,
pull_orig = 0, pull = 0;
cfg = &reg_cfg_table[reg_cfg];
/*
* We do a pretty long section here with lock on, but pin muxing
* should only happen on driver init for each driver, so it's not time
* critical.
*/
spin_lock_irqsave(&mux_spin_lock, flags);
/* Check the mux register in question */
if (cfg->mux_reg) {
reg_orig = __raw_readl(cfg->mux_reg);
/* The mux registers always seem to be 3 bits long */
reg = reg_orig & ~(0x7 << cfg->mask_offset);
reg |= (cfg->mask << cfg->mask_offset);
__raw_writel(reg, cfg->mux_reg);
}
/* Check for pull up or pull down selection on 1610 */
if (!cpu_is_omap1510()) {
if (cfg->pu_pd_reg && cfg->pull_val) {
pu_pd_orig = __raw_readl(cfg->pu_pd_reg);
if (cfg->pu_pd_val) {
/* Use pull up */
pu_pd = pu_pd_orig | (1 << cfg->pull_bit);
} else {
/* Use pull down */
pu_pd = pu_pd_orig & ~(1 << cfg->pull_bit);
}
__raw_writel(pu_pd, cfg->pu_pd_reg);
}
}
/* Check for an associated pull down register */
if (cfg->pull_reg) {
pull_orig = __raw_readl(cfg->pull_reg);
if (cfg->pull_val) {
/* Low bit = pull enabled */
pull = pull_orig & ~(1 << cfg->pull_bit);
} else {
/* High bit = pull disabled */
pull = pull_orig | (1 << cfg->pull_bit);
}
__raw_writel(pull, cfg->pull_reg);
}
#ifdef CONFIG_OMAP_MUX_DEBUG
if (cfg->debug) {
printk("Omap: Setting register %s\n", cfg->name);
printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n",
cfg->mux_reg_name, cfg->mux_reg, reg_orig, reg);
if (!cpu_is_omap1510()) {
if (cfg->pu_pd_reg && cfg->pull_val) {
printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n",
cfg->pu_pd_name, cfg->pu_pd_reg,
pu_pd_orig, pu_pd);
}
}
printk(" %s (0x%08x) = 0x%08x -> 0x%08x\n",
cfg->pull_name, cfg->pull_reg, pull_orig, pull);
}
#endif
spin_unlock_irqrestore(&mux_spin_lock, flags);
#endif
return 0;
}
EXPORT_SYMBOL(omap_cfg_reg);
/*
* linux/arch/arm/mach-omap/ocpi.c
*
* Minimal OCP bus support for OMAP-1610
*
* Copyright (C) 2003 - 2004 Nokia Corporation
* Written by Tony Lindgren <tony@atomide.com>
*
* 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 program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#define OCPI_BASE 0xfffec320
#define OCPI_FAULT (OCPI_BASE + 0x00)
#define OCPI_CMD_FAULT (OCPI_BASE + 0x04)
#define OCPI_SINT0 (OCPI_BASE + 0x08)
#define OCPI_TABORT (OCPI_BASE + 0x0c)
#define OCPI_SINT1 (OCPI_BASE + 0x10)
#define OCPI_PROT (OCPI_BASE + 0x14)
#define OCPI_SEC (OCPI_BASE + 0x18)
#define EN_OCPI_CK (1 << 0)
#define IDLOCPI_ARM (1 << 1)
/* USB OHCI OCPI access error registers */
#define HOSTUEADDR 0xfffba0e0
#define HOSTUESTATUS 0xfffba0e4
/*
* Enables device access to OMAP buses via the OCPI bridge
* FIXME: Add locking
*/
int ocpi_enable(void)
{
unsigned int val;
/* Make sure there's clock for OCPI */
val = __raw_readl(ARM_IDLECT3);
val |= EN_OCPI_CK;
val &= ~IDLOCPI_ARM;
__raw_writel(val, ARM_IDLECT3);
/* Enable access for OHCI in OCPI */
val = __raw_readl(OCPI_PROT);
val &= ~0xff;
//val &= (1 << 0); /* Allow access only to EMIFS */
__raw_writel(val, OCPI_PROT);
val = __raw_readl(OCPI_SEC);
val &= ~0xff;
__raw_writel(val, OCPI_SEC);
val = __raw_readl(OCPI_SEC);
val |= 0;
__raw_writel(val, OCPI_SEC);
val = __raw_readl(OCPI_SINT0);
val |= 0;
__raw_writel(val, OCPI_SINT1);
return 0;
}
EXPORT_SYMBOL(ocpi_enable);
int ocpi_status(void)
{
printk("OCPI: addr: 0x%08x cmd: 0x%08x\n"
" ohci-addr: 0x%08x ohci-status: 0x%08x\n",
__raw_readl(OCPI_FAULT), __raw_readl(OCPI_CMD_FAULT),
__raw_readl(HOSTUEADDR), __raw_readl(HOSTUESTATUS));
return 1;
}
EXPORT_SYMBOL(ocpi_status);
static int __init omap_ocpi_init(void)
{
ocpi_enable();
printk("OMAP OCPI interconnect driver loaded\n");
return 0;
}
static void __exit omap_ocpi_exit(void)
{
/* FIXME: Disable OCPI */
}
MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
MODULE_DESCRIPTION("OMAP OCPI bus controller module");
MODULE_LICENSE("GPL");
module_init(omap_ocpi_init);
module_exit(omap_ocpi_exit);
/*
* linux/arch/arm/mach-omap/generic.c
*
* Modified from innovator.c
*
* Code for generic OMAP board. Should work on many OMAP systems where
* the device drivers take care of all the necessary hardware initialization.
* Do not put any board specific code to this file; create a new machine
* type if you need custom low-level initializations.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <asm/hardware.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/arch/clocks.h>
#include <asm/arch/gpio.h>
#include <asm/arch/mux.h>
#include "common.h"
static void __init omap_generic_init_irq(void)
{
omap_init_irq();
}
/*
* Muxes the serial ports on
*/
static void __init omap_early_serial_init(void)
{
omap_cfg_reg(UART1_TX);
omap_cfg_reg(UART1_RTS);
omap_cfg_reg(UART2_TX);
omap_cfg_reg(UART2_RTS);
omap_cfg_reg(UART3_TX);
omap_cfg_reg(UART3_RX);
}
static void __init omap_generic_init(void)
{
if (!machine_is_omap_generic())
return;
/*
* Make sure the serial ports are muxed on at this point.
* You have to mux them off in device drivers later on
* if not needed.
*/
if (cpu_is_omap1510()) {
omap_early_serial_init();
}
}
static void __init omap_generic_map_io(void)
{
omap_map_io();
}
MACHINE_START(OMAP_GENERIC, "Generic OMAP-1510/1610")
MAINTAINER("Tony Lindgren <tony@atomide.com>")
BOOT_MEM(0x10000000, 0xe0000000, 0xe0000000)
BOOT_PARAMS(0x10000100)
MAPIO(omap_generic_map_io)
INITIRQ(omap_generic_init_irq)
INIT_MACHINE(omap_generic_init)
MACHINE_END
/*
* linux/arch/arm/mach-omap/omap-perseus2.c
*
* Modified from omap-generic.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <asm/hardware.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/arch/clocks.h>
#include <asm/arch/gpio.h>
#include <asm/arch/mux.h>
#include <asm/arch/omap-perseus2.h>
#include "common.h"
void omap_perseus2_init_irq(void)
{
omap_init_irq();
}
static struct resource smc91x_resources[] = {
[0] = {
.start = OMAP730_FPGA_ETHR_START, /* Physical */
.end = OMAP730_FPGA_ETHR_START + SZ_4K,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 0,
.end = 0,
.flags = INT_ETHER,
},
};
static struct platform_device smc91x_device = {
.name = "smc91x",
.id = 0,
.num_resources = ARRAY_SIZE(smc91x_resources),
.resource = smc91x_resources,
};
static struct platform_device *devices[] __initdata = {
&smc91x_device,
};
static void __init omap_perseus2_init(void)
{
if (!machine_is_omap_perseus2())
return;
(void) platform_add_devices(devices, ARRAY_SIZE(devices));
}
/* Only FPGA needs to be mapped here. All others are done with ioremap */
static struct map_desc omap_perseus2_io_desc[] __initdata = {
{OMAP730_FPGA_BASE, OMAP730_FPGA_START, OMAP730_FPGA_SIZE,
MT_DEVICE},
};
static void __init omap_perseus2_map_io(void)
{
omap_map_io();
iotable_init(omap_perseus2_io_desc,
ARRAY_SIZE(omap_perseus2_io_desc));
/* Early, board-dependent init */
/*
* Hold GSM Reset until needed
*/
*DSP_M_CTL &= ~1;
/*
* UARTs -> done automagically by 8250 driver
*/
/*
* CSx timings, GPIO Mux ... setup
*/
/* Flash: CS0 timings setup */
*((volatile __u32 *) OMAP_FLASH_CFG_0) = 0x0000fff3;
*((volatile __u32 *) OMAP_FLASH_ACFG_0) = 0x00000088;
/*
* Ethernet support trough the debug board
* CS1 timings setup
*/
*((volatile __u32 *) OMAP_FLASH_CFG_1) = 0x0000fff3;
*((volatile __u32 *) OMAP_FLASH_ACFG_1) = 0x00000000;
/*
* Configure MPU_EXT_NIRQ IO in IO_CONF9 register,
* It is used as the Ethernet controller interrupt
*/
*((volatile __u32 *) PERSEUS2_IO_CONF_9) &= 0x1FFFFFFF;
}
MACHINE_START(OMAP_PERSEUS2, "OMAP730 Perseus2")
MAINTAINER("Kevin Hilman <k-hilman@ti.com>")
BOOT_MEM(0x10000000, 0xe0000000, 0xe0000000)
BOOT_PARAMS(0x10000100)
MAPIO(omap_perseus2_map_io)
INITIRQ(omap_perseus2_init_irq)
INIT_MACHINE(omap_perseus2_init)
MACHINE_END
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment