Commit 82e6923e authored by Russell King's avatar Russell King

ARM: lh7a40x: remove unmaintained platform support

lh7a40x has only been receiving updates for updates to generic code.
The last involvement from the maintainer according to the git logs was
in 2006.  As such, it is a maintainence burden with no benefit.

This gets rid of two defconfigs.
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 1bae4ce2
README on the ADC/Touchscreen Controller
========================================
The LH79524 and LH7A404 include a built-in Analog to Digital
controller (ADC) that is used to process input from a touchscreen.
The driver only implements a four-wire touch panel protocol.
The touchscreen driver is maintenance free except for the pen-down or
touch threshold. Some resistive displays and board combinations may
require tuning of this threshold. The driver exposes some of its
internal state in the sys filesystem. If the kernel is configured
with it, CONFIG_SYSFS, and sysfs is mounted at /sys, there will be a
directory
/sys/devices/platform/adc-lh7.0
containing these files.
-r--r--r-- 1 root root 4096 Jan 1 00:00 samples
-rw-r--r-- 1 root root 4096 Jan 1 00:00 threshold
-r--r--r-- 1 root root 4096 Jan 1 00:00 threshold_range
The threshold is the current touch threshold. It defaults to 750 on
most targets.
# cat threshold
750
The threshold_range contains the range of valid values for the
threshold. Values outside of this range will be silently ignored.
# cat threshold_range
0 1023
To change the threshold, write a value to the threshold file.
# echo 500 > threshold
# cat threshold
500
The samples file contains the most recently sampled values from the
ADC. There are 12. Below are typical of the last sampled values when
the pen has been released. The first two and last two samples are for
detecting whether or not the pen is down. The third through sixth are
X coordinate samples. The seventh through tenth are Y coordinate
samples.
# cat samples
1023 1023 0 0 0 0 530 529 530 529 1023 1023
To determine a reasonable threshold, press on the touch panel with an
appropriate stylus and read the values from samples.
# cat samples
1023 676 92 103 101 102 855 919 922 922 1023 679
The first and eleventh samples are discarded. Thus, the important
values are the second and twelfth which are used to determine if the
pen is down. When both are below the threshold, the driver registers
that the pen is down. When either is above the threshold, it
registers then pen is up.
README on the Compact Flash for Card Engines
============================================
There are three challenges in supporting the CF interface of the Card
Engines. First, every IO operation must be followed with IO to
another memory region. Second, the slot is wired for one-to-one
address mapping *and* it is wired for 16 bit access only. Second, the
interrupt request line from the CF device isn't wired.
The IOBARRIER issue is covered in README.IOBARRIER. This isn't an
onerous problem. Enough said here.
The addressing issue is solved in the
arch/arm/mach-lh7a40x/ide-lpd7a40x.c file with some awkward
work-arounds. We implement a special SELECT_DRIVE routine that is
called before the IDE driver performs its own SELECT_DRIVE. Our code
recognizes that the SELECT register cannot be modified without also
writing a command. It send an IDLE_IMMEDIATE command on selecting a
drive. The function also prevents drive select to the slave drive
since there can be only one. The awkward part is that the IDE driver,
even though we have a select procedure, also attempts to change the
drive by writing directly the SELECT register. This attempt is
explicitly blocked by the OUTB function--not pretty, but effective.
The lack of interrupts is a more serious problem. Even though the CF
card is fast when compared to a normal IDE device, we don't know that
the CF is really flash. A user could use one of the very small hard
drives being shipped with a CF interface. The IDE code includes a
check for interfaces that lack an IRQ. In these cases, submitting a
command to the IDE controller is followed by a call to poll for
completion. If the device isn't immediately ready, it schedules a
timer to poll again later.
README on the IOBARRIER for CardEngine IO
=========================================
Due to an unfortunate oversight when the Card Engines were designed,
the signals that control access to some peripherals, most notably the
SMC91C9111 ethernet controller, are not properly handled.
The symptom is that some back to back IO with the peripheral returns
unreliable data. With the SMC chip, you'll see errors about the bank
register being 'screwed'.
The cause is that the AEN signal to the SMC chip does not transition
for every memory access. It is driven through the CPLD from the CS7
line of the CPU's static memory controller which is optimized to
eliminate unnecessary transitions. Yet, the SMC requires a transition
for every write access. The Sharp website has more information about
the effect this power-conserving feature has on peripheral
interfacing.
The solution is to follow every write access to the SMC chip with an
access to another memory region that will force the CPU to release the
chip select line. It is important to guarantee that this access
forces the CPU off-chip. We map a page of SDRAM as if it were an
uncacheable IO device and read from it after every SMC IO write
operation.
SMC IO
BARRIER IO
Only this sequence is important. It does not matter that there is no
BARRIER IO before the access to the SMC chip because the AEN latch
only needs occurs after the SMC IO write cycle. The routines that
implement this work-around make an additional concession which is to
disable interrupts during the IO sequence. Other hardware devices
(the LogicPD CPLD) have registers in the same physical memory
region as the SMC chip. An interrupt might allow an access to one of
those registers while SMC IO is being performed.
You might be tempted to think that we have to access another device
attached to the static memory controller, but the empirical evidence
indicates that this is not so. Mapping 0x00000000 (flash) and
0xc0000000 (SDRAM) appear to have the same effect. Using SDRAM seems
to be faster. Choosing to access an undecoded memory region is not
desirable as there is no way to know how that chip select will be used
in the future.
README on Implementing Linux for Sharp's KEV7a400
=================================================
This product has been discontinued by Sharp. For the time being, the
partially implemented code remains in the kernel. At some point in
the future, either the code will be finished or it will be removed
completely. This depends primarily on how many of the development
boards are in the field.
README on the LCD Panels
========================
Configuration options for several LCD panels, available from Logic PD,
are included in the kernel source. This README will help you
understand the configuration data and give you some guidance for
adding support for other panels if you wish.
lcd-panels.h
------------
There is no way, at present, to detect which panel is attached to the
system at runtime. Thus the kernel configuration is static. The file
arch/arm/mach-ld7a40x/lcd-panels.h (or similar) defines all of the
panel specific parameters.
It should be possible for this data to be shared among several device
families. The current layout may be insufficiently general, but it is
amenable to improvement.
PIXEL_CLOCK
-----------
The panel data sheets will give a range of acceptable pixel clocks.
The fundamental LCDCLK input frequency is divided down by a PCD
constant in field '.tim2'. It may happen that it is impossible to set
the pixel clock within this range. A clock which is too slow will
tend to flicker. For the highest quality image, set the clock as high
as possible.
MARGINS
-------
These values may be difficult to glean from the panel data sheet. In
the case of the Sharp panels, the upper margin is explicitly called
out as a specific number of lines from the top of the frame. The
other values may not matter as much as the panels tend to
automatically center the image.
Sync Sense
----------
The sense of the hsync and vsync pulses may be called out in the data
sheet. On one panel, the sense of these pulses determine the height
of the visible region on the panel. Most of the Sharp panels use
negative sense sync pulses set by the TIM2_IHS and TIM2_IVS bits in
'.tim2'.
Pel Layout
----------
The Sharp color TFT panels are all configured for 16 bit direct color
modes. The amba-lcd driver sets the pel mode to 565 for 5 bits of
each red and blue and 6 bits of green.
README on Implementing Linux for the Logic PD LPD7A400-10
=========================================================
- CPLD memory mapping
The board designers chose to use high address lines for controlling
access to the CPLD registers. It turns out to be a big waste
because we're using an MMU and must map IO space into virtual
memory. The result is that we have to make a mapping for every
register.
- Serial Console
It may be OK not to use the serial console option if the user passes
the console device name to the kernel. This deserves some exploration.
README on Implementing Linux for the Logic PD LPD7A40X-10
=========================================================
- CPLD memory mapping
The board designers chose to use high address lines for controlling
access to the CPLD registers. It turns out to be a big waste
because we're using an MMU and must map IO space into virtual
memory. The result is that we have to make a mapping for every
register.
- Serial Console
It may be OK not to use the serial console option if the user passes
the console device name to the kernel. This deserves some exploration.
README on the SDRAM Controller for the LH7a40X
==============================================
The standard configuration for the SDRAM controller generates a sparse
memory array. The precise layout is determined by the SDRAM chips. A
default kernel configuration assembles the discontiguous memory
regions into separate memory nodes via the NUMA (Non-Uniform Memory
Architecture) facilities. In this default configuration, the kernel
is forgiving about the precise layout. As long as it is given an
accurate picture of available memory by the bootloader the kernel will
execute correctly.
The SDRC supports a mode where some of the chip select lines are
swapped in order to make SDRAM look like a synchronous ROM. Setting
this bit means that the RAM will present as a contiguous array. Some
programmers prefer this to the discontiguous layout. Be aware that
may be a penalty for this feature where some some configurations of
memory are significantly reduced; i.e. 64MiB of RAM appears as only 32
MiB.
There are a couple of configuration options to override the default
behavior. When the SROMLL bit is set and memory appears as a
contiguous array, there is no reason to support NUMA.
CONFIG_LH7A40X_CONTIGMEM disables NUMA support. When physical memory
is discontiguous, the memory tables are organized such that there are
two banks per nodes with a small gap between them. This layout wastes
some kernel memory for page tables representing non-existent memory.
CONFIG_LH7A40X_ONE_BANK_PER_NODE optimizes the node tables such that
there are no gaps. These options control the low level organization
of the memory management tables in ways that may prevent the kernel
from booting or may cause the kernel to allocated excessively large
page tables. Be warned. Only change these options if you know what
you are doing. The default behavior is a reasonable compromise that
will suit all users.
--
A typical 32MiB system with the default configuration options will
find physical memory managed as follows.
node 0: 0xc0000000 4MiB
0xc1000000 4MiB
node 1: 0xc4000000 4MiB
0xc5000000 4MiB
node 2: 0xc8000000 4MiB
0xc9000000 4MiB
node 3: 0xcc000000 4MiB
0xcd000000 4MiB
Setting CONFIG_LH7A40X_ONE_BANK_PER_NODE will put each bank into a
separate node.
README on the Vectored Interrupt Controller of the LH7A404
==========================================================
The 404 revision of the LH7A40X series comes with two vectored
interrupts controllers. While the kernel does use some of the
features of these devices, it is far from the purpose for which they
were designed.
When this README was written, the implementation of the VICs was in
flux. It is possible that some details, especially with priorities,
will change.
The VIC support code is inspired by routines written by Sharp.
Priority Control
----------------
The significant reason for using the VIC's vectoring is to control
interrupt priorities. There are two tables in
arch/arm/mach-lh7a40x/irq-lh7a404.c that look something like this.
static unsigned char irq_pri_vic1[] = { IRQ_GPIO3INTR, };
static unsigned char irq_pri_vic2[] = {
IRQ_T3UI, IRQ_GPIO7INTR,
IRQ_UART1INTR, IRQ_UART2INTR, IRQ_UART3INTR, };
The initialization code reads these tables and inserts a vector
address and enable for each indicated IRQ. Vectored interrupts have
higher priority than non-vectored interrupts. So, on VIC1,
IRQ_GPIO3INTR will be served before any other non-FIQ interrupt. Due
to the way that the vectoring works, IRQ_T3UI is the next highest
priority followed by the other vectored interrupts on VIC2. After
that, the non-vectored interrupts are scanned in VIC1 then in VIC2.
ISR
---
The interrupt service routine macro get_irqnr() in
arch/arm/kernel/entry-armv.S scans the VICs for the next active
interrupt. The vectoring makes this code somewhat larger than it was
before using vectoring (refer to the LH7A400 implementation). In the
case where an interrupt is vectored, the implementation will tend to
be faster than the non-vectored version. However, the worst-case path
is longer.
It is worth noting that at present, there is no need to read
VIC2_VECTADDR because the register appears to be shared between the
controllers. The code is written such that if this changes, it ought
to still work properly.
Vector Addresses
----------------
The proper use of the vectoring hardware would jump to the ISR
specified by the vectoring address. Linux isn't structured to take
advantage of this feature, though it might be possible to change
things to support it.
In this implementation, the vectoring address is used to speed the
search for the active IRQ. The address is coded such that the lowest
6 bits store the IRQ number for vectored interrupts. These numbers
correspond to the bits in the interrupt status registers. IRQ zero is
the lowest interrupt bit in VIC1. IRQ 32 is the lowest interrupt bit
in VIC2. Because zero is a valid IRQ number and because we cannot
detect whether or not there is a valid vectoring address if that
address is zero, the eigth bit (0x100) is set for vectored interrupts.
The address for IRQ 0x18 (VIC2) is 0x118. Only the ninth bit is set
for the default handler on VIC1 and only the tenth bit is set for the
default handler on VIC2.
In other words.
0x000 - no active interrupt
0x1ii - vectored interrupt 0xii
0x2xx - unvectored interrupt on VIC1 (xx is don't care)
0x4xx - unvectored interrupt on VIC2 (xx is don't care)
...@@ -795,17 +795,6 @@ config ARCH_TCC_926 ...@@ -795,17 +795,6 @@ config ARCH_TCC_926
help help
Support for Telechips TCC ARM926-based systems. Support for Telechips TCC ARM926-based systems.
config ARCH_LH7A40X
bool "Sharp LH7A40X"
select CPU_ARM922T
select ARCH_SPARSEMEM_ENABLE if !LH7A40X_CONTIGMEM
select ARCH_USES_GETTIMEOFFSET
help
Say Y here for systems based on one of the Sharp LH7A40X
System on a Chip processors. These CPUs include an ARM922T
core with a wide array of integrated devices for
hand-held and low-power applications.
config ARCH_U300 config ARCH_U300
bool "ST-Ericsson U300 Series" bool "ST-Ericsson U300 Series"
depends on MMU depends on MMU
...@@ -922,8 +911,6 @@ source "arch/arm/mach-kirkwood/Kconfig" ...@@ -922,8 +911,6 @@ source "arch/arm/mach-kirkwood/Kconfig"
source "arch/arm/mach-ks8695/Kconfig" source "arch/arm/mach-ks8695/Kconfig"
source "arch/arm/mach-lh7a40x/Kconfig"
source "arch/arm/mach-loki/Kconfig" source "arch/arm/mach-loki/Kconfig"
source "arch/arm/mach-lpc32xx/Kconfig" source "arch/arm/mach-lpc32xx/Kconfig"
......
...@@ -146,7 +146,6 @@ machine-$(CONFIG_ARCH_IXP23XX) := ixp23xx ...@@ -146,7 +146,6 @@ machine-$(CONFIG_ARCH_IXP23XX) := ixp23xx
machine-$(CONFIG_ARCH_IXP4XX) := ixp4xx machine-$(CONFIG_ARCH_IXP4XX) := ixp4xx
machine-$(CONFIG_ARCH_KIRKWOOD) := kirkwood machine-$(CONFIG_ARCH_KIRKWOOD) := kirkwood
machine-$(CONFIG_ARCH_KS8695) := ks8695 machine-$(CONFIG_ARCH_KS8695) := ks8695
machine-$(CONFIG_ARCH_LH7A40X) := lh7a40x
machine-$(CONFIG_ARCH_LOKI) := loki machine-$(CONFIG_ARCH_LOKI) := loki
machine-$(CONFIG_ARCH_LPC32XX) := lpc32xx machine-$(CONFIG_ARCH_LPC32XX) := lpc32xx
machine-$(CONFIG_ARCH_MMP) := mmp machine-$(CONFIG_ARCH_MMP) := mmp
......
CONFIG_EXPERIMENTAL=y
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
CONFIG_IKCONFIG=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_EXPERT=y
# CONFIG_HOTPLUG is not set
# CONFIG_EPOLL is not set
# CONFIG_IOSCHED_DEADLINE is not set
CONFIG_ARCH_LH7A40X=y
CONFIG_MACH_LPD7A400=y
CONFIG_PREEMPT=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_FPE_NWFPE=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_IP_PNP_BOOTP=y
CONFIG_IP_PNP_RARP=y
# CONFIG_IPV6 is not set
CONFIG_MTD=y
CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_PHYSMAP=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_IDE=y
CONFIG_SCSI=y
# CONFIG_SCSI_PROC_FS is not set
CONFIG_NETDEVICES=y
CONFIG_NET_ETHERNET=y
CONFIG_SMC91X=y
# CONFIG_INPUT_MOUSEDEV is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_SERIO is not set
CONFIG_SERIAL_LH7A40X=y
CONFIG_SERIAL_LH7A40X_CONSOLE=y
CONFIG_FB=y
# CONFIG_VGA_CONSOLE is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_MIXER_OSS=y
CONFIG_SND_PCM_OSS=y
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_JFFS2_FS=y
CONFIG_CRAMFS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
CONFIG_ROOT_NFS=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_ERRORS=y
CONFIG_EXPERIMENTAL=y
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
CONFIG_IKCONFIG=y
CONFIG_LOG_BUF_SHIFT=16
CONFIG_EXPERT=y
# CONFIG_HOTPLUG is not set
# CONFIG_EPOLL is not set
CONFIG_SLAB=y
# CONFIG_IOSCHED_DEADLINE is not set
CONFIG_ARCH_LH7A40X=y
CONFIG_MACH_LPD7A404=y
CONFIG_PREEMPT=y
CONFIG_DISCONTIGMEM_MANUAL=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_FPE_NWFPE=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_IP_PNP_BOOTP=y
CONFIG_IP_PNP_RARP=y
# CONFIG_IPV6 is not set
CONFIG_MTD=y
CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_PHYSMAP=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_IDE=y
CONFIG_SCSI=y
# CONFIG_SCSI_PROC_FS is not set
CONFIG_NETDEVICES=y
CONFIG_NET_ETHERNET=y
CONFIG_SMC91X=y
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_SERIO is not set
CONFIG_SERIAL_LH7A40X=y
CONFIG_SERIAL_LH7A40X_CONSOLE=y
CONFIG_FB=y
# CONFIG_VGA_CONSOLE is not set
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_MIXER_OSS=y
CONFIG_SND_PCM_OSS=y
CONFIG_USB=y
CONFIG_USB_DEVICEFS=y
CONFIG_USB_MON=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_STORAGE=y
CONFIG_USB_STORAGE_DEBUG=y
CONFIG_USB_STORAGE_DATAFAB=y
CONFIG_USB_GADGET=y
CONFIG_USB_ZERO=y
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
CONFIG_INOTIFY=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_JFFS2_FS=y
CONFIG_CRAMFS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
CONFIG_ROOT_NFS=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_ERRORS=y
...@@ -192,11 +192,7 @@ static struct tagtable __tagtable_##fn __tag = { tag, fn } ...@@ -192,11 +192,7 @@ static struct tagtable __tagtable_##fn __tag = { tag, fn }
/* /*
* Memory map description * Memory map description
*/ */
#ifdef CONFIG_ARCH_LH7A40X #define NR_BANKS 8
# define NR_BANKS 16
#else
# define NR_BANKS 8
#endif
struct membank { struct membank {
unsigned long start; unsigned long start;
......
if ARCH_LH7A40X
menu "LH7A40X Implementations"
config MACH_KEV7A400
bool "KEV7A400"
select ARCH_LH7A400
help
Say Y here if you are using the Sharp KEV7A400 development
board. This hardware is discontinued, so I'd be very
surprised if you wanted this option.
config MACH_LPD7A400
bool "LPD7A400 Card Engine"
select ARCH_LH7A400
# select IDE_POLL
# select HAS_TOUCHSCREEN_ADS7843_LH7
help
Say Y here if you are using Logic Product Development's
LPD7A400 CardEngine. For the time being, the LPD7A400 and
LPD7A404 options are mutually exclusive.
config MACH_LPD7A404
bool "LPD7A404 Card Engine"
select ARCH_LH7A404
# select IDE_POLL
# select HAS_TOUCHSCREEN_ADC_LH7
help
Say Y here if you are using Logic Product Development's
LPD7A404 CardEngine. For the time being, the LPD7A400 and
LPD7A404 options are mutually exclusive.
config ARCH_LH7A400
bool
config ARCH_LH7A404
bool
config LPD7A40X_CPLD_SSP
bool
config LH7A40X_CONTIGMEM
bool "Disable NUMA/SparseMEM Support"
help
Say Y here if your bootloader sets the SROMLL bit(s) in
the SDRAM controller, organizing memory as a contiguous
array. This option will disable sparse memory support
and force the kernel to manage all memory in one node.
Setting this option incorrectly may prevent the kernel
from booting. It is OK to leave it N.
For more information, consult
<file:Documentation/arm/Sharp-LH/SDRAM>.
config LH7A40X_ONE_BANK_PER_NODE
bool "Optimize NUMA Node Tables for Size"
depends on !LH7A40X_CONTIGMEM
help
Say Y here to produce compact memory node tables. By
default pairs of adjacent physical RAM banks are managed
together in a single node, incurring some wasted overhead
in the node tables, however also maintaining compatibility
with systems where physical memory is truly contiguous.
Setting this option incorrectly may prevent the kernel from
booting. It is OK to leave it N.
For more information, consult
<file:Documentation/arm/Sharp-LH/SDRAM>.
endmenu
endif
#
# Makefile for the linux kernel.
#
# Object file lists.
obj-y := time.o clocks.o
obj-m :=
obj-n :=
obj- :=
obj-$(CONFIG_MACH_KEV7A400) += arch-kev7a400.o irq-lh7a400.o
obj-$(CONFIG_MACH_LPD7A400) += arch-lpd7a40x.o irq-lh7a400.o
obj-$(CONFIG_MACH_LPD7A404) += arch-lpd7a40x.o irq-lh7a404.o
obj-$(CONFIG_LPD7A40X_CPLD_SSP) += ssp-cpld.o
obj-$(CONFIG_FB_ARMCLCD) += clcd.o
zreladdr-y := 0xc0008000
params_phys-y := 0xc0000100
initrd_phys-y := 0xc4000000
/* arch/arm/mach-lh7a40x/arch-kev7a400.c
*
* Copyright (C) 2004 Logic Product Development
*
* 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/tty.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <mach/hardware.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/mach/map.h>
#include "common.h"
/* This function calls the board specific IRQ initialization function. */
static struct map_desc kev7a400_io_desc[] __initdata = {
{
.virtual = IO_VIRT,
.pfn = __phys_to_pfn(IO_PHYS),
.length = IO_SIZE,
.type = MT_DEVICE
}, {
.virtual = CPLD_VIRT,
.pfn = __phys_to_pfn(CPLD_PHYS),
.length = CPLD_SIZE,
.type = MT_DEVICE
}
};
void __init kev7a400_map_io(void)
{
iotable_init (kev7a400_io_desc, ARRAY_SIZE (kev7a400_io_desc));
}
static u16 CPLD_IRQ_mask; /* Mask for CPLD IRQs, 1 == unmasked */
static void kev7a400_ack_cpld_irq(struct irq_data *d)
{
CPLD_CL_INT = 1 << (d->irq - IRQ_KEV7A400_CPLD);
}
static void kev7a400_mask_cpld_irq(struct irq_data *d)
{
CPLD_IRQ_mask &= ~(1 << (d->irq - IRQ_KEV7A400_CPLD));
CPLD_WR_PB_INT_MASK = CPLD_IRQ_mask;
}
static void kev7a400_unmask_cpld_irq(struct irq_data *d)
{
CPLD_IRQ_mask |= 1 << (d->irq - IRQ_KEV7A400_CPLD);
CPLD_WR_PB_INT_MASK = CPLD_IRQ_mask;
}
static struct irq_chip kev7a400_cpld_chip = {
.name = "CPLD",
.irq_ack = kev7a400_ack_cpld_irq,
.irq_mask = kev7a400_mask_cpld_irq,
.irq_unmask = kev7a400_unmask_cpld_irq,
};
static void kev7a400_cpld_handler (unsigned int irq, struct irq_desc *desc)
{
u32 mask = CPLD_LATCHED_INTS;
irq = IRQ_KEV7A400_CPLD;
for (; mask; mask >>= 1, ++irq)
if (mask & 1)
generic_handle_irq(irq);
}
void __init lh7a40x_init_board_irq (void)
{
int irq;
for (irq = IRQ_KEV7A400_CPLD;
irq < IRQ_KEV7A400_CPLD + NR_IRQ_BOARD; ++irq) {
set_irq_chip (irq, &kev7a400_cpld_chip);
set_irq_handler (irq, handle_edge_irq);
set_irq_flags (irq, IRQF_VALID);
}
set_irq_chained_handler (IRQ_CPLD, kev7a400_cpld_handler);
/* Clear all CPLD interrupts */
CPLD_CL_INT = 0xff; /* CPLD_INTR_MMC_CD | CPLD_INTR_ETH_INT; */
GPIO_GPIOINTEN = 0; /* Disable all GPIO interrupts */
barrier();
#if 0
GPIO_INTTYPE1
= (GPIO_INTR_PCC1_CD | GPIO_INTR_PCC1_CD); /* Edge trig. */
GPIO_INTTYPE2 = 0; /* Falling edge & low-level */
GPIO_GPIOFEOI = 0xff; /* Clear all GPIO interrupts */
GPIO_GPIOINTEN = 0xff; /* Enable all GPIO interrupts */
init_FIQ();
#endif
}
MACHINE_START (KEV7A400, "Sharp KEV7a400")
/* Maintainer: Marc Singer */
.boot_params = 0xc0000100,
.map_io = kev7a400_map_io,
.init_irq = lh7a400_init_irq,
.timer = &lh7a40x_timer,
MACHINE_END
/* arch/arm/mach-lh7a40x/arch-lpd7a40x.c
*
* Copyright (C) 2004 Logic Product Development
*
* 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/tty.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <mach/hardware.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/mach/map.h>
#include "common.h"
#define CPLD_INT_NETHERNET (1<<0)
#define CPLD_INTMASK_ETHERNET (1<<2)
#if defined (CONFIG_MACH_LPD7A400)
# define CPLD_INT_NTOUCH (1<<1)
# define CPLD_INTMASK_TOUCH (1<<3)
# define CPLD_INT_PEN (1<<4)
# define CPLD_INTMASK_PEN (1<<4)
# define CPLD_INT_PIRQ (1<<4)
#endif
#define CPLD_INTMASK_CPLD (1<<7)
#define CPLD_INT_CPLD (1<<6)
#define CPLD_CONTROL_SWINT (1<<7) /* Disable all CPLD IRQs */
#define CPLD_CONTROL_OCMSK (1<<6) /* Mask USB1 connect IRQ */
#define CPLD_CONTROL_PDRV (1<<5) /* PCC_nDRV high */
#define CPLD_CONTROL_USB1C (1<<4) /* USB1 connect IRQ active */
#define CPLD_CONTROL_USB1P (1<<3) /* USB1 power disable */
#define CPLD_CONTROL_AWKP (1<<2) /* Auto-wakeup disabled */
#define CPLD_CONTROL_LCD_ENABLE (1<<1) /* LCD Vee enable */
#define CPLD_CONTROL_WRLAN_NENABLE (1<<0) /* SMC91x power disable */
static struct resource smc91x_resources[] = {
[0] = {
.start = CPLD00_PHYS,
.end = CPLD00_PHYS + CPLD00_SIZE - 1, /* Only needs 16B */
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LPD7A40X_ETH_INT,
.end = IRQ_LPD7A40X_ETH_INT,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device smc91x_device = {
.name = "smc91x",
.id = 0,
.num_resources = ARRAY_SIZE(smc91x_resources),
.resource = smc91x_resources,
};
static struct resource lh7a40x_usbclient_resources[] = {
[0] = {
.start = USB_PHYS,
.end = (USB_PHYS + PAGE_SIZE),
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_USB,
.end = IRQ_USB,
.flags = IORESOURCE_IRQ,
},
};
static u64 lh7a40x_usbclient_dma_mask = 0xffffffffUL;
static struct platform_device lh7a40x_usbclient_device = {
// .name = "lh7a40x_udc",
.name = "lh7-udc",
.id = 0,
.dev = {
.dma_mask = &lh7a40x_usbclient_dma_mask,
.coherent_dma_mask = 0xffffffffUL,
},
.num_resources = ARRAY_SIZE (lh7a40x_usbclient_resources),
.resource = lh7a40x_usbclient_resources,
};
#if defined (CONFIG_ARCH_LH7A404)
static struct resource lh7a404_usbhost_resources [] = {
[0] = {
.start = USBH_PHYS,
.end = (USBH_PHYS + 0xFF),
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_USHINTR,
.end = IRQ_USHINTR,
.flags = IORESOURCE_IRQ,
},
};
static u64 lh7a404_usbhost_dma_mask = 0xffffffffUL;
static struct platform_device lh7a404_usbhost_device = {
.name = "lh7a404-ohci",
.id = 0,
.dev = {
.dma_mask = &lh7a404_usbhost_dma_mask,
.coherent_dma_mask = 0xffffffffUL,
},
.num_resources = ARRAY_SIZE (lh7a404_usbhost_resources),
.resource = lh7a404_usbhost_resources,
};
#endif
static struct platform_device* lpd7a40x_devs[] __initdata = {
&smc91x_device,
&lh7a40x_usbclient_device,
#if defined (CONFIG_ARCH_LH7A404)
&lh7a404_usbhost_device,
#endif
};
extern void lpd7a400_map_io (void);
static void __init lpd7a40x_init (void)
{
#if defined (CONFIG_MACH_LPD7A400)
CPLD_CONTROL |= 0
| CPLD_CONTROL_SWINT /* Disable software interrupt */
| CPLD_CONTROL_OCMSK; /* Mask USB1 connection IRQ */
CPLD_CONTROL &= ~(0
| CPLD_CONTROL_LCD_ENABLE /* Disable LCD */
| CPLD_CONTROL_WRLAN_NENABLE /* Enable SMC91x */
);
#endif
#if defined (CONFIG_MACH_LPD7A404)
CPLD_CONTROL &= ~(0
| CPLD_CONTROL_WRLAN_NENABLE /* Enable SMC91x */
);
#endif
platform_add_devices (lpd7a40x_devs, ARRAY_SIZE (lpd7a40x_devs));
#if defined (CONFIG_FB_ARMCLCD)
lh7a40x_clcd_init ();
#endif
}
static void lh7a40x_ack_cpld_irq(struct irq_data *d)
{
/* CPLD doesn't have ack capability, but some devices may */
#if defined (CPLD_INTMASK_TOUCH)
/* The touch control *must* mask the interrupt because the
* interrupt bit is read by the driver to determine if the pen
* is still down. */
if (d->irq == IRQ_TOUCH)
CPLD_INTERRUPTS |= CPLD_INTMASK_TOUCH;
#endif
}
static void lh7a40x_mask_cpld_irq(struct irq_data *d)
{
switch (d->irq) {
case IRQ_LPD7A40X_ETH_INT:
CPLD_INTERRUPTS |= CPLD_INTMASK_ETHERNET;
break;
#if defined (IRQ_TOUCH)
case IRQ_TOUCH:
CPLD_INTERRUPTS |= CPLD_INTMASK_TOUCH;
break;
#endif
}
}
static void lh7a40x_unmask_cpld_irq(struct irq_data *d)
{
switch (d->irq) {
case IRQ_LPD7A40X_ETH_INT:
CPLD_INTERRUPTS &= ~CPLD_INTMASK_ETHERNET;
break;
#if defined (IRQ_TOUCH)
case IRQ_TOUCH:
CPLD_INTERRUPTS &= ~CPLD_INTMASK_TOUCH;
break;
#endif
}
}
static struct irq_chip lpd7a40x_cpld_chip = {
.name = "CPLD",
.irq_ack = lh7a40x_ack_cpld_irq,
.irq_mask = lh7a40x_mask_cpld_irq,
.irq_unmask = lh7a40x_unmask_cpld_irq,
};
static void lpd7a40x_cpld_handler (unsigned int irq, struct irq_desc *desc)
{
unsigned int mask = CPLD_INTERRUPTS;
desc->irq_data.chip->irq_ack(&desc->irq_data);
if ((mask & (1<<0)) == 0) /* WLAN */
generic_handle_irq(IRQ_LPD7A40X_ETH_INT);
#if defined (IRQ_TOUCH)
if ((mask & (1<<1)) == 0) /* Touch */
generic_handle_irq(IRQ_TOUCH);
#endif
/* Level-triggered need this */
desc->irq_data.chip->irq_unmask(&desc->irq_data);
}
void __init lh7a40x_init_board_irq (void)
{
int irq;
/* Rev A (v2.8): PF0, PF1, PF2, and PF3 are available IRQs.
PF7 supports the CPLD.
Rev B (v3.4): PF0, PF1, and PF2 are available IRQs.
PF3 supports the CPLD.
(Some) LPD7A404 prerelease boards report a version
number of 0x16, but we force an override since the
hardware is of the newer variety.
*/
unsigned char cpld_version = CPLD_REVISION;
int pinCPLD = (cpld_version == 0x28) ? 7 : 3;
#if defined CONFIG_MACH_LPD7A404
cpld_version = 0x34; /* Coerce LPD7A404 to RevB */
#endif
/* First, configure user controlled GPIOF interrupts */
GPIO_PFDD &= ~0x0f; /* PF0-3 are inputs */
GPIO_INTTYPE1 &= ~0x0f; /* PF0-3 are level triggered */
GPIO_INTTYPE2 &= ~0x0f; /* PF0-3 are active low */
barrier ();
GPIO_GPIOFINTEN |= 0x0f; /* Enable PF0, PF1, PF2, and PF3 IRQs */
/* Then, configure CPLD interrupt */
/* Disable all CPLD interrupts */
#if defined (CONFIG_MACH_LPD7A400)
CPLD_INTERRUPTS = CPLD_INTMASK_TOUCH | CPLD_INTMASK_PEN
| CPLD_INTMASK_ETHERNET;
/* *** FIXME: don't know why we need 7 and 4. 7 is way wrong
and 4 is uncefined. */
// (1<<7)|(1<<4)|(1<<3)|(1<<2);
#endif
#if defined (CONFIG_MACH_LPD7A404)
CPLD_INTERRUPTS = CPLD_INTMASK_ETHERNET;
/* *** FIXME: don't know why we need 6 and 5, neither is defined. */
// (1<<6)|(1<<5)|(1<<3);
#endif
GPIO_PFDD &= ~(1 << pinCPLD); /* Make input */
GPIO_INTTYPE1 &= ~(1 << pinCPLD); /* Level triggered */
GPIO_INTTYPE2 &= ~(1 << pinCPLD); /* Active low */
barrier ();
GPIO_GPIOFINTEN |= (1 << pinCPLD); /* Enable */
/* Cascade CPLD interrupts */
for (irq = IRQ_BOARD_START;
irq < IRQ_BOARD_START + NR_IRQ_BOARD; ++irq) {
set_irq_chip (irq, &lpd7a40x_cpld_chip);
set_irq_handler (irq, handle_level_irq);
set_irq_flags (irq, IRQF_VALID);
}
set_irq_chained_handler ((cpld_version == 0x28)
? IRQ_CPLD_V28
: IRQ_CPLD_V34,
lpd7a40x_cpld_handler);
}
static struct map_desc lpd7a40x_io_desc[] __initdata = {
{
.virtual = IO_VIRT,
.pfn = __phys_to_pfn(IO_PHYS),
.length = IO_SIZE,
.type = MT_DEVICE
},
{ /* Mapping added to work around chip select problems */
.virtual = IOBARRIER_VIRT,
.pfn = __phys_to_pfn(IOBARRIER_PHYS),
.length = IOBARRIER_SIZE,
.type = MT_DEVICE
},
{
.virtual = CF_VIRT,
.pfn = __phys_to_pfn(CF_PHYS),
.length = CF_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD02_VIRT,
.pfn = __phys_to_pfn(CPLD02_PHYS),
.length = CPLD02_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD06_VIRT,
.pfn = __phys_to_pfn(CPLD06_PHYS),
.length = CPLD06_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD08_VIRT,
.pfn = __phys_to_pfn(CPLD08_PHYS),
.length = CPLD08_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD08_VIRT,
.pfn = __phys_to_pfn(CPLD08_PHYS),
.length = CPLD08_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD0A_VIRT,
.pfn = __phys_to_pfn(CPLD0A_PHYS),
.length = CPLD0A_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD0C_VIRT,
.pfn = __phys_to_pfn(CPLD0C_PHYS),
.length = CPLD0C_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD0E_VIRT,
.pfn = __phys_to_pfn(CPLD0E_PHYS),
.length = CPLD0E_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD10_VIRT,
.pfn = __phys_to_pfn(CPLD10_PHYS),
.length = CPLD10_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD12_VIRT,
.pfn = __phys_to_pfn(CPLD12_PHYS),
.length = CPLD12_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD14_VIRT,
.pfn = __phys_to_pfn(CPLD14_PHYS),
.length = CPLD14_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD16_VIRT,
.pfn = __phys_to_pfn(CPLD16_PHYS),
.length = CPLD16_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD18_VIRT,
.pfn = __phys_to_pfn(CPLD18_PHYS),
.length = CPLD18_SIZE,
.type = MT_DEVICE
},
{
.virtual = CPLD1A_VIRT,
.pfn = __phys_to_pfn(CPLD1A_PHYS),
.length = CPLD1A_SIZE,
.type = MT_DEVICE
},
};
void __init
lpd7a40x_map_io(void)
{
iotable_init (lpd7a40x_io_desc, ARRAY_SIZE (lpd7a40x_io_desc));
}
#ifdef CONFIG_MACH_LPD7A400
MACHINE_START (LPD7A400, "Logic Product Development LPD7A400-10")
/* Maintainer: Marc Singer */
.boot_params = 0xc0000100,
.map_io = lpd7a40x_map_io,
.init_irq = lh7a400_init_irq,
.timer = &lh7a40x_timer,
.init_machine = lpd7a40x_init,
MACHINE_END
#endif
#ifdef CONFIG_MACH_LPD7A404
MACHINE_START (LPD7A404, "Logic Product Development LPD7A404-10")
/* Maintainer: Marc Singer */
.boot_params = 0xc0000100,
.map_io = lpd7a40x_map_io,
.init_irq = lh7a404_init_irq,
.timer = &lh7a40x_timer,
.init_machine = lpd7a40x_init,
MACHINE_END
#endif
/*
* arch/arm/mach-lh7a40x/clcd.c
*
* Copyright (C) 2004 Marc Singer
*
* 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/init.h>
#include <linux/gfp.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/sysdev.h>
#include <linux/interrupt.h>
//#include <linux/module.h>
//#include <linux/time.h>
//#include <asm/mach/time.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/system.h>
#include <mach/hardware.h>
#include <linux/amba/bus.h>
#include <linux/amba/clcd.h>
#define HRTFTC_HRSETUP __REG(HRTFTC_PHYS + 0x00)
#define HRTFTC_HRCON __REG(HRTFTC_PHYS + 0x04)
#define HRTFTC_HRTIMING1 __REG(HRTFTC_PHYS + 0x08)
#define HRTFTC_HRTIMING2 __REG(HRTFTC_PHYS + 0x0c)
#define ALI_SETUP __REG(ALI_PHYS + 0x00)
#define ALI_CONTROL __REG(ALI_PHYS + 0x04)
#define ALI_TIMING1 __REG(ALI_PHYS + 0x08)
#define ALI_TIMING2 __REG(ALI_PHYS + 0x0c)
#include "lcd-panel.h"
static void lh7a40x_clcd_disable (struct clcd_fb *fb)
{
#if defined (CONFIG_MACH_LPD7A400)
CPLD_CONTROL &= ~(1<<1); /* Disable LCD Vee */
#endif
#if defined (CONFIG_MACH_LPD7A404)
GPIO_PCD &= ~(1<<3); /* Disable LCD Vee */
#endif
#if defined (CONFIG_ARCH_LH7A400)
HRTFTC_HRSETUP &= ~(1<<13); /* Disable HRTFT controller */
#endif
#if defined (CONFIG_ARCH_LH7A404)
ALI_SETUP &= ~(1<<13); /* Disable ALI */
#endif
}
static void lh7a40x_clcd_enable (struct clcd_fb *fb)
{
struct clcd_panel_extra* extra
= (struct clcd_panel_extra*) fb->board_data;
#if defined (CONFIG_MACH_LPD7A400)
CPLD_CONTROL |= (1<<1); /* Enable LCD Vee */
#endif
#if defined (CONFIG_MACH_LPD7A404)
GPIO_PCDD &= ~(1<<3); /* Enable LCD Vee */
GPIO_PCD |= (1<<3);
#endif
#if defined (CONFIG_ARCH_LH7A400)
if (extra) {
HRTFTC_HRSETUP
= (1 << 13)
| ((fb->fb.var.xres - 1) << 4)
| 0xc
| (extra->hrmode ? 1 : 0);
HRTFTC_HRCON
= ((extra->clsen ? 1 : 0) << 1)
| ((extra->spsen ? 1 : 0) << 0);
HRTFTC_HRTIMING1
= (extra->pcdel << 8)
| (extra->revdel << 4)
| (extra->lpdel << 0);
HRTFTC_HRTIMING2
= (extra->spldel << 9)
| (extra->pc2del << 0);
}
else
HRTFTC_HRSETUP
= (1 << 13)
| 0xc;
#endif
#if defined (CONFIG_ARCH_LH7A404)
if (extra) {
ALI_SETUP
= (1 << 13)
| ((fb->fb.var.xres - 1) << 4)
| 0xc
| (extra->hrmode ? 1 : 0);
ALI_CONTROL
= ((extra->clsen ? 1 : 0) << 1)
| ((extra->spsen ? 1 : 0) << 0);
ALI_TIMING1
= (extra->pcdel << 8)
| (extra->revdel << 4)
| (extra->lpdel << 0);
ALI_TIMING2
= (extra->spldel << 9)
| (extra->pc2del << 0);
}
else
ALI_SETUP
= (1 << 13)
| 0xc;
#endif
}
#define FRAMESIZE(s) (((s) + PAGE_SIZE - 1)&PAGE_MASK)
static int lh7a40x_clcd_setup (struct clcd_fb *fb)
{
dma_addr_t dma;
u32 len = FRAMESIZE (lcd_panel.mode.xres*lcd_panel.mode.yres
*(lcd_panel.bpp/8));
fb->panel = &lcd_panel;
/* Enforce the sync polarity defaults */
if (!(fb->panel->tim2 & TIM2_IHS))
fb->fb.var.sync |= FB_SYNC_HOR_HIGH_ACT;
if (!(fb->panel->tim2 & TIM2_IVS))
fb->fb.var.sync |= FB_SYNC_VERT_HIGH_ACT;
#if defined (HAS_LCD_PANEL_EXTRA)
fb->board_data = &lcd_panel_extra;
#endif
fb->fb.screen_base
= dma_alloc_writecombine (&fb->dev->dev, len,
&dma, GFP_KERNEL);
printk ("CLCD: LCD setup fb virt 0x%p phys 0x%p l %x io 0x%p \n",
fb->fb.screen_base, (void*) dma, len,
(void*) io_p2v (CLCDC_PHYS));
printk ("CLCD: pixclock %d\n", lcd_panel.mode.pixclock);
if (!fb->fb.screen_base) {
printk(KERN_ERR "CLCD: unable to map framebuffer\n");
return -ENOMEM;
}
#if defined (USE_RGB555)
fb->fb.var.green.length = 5; /* Panel uses RGB 5:5:5 */
#endif
fb->fb.fix.smem_start = dma;
fb->fb.fix.smem_len = len;
/* Drive PE4 high to prevent CPLD crash */
GPIO_PEDD |= (1<<4);
GPIO_PED |= (1<<4);
GPIO_PINMUX |= (1<<1) | (1<<0); /* LCDVD[15:4] */
// fb->fb.fbops->fb_check_var (&fb->fb.var, &fb->fb);
// fb->fb.fbops->fb_set_par (&fb->fb);
return 0;
}
static int lh7a40x_clcd_mmap (struct clcd_fb *fb, struct vm_area_struct *vma)
{
return dma_mmap_writecombine(&fb->dev->dev, vma,
fb->fb.screen_base,
fb->fb.fix.smem_start,
fb->fb.fix.smem_len);
}
static void lh7a40x_clcd_remove (struct clcd_fb *fb)
{
dma_free_writecombine (&fb->dev->dev, fb->fb.fix.smem_len,
fb->fb.screen_base, fb->fb.fix.smem_start);
}
static struct clcd_board clcd_platform_data = {
.name = "lh7a40x FB",
.check = clcdfb_check,
.decode = clcdfb_decode,
.enable = lh7a40x_clcd_enable,
.setup = lh7a40x_clcd_setup,
.mmap = lh7a40x_clcd_mmap,
.remove = lh7a40x_clcd_remove,
.disable = lh7a40x_clcd_disable,
};
#define IRQ_CLCDC (IRQ_LCDINTR)
#define AMBA_DEVICE(name,busid,base,plat,pid) \
static struct amba_device name##_device = { \
.dev = { \
.coherent_dma_mask = ~0, \
.init_name = busid, \
.platform_data = plat, \
}, \
.res = { \
.start = base##_PHYS, \
.end = (base##_PHYS) + (4*1024) - 1, \
.flags = IORESOURCE_MEM, \
}, \
.dma_mask = ~0, \
.irq = { IRQ_##base, }, \
/* .dma = base##_DMA,*/ \
.periphid = pid, \
}
AMBA_DEVICE(clcd, "cldc-lh7a40x", CLCDC, &clcd_platform_data, 0x41110);
static struct amba_device *amba_devs[] __initdata = {
&clcd_device,
};
void __init lh7a40x_clcd_init (void)
{
int i;
int result;
printk ("CLCD: registering amba devices\n");
for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
struct amba_device *d = amba_devs[i];
result = amba_device_register(d, &iomem_resource);
printk (" %d -> %d\n", i ,result);
}
}
/* arch/arm/mach-lh7a40x/clocks.c
*
* Copyright (C) 2004 Marc Singer
*
* 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 <mach/hardware.h>
#include <mach/clocks.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/string.h>
struct module;
struct clk {
struct list_head node;
unsigned long rate;
struct module *owner;
const char *name;
};
/* ----- */
#define MAINDIV1(c) (((c) >> 7) & 0x0f)
#define MAINDIV2(c) (((c) >> 11) & 0x1f)
#define PS(c) (((c) >> 18) & 0x03)
#define PREDIV(c) (((c) >> 2) & 0x1f)
#define HCLKDIV(c) (((c) >> 0) & 0x02)
#define PCLKDIV(c) (((c) >> 16) & 0x03)
unsigned int fclkfreq_get (void)
{
unsigned int clkset = CSC_CLKSET;
unsigned int gclk
= XTAL_IN
/ (1 << PS(clkset))
* (MAINDIV1(clkset) + 2)
/ (PREDIV(clkset) + 2)
* (MAINDIV2(clkset) + 2)
;
return gclk;
}
unsigned int hclkfreq_get (void)
{
unsigned int clkset = CSC_CLKSET;
unsigned int hclk = fclkfreq_get () / (HCLKDIV(clkset) + 1);
return hclk;
}
unsigned int pclkfreq_get (void)
{
unsigned int clkset = CSC_CLKSET;
int pclkdiv = PCLKDIV(clkset);
unsigned int pclk;
if (pclkdiv == 0x3)
pclkdiv = 0x2;
pclk = hclkfreq_get () / (1 << pclkdiv);
return pclk;
}
/* ----- */
struct clk *clk_get (struct device *dev, const char *id)
{
return dev && strcmp(dev_name(dev), "cldc-lh7a40x") == 0
? NULL : ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL(clk_get);
void clk_put (struct clk *clk)
{
}
EXPORT_SYMBOL(clk_put);
int clk_enable (struct clk *clk)
{
return 0;
}
EXPORT_SYMBOL(clk_enable);
void clk_disable (struct clk *clk)
{
}
EXPORT_SYMBOL(clk_disable);
unsigned long clk_get_rate (struct clk *clk)
{
return 0;
}
EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate (struct clk *clk, unsigned long rate)
{
return rate;
}
EXPORT_SYMBOL(clk_round_rate);
int clk_set_rate (struct clk *clk, unsigned long rate)
{
return -EIO;
}
EXPORT_SYMBOL(clk_set_rate);
/* arch/arm/mach-lh7a40x/common.h
*
* Copyright (C) 2004 Marc Singer
*
* 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.
*
*/
extern struct sys_timer lh7a40x_timer;
extern void lh7a400_init_irq (void);
extern void lh7a404_init_irq (void);
extern void lh7a40x_clcd_init (void);
extern void lh7a40x_init_board_irq (void);
/* arch/arm/mach-lh7a40x/include/mach/clocks.h
*
* Copyright (C) 2004 Marc Singer
*
* 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.
*
*/
#ifndef __ASM_ARCH_CLOCKS_H
#define __ASM_ARCH_CLOCKS_H
unsigned int fclkfreq_get (void);
unsigned int hclkfreq_get (void);
unsigned int pclkfreq_get (void);
#endif /* _ASM_ARCH_CLOCKS_H */
/* arch/arm/mach-lh7a40x/include/mach/constants.h
*
* Copyright (C) 2004 Coastal Environmental Systems
* Copyright (C) 2004 Logic Product Development
*
* 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.
*
*/
#ifndef __ASM_ARCH_CONSTANTS_H
#define __ASM_ARCH_CONSTANTS_H
/* Addressing constants */
/* SoC CPU IO addressing */
#define IO_PHYS (0x80000000)
#define IO_VIRT (0xf8000000)
#define IO_SIZE (0x0000B000)
#ifdef CONFIG_MACH_KEV7A400
# define CPLD_PHYS (0x20000000)
# define CPLD_VIRT (0xf2000000)
# define CPLD_SIZE PAGE_SIZE
#endif
#if defined (CONFIG_MACH_LPD7A400) || defined (CONFIG_MACH_LPD7A404)
# define IOBARRIER_PHYS 0x10000000 /* Second bank, fastest timing */
# define IOBARRIER_VIRT 0xf0000000
# define IOBARRIER_SIZE PAGE_SIZE
# define CF_PHYS 0x60200000
# define CF_VIRT 0xf6020000
# define CF_SIZE (8*1024)
/* The IO mappings for the LPD CPLD are, unfortunately, sparse. */
# define CPLDX_PHYS(x) (0x70000000 | ((x) << 20))
# define CPLDX_VIRT(x) (0xf7000000 | ((x) << 16))
# define CPLD00_PHYS CPLDX_PHYS (0x00) /* Wired LAN */
# define CPLD00_VIRT CPLDX_VIRT (0x00)
# define CPLD00_SIZE PAGE_SIZE
# define CPLD02_PHYS CPLDX_PHYS (0x02)
# define CPLD02_VIRT CPLDX_VIRT (0x02)
# define CPLD02_SIZE PAGE_SIZE
# define CPLD06_PHYS CPLDX_PHYS (0x06)
# define CPLD06_VIRT CPLDX_VIRT (0x06)
# define CPLD06_SIZE PAGE_SIZE
# define CPLD08_PHYS CPLDX_PHYS (0x08)
# define CPLD08_VIRT CPLDX_VIRT (0x08)
# define CPLD08_SIZE PAGE_SIZE
# define CPLD0A_PHYS CPLDX_PHYS (0x0a)
# define CPLD0A_VIRT CPLDX_VIRT (0x0a)
# define CPLD0A_SIZE PAGE_SIZE
# define CPLD0C_PHYS CPLDX_PHYS (0x0c)
# define CPLD0C_VIRT CPLDX_VIRT (0x0c)
# define CPLD0C_SIZE PAGE_SIZE
# define CPLD0E_PHYS CPLDX_PHYS (0x0e)
# define CPLD0E_VIRT CPLDX_VIRT (0x0e)
# define CPLD0E_SIZE PAGE_SIZE
# define CPLD10_PHYS CPLDX_PHYS (0x10)
# define CPLD10_VIRT CPLDX_VIRT (0x10)
# define CPLD10_SIZE PAGE_SIZE
# define CPLD12_PHYS CPLDX_PHYS (0x12)
# define CPLD12_VIRT CPLDX_VIRT (0x12)
# define CPLD12_SIZE PAGE_SIZE
# define CPLD14_PHYS CPLDX_PHYS (0x14)
# define CPLD14_VIRT CPLDX_VIRT (0x14)
# define CPLD14_SIZE PAGE_SIZE
# define CPLD16_PHYS CPLDX_PHYS (0x16)
# define CPLD16_VIRT CPLDX_VIRT (0x16)
# define CPLD16_SIZE PAGE_SIZE
# define CPLD18_PHYS CPLDX_PHYS (0x18)
# define CPLD18_VIRT CPLDX_VIRT (0x18)
# define CPLD18_SIZE PAGE_SIZE
# define CPLD1A_PHYS CPLDX_PHYS (0x1a)
# define CPLD1A_VIRT CPLDX_VIRT (0x1a)
# define CPLD1A_SIZE PAGE_SIZE
#endif
/* Timing constants */
#define XTAL_IN 14745600 /* 14.7456 MHz crystal */
#define PLL_CLOCK (XTAL_IN * 21) /* 309 MHz PLL clock */
#define MAX_HCLK_KHZ 100000 /* HCLK max limit ~100MHz */
#define HCLK (99993600)
//#define HCLK (119808000)
#endif /* __ASM_ARCH_CONSTANTS_H */
/* arch/arm/mach-lh7a40x/include/mach/debug-macro.S
*
* Debugging macro include header
*
* Copyright (C) 1994-1999 Russell King
* Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
*
* 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.
*
*/
@ It is not known if this will be appropriate for every 40x
@ board.
.macro addruart, rp, rv
mov \rp, #0x00000700 @ offset from base
orr \rv, \rp, #0xf8000000 @ virtual base
orr \rp, \rp, #0x80000000 @ physical base
.endm
.macro senduart,rd,rx
strb \rd, [\rx] @ DATA
.endm
.macro busyuart,rd,rx @ spin while busy
1001: ldr \rd, [\rx, #0x10] @ STATUS
tst \rd, #1 << 3 @ BUSY (TX FIFO not empty)
bne 1001b @ yes, spin
.endm
.macro waituart,rd,rx @ wait for Tx FIFO room
1001: ldrb \rd, [\rx, #0x10] @ STATUS
tst \rd, #1 << 5 @ TXFF (TX FIFO full)
bne 1001b @ yes, spin
.endm
/* arch/arm/mach-lh7a40x/include/mach/dma.h
*
* Copyright (C) 2005 Marc Singer
*
* 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.
*
*/
typedef enum {
DMA_M2M0 = 0,
DMA_M2M1 = 1,
DMA_M2P0 = 2, /* Tx */
DMA_M2P1 = 3, /* Rx */
DMA_M2P2 = 4, /* Tx */
DMA_M2P3 = 5, /* Rx */
DMA_M2P4 = 6, /* Tx - AC97 */
DMA_M2P5 = 7, /* Rx - AC97 */
DMA_M2P6 = 8, /* Tx */
DMA_M2P7 = 9, /* Rx */
} dma_device_t;
#define DMA_LENGTH_MAX ((64*1024) - 4) /* bytes */
#define DMAC_GCA __REG(DMAC_PHYS + 0x2b80)
#define DMAC_GIR __REG(DMAC_PHYS + 0x2bc0)
#define DMAC_GIR_MMI1 (1<<11)
#define DMAC_GIR_MMI0 (1<<10)
#define DMAC_GIR_MPI8 (1<<9)
#define DMAC_GIR_MPI9 (1<<8)
#define DMAC_GIR_MPI6 (1<<7)
#define DMAC_GIR_MPI7 (1<<6)
#define DMAC_GIR_MPI4 (1<<5)
#define DMAC_GIR_MPI5 (1<<4)
#define DMAC_GIR_MPI2 (1<<3)
#define DMAC_GIR_MPI3 (1<<2)
#define DMAC_GIR_MPI0 (1<<1)
#define DMAC_GIR_MPI1 (1<<0)
#define DMAC_M2P0 0x0000
#define DMAC_M2P1 0x0040
#define DMAC_M2P2 0x0080
#define DMAC_M2P3 0x00c0
#define DMAC_M2P4 0x0240
#define DMAC_M2P5 0x0200
#define DMAC_M2P6 0x02c0
#define DMAC_M2P7 0x0280
#define DMAC_M2P8 0x0340
#define DMAC_M2P9 0x0300
#define DMAC_M2M0 0x0100
#define DMAC_M2M1 0x0140
#define DMAC_P_PCONTROL(c) __REG(DMAC_PHYS + (c) + 0x00)
#define DMAC_P_PINTERRUPT(c) __REG(DMAC_PHYS + (c) + 0x04)
#define DMAC_P_PPALLOC(c) __REG(DMAC_PHYS + (c) + 0x08)
#define DMAC_P_PSTATUS(c) __REG(DMAC_PHYS + (c) + 0x0c)
#define DMAC_P_REMAIN(c) __REG(DMAC_PHYS + (c) + 0x14)
#define DMAC_P_MAXCNT0(c) __REG(DMAC_PHYS + (c) + 0x20)
#define DMAC_P_BASE0(c) __REG(DMAC_PHYS + (c) + 0x24)
#define DMAC_P_CURRENT0(c) __REG(DMAC_PHYS + (c) + 0x28)
#define DMAC_P_MAXCNT1(c) __REG(DMAC_PHYS + (c) + 0x30)
#define DMAC_P_BASE1(c) __REG(DMAC_PHYS + (c) + 0x34)
#define DMAC_P_CURRENT1(c) __REG(DMAC_PHYS + (c) + 0x38)
#define DMAC_PCONTROL_ENABLE (1<<4)
#define DMAC_PORT_USB 0
#define DMAC_PORT_SDMMC 1
#define DMAC_PORT_AC97_1 2
#define DMAC_PORT_AC97_2 3
#define DMAC_PORT_AC97_3 4
#define DMAC_PORT_UART1 6
#define DMAC_PORT_UART2 7
#define DMAC_PORT_UART3 8
#define DMAC_PSTATUS_CURRSTATE_SHIFT 4
#define DMAC_PSTATUS_CURRSTATE_MASK 0x3
#define DMAC_PSTATUS_NEXTBUF (1<<6)
#define DMAC_PSTATUS_STALLRINT (1<<0)
#define DMAC_INT_CHE (1<<3)
#define DMAC_INT_NFB (1<<1)
#define DMAC_INT_STALL (1<<0)
/*
* arch/arm/mach-lh7a40x/include/mach/entry-macro.S
*
* Low-level IRQ helper macros for LH7A40x platforms
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <mach/hardware.h>
#include <mach/irqs.h>
/* In order to allow there to be support for both of the processor
classes at the same time, we make a hack here that isn't very
pretty. At startup, the link pointed to with the
branch_irq_lh7a400 symbol is replaced with a NOP when the CPU is
detected as a lh7a404.
*** FIXME: we should clean this up so that there is only one
implementation for each CPU's design.
*/
#if defined (CONFIG_ARCH_LH7A400) && defined (CONFIG_ARCH_LH7A404)
.macro disable_fiq
.endm
.macro get_irqnr_preamble, base, tmp
.endm
.macro arch_ret_to_user, tmp1, tmp2
.endm
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
branch_irq_lh7a400: b 1000f
@ Implementation of the LH7A404 get_irqnr_and_base.
mov \irqnr, #0 @ VIC1 irq base
mov \base, #io_p2v(0x80000000) @ APB registers
add \base, \base, #0x8000
ldr \tmp, [\base, #0x0030] @ VIC1_VECTADDR
tst \tmp, #VA_VECTORED @ Direct vectored
bne 1002f
tst \tmp, #VA_VIC1DEFAULT @ Default vectored VIC1
ldrne \irqstat, [\base, #0] @ VIC1_IRQSTATUS
bne 1001f
add \base, \base, #(0xa000 - 0x8000)
ldr \tmp, [\base, #0x0030] @ VIC2_VECTADDR
tst \tmp, #VA_VECTORED @ Direct vectored
bne 1002f
ldr \irqstat, [\base, #0] @ VIC2_IRQSTATUS
mov \irqnr, #32 @ VIC2 irq base
1001: movs \irqstat, \irqstat, lsr #1 @ Shift into carry
bcs 1008f @ Bit set; irq found
add \irqnr, \irqnr, #1
bne 1001b @ Until no bits
b 1009f @ Nothing? Hmm.
1002: and \irqnr, \tmp, #0x3f @ Mask for valid bits
1008: movs \irqstat, #1 @ Force !Z
str \tmp, [\base, #0x0030] @ Clear vector
b 1009f
@ Implementation of the LH7A400 get_irqnr_and_base.
1000: mov \irqnr, #0
mov \base, #io_p2v(0x80000000) @ APB registers
ldr \irqstat, [\base, #0x500] @ PIC INTSR
1001: movs \irqstat, \irqstat, lsr #1 @ Shift into carry
bcs 1008f @ Bit set; irq found
add \irqnr, \irqnr, #1
bne 1001b @ Until no bits
b 1009f @ Nothing? Hmm.
1008: movs \irqstat, #1 @ Force !Z
1009:
.endm
#elif defined (CONFIG_ARCH_LH7A400)
.macro disable_fiq
.endm
.macro get_irqnr_preamble, base, tmp
.endm
.macro arch_ret_to_user, tmp1, tmp2
.endm
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
mov \irqnr, #0
mov \base, #io_p2v(0x80000000) @ APB registers
ldr \irqstat, [\base, #0x500] @ PIC INTSR
1001: movs \irqstat, \irqstat, lsr #1 @ Shift into carry
bcs 1008f @ Bit set; irq found
add \irqnr, \irqnr, #1
bne 1001b @ Until no bits
b 1009f @ Nothing? Hmm.
1008: movs \irqstat, #1 @ Force !Z
1009:
.endm
#elif defined(CONFIG_ARCH_LH7A404)
.macro disable_fiq
.endm
.macro get_irqnr_preamble, base, tmp
.endm
.macro arch_ret_to_user, tmp1, tmp2
.endm
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
mov \irqnr, #0 @ VIC1 irq base
mov \base, #io_p2v(0x80000000) @ APB registers
add \base, \base, #0x8000
ldr \tmp, [\base, #0x0030] @ VIC1_VECTADDR
tst \tmp, #VA_VECTORED @ Direct vectored
bne 1002f
tst \tmp, #VA_VIC1DEFAULT @ Default vectored VIC1
ldrne \irqstat, [\base, #0] @ VIC1_IRQSTATUS
bne 1001f
add \base, \base, #(0xa000 - 0x8000)
ldr \tmp, [\base, #0x0030] @ VIC2_VECTADDR
tst \tmp, #VA_VECTORED @ Direct vectored
bne 1002f
ldr \irqstat, [\base, #0] @ VIC2_IRQSTATUS
mov \irqnr, #32 @ VIC2 irq base
1001: movs \irqstat, \irqstat, lsr #1 @ Shift into carry
bcs 1008f @ Bit set; irq found
add \irqnr, \irqnr, #1
bne 1001b @ Until no bits
b 1009f @ Nothing? Hmm.
1002: and \irqnr, \tmp, #0x3f @ Mask for valid bits
1008: movs \irqstat, #1 @ Force !Z
str \tmp, [\base, #0x0030] @ Clear vector
1009:
.endm
#endif
/* arch/arm/mach-lh7a40x/include/mach/hardware.h
*
* Copyright (C) 2004 Coastal Environmental Systems
*
* [ Substantially cribbed from arch/arm/mach-pxa/include/mach/hardware.h ]
*
* 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.
*
*/
#ifndef __ASM_ARCH_HARDWARE_H
#define __ASM_ARCH_HARDWARE_H
#include <asm/sizes.h> /* Added for the sake of amba-clcd driver */
#define io_p2v(x) (0xf0000000 | (((x) & 0xfff00000) >> 4) | ((x) & 0x0000ffff))
#define io_v2p(x) ( (((x) & 0x0fff0000) << 4) | ((x) & 0x0000ffff))
#ifdef __ASSEMBLY__
# define __REG(x) io_p2v(x)
# define __PREG(x) io_v2p(x)
#else
# if 0
# define __REG(x) (*((volatile u32 *)io_p2v(x)))
# else
/*
* This __REG() version gives the same results as the one above, except
* that we are fooling gcc somehow so it generates far better and smaller
* assembly code for access to contiguous registers. It's a shame that gcc
* doesn't guess this by itself.
*/
#include <asm/types.h>
typedef struct { volatile u32 offset[4096]; } __regbase;
# define __REGP(x) ((__regbase *)((x)&~4095))->offset[((x)&4095)>>2]
# define __REG(x) __REGP(io_p2v(x))
typedef struct { volatile u16 offset[4096]; } __regbase16;
# define __REGP16(x) ((__regbase16 *)((x)&~4095))->offset[((x)&4095)>>1]
# define __REG16(x) __REGP16(io_p2v(x))
typedef struct { volatile u8 offset[4096]; } __regbase8;
# define __REGP8(x) ((__regbase8 *)((x)&~4095))->offset[(x)&4095]
# define __REG8(x) __REGP8(io_p2v(x))
#endif
/* Let's kick gcc's ass again... */
# define __REG2(x,y) \
( __builtin_constant_p(y) ? (__REG((x) + (y))) \
: (*(volatile u32 *)((u32)&__REG(x) + (y))) )
# define __PREG(x) (io_v2p((u32)&(x)))
#endif
#define MASK_AND_SET(v,m,s) (v) = ((v)&~(m))|(s)
#include "registers.h"
#endif /* _ASM_ARCH_HARDWARE_H */
/* arch/arm/mach-lh7a40x/include/mach/io.h
*
* Copyright (C) 2004 Coastal Environmental Systems
*
* 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.
*
*/
#ifndef __ASM_ARCH_IO_H
#define __ASM_ARCH_IO_H
#define IO_SPACE_LIMIT 0xffffffff
/* No ISA or PCI bus on this machine. */
#define __io(a) __typesafe_io(a)
#define __mem_pci(a) (a)
#endif /* __ASM_ARCH_IO_H */
/* arch/arm/mach-lh7a40x/include/mach/irqs.h
*
* Copyright (C) 2004 Coastal Environmental Systems
* Copyright (C) 2004 Logic Product Development
*
* 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.
*
*/
/* It is to be seen whether or not we can build a kernel for more than
* one board. For the time being, these macros assume that we cannot.
* Thus, it is OK to ifdef machine/board specific IRQ assignments.
*/
#ifndef __ASM_ARCH_IRQS_H
#define __ASM_ARCH_IRQS_H
#define FIQ_START 80
#if defined (CONFIG_ARCH_LH7A400)
/* FIQs */
# define IRQ_GPIO0FIQ 0 /* GPIO External FIQ Interrupt on F0 */
# define IRQ_BLINT 1 /* Battery Low */
# define IRQ_WEINT 2 /* Watchdog Timer, WDT overflow */
# define IRQ_MCINT 3 /* Media Change, MEDCHG pin rising */
/* IRQs */
# define IRQ_CSINT 4 /* Audio Codec (ACI) */
# define IRQ_GPIO1INTR 5 /* GPIO External IRQ Interrupt on F1 */
# define IRQ_GPIO2INTR 6 /* GPIO External IRQ Interrupt on F2 */
# define IRQ_GPIO3INTR 7 /* GPIO External IRQ Interrupt on F3 */
# define IRQ_T1UI 8 /* Timer 1 underflow */
# define IRQ_T2UI 9 /* Timer 2 underflow */
# define IRQ_RTCMI 10
# define IRQ_TINTR 11 /* Clock State Controller 64 Hz tick (CSC) */
# define IRQ_UART1INTR 12
# define IRQ_UART2INTR 13
# define IRQ_LCDINTR 14
# define IRQ_SSIEOT 15 /* Synchronous Serial Interface (SSI) */
# define IRQ_UART3INTR 16
# define IRQ_SCIINTR 17 /* Smart Card Interface (SCI) */
# define IRQ_AACINTR 18 /* Advanced Audio Codec (AAC) */
# define IRQ_MMCINTR 19 /* Multimedia Card (MMC) */
# define IRQ_USBINTR 20
# define IRQ_DMAINTR 21
# define IRQ_T3UI 22 /* Timer 3 underflow */
# define IRQ_GPIO4INTR 23 /* GPIO External IRQ Interrupt on F4 */
# define IRQ_GPIO5INTR 24 /* GPIO External IRQ Interrupt on F5 */
# define IRQ_GPIO6INTR 25 /* GPIO External IRQ Interrupt on F6 */
# define IRQ_GPIO7INTR 26 /* GPIO External IRQ Interrupt on F7 */
# define IRQ_BMIINTR 27 /* Battery Monitor Interface (BMI) */
# define NR_IRQ_CPU 28 /* IRQs directly recognized by CPU */
/* Given IRQ, return GPIO interrupt number 0-7 */
# define IRQ_TO_GPIO(i) ((i) \
- (((i) > IRQ_GPIO3INTR) ? IRQ_GPIO4INTR - IRQ_GPIO3INTR - 1 : 0)\
- (((i) > IRQ_GPIO0INTR) ? IRQ_GPIO1INTR - IRQ_GPIO0INTR - 1 : 0))
#endif
#if defined (CONFIG_ARCH_LH7A404)
# define IRQ_BROWN 0 /* Brownout */
# define IRQ_WDTINTR 1 /* Watchdog Timer */
# define IRQ_COMMRX 2 /* ARM Comm Rx for Debug */
# define IRQ_COMMTX 3 /* ARM Comm Tx for Debug */
# define IRQ_T1UI 4 /* Timer 1 underflow */
# define IRQ_T2UI 5 /* Timer 2 underflow */
# define IRQ_CSINT 6 /* Codec Interrupt (shared by AAC on 404) */
# define IRQ_DMAM2P0 7 /* -- DMA Memory to Peripheral */
# define IRQ_DMAM2P1 8
# define IRQ_DMAM2P2 9
# define IRQ_DMAM2P3 10
# define IRQ_DMAM2P4 11
# define IRQ_DMAM2P5 12
# define IRQ_DMAM2P6 13
# define IRQ_DMAM2P7 14
# define IRQ_DMAM2P8 15
# define IRQ_DMAM2P9 16
# define IRQ_DMAM2M0 17 /* -- DMA Memory to Memory */
# define IRQ_DMAM2M1 18
# define IRQ_GPIO0INTR 19 /* -- GPIOF Interrupt */
# define IRQ_GPIO1INTR 20
# define IRQ_GPIO2INTR 21
# define IRQ_GPIO3INTR 22
# define IRQ_SOFT_V1_23 23 /* -- Unassigned */
# define IRQ_SOFT_V1_24 24
# define IRQ_SOFT_V1_25 25
# define IRQ_SOFT_V1_26 26
# define IRQ_SOFT_V1_27 27
# define IRQ_SOFT_V1_28 28
# define IRQ_SOFT_V1_29 29
# define IRQ_SOFT_V1_30 30
# define IRQ_SOFT_V1_31 31
# define IRQ_BLINT 32 /* Battery Low */
# define IRQ_BMIINTR 33 /* Battery Monitor */
# define IRQ_MCINTR 34 /* Media Change */
# define IRQ_TINTR 35 /* 64Hz Tick */
# define IRQ_WEINT 36 /* Watchdog Expired */
# define IRQ_RTCMI 37 /* Real-time Clock Match */
# define IRQ_UART1INTR 38 /* UART1 Interrupt (including error) */
# define IRQ_UART1ERR 39 /* UART1 Error */
# define IRQ_UART2INTR 40 /* UART2 Interrupt (including error) */
# define IRQ_UART2ERR 41 /* UART2 Error */
# define IRQ_UART3INTR 42 /* UART3 Interrupt (including error) */
# define IRQ_UART3ERR 43 /* UART3 Error */
# define IRQ_SCIINTR 44 /* Smart Card */
# define IRQ_TSCINTR 45 /* Touchscreen */
# define IRQ_KMIINTR 46 /* Keyboard/Mouse (PS/2) */
# define IRQ_GPIO4INTR 47 /* -- GPIOF Interrupt */
# define IRQ_GPIO5INTR 48
# define IRQ_GPIO6INTR 49
# define IRQ_GPIO7INTR 50
# define IRQ_T3UI 51 /* Timer 3 underflow */
# define IRQ_LCDINTR 52 /* LCD Controller */
# define IRQ_SSPINTR 53 /* Synchronous Serial Port */
# define IRQ_SDINTR 54 /* Secure Digital Port (MMC) */
# define IRQ_USBINTR 55 /* USB Device Port */
# define IRQ_USHINTR 56 /* USB Host Port */
# define IRQ_SOFT_V2_25 57 /* -- Unassigned */
# define IRQ_SOFT_V2_26 58
# define IRQ_SOFT_V2_27 59
# define IRQ_SOFT_V2_28 60
# define IRQ_SOFT_V2_29 61
# define IRQ_SOFT_V2_30 62
# define IRQ_SOFT_V2_31 63
# define NR_IRQ_CPU 64 /* IRQs directly recognized by CPU */
/* Given IRQ, return GPIO interrupt number 0-7 */
# define IRQ_TO_GPIO(i) ((i) \
- (((i) > IRQ_GPIO3INTR) ? IRQ_GPIO4INTR - IRQ_GPIO3INTR - 1 : 0)\
- IRQ_GPIO0INTR)
/* Vector Address constants */
# define VA_VECTORED 0x100 /* Set for vectored interrupt */
# define VA_VIC1DEFAULT 0x200 /* Set as default VECTADDR for VIC1 */
# define VA_VIC2DEFAULT 0x400 /* Set as default VECTADDR for VIC2 */
#endif
/* IRQ aliases */
#if !defined (IRQ_GPIO0INTR)
# define IRQ_GPIO0INTR IRQ_GPIO0FIQ
#endif
#define IRQ_TICK IRQ_TINTR
#define IRQ_PCC1_RDY IRQ_GPIO6INTR /* PCCard 1 ready */
#define IRQ_PCC2_RDY IRQ_GPIO7INTR /* PCCard 2 ready */
#define IRQ_USB IRQ_USBINTR /* USB device */
#ifdef CONFIG_MACH_KEV7A400
# define IRQ_TS IRQ_GPIOFIQ /* Touchscreen */
# define IRQ_CPLD IRQ_GPIO1INTR /* CPLD cascade */
# define IRQ_PCC1_CD IRQ_GPIO_F2 /* PCCard 1 card detect */
# define IRQ_PCC2_CD IRQ_GPIO_F3 /* PCCard 2 card detect */
#endif
#if defined (CONFIG_MACH_LPD7A400) || defined (CONFIG_MACH_LPD7A404)
# define IRQ_CPLD_V28 IRQ_GPIO7INTR /* CPLD cascade through GPIO_PF7 */
# define IRQ_CPLD_V34 IRQ_GPIO3INTR /* CPLD cascade through GPIO_PF3 */
#endif
/* System specific IRQs */
#define IRQ_BOARD_START NR_IRQ_CPU
#ifdef CONFIG_MACH_KEV7A400
# define IRQ_KEV7A400_CPLD IRQ_BOARD_START
# define NR_IRQ_BOARD 5
# define IRQ_KEV7A400_MMC_CD IRQ_KEV7A400_CPLD + 0 /* MMC Card Detect */
# define IRQ_KEV7A400_RI2 IRQ_KEV7A400_CPLD + 1 /* Ring Indicator 2 */
# define IRQ_KEV7A400_IDE_CF IRQ_KEV7A400_CPLD + 2 /* Compact Flash (?) */
# define IRQ_KEV7A400_ETH_INT IRQ_KEV7A400_CPLD + 3 /* Ethernet chip */
# define IRQ_KEV7A400_INT IRQ_KEV7A400_CPLD + 4
#endif
#if defined (CONFIG_MACH_LPD7A400) || defined (CONFIG_MACH_LPD7A404)
# define IRQ_LPD7A40X_CPLD IRQ_BOARD_START
# define NR_IRQ_BOARD 2
# define IRQ_LPD7A40X_ETH_INT IRQ_LPD7A40X_CPLD + 0 /* Ethernet chip */
# define IRQ_LPD7A400_TS IRQ_LPD7A40X_CPLD + 1 /* Touch screen */
#endif
#if defined (CONFIG_MACH_LPD7A400)
# define IRQ_TOUCH IRQ_LPD7A400_TS
#endif
#define NR_IRQS (NR_IRQ_CPU + NR_IRQ_BOARD)
#endif
/* arch/arm/mach-lh7a40x/include/mach/memory.h
*
* Copyright (C) 2004 Coastal Environmental Systems
*
* 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.
*
*
* Refer to <file:Documentation/arm/Sharp-LH/SDRAM> for more information.
*
*/
#ifndef __ASM_ARCH_MEMORY_H
#define __ASM_ARCH_MEMORY_H
/*
* Physical DRAM offset.
*/
#define PHYS_OFFSET UL(0xc0000000)
/*
* Sparsemem version of the above
*/
#define MAX_PHYSMEM_BITS 32
#define SECTION_SIZE_BITS 24
#endif
/* arch/arm/mach-lh7a40x/include/mach/registers.h
*
* Copyright (C) 2004 Coastal Environmental Systems
* Copyright (C) 2004 Logic Product Development
*
* 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 <mach/constants.h>
#ifndef __ASM_ARCH_REGISTERS_H
#define __ASM_ARCH_REGISTERS_H
/* Physical register base addresses */
#define AC97C_PHYS (0x80000000) /* AC97 Controller */
#define MMC_PHYS (0x80000100) /* Multimedia Card Controller */
#define USB_PHYS (0x80000200) /* USB Client */
#define SCI_PHYS (0x80000300) /* Secure Card Interface */
#define CSC_PHYS (0x80000400) /* Clock/State Controller */
#define INTC_PHYS (0x80000500) /* Interrupt Controller */
#define UART1_PHYS (0x80000600) /* UART1 Controller */
#define SIR_PHYS (0x80000600) /* IR Controller, same are UART1 */
#define UART2_PHYS (0x80000700) /* UART2 Controller */
#define UART3_PHYS (0x80000800) /* UART3 Controller */
#define DCDC_PHYS (0x80000900) /* DC to DC Controller */
#define ACI_PHYS (0x80000a00) /* Audio Codec Interface */
#define SSP_PHYS (0x80000b00) /* Synchronous ... */
#define TIMER_PHYS (0x80000c00) /* Timer Controller */
#define RTC_PHYS (0x80000d00) /* Real-time Clock */
#define GPIO_PHYS (0x80000e00) /* General Purpose IO */
#define BMI_PHYS (0x80000f00) /* Battery Monitor Interface */
#define HRTFTC_PHYS (0x80001000) /* High-res TFT Controller (LH7A400) */
#define ALI_PHYS (0x80001000) /* Advanced LCD Interface (LH7A404) */
#define WDT_PHYS (0x80001400) /* Watchdog Timer */
#define SMC_PHYS (0x80002000) /* Static Memory Controller */
#define SDRC_PHYS (0x80002400) /* SDRAM Controller */
#define DMAC_PHYS (0x80002800) /* DMA Controller */
#define CLCDC_PHYS (0x80003000) /* Color LCD Controller */
/* Physical registers of the LH7A404 */
#define ADC_PHYS (0x80001300) /* A/D & Touchscreen Controller */
#define VIC1_PHYS (0x80008000) /* Vectored Interrupt Controller 1 */
#define USBH_PHYS (0x80009000) /* USB OHCI host controller */
#define VIC2_PHYS (0x8000a000) /* Vectored Interrupt Controller 2 */
/*#define KBD_PHYS (0x80000e00) */
/*#define LCDICP_PHYS (0x80001000) */
/* Clock/State Controller register */
#define CSC_PWRSR __REG(CSC_PHYS + 0x00) /* Reset register & ID */
#define CSC_PWRCNT __REG(CSC_PHYS + 0x04) /* Power control */
#define CSC_CLKSET __REG(CSC_PHYS + 0x20) /* Clock speed control */
#define CSC_USBDRESET __REG(CSC_PHYS + 0x4c) /* USB Device resets */
#define CSC_PWRCNT_USBH_EN (1<<28) /* USB Host power enable */
#define CSC_PWRCNT_DMAC_M2M1_EN (1<<27)
#define CSC_PWRCNT_DMAC_M2M0_EN (1<<26)
#define CSC_PWRCNT_DMAC_M2P8_EN (1<<25)
#define CSC_PWRCNT_DMAC_M2P9_EN (1<<24)
#define CSC_PWRCNT_DMAC_M2P6_EN (1<<23)
#define CSC_PWRCNT_DMAC_M2P7_EN (1<<22)
#define CSC_PWRCNT_DMAC_M2P4_EN (1<<21)
#define CSC_PWRCNT_DMAC_M2P5_EN (1<<20)
#define CSC_PWRCNT_DMAC_M2P2_EN (1<<19)
#define CSC_PWRCNT_DMAC_M2P3_EN (1<<18)
#define CSC_PWRCNT_DMAC_M2P0_EN (1<<17)
#define CSC_PWRCNT_DMAC_M2P1_EN (1<<16)
#define CSC_PWRSR_CHIPMAN_SHIFT (24)
#define CSC_PWRSR_CHIPMAN_MASK (0xff)
#define CSC_PWRSR_CHIPID_SHIFT (16)
#define CSC_PWRSR_CHIPID_MASK (0xff)
#define CSC_USBDRESET_APBRESETREG (1<<1)
#define CSC_USBDRESET_IORESETREG (1<<0)
/* Interrupt Controller registers */
#define INTC_INTSR __REG(INTC_PHYS + 0x00) /* Status */
#define INTC_INTRSR __REG(INTC_PHYS + 0x04) /* Raw Status */
#define INTC_INTENS __REG(INTC_PHYS + 0x08) /* Enable Set */
#define INTC_INTENC __REG(INTC_PHYS + 0x0c) /* Enable Clear */
/* Vectored Interrupted Controller registers */
#define VIC1_IRQSTATUS __REG(VIC1_PHYS + 0x00)
#define VIC1_FIQSTATUS __REG(VIC1_PHYS + 0x04)
#define VIC1_RAWINTR __REG(VIC1_PHYS + 0x08)
#define VIC1_INTSEL __REG(VIC1_PHYS + 0x0c)
#define VIC1_INTEN __REG(VIC1_PHYS + 0x10)
#define VIC1_INTENCLR __REG(VIC1_PHYS + 0x14)
#define VIC1_SOFTINT __REG(VIC1_PHYS + 0x18)
#define VIC1_SOFTINTCLR __REG(VIC1_PHYS + 0x1c)
#define VIC1_PROTECT __REG(VIC1_PHYS + 0x20)
#define VIC1_VECTADDR __REG(VIC1_PHYS + 0x30)
#define VIC1_NVADDR __REG(VIC1_PHYS + 0x34)
#define VIC1_VAD0 __REG(VIC1_PHYS + 0x100)
#define VIC1_VECTCNTL0 __REG(VIC1_PHYS + 0x200)
#define VIC2_IRQSTATUS __REG(VIC2_PHYS + 0x00)
#define VIC2_FIQSTATUS __REG(VIC2_PHYS + 0x04)
#define VIC2_RAWINTR __REG(VIC2_PHYS + 0x08)
#define VIC2_INTSEL __REG(VIC2_PHYS + 0x0c)
#define VIC2_INTEN __REG(VIC2_PHYS + 0x10)
#define VIC2_INTENCLR __REG(VIC2_PHYS + 0x14)
#define VIC2_SOFTINT __REG(VIC2_PHYS + 0x18)
#define VIC2_SOFTINTCLR __REG(VIC2_PHYS + 0x1c)
#define VIC2_PROTECT __REG(VIC2_PHYS + 0x20)
#define VIC2_VECTADDR __REG(VIC2_PHYS + 0x30)
#define VIC2_NVADDR __REG(VIC2_PHYS + 0x34)
#define VIC2_VAD0 __REG(VIC2_PHYS + 0x100)
#define VIC2_VECTCNTL0 __REG(VIC2_PHYS + 0x200)
#define VIC_CNTL_ENABLE (0x20)
/* USB Host registers (Open HCI compatible) */
#define USBH_CMDSTATUS __REG(USBH_PHYS + 0x08)
/* GPIO registers */
#define GPIO_INTTYPE1 __REG(GPIO_PHYS + 0x4c) /* Interrupt Type 1 (Edge) */
#define GPIO_INTTYPE2 __REG(GPIO_PHYS + 0x50) /* Interrupt Type 2 */
#define GPIO_GPIOFEOI __REG(GPIO_PHYS + 0x54) /* GPIO End-of-Interrupt */
#define GPIO_GPIOINTEN __REG(GPIO_PHYS + 0x58) /* GPIO Interrupt Enable */
#define GPIO_INTSTATUS __REG(GPIO_PHYS + 0x5c) /* GPIO Interrupt Status */
#define GPIO_PINMUX __REG(GPIO_PHYS + 0x2c)
#define GPIO_PADD __REG(GPIO_PHYS + 0x10)
#define GPIO_PAD __REG(GPIO_PHYS + 0x00)
#define GPIO_PCD __REG(GPIO_PHYS + 0x08)
#define GPIO_PCDD __REG(GPIO_PHYS + 0x18)
#define GPIO_PEDD __REG(GPIO_PHYS + 0x24)
#define GPIO_PED __REG(GPIO_PHYS + 0x20)
/* Static Memory Controller registers */
#define SMC_BCR0 __REG(SMC_PHYS + 0x00) /* Bank 0 Configuration */
#define SMC_BCR1 __REG(SMC_PHYS + 0x04) /* Bank 1 Configuration */
#define SMC_BCR2 __REG(SMC_PHYS + 0x08) /* Bank 2 Configuration */
#define SMC_BCR3 __REG(SMC_PHYS + 0x0C) /* Bank 3 Configuration */
#define SMC_BCR6 __REG(SMC_PHYS + 0x18) /* Bank 6 Configuration */
#define SMC_BCR7 __REG(SMC_PHYS + 0x1c) /* Bank 7 Configuration */
#ifdef CONFIG_MACH_KEV7A400
# define CPLD_RD_OPT_DIP_SW __REG16(CPLD_PHYS + 0x00) /* Read Option SW */
# define CPLD_WR_IO_BRD_CTL __REG16(CPLD_PHYS + 0x00) /* Write Control */
# define CPLD_RD_PB_KEYS __REG16(CPLD_PHYS + 0x02) /* Read Btn Keys */
# define CPLD_LATCHED_INTS __REG16(CPLD_PHYS + 0x04) /* Read INTR stat. */
# define CPLD_CL_INT __REG16(CPLD_PHYS + 0x04) /* Clear INTR stat */
# define CPLD_BOOT_MMC_STATUS __REG16(CPLD_PHYS + 0x06) /* R/O */
# define CPLD_RD_KPD_ROW_SENSE __REG16(CPLD_PHYS + 0x08)
# define CPLD_WR_PB_INT_MASK __REG16(CPLD_PHYS + 0x08)
# define CPLD_RD_BRD_DISP_SW __REG16(CPLD_PHYS + 0x0a)
# define CPLD_WR_EXT_INT_MASK __REG16(CPLD_PHYS + 0x0a)
# define CPLD_LCD_PWR_CNTL __REG16(CPLD_PHYS + 0x0c)
# define CPLD_SEVEN_SEG __REG16(CPLD_PHYS + 0x0e) /* 7 seg. LED mask */
#endif
#if defined (CONFIG_MACH_LPD7A400) || defined (CONFIG_MACH_LPD7A404)
# define CPLD_CONTROL __REG16(CPLD02_PHYS)
# define CPLD_SPI_DATA __REG16(CPLD06_PHYS)
# define CPLD_SPI_CONTROL __REG16(CPLD08_PHYS)
# define CPLD_SPI_EEPROM __REG16(CPLD0A_PHYS)
# define CPLD_INTERRUPTS __REG16(CPLD0C_PHYS) /* IRQ mask/status */
# define CPLD_BOOT_MODE __REG16(CPLD0E_PHYS)
# define CPLD_FLASH __REG16(CPLD10_PHYS)
# define CPLD_POWER_MGMT __REG16(CPLD12_PHYS)
# define CPLD_REVISION __REG16(CPLD14_PHYS)
# define CPLD_GPIO_EXT __REG16(CPLD16_PHYS)
# define CPLD_GPIO_DATA __REG16(CPLD18_PHYS)
# define CPLD_GPIO_DIR __REG16(CPLD1A_PHYS)
#endif
/* Timer registers */
#define TIMER_LOAD1 __REG(TIMER_PHYS + 0x00) /* Timer 1 initial value */
#define TIMER_VALUE1 __REG(TIMER_PHYS + 0x04) /* Timer 1 current value */
#define TIMER_CONTROL1 __REG(TIMER_PHYS + 0x08) /* Timer 1 control word */
#define TIMER_EOI1 __REG(TIMER_PHYS + 0x0c) /* Timer 1 interrupt clear */
#define TIMER_LOAD2 __REG(TIMER_PHYS + 0x20) /* Timer 2 initial value */
#define TIMER_VALUE2 __REG(TIMER_PHYS + 0x24) /* Timer 2 current value */
#define TIMER_CONTROL2 __REG(TIMER_PHYS + 0x28) /* Timer 2 control word */
#define TIMER_EOI2 __REG(TIMER_PHYS + 0x2c) /* Timer 2 interrupt clear */
#define TIMER_BUZZCON __REG(TIMER_PHYS + 0x40) /* Buzzer configuration */
#define TIMER_LOAD3 __REG(TIMER_PHYS + 0x80) /* Timer 3 initial value */
#define TIMER_VALUE3 __REG(TIMER_PHYS + 0x84) /* Timer 3 current value */
#define TIMER_CONTROL3 __REG(TIMER_PHYS + 0x88) /* Timer 3 control word */
#define TIMER_EOI3 __REG(TIMER_PHYS + 0x8c) /* Timer 3 interrupt clear */
#define TIMER_C_ENABLE (1<<7)
#define TIMER_C_PERIODIC (1<<6)
#define TIMER_C_FREERUNNING (0)
#define TIMER_C_2KHZ (0x00) /* 1.986 kHz */
#define TIMER_C_508KHZ (0x08)
/* GPIO registers */
#define GPIO_PFDD __REG(GPIO_PHYS + 0x34) /* PF direction */
#define GPIO_INTTYPE1 __REG(GPIO_PHYS + 0x4c) /* IRQ edge or lvl */
#define GPIO_INTTYPE2 __REG(GPIO_PHYS + 0x50) /* IRQ activ hi/lo */
#define GPIO_GPIOFEOI __REG(GPIO_PHYS + 0x54) /* GPIOF end of IRQ */
#define GPIO_GPIOFINTEN __REG(GPIO_PHYS + 0x58) /* GPIOF IRQ enable */
#define GPIO_INTSTATUS __REG(GPIO_PHYS + 0x5c) /* GPIOF IRQ latch */
#define GPIO_RAWINTSTATUS __REG(GPIO_PHYS + 0x60) /* GPIOF IRQ raw */
#endif /* _ASM_ARCH_REGISTERS_H */
/* ssp.h
written by Marc Singer
6 Dec 2004
Copyright (C) 2004 Marc Singer
-----------
DESCRIPTION
-----------
This SSP header is available throughout the kernel, for this
machine/architecture, because drivers that use it may be dispersed.
This file was cloned from the 7952x implementation. It would be
better to share them, but we're taking an easier approach for the
time being.
*/
#if !defined (__SSP_H__)
# define __SSP_H__
/* ----- Includes */
/* ----- Types */
struct ssp_driver {
int (*init) (void);
void (*exit) (void);
void (*acquire) (void);
void (*release) (void);
int (*configure) (int device, int mode, int speed,
int frame_size_write, int frame_size_read);
void (*chip_select) (int enable);
void (*set_callbacks) (void* handle,
irqreturn_t (*callback_tx)(void*),
irqreturn_t (*callback_rx)(void*));
void (*enable) (void);
void (*disable) (void);
// int (*save_state) (void*);
// void (*restore_state) (void*);
int (*read) (void);
int (*write) (u16 data);
int (*write_read) (u16 data);
void (*flush) (void);
void (*write_async) (void* pv, size_t cb);
size_t (*write_pos) (void);
};
/* These modes are only available on the LH79524 */
#define SSP_MODE_SPI (1)
#define SSP_MODE_SSI (2)
#define SSP_MODE_MICROWIRE (3)
#define SSP_MODE_I2S (4)
/* CPLD SPI devices */
#define DEVICE_EEPROM 0 /* Configuration eeprom */
#define DEVICE_MAC 1 /* MAC eeprom (LPD79524) */
#define DEVICE_CODEC 2 /* Audio codec */
#define DEVICE_TOUCH 3 /* Touch screen (LPD79520) */
/* ----- Globals */
/* ----- Prototypes */
//extern struct ssp_driver lh79520_i2s_driver;
extern struct ssp_driver lh7a400_cpld_ssp_driver;
#endif /* __SSP_H__ */
/* arch/arm/mach-lh7a40x/include/mach/system.h
*
* Copyright (C) 2004 Coastal Environmental Systems
*
* 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.
*
*/
static inline void arch_idle(void)
{
cpu_do_idle ();
}
static inline void arch_reset(char mode, const char *cmd)
{
cpu_reset (0);
}
/* arch/arm/mach-lh7a40x/include/mach/timex.h
*
* Copyright (C) 2004 Coastal Environmental Systems
*
* 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 <mach/constants.h>
#define CLOCK_TICK_RATE (PLL_CLOCK/6/16)
/*
#define CLOCK_TICK_RATE 3686400
*/
/* arch/arm/mach-lh7a40x/include/mach/uncompress.h
*
* Copyright (C) 2004 Coastal Environmental Systems
*
* 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 <mach/registers.h>
#ifndef UART_R_DATA
# define UART_R_DATA (0x00)
#endif
#ifndef UART_R_STATUS
# define UART_R_STATUS (0x10)
#endif
#define nTxRdy (0x20) /* Not TxReady (literally Tx FIFO full) */
/* Access UART with physical addresses before MMU is setup */
#define UART_STATUS (*(volatile unsigned long*) (UART2_PHYS + UART_R_STATUS))
#define UART_DATA (*(volatile unsigned long*) (UART2_PHYS + UART_R_DATA))
static inline void putc(int ch)
{
while (UART_STATUS & nTxRdy)
barrier();
UART_DATA = ch;
}
static inline void flush(void)
{
}
/* NULL functions; we don't presently need them */
#define arch_decomp_setup()
#define arch_decomp_wdog()
/* arch/arm/mach-lh7a40x/include/mach/vmalloc.h
*
* Copyright (C) 2004 Coastal Environmental Systems
*
* 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.
*
*/
#define VMALLOC_END (0xe8000000UL)
/* arch/arm/mach-lh7a40x/irq-kev7a400.c
*
* Copyright (C) 2004 Coastal Environmental Systems
*
* 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/interrupt.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/mach/hardware.h>
#include <asm/mach/irqs.h>
#include "common.h"
/* KEV7a400 CPLD IRQ handling */
static u16 CPLD_IRQ_mask; /* Mask for CPLD IRQs, 1 == unmasked */
static void
lh7a400_ack_cpld_irq (u32 irq)
{
CPLD_CL_INT = 1 << (irq - IRQ_KEV7A400_CPLD);
}
static void
lh7a400_mask_cpld_irq (u32 irq)
{
CPLD_IRQ_mask &= ~(1 << (irq - IRQ_KEV7A400_CPLD));
CPLD_WR_PB_INT_MASK = CPLD_IRQ_mask;
}
static void
lh7a400_unmask_cpld_irq (u32 irq)
{
CPLD_IRQ_mask |= 1 << (irq - IRQ_KEV7A400_CPLD);
CPLD_WR_PB_INT_MASK = CPLD_IRQ_mask;
}
static struct
irq_chip lh7a400_cpld_chip = {
.name = "CPLD",
.ack = lh7a400_ack_cpld_irq,
.mask = lh7a400_mask_cpld_irq,
.unmask = lh7a400_unmask_cpld_irq,
};
static void
lh7a400_cpld_handler (unsigned int irq, struct irq_desc *desc)
{
u32 mask = CPLD_LATCHED_INTS;
irq = IRQ_KEV_7A400_CPLD;
for (; mask; mask >>= 1, ++irq) {
if (mask & 1)
desc[irq].handle (irq, desc);
}
}
/* IRQ initialization */
void __init
lh7a400_init_board_irq (void)
{
int irq;
for (irq = IRQ_KEV7A400_CPLD;
irq < IRQ_KEV7A400_CPLD + NR_IRQ_KEV7A400_CPLD; ++irq) {
set_irq_chip (irq, &lh7a400_cpld_chip);
set_irq_handler (irq, handle_edge_irq);
set_irq_flags (irq, IRQF_VALID);
}
set_irq_chained_handler (IRQ_CPLD, kev7a400_cpld_handler);
/* Clear all CPLD interrupts */
CPLD_CL_INT = 0xff; /* CPLD_INTR_MMC_CD | CPLD_INTR_ETH_INT; */
/* *** FIXME CF enabled in ide-probe.c */
GPIO_GPIOINTEN = 0; /* Disable all GPIO interrupts */
barrier();
GPIO_INTTYPE1
= (GPIO_INTR_PCC1_CD | GPIO_INTR_PCC1_CD); /* Edge trig. */
GPIO_INTTYPE2 = 0; /* Falling edge & low-level */
GPIO_GPIOFEOI = 0xff; /* Clear all GPIO interrupts */
GPIO_GPIOINTEN = 0xff; /* Enable all GPIO interrupts */
init_FIQ();
}
/* arch/arm/mach-lh7a40x/irq-lh7a400.c
*
* Copyright (C) 2004 Coastal Environmental Systems
*
* 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/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <mach/irqs.h>
#include "common.h"
/* CPU IRQ handling */
static void lh7a400_mask_irq(struct irq_data *d)
{
INTC_INTENC = (1 << d->irq);
}
static void lh7a400_unmask_irq(struct irq_data *d)
{
INTC_INTENS = (1 << d->irq);
}
static void lh7a400_ack_gpio_irq(struct irq_data *d)
{
GPIO_GPIOFEOI = (1 << IRQ_TO_GPIO (d->irq));
INTC_INTENC = (1 << d->irq);
}
static struct irq_chip lh7a400_internal_chip = {
.name = "MPU",
.irq_ack = lh7a400_mask_irq, /* Level triggering -> mask is ack */
.irq_mask = lh7a400_mask_irq,
.irq_unmask = lh7a400_unmask_irq,
};
static struct irq_chip lh7a400_gpio_chip = {
.name = "GPIO",
.irq_ack = lh7a400_ack_gpio_irq,
.irq_mask = lh7a400_mask_irq,
.irq_unmask = lh7a400_unmask_irq,
};
/* IRQ initialization */
void __init lh7a400_init_irq (void)
{
int irq;
INTC_INTENC = 0xffffffff; /* Disable all interrupts */
GPIO_GPIOFINTEN = 0x00; /* Disable all GPIOF interrupts */
barrier ();
for (irq = 0; irq < NR_IRQS; ++irq) {
switch (irq) {
case IRQ_GPIO0INTR:
case IRQ_GPIO1INTR:
case IRQ_GPIO2INTR:
case IRQ_GPIO3INTR:
case IRQ_GPIO4INTR:
case IRQ_GPIO5INTR:
case IRQ_GPIO6INTR:
case IRQ_GPIO7INTR:
set_irq_chip (irq, &lh7a400_gpio_chip);
set_irq_handler (irq, handle_level_irq); /* OK default */
break;
default:
set_irq_chip (irq, &lh7a400_internal_chip);
set_irq_handler (irq, handle_level_irq);
}
set_irq_flags (irq, IRQF_VALID);
}
lh7a40x_init_board_irq ();
/* *** FIXME: the LH7a400 does use FIQ interrupts in some cases. For
the time being, these are not initialized. */
/* init_FIQ(); */
}
/* arch/arm/mach-lh7a40x/irq-lh7a404.c
*
* Copyright (C) 2004 Logic Product Development
*
* 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/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <mach/irqs.h>
#include "common.h"
#define USE_PRIORITIES
/* See Documentation/arm/Sharp-LH/VectoredInterruptController for more
* information on using the vectored interrupt controller's
* prioritizing feature. */
static unsigned char irq_pri_vic1[] = {
#if defined (USE_PRIORITIES)
IRQ_GPIO3INTR, /* CPLD */
IRQ_DMAM2P4, IRQ_DMAM2P5, /* AC97 */
#endif
};
static unsigned char irq_pri_vic2[] = {
#if defined (USE_PRIORITIES)
IRQ_T3UI, /* Timer */
IRQ_GPIO7INTR, /* CPLD */
IRQ_UART1INTR, IRQ_UART2INTR, IRQ_UART3INTR,
IRQ_LCDINTR, /* LCD */
IRQ_TSCINTR, /* ADC/Touchscreen */
#endif
};
/* CPU IRQ handling */
static void lh7a404_vic1_mask_irq(struct irq_data *d)
{
VIC1_INTENCLR = (1 << d->irq);
}
static void lh7a404_vic1_unmask_irq(struct irq_data *d)
{
VIC1_INTEN = (1 << d->irq);
}
static void lh7a404_vic2_mask_irq(struct irq_data *d)
{
VIC2_INTENCLR = (1 << (d->irq - 32));
}
static void lh7a404_vic2_unmask_irq(struct irq_data *d)
{
VIC2_INTEN = (1 << (d->irq - 32));
}
static void lh7a404_vic1_ack_gpio_irq(struct irq_data *d)
{
GPIO_GPIOFEOI = (1 << IRQ_TO_GPIO (d->irq));
VIC1_INTENCLR = (1 << d->irq);
}
static void lh7a404_vic2_ack_gpio_irq(struct irq_data *d)
{
GPIO_GPIOFEOI = (1 << IRQ_TO_GPIO (d->irq));
VIC2_INTENCLR = (1 << d->irq);
}
static struct irq_chip lh7a404_vic1_chip = {
.name = "VIC1",
.irq_ack = lh7a404_vic1_mask_irq, /* Because level-triggered */
.irq_mask = lh7a404_vic1_mask_irq,
.irq_unmask = lh7a404_vic1_unmask_irq,
};
static struct irq_chip lh7a404_vic2_chip = {
.name = "VIC2",
.irq_ack = lh7a404_vic2_mask_irq, /* Because level-triggered */
.irq_mask = lh7a404_vic2_mask_irq,
.irq_unmask = lh7a404_vic2_unmask_irq,
};
static struct irq_chip lh7a404_gpio_vic1_chip = {
.name = "GPIO-VIC1",
.irq_ack = lh7a404_vic1_ack_gpio_irq,
.irq_mask = lh7a404_vic1_mask_irq,
.irq_unmask = lh7a404_vic1_unmask_irq,
};
static struct irq_chip lh7a404_gpio_vic2_chip = {
.name = "GPIO-VIC2",
.irq_ack = lh7a404_vic2_ack_gpio_irq,
.irq_mask = lh7a404_vic2_mask_irq,
.irq_unmask = lh7a404_vic2_unmask_irq,
};
/* IRQ initialization */
#if defined (CONFIG_ARCH_LH7A400) && defined (CONFIG_ARCH_LH7A404)
extern void* branch_irq_lh7a400;
#endif
void __init lh7a404_init_irq (void)
{
int irq;
#if defined (CONFIG_ARCH_LH7A400) && defined (CONFIG_ARCH_LH7A404)
#define NOP 0xe1a00000 /* mov r0, r0 */
branch_irq_lh7a400 = NOP;
#endif
VIC1_INTENCLR = 0xffffffff;
VIC2_INTENCLR = 0xffffffff;
VIC1_INTSEL = 0; /* All IRQs */
VIC2_INTSEL = 0; /* All IRQs */
VIC1_NVADDR = VA_VIC1DEFAULT;
VIC2_NVADDR = VA_VIC2DEFAULT;
VIC1_VECTADDR = 0;
VIC2_VECTADDR = 0;
GPIO_GPIOFINTEN = 0x00; /* Disable all GPIOF interrupts */
barrier ();
/* Install prioritized interrupts, if there are any. */
/* The | 0x20*/
for (irq = 0; irq < 16; ++irq) {
(&VIC1_VAD0)[irq]
= (irq < ARRAY_SIZE (irq_pri_vic1))
? (irq_pri_vic1[irq] | VA_VECTORED) : 0;
(&VIC1_VECTCNTL0)[irq]
= (irq < ARRAY_SIZE (irq_pri_vic1))
? (irq_pri_vic1[irq] | VIC_CNTL_ENABLE) : 0;
(&VIC2_VAD0)[irq]
= (irq < ARRAY_SIZE (irq_pri_vic2))
? (irq_pri_vic2[irq] | VA_VECTORED) : 0;
(&VIC2_VECTCNTL0)[irq]
= (irq < ARRAY_SIZE (irq_pri_vic2))
? (irq_pri_vic2[irq] | VIC_CNTL_ENABLE) : 0;
}
for (irq = 0; irq < NR_IRQS; ++irq) {
switch (irq) {
case IRQ_GPIO0INTR:
case IRQ_GPIO1INTR:
case IRQ_GPIO2INTR:
case IRQ_GPIO3INTR:
case IRQ_GPIO4INTR:
case IRQ_GPIO5INTR:
case IRQ_GPIO6INTR:
case IRQ_GPIO7INTR:
set_irq_chip (irq, irq < 32
? &lh7a404_gpio_vic1_chip
: &lh7a404_gpio_vic2_chip);
set_irq_handler (irq, handle_level_irq); /* OK default */
break;
default:
set_irq_chip (irq, irq < 32
? &lh7a404_vic1_chip
: &lh7a404_vic2_chip);
set_irq_handler (irq, handle_level_irq);
}
set_irq_flags (irq, IRQF_VALID);
}
lh7a40x_init_board_irq ();
}
/* arch/arm/mach-lh7a40x/irq-lpd7a40x.c
*
* Copyright (C) 2004 Coastal Environmental Systems
* Copyright (C) 2004 Logic Product Development
*
* 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/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <mach/irqs.h>
#include "common.h"
static void lh7a40x_ack_cpld_irq(struct irq_data *d)
{
/* CPLD doesn't have ack capability */
}
static void lh7a40x_mask_cpld_irq(struct irq_data *d)
{
switch (d->irq) {
case IRQ_LPD7A40X_ETH_INT:
CPLD_INTERRUPTS = CPLD_INTERRUPTS | 0x4;
break;
case IRQ_LPD7A400_TS:
CPLD_INTERRUPTS = CPLD_INTERRUPTS | 0x8;
break;
}
}
static void lh7a40x_unmask_cpld_irq(struct irq_data *d)
{
switch (d->irq) {
case IRQ_LPD7A40X_ETH_INT:
CPLD_INTERRUPTS = CPLD_INTERRUPTS & ~ 0x4;
break;
case IRQ_LPD7A400_TS:
CPLD_INTERRUPTS = CPLD_INTERRUPTS & ~ 0x8;
break;
}
}
static struct irq_chip lh7a40x_cpld_chip = {
.name = "CPLD",
.irq_ack = lh7a40x_ack_cpld_irq,
.irq_mask = lh7a40x_mask_cpld_irq,
.irq_unmask = lh7a40x_unmask_cpld_irq,
};
static void lh7a40x_cpld_handler (unsigned int irq, struct irq_desc *desc)
{
unsigned int mask = CPLD_INTERRUPTS;
desc->irq_data.chip->ack (irq);
if ((mask & 0x1) == 0) /* WLAN */
generic_handle_irq(IRQ_LPD7A40X_ETH_INT);
if ((mask & 0x2) == 0) /* Touch */
generic_handle_irq(IRQ_LPD7A400_TS);
desc->irq_data.chip->unmask (irq); /* Level-triggered need this */
}
/* IRQ initialization */
void __init lh7a40x_init_board_irq (void)
{
int irq;
/* Rev A (v2.8): PF0, PF1, PF2, and PF3 are available IRQs.
PF7 supports the CPLD.
Rev B (v3.4): PF0, PF1, and PF2 are available IRQs.
PF3 supports the CPLD.
(Some) LPD7A404 prerelease boards report a version
number of 0x16, but we force an override since the
hardware is of the newer variety.
*/
unsigned char cpld_version = CPLD_REVISION;
int pinCPLD;
#if defined CONFIG_MACH_LPD7A404
cpld_version = 0x34; /* Override, for now */
#endif
pinCPLD = (cpld_version == 0x28) ? 7 : 3;
/* First, configure user controlled GPIOF interrupts */
GPIO_PFDD &= ~0x0f; /* PF0-3 are inputs */
GPIO_INTTYPE1 &= ~0x0f; /* PF0-3 are level triggered */
GPIO_INTTYPE2 &= ~0x0f; /* PF0-3 are active low */
barrier ();
GPIO_GPIOFINTEN |= 0x0f; /* Enable PF0, PF1, PF2, and PF3 IRQs */
/* Then, configure CPLD interrupt */
CPLD_INTERRUPTS = 0x0c; /* Disable all CPLD interrupts */
GPIO_PFDD &= ~(1 << pinCPLD); /* Make input */
GPIO_INTTYPE1 |= (1 << pinCPLD); /* Edge triggered */
GPIO_INTTYPE2 &= ~(1 << pinCPLD); /* Active low */
barrier ();
GPIO_GPIOFINTEN |= (1 << pinCPLD); /* Enable */
/* Cascade CPLD interrupts */
for (irq = IRQ_BOARD_START;
irq < IRQ_BOARD_START + NR_IRQ_BOARD; ++irq) {
set_irq_chip (irq, &lh7a40x_cpld_chip);
set_irq_handler (irq, handle_edge_irq);
set_irq_flags (irq, IRQF_VALID);
}
set_irq_chained_handler ((cpld_version == 0x28)
? IRQ_CPLD_V28
: IRQ_CPLD_V34,
lh7a40x_cpld_handler);
}
/* lcd-panel.h
written by Marc Singer
18 Jul 2005
Copyright (C) 2005 Marc Singer
-----------
DESCRIPTION
-----------
Only one panel may be defined at a time.
The pixel clock is calculated to be no greater than the target.
Each timing value is accompanied by a specification comment.
UNITS/MIN/TYP/MAX
Most of the units will be in clocks.
USE_RGB555
Define this macro to configure the AMBA LCD controller to use an
RGB555 encoding for the pels instead of the normal RGB565.
LPD9520, LPD79524, LPD7A400, LPD7A404-10, LPD7A404-11
These boards are best approximated by 555 for all panels. Some
can use an extra low-order bit of blue in bit 16 of the color
value, but we don't have a way to communicate this non-linear
mapping to the kernel.
*/
#if !defined (__LCD_PANEL_H__)
# define __LCD_PANEL_H__
#if defined (MACH_LPD79520)\
|| defined (MACH_LPD79524)\
|| defined (MACH_LPD7A400)\
|| defined (MACH_LPD7A404)
# define USE_RGB555
#endif
struct clcd_panel_extra {
unsigned int hrmode;
unsigned int clsen;
unsigned int spsen;
unsigned int pcdel;
unsigned int revdel;
unsigned int lpdel;
unsigned int spldel;
unsigned int pc2del;
};
#define NS_TO_CLOCK(ns,c) ((((ns)*((c)/1000) + (1000000 - 1))/1000000))
#define CLOCK_TO_DIV(e,c) (((c) + (e) - 1)/(e))
#if defined CONFIG_FB_ARMCLCD_SHARP_LQ035Q7DB02_HRTFT
/* Logic Product Development LCD 3.5" QVGA HRTFT -10 */
/* Sharp PN LQ035Q7DB02 w/HRTFT controller chip */
#define PIX_CLOCK_TARGET (6800000)
#define PIX_CLOCK_DIVIDER CLOCK_TO_DIV (PIX_CLOCK_TARGET, HCLK)
#define PIX_CLOCK (HCLK/PIX_CLOCK_DIVIDER)
static struct clcd_panel lcd_panel = {
.mode = {
.name = "3.5in QVGA (LQ035Q7DB02)",
.xres = 240,
.yres = 320,
.pixclock = PIX_CLOCK,
.left_margin = 16,
.right_margin = 21,
.upper_margin = 8, // line/8/8/8
.lower_margin = 5,
.hsync_len = 61,
.vsync_len = NS_TO_CLOCK (60, PIX_CLOCK),
.vmode = FB_VMODE_NONINTERLACED,
},
.width = -1,
.height = -1,
.tim2 = TIM2_IPC | (PIX_CLOCK_DIVIDER - 2),
.cntl = CNTL_LCDTFT | CNTL_WATERMARK,
.bpp = 16,
};
#define HAS_LCD_PANEL_EXTRA
static struct clcd_panel_extra lcd_panel_extra = {
.hrmode = 1,
.clsen = 1,
.spsen = 1,
.pcdel = 8,
.revdel = 7,
.lpdel = 13,
.spldel = 77,
.pc2del = 208,
};
#endif
#if defined CONFIG_FB_ARMCLCD_SHARP_LQ057Q3DC02
/* Logic Product Development LCD 5.7" QVGA -10 */
/* Sharp PN LQ057Q3DC02 */
/* QVGA mode, V/Q=LOW */
/* From Sharp on 2006.1.3. I believe some of the values are incorrect
* based on the datasheet.
Timing0 TIMING1 TIMING2 CONTROL
0x140A0C4C 0x080504EF 0x013F380D 0x00000829
HBP= 20 VBP= 8 BCD= 0
HFP= 10 VFP= 5 CPL=319
HSW= 12 VSW= 1 IOE= 0
PPL= 19 LPP=239 IPC= 1
IHS= 1
IVS= 1
ACB= 0
CSEL= 0
PCD= 13
*/
/* The full horizontal cycle (Th) is clock/360/400/450. */
/* The full vertical cycle (Tv) is line/251/262/280. */
#define PIX_CLOCK_TARGET (6300000) /* -/6.3/7 MHz */
#define PIX_CLOCK_DIVIDER CLOCK_TO_DIV (PIX_CLOCK_TARGET, HCLK)
#define PIX_CLOCK (HCLK/PIX_CLOCK_DIVIDER)
static struct clcd_panel lcd_panel = {
.mode = {
.name = "5.7in QVGA (LQ057Q3DC02)",
.xres = 320,
.yres = 240,
.pixclock = PIX_CLOCK,
.left_margin = 11,
.right_margin = 400-11-320-2,
.upper_margin = 7, // line/7/7/7
.lower_margin = 262-7-240-2,
.hsync_len = 2, // clk/2/96/200
.vsync_len = 2, // line/2/-/34
.vmode = FB_VMODE_NONINTERLACED,
},
.width = -1,
.height = -1,
.tim2 = TIM2_IHS | TIM2_IVS
| (PIX_CLOCK_DIVIDER - 2),
.cntl = CNTL_LCDTFT | CNTL_WATERMARK,
.bpp = 16,
};
#endif
#if defined CONFIG_FB_ARMCLCD_SHARP_LQ64D343
/* Logic Product Development LCD 6.4" VGA -10 */
/* Sharp PN LQ64D343 */
/* The full horizontal cycle (Th) is clock/750/800/900. */
/* The full vertical cycle (Tv) is line/515/525/560. */
#define PIX_CLOCK_TARGET (28330000)
#define PIX_CLOCK_DIVIDER CLOCK_TO_DIV (PIX_CLOCK_TARGET, HCLK)
#define PIX_CLOCK (HCLK/PIX_CLOCK_DIVIDER)
static struct clcd_panel lcd_panel = {
.mode = {
.name = "6.4in QVGA (LQ64D343)",
.xres = 640,
.yres = 480,
.pixclock = PIX_CLOCK,
.left_margin = 32,
.right_margin = 800-32-640-96,
.upper_margin = 32, // line/34/34/34
.lower_margin = 540-32-480-2,
.hsync_len = 96, // clk/2/96/200
.vsync_len = 2, // line/2/-/34
.vmode = FB_VMODE_NONINTERLACED,
},
.width = -1,
.height = -1,
.tim2 = TIM2_IHS | TIM2_IVS
| (PIX_CLOCK_DIVIDER - 2),
.cntl = CNTL_LCDTFT | CNTL_WATERMARK,
.bpp = 16,
};
#endif
#if defined CONFIG_FB_ARMCLCD_SHARP_LQ10D368
/* Logic Product Development LCD 10.4" VGA -10 */
/* Sharp PN LQ10D368 */
#define PIX_CLOCK_TARGET (28330000)
#define PIX_CLOCK_DIVIDER CLOCK_TO_DIV (PIX_CLOCK_TARGET, HCLK)
#define PIX_CLOCK (HCLK/PIX_CLOCK_DIVIDER)
static struct clcd_panel lcd_panel = {
.mode = {
.name = "10.4in VGA (LQ10D368)",
.xres = 640,
.yres = 480,
.pixclock = PIX_CLOCK,
.left_margin = 21,
.right_margin = 15,
.upper_margin = 34,
.lower_margin = 5,
.hsync_len = 96,
.vsync_len = 16,
.vmode = FB_VMODE_NONINTERLACED,
},
.width = -1,
.height = -1,
.tim2 = TIM2_IHS | TIM2_IVS
| (PIX_CLOCK_DIVIDER - 2),
.cntl = CNTL_LCDTFT | CNTL_WATERMARK,
.bpp = 16,
};
#endif
#if defined CONFIG_FB_ARMCLCD_SHARP_LQ121S1DG41
/* Logic Product Development LCD 12.1" SVGA -10 */
/* Sharp PN LQ121S1DG41, was LQ121S1DG31 */
/* Note that with a 99993900 Hz HCLK, it is not possible to hit the
* target clock frequency range of 35MHz to 42MHz. */
/* If the target pixel clock is substantially lower than the panel
* spec, this is done to prevent the LCD display from glitching when
* the CPU is under load. A pixel clock higher than 25MHz
* (empirically determined) will compete with the CPU for bus cycles
* for the Ethernet chip. However, even a pixel clock of 10MHz
* competes with Compact Flash interface during some operations
* (fdisk, e2fsck). And, at that speed the display may have a visible
* flicker. */
/* The full horizontal cycle (Th) is clock/832/1056/1395. */
#define PIX_CLOCK_TARGET (20000000)
#define PIX_CLOCK_DIVIDER CLOCK_TO_DIV (PIX_CLOCK_TARGET, HCLK)
#define PIX_CLOCK (HCLK/PIX_CLOCK_DIVIDER)
static struct clcd_panel lcd_panel = {
.mode = {
.name = "12.1in SVGA (LQ121S1DG41)",
.xres = 800,
.yres = 600,
.pixclock = PIX_CLOCK,
.left_margin = 89, // ns/5/-/(1/PIX_CLOCK)-10
.right_margin = 1056-800-89-128,
.upper_margin = 23, // line/23/23/23
.lower_margin = 44,
.hsync_len = 128, // clk/2/128/200
.vsync_len = 4, // line/2/4/6
.vmode = FB_VMODE_NONINTERLACED,
},
.width = -1,
.height = -1,
.tim2 = TIM2_IHS | TIM2_IVS
| (PIX_CLOCK_DIVIDER - 2),
.cntl = CNTL_LCDTFT | CNTL_WATERMARK,
.bpp = 16,
};
#endif
#if defined CONFIG_FB_ARMCLCD_HITACHI
/* Hitachi*/
/* Submitted by Michele Da Rold <michele.darold@ecsproject.com> */
#define PIX_CLOCK_TARGET (49000000)
#define PIX_CLOCK_DIVIDER CLOCK_TO_DIV (PIX_CLOCK_TARGET, HCLK)
#define PIX_CLOCK (HCLK/PIX_CLOCK_DIVIDER)
static struct clcd_panel lcd_panel = {
.mode = {
.name = "Hitachi 800x480",
.xres = 800,
.yres = 480,
.pixclock = PIX_CLOCK,
.left_margin = 88,
.right_margin = 40,
.upper_margin = 32,
.lower_margin = 11,
.hsync_len = 128,
.vsync_len = 2,
.vmode = FB_VMODE_NONINTERLACED,
},
.width = -1,
.height = -1,
.tim2 = TIM2_IPC | TIM2_IHS | TIM2_IVS
| (PIX_CLOCK_DIVIDER - 2),
.cntl = CNTL_LCDTFT | CNTL_WATERMARK,
.bpp = 16,
};
#endif
#if defined CONFIG_FB_ARMCLCD_AUO_A070VW01_WIDE
/* AU Optotronics A070VW01 7.0 Wide Screen color Display*/
/* Submitted by Michele Da Rold <michele.darold@ecsproject.com> */
#define PIX_CLOCK_TARGET (10000000)
#define PIX_CLOCK_DIVIDER CLOCK_TO_DIV (PIX_CLOCK_TARGET, HCLK)
#define PIX_CLOCK (HCLK/PIX_CLOCK_DIVIDER)
static struct clcd_panel lcd_panel = {
.mode = {
.name = "7.0in Wide (A070VW01)",
.xres = 480,
.yres = 234,
.pixclock = PIX_CLOCK,
.left_margin = 30,
.right_margin = 25,
.upper_margin = 14,
.lower_margin = 12,
.hsync_len = 100,
.vsync_len = 1,
.vmode = FB_VMODE_NONINTERLACED,
},
.width = -1,
.height = -1,
.tim2 = TIM2_IPC | TIM2_IHS | TIM2_IVS
| (PIX_CLOCK_DIVIDER - 2),
.cntl = CNTL_LCDTFT | CNTL_WATERMARK,
.bpp = 16,
};
#endif
#undef NS_TO_CLOCK
#undef CLOCK_TO_DIV
#endif /* __LCD_PANEL_H__ */
/* arch/arm/mach-lh7a40x/ssp-cpld.c
*
* Copyright (C) 2004,2005 Marc Singer
*
* 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.
*
* SSP/SPI driver for the CardEngine CPLD.
*
*/
/* NOTES
-----
o *** This driver is cribbed from the 7952x implementation.
Some comments may not apply.
o This driver contains sufficient logic to control either the
serial EEPROMs or the audio codec. It is included in the kernel
to support the codec. The EEPROMs are really the responsibility
of the boot loader and should probably be left alone.
o The code must be augmented to cope with multiple, simultaneous
clients.
o The audio codec writes to the codec chip whenever playback
starts.
o The touchscreen driver writes to the ads chip every time it
samples.
o The audio codec must write 16 bits, but the touch chip writes
are 8 bits long.
o We need to be able to keep these configurations separate while
simultaneously active.
*/
#include <linux/module.h>
#include <linux/kernel.h>
//#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
//#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/ssp.h>
//#define TALK
#if defined (TALK)
#define PRINTK(f...) printk (f)
#else
#define PRINTK(f...) do {} while (0)
#endif
#if defined (CONFIG_ARCH_LH7A400)
# define CPLD_SPID __REGP16(CPLD06_VIRT) /* SPI data */
# define CPLD_SPIC __REGP16(CPLD08_VIRT) /* SPI control */
# define CPLD_SPIC_CS_CODEC (1<<0)
# define CPLD_SPIC_CS_TOUCH (1<<1)
# define CPLD_SPIC_WRITE (0<<2)
# define CPLD_SPIC_READ (1<<2)
# define CPLD_SPIC_DONE (1<<3) /* r/o */
# define CPLD_SPIC_LOAD (1<<4)
# define CPLD_SPIC_START (1<<4)
# define CPLD_SPIC_LOADED (1<<5) /* r/o */
#endif
#define CPLD_SPI __REGP16(CPLD0A_VIRT) /* SPI operation */
#define CPLD_SPI_CS_EEPROM (1<<3)
#define CPLD_SPI_SCLK (1<<2)
#define CPLD_SPI_TX_SHIFT (1)
#define CPLD_SPI_TX (1<<CPLD_SPI_TX_SHIFT)
#define CPLD_SPI_RX_SHIFT (0)
#define CPLD_SPI_RX (1<<CPLD_SPI_RX_SHIFT)
/* *** FIXME: these timing values are substantially larger than the
*** chip requires. We may implement an nsleep () function. */
#define T_SKH 1 /* Clock time high (us) */
#define T_SKL 1 /* Clock time low (us) */
#define T_CS 1 /* Minimum chip select low time (us) */
#define T_CSS 1 /* Minimum chip select setup time (us) */
#define T_DIS 1 /* Data setup time (us) */
/* EEPROM SPI bits */
#define P_START (1<<9)
#define P_WRITE (1<<7)
#define P_READ (2<<7)
#define P_ERASE (3<<7)
#define P_EWDS (0<<7)
#define P_WRAL (0<<7)
#define P_ERAL (0<<7)
#define P_EWEN (0<<7)
#define P_A_EWDS (0<<5)
#define P_A_WRAL (1<<5)
#define P_A_ERAL (2<<5)
#define P_A_EWEN (3<<5)
struct ssp_configuration {
int device;
int mode;
int speed;
int frame_size_write;
int frame_size_read;
};
static struct ssp_configuration ssp_configuration;
static spinlock_t ssp_lock;
static void enable_cs (void)
{
switch (ssp_configuration.device) {
case DEVICE_EEPROM:
CPLD_SPI |= CPLD_SPI_CS_EEPROM;
break;
}
udelay (T_CSS);
}
static void disable_cs (void)
{
switch (ssp_configuration.device) {
case DEVICE_EEPROM:
CPLD_SPI &= ~CPLD_SPI_CS_EEPROM;
break;
}
udelay (T_CS);
}
static void pulse_clock (void)
{
CPLD_SPI |= CPLD_SPI_SCLK;
udelay (T_SKH);
CPLD_SPI &= ~CPLD_SPI_SCLK;
udelay (T_SKL);
}
/* execute_spi_command
sends an spi command to a device. It first sends cwrite bits from
v. If cread is greater than zero it will read cread bits
(discarding the leading 0 bit) and return them. If cread is less
than zero it will check for completetion status and return 0 on
success or -1 on timeout. If cread is zero it does nothing other
than sending the command.
On the LPD7A400, we can only read or write multiples of 8 bits on
the codec and the touch screen device. Here, we round up.
*/
static int execute_spi_command (int v, int cwrite, int cread)
{
unsigned long l = 0;
#if defined (CONFIG_MACH_LPD7A400)
/* The codec and touch devices cannot be bit-banged. Instead,
* the CPLD provides an eight-bit shift register and a crude
* interface. */
if ( ssp_configuration.device == DEVICE_CODEC
|| ssp_configuration.device == DEVICE_TOUCH) {
int select = 0;
PRINTK ("spi(%d %d.%d) 0x%04x",
ssp_configuration.device, cwrite, cread,
v);
#if defined (TALK)
if (ssp_configuration.device == DEVICE_CODEC)
PRINTK (" 0x%03x -> %2d", v & 0x1ff, (v >> 9) & 0x7f);
#endif
PRINTK ("\n");
if (ssp_configuration.device == DEVICE_CODEC)
select = CPLD_SPIC_CS_CODEC;
if (ssp_configuration.device == DEVICE_TOUCH)
select = CPLD_SPIC_CS_TOUCH;
if (cwrite) {
for (cwrite = (cwrite + 7)/8; cwrite-- > 0; ) {
CPLD_SPID = (v >> (8*cwrite)) & 0xff;
CPLD_SPIC = select | CPLD_SPIC_LOAD;
while (!(CPLD_SPIC & CPLD_SPIC_LOADED))
;
CPLD_SPIC = select;
while (!(CPLD_SPIC & CPLD_SPIC_DONE))
;
}
v = 0;
}
if (cread) {
mdelay (2); /* *** FIXME: required by ads7843? */
v = 0;
for (cread = (cread + 7)/8; cread-- > 0;) {
CPLD_SPID = 0;
CPLD_SPIC = select | CPLD_SPIC_READ
| CPLD_SPIC_START;
while (!(CPLD_SPIC & CPLD_SPIC_LOADED))
;
CPLD_SPIC = select | CPLD_SPIC_READ;
while (!(CPLD_SPIC & CPLD_SPIC_DONE))
;
v = (v << 8) | CPLD_SPID;
}
}
return v;
}
#endif
PRINTK ("spi(%d) 0x%04x -> 0x%x\r\n", ssp_configuration.device,
v & 0x1ff, (v >> 9) & 0x7f);
enable_cs ();
v <<= CPLD_SPI_TX_SHIFT; /* Correction for position of SPI_TX bit */
while (cwrite--) {
CPLD_SPI
= (CPLD_SPI & ~CPLD_SPI_TX)
| ((v >> cwrite) & CPLD_SPI_TX);
udelay (T_DIS);
pulse_clock ();
}
if (cread < 0) {
int delay = 10;
disable_cs ();
udelay (1);
enable_cs ();
l = -1;
do {
if (CPLD_SPI & CPLD_SPI_RX) {
l = 0;
break;
}
} while (udelay (1), --delay);
}
else
/* We pulse the clock before the data to skip the leading zero. */
while (cread-- > 0) {
pulse_clock ();
l = (l<<1)
| (((CPLD_SPI & CPLD_SPI_RX)
>> CPLD_SPI_RX_SHIFT) & 0x1);
}
disable_cs ();
return l;
}
static int ssp_init (void)
{
spin_lock_init (&ssp_lock);
memset (&ssp_configuration, 0, sizeof (ssp_configuration));
return 0;
}
/* ssp_chip_select
drops the chip select line for the CPLD shift-register controlled
devices. It doesn't enable chip
*/
static void ssp_chip_select (int enable)
{
#if defined (CONFIG_MACH_LPD7A400)
int select;
if (ssp_configuration.device == DEVICE_CODEC)
select = CPLD_SPIC_CS_CODEC;
else if (ssp_configuration.device == DEVICE_TOUCH)
select = CPLD_SPIC_CS_TOUCH;
else
return;
if (enable)
CPLD_SPIC = select;
else
CPLD_SPIC = 0;
#endif
}
static void ssp_acquire (void)
{
spin_lock (&ssp_lock);
}
static void ssp_release (void)
{
ssp_chip_select (0); /* just in case */
spin_unlock (&ssp_lock);
}
static int ssp_configure (int device, int mode, int speed,
int frame_size_write, int frame_size_read)
{
ssp_configuration.device = device;
ssp_configuration.mode = mode;
ssp_configuration.speed = speed;
ssp_configuration.frame_size_write = frame_size_write;
ssp_configuration.frame_size_read = frame_size_read;
return 0;
}
static int ssp_read (void)
{
return execute_spi_command (0, 0, ssp_configuration.frame_size_read);
}
static int ssp_write (u16 data)
{
execute_spi_command (data, ssp_configuration.frame_size_write, 0);
return 0;
}
static int ssp_write_read (u16 data)
{
return execute_spi_command (data, ssp_configuration.frame_size_write,
ssp_configuration.frame_size_read);
}
struct ssp_driver lh7a40x_cpld_ssp_driver = {
.init = ssp_init,
.acquire = ssp_acquire,
.release = ssp_release,
.configure = ssp_configure,
.chip_select = ssp_chip_select,
.read = ssp_read,
.write = ssp_write,
.write_read = ssp_write_read,
};
MODULE_AUTHOR("Marc Singer");
MODULE_DESCRIPTION("LPD7A40X CPLD SPI driver");
MODULE_LICENSE("GPL");
/*
* arch/arm/mach-lh7a40x/time.c
*
* Copyright (C) 2004 Logic Product Development
*
* 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/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/time.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <asm/irq.h>
#include <asm/leds.h>
#include <asm/mach/time.h>
#include "common.h"
#if HZ < 100
# define TIMER_CONTROL TIMER_CONTROL2
# define TIMER_LOAD TIMER_LOAD2
# define TIMER_CONSTANT (508469/HZ)
# define TIMER_MODE (TIMER_C_ENABLE | TIMER_C_PERIODIC | TIMER_C_508KHZ)
# define TIMER_EOI TIMER_EOI2
# define TIMER_IRQ IRQ_T2UI
#else
# define TIMER_CONTROL TIMER_CONTROL3
# define TIMER_LOAD TIMER_LOAD3
# define TIMER_CONSTANT (3686400/HZ)
# define TIMER_MODE (TIMER_C_ENABLE | TIMER_C_PERIODIC)
# define TIMER_EOI TIMER_EOI3
# define TIMER_IRQ IRQ_T3UI
#endif
static irqreturn_t
lh7a40x_timer_interrupt(int irq, void *dev_id)
{
TIMER_EOI = 0;
timer_tick();
return IRQ_HANDLED;
}
static struct irqaction lh7a40x_timer_irq = {
.name = "LHA740x Timer Tick",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = lh7a40x_timer_interrupt,
};
static void __init lh7a40x_timer_init (void)
{
/* Stop/disable all timers */
TIMER_CONTROL1 = 0;
TIMER_CONTROL2 = 0;
TIMER_CONTROL3 = 0;
setup_irq (TIMER_IRQ, &lh7a40x_timer_irq);
TIMER_LOAD = TIMER_CONSTANT;
TIMER_CONTROL = TIMER_MODE;
}
struct sys_timer lh7a40x_timer = {
.init = &lh7a40x_timer_init,
};
...@@ -206,68 +206,6 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg) ...@@ -206,68 +206,6 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg)
#define RPC_LSA_DEFAULT RPC_LED_TX_RX #define RPC_LSA_DEFAULT RPC_LED_TX_RX
#define RPC_LSB_DEFAULT RPC_LED_100_10 #define RPC_LSB_DEFAULT RPC_LED_100_10
#elif defined(CONFIG_MACH_LPD79520) || \
defined(CONFIG_MACH_LPD7A400) || \
defined(CONFIG_MACH_LPD7A404)
/* The LPD7X_IOBARRIER is necessary to overcome a mismatch between the
* way that the CPU handles chip selects and the way that the SMC chip
* expects the chip select to operate. Refer to
* Documentation/arm/Sharp-LH/IOBarrier for details. The read from
* IOBARRIER is a byte, in order that we read the least-common
* denominator. It would be wasteful to read 32 bits from an 8-bit
* accessible region.
*
* There is no explicit protection against interrupts intervening
* between the writew and the IOBARRIER. In SMC ISR there is a
* preamble that performs an IOBARRIER in the extremely unlikely event
* that the driver interrupts itself between a writew to the chip an
* the IOBARRIER that follows *and* the cache is large enough that the
* first off-chip access while handing the interrupt is to the SMC
* chip. Other devices in the same address space as the SMC chip must
* be aware of the potential for trouble and perform a similar
* IOBARRIER on entry to their ISR.
*/
#include <mach/constants.h> /* IOBARRIER_VIRT */
#define SMC_CAN_USE_8BIT 0
#define SMC_CAN_USE_16BIT 1
#define SMC_CAN_USE_32BIT 0
#define SMC_NOWAIT 0
#define LPD7X_IOBARRIER readb (IOBARRIER_VIRT)
#define SMC_inw(a,r)\
({ unsigned short v = readw ((void*) ((a) + (r))); LPD7X_IOBARRIER; v; })
#define SMC_outw(v,a,r) ({ writew ((v), (a) + (r)); LPD7X_IOBARRIER; })
#define SMC_insw LPD7_SMC_insw
static inline void LPD7_SMC_insw (unsigned char* a, int r,
unsigned char* p, int l)
{
unsigned short* ps = (unsigned short*) p;
while (l-- > 0) {
*ps++ = readw (a + r);
LPD7X_IOBARRIER;
}
}
#define SMC_outsw LPD7_SMC_outsw
static inline void LPD7_SMC_outsw (unsigned char* a, int r,
unsigned char* p, int l)
{
unsigned short* ps = (unsigned short*) p;
while (l-- > 0) {
writew (*ps++, a + r);
LPD7X_IOBARRIER;
}
}
#define SMC_INTERRUPT_PREAMBLE LPD7X_IOBARRIER
#define RPC_LSA_DEFAULT RPC_LED_TX_RX
#define RPC_LSB_DEFAULT RPC_LED_100_10
#elif defined(CONFIG_ARCH_VERSATILE) #elif defined(CONFIG_ARCH_VERSATILE)
#define SMC_CAN_USE_8BIT 1 #define SMC_CAN_USE_8BIT 1
......
...@@ -1110,29 +1110,6 @@ config SERIAL_PMACZILOG_CONSOLE ...@@ -1110,29 +1110,6 @@ config SERIAL_PMACZILOG_CONSOLE
on your (Power)Mac as the console, you can do so by answering on your (Power)Mac as the console, you can do so by answering
Y to this option. Y to this option.
config SERIAL_LH7A40X
tristate "Sharp LH7A40X embedded UART support"
depends on ARM && ARCH_LH7A40X
select SERIAL_CORE
help
This enables support for the three on-board UARTs of the
Sharp LH7A40X series CPUs. Choose Y or M.
config SERIAL_LH7A40X_CONSOLE
bool "Support for console on Sharp LH7A40X serial port"
depends on SERIAL_LH7A40X=y
select SERIAL_CORE_CONSOLE
help
Say Y here if you wish to use one of the serial ports as the
system console--the system console is the device which
receives all kernel messages and warnings and which allows
logins in single user mode.
Even if you say Y here, the currently visible framebuffer console
(/dev/tty0) will still be used as the default system console, but
you can alter that using a kernel command line, for example
"console=ttyAM1".
config SERIAL_CPM config SERIAL_CPM
tristate "CPM SCC/SMC serial port support" tristate "CPM SCC/SMC serial port support"
depends on CPM2 || 8xx depends on CPM2 || 8xx
......
...@@ -54,7 +54,6 @@ obj-$(CONFIG_SERIAL_68328) += 68328serial.o ...@@ -54,7 +54,6 @@ obj-$(CONFIG_SERIAL_68328) += 68328serial.o
obj-$(CONFIG_SERIAL_68360) += 68360serial.o obj-$(CONFIG_SERIAL_68360) += 68360serial.o
obj-$(CONFIG_SERIAL_MCF) += mcf.o obj-$(CONFIG_SERIAL_MCF) += mcf.o
obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o
obj-$(CONFIG_SERIAL_LH7A40X) += serial_lh7a40x.o
obj-$(CONFIG_SERIAL_DZ) += dz.o obj-$(CONFIG_SERIAL_DZ) += dz.o
obj-$(CONFIG_SERIAL_ZS) += zs.o obj-$(CONFIG_SERIAL_ZS) += zs.o
obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o
......
/* drivers/serial/serial_lh7a40x.c
*
* Copyright (C) 2004 Coastal Environmental Systems
*
* 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.
*
*/
/* Driver for Sharp LH7A40X embedded serial ports
*
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
* Based on drivers/serial/amba.c, by Deep Blue Solutions Ltd.
*
* ---
*
* This driver supports the embedded UARTs of the Sharp LH7A40X series
* CPUs. While similar to the 16550 and other UART chips, there is
* nothing close to register compatibility. Moreover, some of the
* modem control lines are not available, either in the chip or they
* are lacking in the board-level implementation.
*
* - Use of SIRDIS
* For simplicity, we disable the IR functions of any UART whenever
* we enable it.
*
*/
#if defined(CONFIG_SERIAL_LH7A40X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#define DEV_MAJOR 204
#define DEV_MINOR 16
#define DEV_NR 3
#define ISR_LOOP_LIMIT 256
#define UR(p,o) _UR ((p)->membase, o)
#define _UR(b,o) (*((volatile unsigned int*)(((unsigned char*) b) + (o))))
#define BIT_CLR(p,o,m) UR(p,o) = UR(p,o) & (~(unsigned int)m)
#define BIT_SET(p,o,m) UR(p,o) = UR(p,o) | ( (unsigned int)m)
#define UART_REG_SIZE 32
#define UART_R_DATA (0x00)
#define UART_R_FCON (0x04)
#define UART_R_BRCON (0x08)
#define UART_R_CON (0x0c)
#define UART_R_STATUS (0x10)
#define UART_R_RAWISR (0x14)
#define UART_R_INTEN (0x18)
#define UART_R_ISR (0x1c)
#define UARTEN (0x01) /* UART enable */
#define SIRDIS (0x02) /* Serial IR disable (UART1 only) */
#define RxEmpty (0x10)
#define TxEmpty (0x80)
#define TxFull (0x20)
#define nRxRdy RxEmpty
#define nTxRdy TxFull
#define TxBusy (0x08)
#define RxBreak (0x0800)
#define RxOverrunError (0x0400)
#define RxParityError (0x0200)
#define RxFramingError (0x0100)
#define RxError (RxBreak | RxOverrunError | RxParityError | RxFramingError)
#define DCD (0x04)
#define DSR (0x02)
#define CTS (0x01)
#define RxInt (0x01)
#define TxInt (0x02)
#define ModemInt (0x04)
#define RxTimeoutInt (0x08)
#define MSEOI (0x10)
#define WLEN_8 (0x60)
#define WLEN_7 (0x40)
#define WLEN_6 (0x20)
#define WLEN_5 (0x00)
#define WLEN (0x60) /* Mask for all word-length bits */
#define STP2 (0x08)
#define PEN (0x02) /* Parity Enable */
#define EPS (0x04) /* Even Parity Set */
#define FEN (0x10) /* FIFO Enable */
#define BRK (0x01) /* Send Break */
struct uart_port_lh7a40x {
struct uart_port port;
unsigned int statusPrev; /* Most recently read modem status */
};
static void lh7a40xuart_stop_tx (struct uart_port* port)
{
BIT_CLR (port, UART_R_INTEN, TxInt);
}
static void lh7a40xuart_start_tx (struct uart_port* port)
{
BIT_SET (port, UART_R_INTEN, TxInt);
/* *** FIXME: do I need to check for startup of the
transmitter? The old driver did, but AMBA
doesn't . */
}
static void lh7a40xuart_stop_rx (struct uart_port* port)
{
BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
}
static void lh7a40xuart_enable_ms (struct uart_port* port)
{
BIT_SET (port, UART_R_INTEN, ModemInt);
}
static void lh7a40xuart_rx_chars (struct uart_port* port)
{
struct tty_struct* tty = port->state->port.tty;
int cbRxMax = 256; /* (Gross) limit on receive */
unsigned int data; /* Received data and status */
unsigned int flag;
while (!(UR (port, UART_R_STATUS) & nRxRdy) && --cbRxMax) {
data = UR (port, UART_R_DATA);
flag = TTY_NORMAL;
++port->icount.rx;
if (unlikely(data & RxError)) {
if (data & RxBreak) {
data &= ~(RxFramingError | RxParityError);
++port->icount.brk;
if (uart_handle_break (port))
continue;
}
else if (data & RxParityError)
++port->icount.parity;
else if (data & RxFramingError)
++port->icount.frame;
if (data & RxOverrunError)
++port->icount.overrun;
/* Mask by termios, leave Rx'd byte */
data &= port->read_status_mask | 0xff;
if (data & RxBreak)
flag = TTY_BREAK;
else if (data & RxParityError)
flag = TTY_PARITY;
else if (data & RxFramingError)
flag = TTY_FRAME;
}
if (uart_handle_sysrq_char (port, (unsigned char) data))
continue;
uart_insert_char(port, data, RxOverrunError, data, flag);
}
tty_flip_buffer_push (tty);
return;
}
static void lh7a40xuart_tx_chars (struct uart_port* port)
{
struct circ_buf* xmit = &port->state->xmit;
int cbTxMax = port->fifosize;
if (port->x_char) {
UR (port, UART_R_DATA) = port->x_char;
++port->icount.tx;
port->x_char = 0;
return;
}
if (uart_circ_empty (xmit) || uart_tx_stopped (port)) {
lh7a40xuart_stop_tx (port);
return;
}
/* Unlike the AMBA UART, the lh7a40x UART does not guarantee
that at least half of the FIFO is empty. Instead, we check
status for every character. Using the AMBA method causes
the transmitter to drop characters. */
do {
UR (port, UART_R_DATA) = xmit->buf[xmit->tail];
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
++port->icount.tx;
if (uart_circ_empty(xmit))
break;
} while (!(UR (port, UART_R_STATUS) & nTxRdy)
&& cbTxMax--);
if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS)
uart_write_wakeup (port);
if (uart_circ_empty (xmit))
lh7a40xuart_stop_tx (port);
}
static void lh7a40xuart_modem_status (struct uart_port* port)
{
unsigned int status = UR (port, UART_R_STATUS);
unsigned int delta
= status ^ ((struct uart_port_lh7a40x*) port)->statusPrev;
BIT_SET (port, UART_R_RAWISR, MSEOI); /* Clear modem status intr */
if (!delta) /* Only happens if we missed 2 transitions */
return;
((struct uart_port_lh7a40x*) port)->statusPrev = status;
if (delta & DCD)
uart_handle_dcd_change (port, status & DCD);
if (delta & DSR)
++port->icount.dsr;
if (delta & CTS)
uart_handle_cts_change (port, status & CTS);
wake_up_interruptible (&port->state->port.delta_msr_wait);
}
static irqreturn_t lh7a40xuart_int (int irq, void* dev_id)
{
struct uart_port* port = dev_id;
unsigned int cLoopLimit = ISR_LOOP_LIMIT;
unsigned int isr = UR (port, UART_R_ISR);
do {
if (isr & (RxInt | RxTimeoutInt))
lh7a40xuart_rx_chars(port);
if (isr & ModemInt)
lh7a40xuart_modem_status (port);
if (isr & TxInt)
lh7a40xuart_tx_chars (port);
if (--cLoopLimit == 0)
break;
isr = UR (port, UART_R_ISR);
} while (isr & (RxInt | TxInt | RxTimeoutInt));
return IRQ_HANDLED;
}
static unsigned int lh7a40xuart_tx_empty (struct uart_port* port)
{
return (UR (port, UART_R_STATUS) & TxEmpty) ? TIOCSER_TEMT : 0;
}
static unsigned int lh7a40xuart_get_mctrl (struct uart_port* port)
{
unsigned int result = 0;
unsigned int status = UR (port, UART_R_STATUS);
if (status & DCD)
result |= TIOCM_CAR;
if (status & DSR)
result |= TIOCM_DSR;
if (status & CTS)
result |= TIOCM_CTS;
return result;
}
static void lh7a40xuart_set_mctrl (struct uart_port* port, unsigned int mctrl)
{
/* None of the ports supports DTR. UART1 supports RTS through GPIO. */
/* Note, kernel appears to be setting DTR and RTS on console. */
/* *** FIXME: this deserves more work. There's some work in
tracing all of the IO pins. */
#if 0
if( port->mapbase == UART1_PHYS) {
gpioRegs_t *gpio = (gpioRegs_t *)IO_ADDRESS(GPIO_PHYS);
if (mctrl & TIOCM_RTS)
gpio->pbdr &= ~GPIOB_UART1_RTS;
else
gpio->pbdr |= GPIOB_UART1_RTS;
}
#endif
}
static void lh7a40xuart_break_ctl (struct uart_port* port, int break_state)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
if (break_state == -1)
BIT_SET (port, UART_R_FCON, BRK); /* Assert break */
else
BIT_CLR (port, UART_R_FCON, BRK); /* Deassert break */
spin_unlock_irqrestore(&port->lock, flags);
}
static int lh7a40xuart_startup (struct uart_port* port)
{
int retval;
retval = request_irq (port->irq, lh7a40xuart_int, 0,
"serial_lh7a40x", port);
if (retval)
return retval;
/* Initial modem control-line settings */
((struct uart_port_lh7a40x*) port)->statusPrev
= UR (port, UART_R_STATUS);
/* There is presently no configuration option to enable IR.
Thus, we always disable it. */
BIT_SET (port, UART_R_CON, UARTEN | SIRDIS);
BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt);
return 0;
}
static void lh7a40xuart_shutdown (struct uart_port* port)
{
free_irq (port->irq, port);
BIT_CLR (port, UART_R_FCON, BRK | FEN);
BIT_CLR (port, UART_R_CON, UARTEN);
}
static void lh7a40xuart_set_termios (struct uart_port* port,
struct ktermios* termios,
struct ktermios* old)
{
unsigned int con;
unsigned int inten;
unsigned int fcon;
unsigned long flags;
unsigned int baud;
unsigned int quot;
baud = uart_get_baud_rate (port, termios, old, 8, port->uartclk/16);
quot = uart_get_divisor (port, baud); /* -1 performed elsewhere */
switch (termios->c_cflag & CSIZE) {
case CS5:
fcon = WLEN_5;
break;
case CS6:
fcon = WLEN_6;
break;
case CS7:
fcon = WLEN_7;
break;
case CS8:
default:
fcon = WLEN_8;
break;
}
if (termios->c_cflag & CSTOPB)
fcon |= STP2;
if (termios->c_cflag & PARENB) {
fcon |= PEN;
if (!(termios->c_cflag & PARODD))
fcon |= EPS;
}
if (port->fifosize > 1)
fcon |= FEN;
spin_lock_irqsave (&port->lock, flags);
uart_update_timeout (port, termios->c_cflag, baud);
port->read_status_mask = RxOverrunError;
if (termios->c_iflag & INPCK)
port->read_status_mask |= RxFramingError | RxParityError;
if (termios->c_iflag & (BRKINT | PARMRK))
port->read_status_mask |= RxBreak;
/* Figure mask for status we ignore */
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= RxFramingError | RxParityError;
if (termios->c_iflag & IGNBRK) {
port->ignore_status_mask |= RxBreak;
/* Ignore overrun when ignorning parity */
/* *** FIXME: is this in the right place? */
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= RxOverrunError;
}
/* Ignore all receive errors when receive disabled */
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= RxError;
con = UR (port, UART_R_CON);
inten = (UR (port, UART_R_INTEN) & ~ModemInt);
if (UART_ENABLE_MS (port, termios->c_cflag))
inten |= ModemInt;
BIT_CLR (port, UART_R_CON, UARTEN); /* Disable UART */
UR (port, UART_R_INTEN) = 0; /* Disable interrupts */
UR (port, UART_R_BRCON) = quot - 1; /* Set baud rate divisor */
UR (port, UART_R_FCON) = fcon; /* Set FIFO and frame ctrl */
UR (port, UART_R_INTEN) = inten; /* Enable interrupts */
UR (port, UART_R_CON) = con; /* Restore UART mode */
spin_unlock_irqrestore(&port->lock, flags);
}
static const char* lh7a40xuart_type (struct uart_port* port)
{
return port->type == PORT_LH7A40X ? "LH7A40X" : NULL;
}
static void lh7a40xuart_release_port (struct uart_port* port)
{
release_mem_region (port->mapbase, UART_REG_SIZE);
}
static int lh7a40xuart_request_port (struct uart_port* port)
{
return request_mem_region (port->mapbase, UART_REG_SIZE,
"serial_lh7a40x") != NULL
? 0 : -EBUSY;
}
static void lh7a40xuart_config_port (struct uart_port* port, int flags)
{
if (flags & UART_CONFIG_TYPE) {
port->type = PORT_LH7A40X;
lh7a40xuart_request_port (port);
}
}
static int lh7a40xuart_verify_port (struct uart_port* port,
struct serial_struct* ser)
{
int ret = 0;
if (ser->type != PORT_UNKNOWN && ser->type != PORT_LH7A40X)
ret = -EINVAL;
if (ser->irq < 0 || ser->irq >= nr_irqs)
ret = -EINVAL;
if (ser->baud_base < 9600) /* *** FIXME: is this true? */
ret = -EINVAL;
return ret;
}
static struct uart_ops lh7a40x_uart_ops = {
.tx_empty = lh7a40xuart_tx_empty,
.set_mctrl = lh7a40xuart_set_mctrl,
.get_mctrl = lh7a40xuart_get_mctrl,
.stop_tx = lh7a40xuart_stop_tx,
.start_tx = lh7a40xuart_start_tx,
.stop_rx = lh7a40xuart_stop_rx,
.enable_ms = lh7a40xuart_enable_ms,
.break_ctl = lh7a40xuart_break_ctl,
.startup = lh7a40xuart_startup,
.shutdown = lh7a40xuart_shutdown,
.set_termios = lh7a40xuart_set_termios,
.type = lh7a40xuart_type,
.release_port = lh7a40xuart_release_port,
.request_port = lh7a40xuart_request_port,
.config_port = lh7a40xuart_config_port,
.verify_port = lh7a40xuart_verify_port,
};
static struct uart_port_lh7a40x lh7a40x_ports[DEV_NR] = {
{
.port = {
.membase = (void*) io_p2v (UART1_PHYS),
.mapbase = UART1_PHYS,
.iotype = UPIO_MEM,
.irq = IRQ_UART1INTR,
.uartclk = 14745600/2,
.fifosize = 16,
.ops = &lh7a40x_uart_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 0,
},
},
{
.port = {
.membase = (void*) io_p2v (UART2_PHYS),
.mapbase = UART2_PHYS,
.iotype = UPIO_MEM,
.irq = IRQ_UART2INTR,
.uartclk = 14745600/2,
.fifosize = 16,
.ops = &lh7a40x_uart_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 1,
},
},
{
.port = {
.membase = (void*) io_p2v (UART3_PHYS),
.mapbase = UART3_PHYS,
.iotype = UPIO_MEM,
.irq = IRQ_UART3INTR,
.uartclk = 14745600/2,
.fifosize = 16,
.ops = &lh7a40x_uart_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 2,
},
},
};
#ifndef CONFIG_SERIAL_LH7A40X_CONSOLE
# define LH7A40X_CONSOLE NULL
#else
# define LH7A40X_CONSOLE &lh7a40x_console
static void lh7a40xuart_console_putchar(struct uart_port *port, int ch)
{
while (UR(port, UART_R_STATUS) & nTxRdy)
;
UR(port, UART_R_DATA) = ch;
}
static void lh7a40xuart_console_write (struct console* co,
const char* s,
unsigned int count)
{
struct uart_port* port = &lh7a40x_ports[co->index].port;
unsigned int con = UR (port, UART_R_CON);
unsigned int inten = UR (port, UART_R_INTEN);
UR (port, UART_R_INTEN) = 0; /* Disable all interrupts */
BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); /* Enable UART */
uart_console_write(port, s, count, lh7a40xuart_console_putchar);
/* Wait until all characters are sent */
while (UR (port, UART_R_STATUS) & TxBusy)
;
/* Restore control and interrupt mask */
UR (port, UART_R_CON) = con;
UR (port, UART_R_INTEN) = inten;
}
static void __init lh7a40xuart_console_get_options (struct uart_port* port,
int* baud,
int* parity,
int* bits)
{
if (UR (port, UART_R_CON) & UARTEN) {
unsigned int fcon = UR (port, UART_R_FCON);
unsigned int quot = UR (port, UART_R_BRCON) + 1;
switch (fcon & (PEN | EPS)) {
default: *parity = 'n'; break;
case PEN: *parity = 'o'; break;
case PEN | EPS: *parity = 'e'; break;
}
switch (fcon & WLEN) {
default:
case WLEN_8: *bits = 8; break;
case WLEN_7: *bits = 7; break;
case WLEN_6: *bits = 6; break;
case WLEN_5: *bits = 5; break;
}
*baud = port->uartclk/(16*quot);
}
}
static int __init lh7a40xuart_console_setup (struct console* co, char* options)
{
struct uart_port* port;
int baud = 38400;
int bits = 8;
int parity = 'n';
int flow = 'n';
if (co->index >= DEV_NR) /* Bounds check on device number */
co->index = 0;
port = &lh7a40x_ports[co->index].port;
if (options)
uart_parse_options (options, &baud, &parity, &bits, &flow);
else
lh7a40xuart_console_get_options (port, &baud, &parity, &bits);
return uart_set_options (port, co, baud, parity, bits, flow);
}
static struct uart_driver lh7a40x_reg;
static struct console lh7a40x_console = {
.name = "ttyAM",
.write = lh7a40xuart_console_write,
.device = uart_console_device,
.setup = lh7a40xuart_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &lh7a40x_reg,
};
static int __init lh7a40xuart_console_init(void)
{
register_console (&lh7a40x_console);
return 0;
}
console_initcall (lh7a40xuart_console_init);
#endif
static struct uart_driver lh7a40x_reg = {
.owner = THIS_MODULE,
.driver_name = "ttyAM",
.dev_name = "ttyAM",
.major = DEV_MAJOR,
.minor = DEV_MINOR,
.nr = DEV_NR,
.cons = LH7A40X_CONSOLE,
};
static int __init lh7a40xuart_init(void)
{
int ret;
printk (KERN_INFO "serial: LH7A40X serial driver\n");
ret = uart_register_driver (&lh7a40x_reg);
if (ret == 0) {
int i;
for (i = 0; i < DEV_NR; i++) {
/* UART3, when used, requires GPIO pin reallocation */
if (lh7a40x_ports[i].port.mapbase == UART3_PHYS)
GPIO_PINMUX |= 1<<3;
uart_add_one_port (&lh7a40x_reg,
&lh7a40x_ports[i].port);
}
}
return ret;
}
static void __exit lh7a40xuart_exit(void)
{
int i;
for (i = 0; i < DEV_NR; i++)
uart_remove_one_port (&lh7a40x_reg, &lh7a40x_ports[i].port);
uart_unregister_driver (&lh7a40x_reg);
}
module_init (lh7a40xuart_init);
module_exit (lh7a40xuart_exit);
MODULE_AUTHOR ("Marc Singer");
MODULE_DESCRIPTION ("Sharp LH7A40X serial port driver");
MODULE_LICENSE ("GPL");
...@@ -31,7 +31,6 @@ config USB_ARCH_HAS_OHCI ...@@ -31,7 +31,6 @@ config USB_ARCH_HAS_OHCI
# ARM: # ARM:
default y if SA1111 default y if SA1111
default y if ARCH_OMAP default y if ARCH_OMAP
default y if ARCH_LH7A404
default y if ARCH_S3C2410 default y if ARCH_S3C2410
default y if PXA27x default y if PXA27x
default y if PXA3xx default y if PXA3xx
......
...@@ -176,18 +176,6 @@ config USB_FSL_USB2 ...@@ -176,18 +176,6 @@ config USB_FSL_USB2
default USB_GADGET default USB_GADGET
select USB_GADGET_SELECTED select USB_GADGET_SELECTED
config USB_GADGET_LH7A40X
boolean "LH7A40X"
depends on ARCH_LH7A40X
help
This driver provides USB Device Controller driver for LH7A40x
config USB_LH7A40X
tristate
depends on USB_GADGET_LH7A40X
default USB_GADGET
select USB_GADGET_SELECTED
config USB_GADGET_OMAP config USB_GADGET_OMAP
boolean "OMAP USB Device Controller" boolean "OMAP USB Device Controller"
depends on ARCH_OMAP depends on ARCH_OMAP
......
...@@ -11,7 +11,6 @@ obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o ...@@ -11,7 +11,6 @@ obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
obj-$(CONFIG_USB_IMX) += imx_udc.o obj-$(CONFIG_USB_IMX) += imx_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
obj-$(CONFIG_USB_AT91) += at91_udc.o obj-$(CONFIG_USB_AT91) += at91_udc.o
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
......
...@@ -45,12 +45,6 @@ ...@@ -45,12 +45,6 @@
#define gadget_is_goku(g) 0 #define gadget_is_goku(g) 0
#endif #endif
#ifdef CONFIG_USB_GADGET_LH7A40X
#define gadget_is_lh7a40x(g) !strcmp("lh7a40x_udc", (g)->name)
#else
#define gadget_is_lh7a40x(g) 0
#endif
#ifdef CONFIG_USB_GADGET_OMAP #ifdef CONFIG_USB_GADGET_OMAP
#define gadget_is_omap(g) !strcmp("omap_udc", (g)->name) #define gadget_is_omap(g) !strcmp("omap_udc", (g)->name)
#else #else
...@@ -181,8 +175,6 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) ...@@ -181,8 +175,6 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x06; return 0x06;
else if (gadget_is_omap(gadget)) else if (gadget_is_omap(gadget))
return 0x08; return 0x08;
else if (gadget_is_lh7a40x(gadget))
return 0x09;
else if (gadget_is_pxa27x(gadget)) else if (gadget_is_pxa27x(gadget))
return 0x11; return 0x11;
else if (gadget_is_s3c2410(gadget)) else if (gadget_is_s3c2410(gadget))
......
/*
* linux/drivers/usb/gadget/lh7a40x_udc.c
* Sharp LH7A40x on-chip full speed USB device controllers
*
* Copyright (C) 2004 Mikko Lahteenmaki, Nordic ID
* Copyright (C) 2004 Bo Henriksen, Nordic ID
*
* 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/platform_device.h>
#include <linux/slab.h>
#include "lh7a40x_udc.h"
//#define DEBUG printk
//#define DEBUG_EP0 printk
//#define DEBUG_SETUP printk
#ifndef DEBUG_EP0
# define DEBUG_EP0(fmt,args...)
#endif
#ifndef DEBUG_SETUP
# define DEBUG_SETUP(fmt,args...)
#endif
#ifndef DEBUG
# define NO_STATES
# define DEBUG(fmt,args...)
#endif
#define DRIVER_DESC "LH7A40x USB Device Controller"
#define DRIVER_VERSION __DATE__
#ifndef _BIT /* FIXME - what happended to _BIT in 2.6.7bk18? */
#define _BIT(x) (1<<(x))
#endif
struct lh7a40x_udc *the_controller;
static const char driver_name[] = "lh7a40x_udc";
static const char driver_desc[] = DRIVER_DESC;
static const char ep0name[] = "ep0-control";
/*
Local definintions.
*/
#ifndef NO_STATES
static char *state_names[] = {
"WAIT_FOR_SETUP",
"DATA_STATE_XMIT",
"DATA_STATE_NEED_ZLP",
"WAIT_FOR_OUT_STATUS",
"DATA_STATE_RECV"
};
#endif
/*
Local declarations.
*/
static int lh7a40x_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *);
static int lh7a40x_ep_disable(struct usb_ep *ep);
static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, gfp_t);
static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *);
static int lh7a40x_queue(struct usb_ep *ep, struct usb_request *, gfp_t);
static int lh7a40x_dequeue(struct usb_ep *ep, struct usb_request *);
static int lh7a40x_set_halt(struct usb_ep *ep, int);
static int lh7a40x_fifo_status(struct usb_ep *ep);
static void lh7a40x_fifo_flush(struct usb_ep *ep);
static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep);
static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr);
static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req,
int status);
static void pio_irq_enable(int bEndpointAddress);
static void pio_irq_disable(int bEndpointAddress);
static void stop_activity(struct lh7a40x_udc *dev,
struct usb_gadget_driver *driver);
static void flush(struct lh7a40x_ep *ep);
static void udc_enable(struct lh7a40x_udc *dev);
static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address);
static struct usb_ep_ops lh7a40x_ep_ops = {
.enable = lh7a40x_ep_enable,
.disable = lh7a40x_ep_disable,
.alloc_request = lh7a40x_alloc_request,
.free_request = lh7a40x_free_request,
.queue = lh7a40x_queue,
.dequeue = lh7a40x_dequeue,
.set_halt = lh7a40x_set_halt,
.fifo_status = lh7a40x_fifo_status,
.fifo_flush = lh7a40x_fifo_flush,
};
/* Inline code */
static __inline__ int write_packet(struct lh7a40x_ep *ep,
struct lh7a40x_request *req, int max)
{
u8 *buf;
int length, count;
volatile u32 *fifo = (volatile u32 *)ep->fifo;
buf = req->req.buf + req->req.actual;
prefetch(buf);
length = req->req.length - req->req.actual;
length = min(length, max);
req->req.actual += length;
DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo);
count = length;
while (count--) {
*fifo = *buf++;
}
return length;
}
static __inline__ void usb_set_index(u32 ep)
{
*(volatile u32 *)io_p2v(USB_INDEX) = ep;
}
static __inline__ u32 usb_read(u32 port)
{
return *(volatile u32 *)io_p2v(port);
}
static __inline__ void usb_write(u32 val, u32 port)
{
*(volatile u32 *)io_p2v(port) = val;
}
static __inline__ void usb_set(u32 val, u32 port)
{
volatile u32 *ioport = (volatile u32 *)io_p2v(port);
u32 after = (*ioport) | val;
*ioport = after;
}
static __inline__ void usb_clear(u32 val, u32 port)
{
volatile u32 *ioport = (volatile u32 *)io_p2v(port);
u32 after = (*ioport) & ~val;
*ioport = after;
}
/*-------------------------------------------------------------------------*/
#define GPIO_PORTC_DR (0x80000E08)
#define GPIO_PORTC_DDR (0x80000E18)
#define GPIO_PORTC_PDR (0x80000E70)
/* get port C pin data register */
#define get_portc_pdr(bit) ((usb_read(GPIO_PORTC_PDR) & _BIT(bit)) != 0)
/* get port C data direction register */
#define get_portc_ddr(bit) ((usb_read(GPIO_PORTC_DDR) & _BIT(bit)) != 0)
/* set port C data register */
#define set_portc_dr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DR) : usb_clear(_BIT(bit), GPIO_PORTC_DR))
/* set port C data direction register */
#define set_portc_ddr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DDR) : usb_clear(_BIT(bit), GPIO_PORTC_DDR))
/*
* LPD7A404 GPIO's:
* Port C bit 1 = USB Port 1 Power Enable
* Port C bit 2 = USB Port 1 Data Carrier Detect
*/
#define is_usb_connected() get_portc_pdr(2)
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static const char proc_node_name[] = "driver/udc";
static int
udc_proc_read(char *page, char **start, off_t off, int count,
int *eof, void *_dev)
{
char *buf = page;
struct lh7a40x_udc *dev = _dev;
char *next = buf;
unsigned size = count;
unsigned long flags;
int t;
if (off != 0)
return 0;
local_irq_save(flags);
/* basic device status */
t = scnprintf(next, size,
DRIVER_DESC "\n"
"%s version: %s\n"
"Gadget driver: %s\n"
"Host: %s\n\n",
driver_name, DRIVER_VERSION,
dev->driver ? dev->driver->driver.name : "(none)",
is_usb_connected()? "full speed" : "disconnected");
size -= t;
next += t;
t = scnprintf(next, size,
"GPIO:\n"
" Port C bit 1: %d, dir %d\n"
" Port C bit 2: %d, dir %d\n\n",
get_portc_pdr(1), get_portc_ddr(1),
get_portc_pdr(2), get_portc_ddr(2)
);
size -= t;
next += t;
t = scnprintf(next, size,
"DCP pullup: %d\n\n",
(usb_read(USB_PM) & PM_USB_DCP) != 0);
size -= t;
next += t;
local_irq_restore(flags);
*eof = 1;
return count - size;
}
#define create_proc_files() create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
#define remove_proc_files() remove_proc_entry(proc_node_name, NULL)
#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
#define create_proc_files() do {} while (0)
#define remove_proc_files() do {} while (0)
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/*
* udc_disable - disable USB device controller
*/
static void udc_disable(struct lh7a40x_udc *dev)
{
DEBUG("%s, %p\n", __func__, dev);
udc_set_address(dev, 0);
/* Disable interrupts */
usb_write(0, USB_IN_INT_EN);
usb_write(0, USB_OUT_INT_EN);
usb_write(0, USB_INT_EN);
/* Disable the USB */
usb_write(0, USB_PM);
#ifdef CONFIG_ARCH_LH7A404
/* Disable USB power */
set_portc_dr(1, 0);
#endif
/* if hardware supports it, disconnect from usb */
/* make_usb_disappear(); */
dev->ep0state = WAIT_FOR_SETUP;
dev->gadget.speed = USB_SPEED_UNKNOWN;
dev->usb_address = 0;
}
/*
* udc_reinit - initialize software state
*/
static void udc_reinit(struct lh7a40x_udc *dev)
{
u32 i;
DEBUG("%s, %p\n", __func__, dev);
/* device/ep0 records init */
INIT_LIST_HEAD(&dev->gadget.ep_list);
INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
dev->ep0state = WAIT_FOR_SETUP;
/* basic endpoint records init */
for (i = 0; i < UDC_MAX_ENDPOINTS; i++) {
struct lh7a40x_ep *ep = &dev->ep[i];
if (i != 0)
list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
ep->desc = 0;
ep->stopped = 0;
INIT_LIST_HEAD(&ep->queue);
ep->pio_irqs = 0;
}
/* the rest was statically initialized, and is read-only */
}
#define BYTES2MAXP(x) (x / 8)
#define MAXP2BYTES(x) (x * 8)
/* until it's enabled, this UDC should be completely invisible
* to any USB host.
*/
static void udc_enable(struct lh7a40x_udc *dev)
{
int ep;
DEBUG("%s, %p\n", __func__, dev);
dev->gadget.speed = USB_SPEED_UNKNOWN;
#ifdef CONFIG_ARCH_LH7A404
/* Set Port C bit 1 & 2 as output */
set_portc_ddr(1, 1);
set_portc_ddr(2, 1);
/* Enable USB power */
set_portc_dr(1, 0);
#endif
/*
* C.f Chapter 18.1.3.1 Initializing the USB
*/
/* Disable the USB */
usb_clear(PM_USB_ENABLE, USB_PM);
/* Reset APB & I/O sides of the USB */
usb_set(USB_RESET_APB | USB_RESET_IO, USB_RESET);
mdelay(5);
usb_clear(USB_RESET_APB | USB_RESET_IO, USB_RESET);
/* Set MAXP values for each */
for (ep = 0; ep < UDC_MAX_ENDPOINTS; ep++) {
struct lh7a40x_ep *ep_reg = &dev->ep[ep];
u32 csr;
usb_set_index(ep);
switch (ep_reg->ep_type) {
case ep_bulk_in:
case ep_interrupt:
usb_clear(USB_IN_CSR2_USB_DMA_EN | USB_IN_CSR2_AUTO_SET,
ep_reg->csr2);
/* Fall through */
case ep_control:
usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)),
USB_IN_MAXP);
break;
case ep_bulk_out:
usb_clear(USB_OUT_CSR2_USB_DMA_EN |
USB_OUT_CSR2_AUTO_CLR, ep_reg->csr2);
usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)),
USB_OUT_MAXP);
break;
}
/* Read & Write CSR1, just in case */
csr = usb_read(ep_reg->csr1);
usb_write(csr, ep_reg->csr1);
flush(ep_reg);
}
/* Disable interrupts */
usb_write(0, USB_IN_INT_EN);
usb_write(0, USB_OUT_INT_EN);
usb_write(0, USB_INT_EN);
/* Enable interrupts */
usb_set(USB_IN_INT_EP0, USB_IN_INT_EN);
usb_set(USB_INT_RESET_INT | USB_INT_RESUME_INT, USB_INT_EN);
/* Dont enable rest of the interrupts */
/* usb_set(USB_IN_INT_EP3 | USB_IN_INT_EP1 | USB_IN_INT_EP0, USB_IN_INT_EN);
usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); */
/* Enable SUSPEND */
usb_set(PM_ENABLE_SUSPEND, USB_PM);
/* Enable the USB */
usb_set(PM_USB_ENABLE, USB_PM);
#ifdef CONFIG_ARCH_LH7A404
/* NOTE: DOES NOT WORK! */
/* Let host detect UDC:
* Software must write a 0 to the PMR:DCP_CTRL bit to turn this
* transistor on and pull the USBDP pin HIGH.
*/
/* usb_clear(PM_USB_DCP, USB_PM);
usb_set(PM_USB_DCP, USB_PM); */
#endif
}
/*
Register entry point for the peripheral controller driver.
*/
int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
int (*bind)(struct usb_gadget *))
{
struct lh7a40x_udc *dev = the_controller;
int retval;
DEBUG("%s: %s\n", __func__, driver->driver.name);
if (!driver
|| driver->speed != USB_SPEED_FULL
|| !bind
|| !driver->disconnect
|| !driver->setup)
return -EINVAL;
if (!dev)
return -ENODEV;
if (dev->driver)
return -EBUSY;
/* first hook up the driver ... */
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
device_add(&dev->gadget.dev);
retval = bind(&dev->gadget);
if (retval) {
printk(KERN_WARNING "%s: bind to driver %s --> error %d\n",
dev->gadget.name, driver->driver.name, retval);
device_del(&dev->gadget.dev);
dev->driver = 0;
dev->gadget.dev.driver = 0;
return retval;
}
/* ... then enable host detection and ep0; and we're ready
* for set_configuration as well as eventual disconnect.
* NOTE: this shouldn't power up until later.
*/
printk(KERN_WARNING "%s: registered gadget driver '%s'\n",
dev->gadget.name, driver->driver.name);
udc_enable(dev);
return 0;
}
EXPORT_SYMBOL(usb_gadget_probe_driver);
/*
Unregister entry point for the peripheral controller driver.
*/
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
struct lh7a40x_udc *dev = the_controller;
unsigned long flags;
if (!dev)
return -ENODEV;
if (!driver || driver != dev->driver || !driver->unbind)
return -EINVAL;
spin_lock_irqsave(&dev->lock, flags);
dev->driver = 0;
stop_activity(dev, driver);
spin_unlock_irqrestore(&dev->lock, flags);
driver->unbind(&dev->gadget);
dev->gadget.dev.driver = NULL;
device_del(&dev->gadget.dev);
udc_disable(dev);
DEBUG("unregistered gadget driver '%s'\n", driver->driver.name);
return 0;
}
EXPORT_SYMBOL(usb_gadget_unregister_driver);
/*-------------------------------------------------------------------------*/
/** Write request to FIFO (max write == maxp size)
* Return: 0 = still running, 1 = completed, negative = errno
* NOTE: INDEX register must be set for EP
*/
static int write_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req)
{
u32 max;
u32 csr;
max = le16_to_cpu(ep->desc->wMaxPacketSize);
csr = usb_read(ep->csr1);
DEBUG("CSR: %x %d\n", csr, csr & USB_IN_CSR1_FIFO_NOT_EMPTY);
if (!(csr & USB_IN_CSR1_FIFO_NOT_EMPTY)) {
unsigned count;
int is_last, is_short;
count = write_packet(ep, req, max);
usb_set(USB_IN_CSR1_IN_PKT_RDY, ep->csr1);
/* last packet is usually short (or a zlp) */
if (unlikely(count != max))
is_last = is_short = 1;
else {
if (likely(req->req.length != req->req.actual)
|| req->req.zero)
is_last = 0;
else
is_last = 1;
/* interrupt/iso maxpacket may not fill the fifo */
is_short = unlikely(max < ep_maxpacket(ep));
}
DEBUG("%s: wrote %s %d bytes%s%s %d left %p\n", __func__,
ep->ep.name, count,
is_last ? "/L" : "", is_short ? "/S" : "",
req->req.length - req->req.actual, req);
/* requests complete when all IN data is in the FIFO */
if (is_last) {
done(ep, req, 0);
if (list_empty(&ep->queue)) {
pio_irq_disable(ep_index(ep));
}
return 1;
}
} else {
DEBUG("Hmm.. %d ep FIFO is not empty!\n", ep_index(ep));
}
return 0;
}
/** Read to request from FIFO (max read == bytes in fifo)
* Return: 0 = still running, 1 = completed, negative = errno
* NOTE: INDEX register must be set for EP
*/
static int read_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req)
{
u32 csr;
u8 *buf;
unsigned bufferspace, count, is_short;
volatile u32 *fifo = (volatile u32 *)ep->fifo;
/* make sure there's a packet in the FIFO. */
csr = usb_read(ep->csr1);
if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY)) {
DEBUG("%s: Packet NOT ready!\n", __func__);
return -EINVAL;
}
buf = req->req.buf + req->req.actual;
prefetchw(buf);
bufferspace = req->req.length - req->req.actual;
/* read all bytes from this packet */
count = usb_read(USB_OUT_FIFO_WC1);
req->req.actual += min(count, bufferspace);
is_short = (count < ep->ep.maxpacket);
DEBUG("read %s %02x, %d bytes%s req %p %d/%d\n",
ep->ep.name, csr, count,
is_short ? "/S" : "", req, req->req.actual, req->req.length);
while (likely(count-- != 0)) {
u8 byte = (u8) (*fifo & 0xff);
if (unlikely(bufferspace == 0)) {
/* this happens when the driver's buffer
* is smaller than what the host sent.
* discard the extra data.
*/
if (req->req.status != -EOVERFLOW)
printk(KERN_WARNING "%s overflow %d\n",
ep->ep.name, count);
req->req.status = -EOVERFLOW;
} else {
*buf++ = byte;
bufferspace--;
}
}
usb_clear(USB_OUT_CSR1_OUT_PKT_RDY, ep->csr1);
/* completion */
if (is_short || req->req.actual == req->req.length) {
done(ep, req, 0);
usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1);
if (list_empty(&ep->queue))
pio_irq_disable(ep_index(ep));
return 1;
}
/* finished that packet. the next one may be waiting... */
return 0;
}
/*
* done - retire a request; caller blocked irqs
* INDEX register is preserved to keep same
*/
static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req, int status)
{
unsigned int stopped = ep->stopped;
u32 index;
DEBUG("%s, %p\n", __func__, ep);
list_del_init(&req->queue);
if (likely(req->req.status == -EINPROGRESS))
req->req.status = status;
else
status = req->req.status;
if (status && status != -ESHUTDOWN)
DEBUG("complete %s req %p stat %d len %u/%u\n",
ep->ep.name, &req->req, status,
req->req.actual, req->req.length);
/* don't modify queue heads during completion callback */
ep->stopped = 1;
/* Read current index (completion may modify it) */
index = usb_read(USB_INDEX);
spin_unlock(&ep->dev->lock);
req->req.complete(&ep->ep, &req->req);
spin_lock(&ep->dev->lock);
/* Restore index */
usb_set_index(index);
ep->stopped = stopped;
}
/** Enable EP interrupt */
static void pio_irq_enable(int ep)
{
DEBUG("%s: %d\n", __func__, ep);
switch (ep) {
case 1:
usb_set(USB_IN_INT_EP1, USB_IN_INT_EN);
break;
case 2:
usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN);
break;
case 3:
usb_set(USB_IN_INT_EP3, USB_IN_INT_EN);
break;
default:
DEBUG("Unknown endpoint: %d\n", ep);
break;
}
}
/** Disable EP interrupt */
static void pio_irq_disable(int ep)
{
DEBUG("%s: %d\n", __func__, ep);
switch (ep) {
case 1:
usb_clear(USB_IN_INT_EP1, USB_IN_INT_EN);
break;
case 2:
usb_clear(USB_OUT_INT_EP2, USB_OUT_INT_EN);
break;
case 3:
usb_clear(USB_IN_INT_EP3, USB_IN_INT_EN);
break;
default:
DEBUG("Unknown endpoint: %d\n", ep);
break;
}
}
/*
* nuke - dequeue ALL requests
*/
void nuke(struct lh7a40x_ep *ep, int status)
{
struct lh7a40x_request *req;
DEBUG("%s, %p\n", __func__, ep);
/* Flush FIFO */
flush(ep);
/* called with irqs blocked */
while (!list_empty(&ep->queue)) {
req = list_entry(ep->queue.next, struct lh7a40x_request, queue);
done(ep, req, status);
}
/* Disable IRQ if EP is enabled (has descriptor) */
if (ep->desc)
pio_irq_disable(ep_index(ep));
}
/*
void nuke_all(struct lh7a40x_udc *dev)
{
int n;
for(n=0; n<UDC_MAX_ENDPOINTS; n++) {
struct lh7a40x_ep *ep = &dev->ep[n];
usb_set_index(n);
nuke(ep, 0);
}
}*/
/*
static void flush_all(struct lh7a40x_udc *dev)
{
int n;
for (n = 0; n < UDC_MAX_ENDPOINTS; n++)
{
struct lh7a40x_ep *ep = &dev->ep[n];
flush(ep);
}
}
*/
/** Flush EP
* NOTE: INDEX register must be set before this call
*/
static void flush(struct lh7a40x_ep *ep)
{
DEBUG("%s, %p\n", __func__, ep);
switch (ep->ep_type) {
case ep_control:
/* check, by implication c.f. 15.1.2.11 */
break;
case ep_bulk_in:
case ep_interrupt:
/* if(csr & USB_IN_CSR1_IN_PKT_RDY) */
usb_set(USB_IN_CSR1_FIFO_FLUSH, ep->csr1);
break;
case ep_bulk_out:
/* if(csr & USB_OUT_CSR1_OUT_PKT_RDY) */
usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1);
break;
}
}
/**
* lh7a40x_in_epn - handle IN interrupt
*/
static void lh7a40x_in_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr)
{
u32 csr;
struct lh7a40x_ep *ep = &dev->ep[ep_idx];
struct lh7a40x_request *req;
usb_set_index(ep_idx);
csr = usb_read(ep->csr1);
DEBUG("%s: %d, csr %x\n", __func__, ep_idx, csr);
if (csr & USB_IN_CSR1_SENT_STALL) {
DEBUG("USB_IN_CSR1_SENT_STALL\n");
usb_set(USB_IN_CSR1_SENT_STALL /*|USB_IN_CSR1_SEND_STALL */ ,
ep->csr1);
return;
}
if (!ep->desc) {
DEBUG("%s: NO EP DESC\n", __func__);
return;
}
if (list_empty(&ep->queue))
req = 0;
else
req = list_entry(ep->queue.next, struct lh7a40x_request, queue);
DEBUG("req: %p\n", req);
if (!req)
return;
write_fifo(ep, req);
}
/* ********************************************************************************************* */
/* Bulk OUT (recv)
*/
static void lh7a40x_out_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr)
{
struct lh7a40x_ep *ep = &dev->ep[ep_idx];
struct lh7a40x_request *req;
DEBUG("%s: %d\n", __func__, ep_idx);
usb_set_index(ep_idx);
if (ep->desc) {
u32 csr;
csr = usb_read(ep->csr1);
while ((csr =
usb_read(ep->
csr1)) & (USB_OUT_CSR1_OUT_PKT_RDY |
USB_OUT_CSR1_SENT_STALL)) {
DEBUG("%s: %x\n", __func__, csr);
if (csr & USB_OUT_CSR1_SENT_STALL) {
DEBUG("%s: stall sent, flush fifo\n",
__func__);
/* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */
flush(ep);
} else if (csr & USB_OUT_CSR1_OUT_PKT_RDY) {
if (list_empty(&ep->queue))
req = 0;
else
req =
list_entry(ep->queue.next,
struct lh7a40x_request,
queue);
if (!req) {
printk(KERN_WARNING
"%s: NULL REQ %d\n",
__func__, ep_idx);
flush(ep);
break;
} else {
read_fifo(ep, req);
}
}
}
} else {
/* Throw packet away.. */
printk(KERN_WARNING "%s: No descriptor?!?\n", __func__);
flush(ep);
}
}
static void stop_activity(struct lh7a40x_udc *dev,
struct usb_gadget_driver *driver)
{
int i;
/* don't disconnect drivers more than once */
if (dev->gadget.speed == USB_SPEED_UNKNOWN)
driver = 0;
dev->gadget.speed = USB_SPEED_UNKNOWN;
/* prevent new request submissions, kill any outstanding requests */
for (i = 0; i < UDC_MAX_ENDPOINTS; i++) {
struct lh7a40x_ep *ep = &dev->ep[i];
ep->stopped = 1;
usb_set_index(i);
nuke(ep, -ESHUTDOWN);
}
/* report disconnect; the driver is already quiesced */
if (driver) {
spin_unlock(&dev->lock);
driver->disconnect(&dev->gadget);
spin_lock(&dev->lock);
}
/* re-init driver-visible data structures */
udc_reinit(dev);
}
/** Handle USB RESET interrupt
*/
static void lh7a40x_reset_intr(struct lh7a40x_udc *dev)
{
#if 0 /* def CONFIG_ARCH_LH7A404 */
/* Does not work always... */
DEBUG("%s: %d\n", __func__, dev->usb_address);
if (!dev->usb_address) {
/*usb_set(USB_RESET_IO, USB_RESET);
mdelay(5);
usb_clear(USB_RESET_IO, USB_RESET); */
return;
}
/* Put the USB controller into reset. */
usb_set(USB_RESET_IO, USB_RESET);
/* Set Device ID to 0 */
udc_set_address(dev, 0);
/* Let PLL2 settle down */
mdelay(5);
/* Release the USB controller from reset */
usb_clear(USB_RESET_IO, USB_RESET);
/* Re-enable UDC */
udc_enable(dev);
#endif
dev->gadget.speed = USB_SPEED_FULL;
}
/*
* lh7a40x usb client interrupt handler.
*/
static irqreturn_t lh7a40x_udc_irq(int irq, void *_dev)
{
struct lh7a40x_udc *dev = _dev;
DEBUG("\n\n");
spin_lock(&dev->lock);
for (;;) {
u32 intr_in = usb_read(USB_IN_INT);
u32 intr_out = usb_read(USB_OUT_INT);
u32 intr_int = usb_read(USB_INT);
/* Test also against enable bits.. (lh7a40x errata).. Sigh.. */
u32 in_en = usb_read(USB_IN_INT_EN);
u32 out_en = usb_read(USB_OUT_INT_EN);
if (!intr_out && !intr_in && !intr_int)
break;
DEBUG("%s (on state %s)\n", __func__,
state_names[dev->ep0state]);
DEBUG("intr_out = %x\n", intr_out);
DEBUG("intr_in = %x\n", intr_in);
DEBUG("intr_int = %x\n", intr_int);
if (intr_in) {
usb_write(intr_in, USB_IN_INT);
if ((intr_in & USB_IN_INT_EP1)
&& (in_en & USB_IN_INT_EP1)) {
DEBUG("USB_IN_INT_EP1\n");
lh7a40x_in_epn(dev, 1, intr_in);
}
if ((intr_in & USB_IN_INT_EP3)
&& (in_en & USB_IN_INT_EP3)) {
DEBUG("USB_IN_INT_EP3\n");
lh7a40x_in_epn(dev, 3, intr_in);
}
if (intr_in & USB_IN_INT_EP0) {
DEBUG("USB_IN_INT_EP0 (control)\n");
lh7a40x_handle_ep0(dev, intr_in);
}
}
if (intr_out) {
usb_write(intr_out, USB_OUT_INT);
if ((intr_out & USB_OUT_INT_EP2)
&& (out_en & USB_OUT_INT_EP2)) {
DEBUG("USB_OUT_INT_EP2\n");
lh7a40x_out_epn(dev, 2, intr_out);
}
}
if (intr_int) {
usb_write(intr_int, USB_INT);
if (intr_int & USB_INT_RESET_INT) {
lh7a40x_reset_intr(dev);
}
if (intr_int & USB_INT_RESUME_INT) {
DEBUG("USB resume\n");
if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver
&& dev->driver->resume
&& is_usb_connected()) {
dev->driver->resume(&dev->gadget);
}
}
if (intr_int & USB_INT_SUSPEND_INT) {
DEBUG("USB suspend%s\n",
is_usb_connected()? "" : "+disconnect");
if (!is_usb_connected()) {
stop_activity(dev, dev->driver);
} else if (dev->gadget.speed !=
USB_SPEED_UNKNOWN && dev->driver
&& dev->driver->suspend) {
dev->driver->suspend(&dev->gadget);
}
}
}
}
spin_unlock(&dev->lock);
return IRQ_HANDLED;
}
static int lh7a40x_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct lh7a40x_ep *ep;
struct lh7a40x_udc *dev;
unsigned long flags;
DEBUG("%s, %p\n", __func__, _ep);
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (!_ep || !desc || ep->desc || _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT
|| ep->bEndpointAddress != desc->bEndpointAddress
|| ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) {
DEBUG("%s, bad ep or descriptor\n", __func__);
return -EINVAL;
}
/* xfer types must match, except that interrupt ~= bulk */
if (ep->bmAttributes != desc->bmAttributes
&& ep->bmAttributes != USB_ENDPOINT_XFER_BULK
&& desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
DEBUG("%s, %s type mismatch\n", __func__, _ep->name);
return -EINVAL;
}
/* hardware _could_ do smaller, but driver doesn't */
if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
&& le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep))
|| !desc->wMaxPacketSize) {
DEBUG("%s, bad %s maxpacket\n", __func__, _ep->name);
return -ERANGE;
}
dev = ep->dev;
if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
DEBUG("%s, bogus device state\n", __func__);
return -ESHUTDOWN;
}
spin_lock_irqsave(&ep->dev->lock, flags);
ep->stopped = 0;
ep->desc = desc;
ep->pio_irqs = 0;
ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
spin_unlock_irqrestore(&ep->dev->lock, flags);
/* Reset halt state (does flush) */
lh7a40x_set_halt(_ep, 0);
DEBUG("%s: enabled %s\n", __func__, _ep->name);
return 0;
}
/** Disable EP
* NOTE: Sets INDEX register
*/
static int lh7a40x_ep_disable(struct usb_ep *_ep)
{
struct lh7a40x_ep *ep;
unsigned long flags;
DEBUG("%s, %p\n", __func__, _ep);
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (!_ep || !ep->desc) {
DEBUG("%s, %s not enabled\n", __func__,
_ep ? ep->ep.name : NULL);
return -EINVAL;
}
spin_lock_irqsave(&ep->dev->lock, flags);
usb_set_index(ep_index(ep));
/* Nuke all pending requests (does flush) */
nuke(ep, -ESHUTDOWN);
/* Disable ep IRQ */
pio_irq_disable(ep_index(ep));
ep->desc = 0;
ep->stopped = 1;
spin_unlock_irqrestore(&ep->dev->lock, flags);
DEBUG("%s: disabled %s\n", __func__, _ep->name);
return 0;
}
static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags)
{
struct lh7a40x_request *req;
DEBUG("%s, %p\n", __func__, ep);
req = kzalloc(sizeof(*req), gfp_flags);
if (!req)
return 0;
INIT_LIST_HEAD(&req->queue);
return &req->req;
}
static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *_req)
{
struct lh7a40x_request *req;
DEBUG("%s, %p\n", __func__, ep);
req = container_of(_req, struct lh7a40x_request, req);
WARN_ON(!list_empty(&req->queue));
kfree(req);
}
/** Queue one request
* Kickstart transfer if needed
* NOTE: Sets INDEX register
*/
static int lh7a40x_queue(struct usb_ep *_ep, struct usb_request *_req,
gfp_t gfp_flags)
{
struct lh7a40x_request *req;
struct lh7a40x_ep *ep;
struct lh7a40x_udc *dev;
unsigned long flags;
DEBUG("\n\n\n%s, %p\n", __func__, _ep);
req = container_of(_req, struct lh7a40x_request, req);
if (unlikely
(!_req || !_req->complete || !_req->buf
|| !list_empty(&req->queue))) {
DEBUG("%s, bad params\n", __func__);
return -EINVAL;
}
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
DEBUG("%s, bad ep\n", __func__);
return -EINVAL;
}
dev = ep->dev;
if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
DEBUG("%s, bogus device state %p\n", __func__, dev->driver);
return -ESHUTDOWN;
}
DEBUG("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length,
_req->buf);
spin_lock_irqsave(&dev->lock, flags);
_req->status = -EINPROGRESS;
_req->actual = 0;
/* kickstart this i/o queue? */
DEBUG("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue),
ep->stopped);
if (list_empty(&ep->queue) && likely(!ep->stopped)) {
u32 csr;
if (unlikely(ep_index(ep) == 0)) {
/* EP0 */
list_add_tail(&req->queue, &ep->queue);
lh7a40x_ep0_kick(dev, ep);
req = 0;
} else if (ep_is_in(ep)) {
/* EP1 & EP3 */
usb_set_index(ep_index(ep));
csr = usb_read(ep->csr1);
pio_irq_enable(ep_index(ep));
if ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY) == 0) {
if (write_fifo(ep, req) == 1)
req = 0;
}
} else {
/* EP2 */
usb_set_index(ep_index(ep));
csr = usb_read(ep->csr1);
pio_irq_enable(ep_index(ep));
if (!(csr & USB_OUT_CSR1_FIFO_FULL)) {
if (read_fifo(ep, req) == 1)
req = 0;
}
}
}
/* pio or dma irq handler advances the queue. */
if (likely(req != 0))
list_add_tail(&req->queue, &ep->queue);
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
/* dequeue JUST ONE request */
static int lh7a40x_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
struct lh7a40x_ep *ep;
struct lh7a40x_request *req;
unsigned long flags;
DEBUG("%s, %p\n", __func__, _ep);
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (!_ep || ep->ep.name == ep0name)
return -EINVAL;
spin_lock_irqsave(&ep->dev->lock, flags);
/* make sure it's actually queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req)
break;
}
if (&req->req != _req) {
spin_unlock_irqrestore(&ep->dev->lock, flags);
return -EINVAL;
}
done(ep, req, -ECONNRESET);
spin_unlock_irqrestore(&ep->dev->lock, flags);
return 0;
}
/** Halt specific EP
* Return 0 if success
* NOTE: Sets INDEX register to EP !
*/
static int lh7a40x_set_halt(struct usb_ep *_ep, int value)
{
struct lh7a40x_ep *ep;
unsigned long flags;
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
DEBUG("%s, bad ep\n", __func__);
return -EINVAL;
}
usb_set_index(ep_index(ep));
DEBUG("%s, ep %d, val %d\n", __func__, ep_index(ep), value);
spin_lock_irqsave(&ep->dev->lock, flags);
if (ep_index(ep) == 0) {
/* EP0 */
usb_set(EP0_SEND_STALL, ep->csr1);
} else if (ep_is_in(ep)) {
u32 csr = usb_read(ep->csr1);
if (value && ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY)
|| !list_empty(&ep->queue))) {
/*
* Attempts to halt IN endpoints will fail (returning -EAGAIN)
* if any transfer requests are still queued, or if the controller
* FIFO still holds bytes that the host hasn't collected.
*/
spin_unlock_irqrestore(&ep->dev->lock, flags);
DEBUG
("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n",
(csr & USB_IN_CSR1_FIFO_NOT_EMPTY),
!list_empty(&ep->queue));
return -EAGAIN;
}
flush(ep);
if (value)
usb_set(USB_IN_CSR1_SEND_STALL, ep->csr1);
else {
usb_clear(USB_IN_CSR1_SEND_STALL, ep->csr1);
usb_set(USB_IN_CSR1_CLR_DATA_TOGGLE, ep->csr1);
}
} else {
flush(ep);
if (value)
usb_set(USB_OUT_CSR1_SEND_STALL, ep->csr1);
else {
usb_clear(USB_OUT_CSR1_SEND_STALL, ep->csr1);
usb_set(USB_OUT_CSR1_CLR_DATA_REG, ep->csr1);
}
}
if (value) {
ep->stopped = 1;
} else {
ep->stopped = 0;
}
spin_unlock_irqrestore(&ep->dev->lock, flags);
DEBUG("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS");
return 0;
}
/** Return bytes in EP FIFO
* NOTE: Sets INDEX register to EP
*/
static int lh7a40x_fifo_status(struct usb_ep *_ep)
{
u32 csr;
int count = 0;
struct lh7a40x_ep *ep;
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (!_ep) {
DEBUG("%s, bad ep\n", __func__);
return -ENODEV;
}
DEBUG("%s, %d\n", __func__, ep_index(ep));
/* LPD can't report unclaimed bytes from IN fifos */
if (ep_is_in(ep))
return -EOPNOTSUPP;
usb_set_index(ep_index(ep));
csr = usb_read(ep->csr1);
if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN ||
csr & USB_OUT_CSR1_OUT_PKT_RDY) {
count = usb_read(USB_OUT_FIFO_WC1);
}
return count;
}
/** Flush EP FIFO
* NOTE: Sets INDEX register to EP
*/
static void lh7a40x_fifo_flush(struct usb_ep *_ep)
{
struct lh7a40x_ep *ep;
ep = container_of(_ep, struct lh7a40x_ep, ep);
if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
DEBUG("%s, bad ep\n", __func__);
return;
}
usb_set_index(ep_index(ep));
flush(ep);
}
/****************************************************************/
/* End Point 0 related functions */
/****************************************************************/
/* return: 0 = still running, 1 = completed, negative = errno */
static int write_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req)
{
u32 max;
unsigned count;
int is_last;
max = ep_maxpacket(ep);
DEBUG_EP0("%s\n", __func__);
count = write_packet(ep, req, max);
/* last packet is usually short (or a zlp) */
if (unlikely(count != max))
is_last = 1;
else {
if (likely(req->req.length != req->req.actual) || req->req.zero)
is_last = 0;
else
is_last = 1;
}
DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __func__,
ep->ep.name, count,
is_last ? "/L" : "", req->req.length - req->req.actual, req);
/* requests complete when all IN data is in the FIFO */
if (is_last) {
done(ep, req, 0);
return 1;
}
return 0;
}
static __inline__ int lh7a40x_fifo_read(struct lh7a40x_ep *ep,
unsigned char *cp, int max)
{
int bytes;
int count = usb_read(USB_OUT_FIFO_WC1);
volatile u32 *fifo = (volatile u32 *)ep->fifo;
if (count > max)
count = max;
bytes = count;
while (count--)
*cp++ = *fifo & 0xFF;
return bytes;
}
static __inline__ void lh7a40x_fifo_write(struct lh7a40x_ep *ep,
unsigned char *cp, int count)
{
volatile u32 *fifo = (volatile u32 *)ep->fifo;
DEBUG_EP0("fifo_write: %d %d\n", ep_index(ep), count);
while (count--)
*fifo = *cp++;
}
static int read_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req)
{
u32 csr;
u8 *buf;
unsigned bufferspace, count, is_short;
volatile u32 *fifo = (volatile u32 *)ep->fifo;
DEBUG_EP0("%s\n", __func__);
csr = usb_read(USB_EP0_CSR);
if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY))
return 0;
buf = req->req.buf + req->req.actual;
prefetchw(buf);
bufferspace = req->req.length - req->req.actual;
/* read all bytes from this packet */
if (likely(csr & EP0_OUT_PKT_RDY)) {
count = usb_read(USB_OUT_FIFO_WC1);
req->req.actual += min(count, bufferspace);
} else /* zlp */
count = 0;
is_short = (count < ep->ep.maxpacket);
DEBUG_EP0("read %s %02x, %d bytes%s req %p %d/%d\n",
ep->ep.name, csr, count,
is_short ? "/S" : "", req, req->req.actual, req->req.length);
while (likely(count-- != 0)) {
u8 byte = (u8) (*fifo & 0xff);
if (unlikely(bufferspace == 0)) {
/* this happens when the driver's buffer
* is smaller than what the host sent.
* discard the extra data.
*/
if (req->req.status != -EOVERFLOW)
DEBUG_EP0("%s overflow %d\n", ep->ep.name,
count);
req->req.status = -EOVERFLOW;
} else {
*buf++ = byte;
bufferspace--;
}
}
/* completion */
if (is_short || req->req.actual == req->req.length) {
done(ep, req, 0);
return 1;
}
/* finished that packet. the next one may be waiting... */
return 0;
}
/**
* udc_set_address - set the USB address for this device
* @address:
*
* Called from control endpoint function after it decodes a set address setup packet.
*/
static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address)
{
DEBUG_EP0("%s: %d\n", __func__, address);
/* c.f. 15.1.2.2 Table 15-4 address will be used after DATA_END is set */
dev->usb_address = address;
usb_set((address & USB_FA_FUNCTION_ADDR), USB_FA);
usb_set(USB_FA_ADDR_UPDATE | (address & USB_FA_FUNCTION_ADDR), USB_FA);
/* usb_read(USB_FA); */
}
/*
* DATA_STATE_RECV (OUT_PKT_RDY)
* - if error
* set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits
* - else
* set EP0_CLR_OUT bit
if last set EP0_DATA_END bit
*/
static void lh7a40x_ep0_out(struct lh7a40x_udc *dev, u32 csr)
{
struct lh7a40x_request *req;
struct lh7a40x_ep *ep = &dev->ep[0];
int ret;
DEBUG_EP0("%s: %x\n", __func__, csr);
if (list_empty(&ep->queue))
req = 0;
else
req = list_entry(ep->queue.next, struct lh7a40x_request, queue);
if (req) {
if (req->req.length == 0) {
DEBUG_EP0("ZERO LENGTH OUT!\n");
usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR);
dev->ep0state = WAIT_FOR_SETUP;
return;
}
ret = read_fifo_ep0(ep, req);
if (ret) {
/* Done! */
DEBUG_EP0("%s: finished, waiting for status\n",
__func__);
usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR);
dev->ep0state = WAIT_FOR_SETUP;
} else {
/* Not done yet.. */
DEBUG_EP0("%s: not finished\n", __func__);
usb_set(EP0_CLR_OUT, USB_EP0_CSR);
}
} else {
DEBUG_EP0("NO REQ??!\n");
}
}
/*
* DATA_STATE_XMIT
*/
static int lh7a40x_ep0_in(struct lh7a40x_udc *dev, u32 csr)
{
struct lh7a40x_request *req;
struct lh7a40x_ep *ep = &dev->ep[0];
int ret, need_zlp = 0;
DEBUG_EP0("%s: %x\n", __func__, csr);
if (list_empty(&ep->queue))
req = 0;
else
req = list_entry(ep->queue.next, struct lh7a40x_request, queue);
if (!req) {
DEBUG_EP0("%s: NULL REQ\n", __func__);
return 0;
}
if (req->req.length == 0) {
usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR);
dev->ep0state = WAIT_FOR_SETUP;
return 1;
}
if (req->req.length - req->req.actual == EP0_PACKETSIZE) {
/* Next write will end with the packet size, */
/* so we need Zero-length-packet */
need_zlp = 1;
}
ret = write_fifo_ep0(ep, req);
if (ret == 1 && !need_zlp) {
/* Last packet */
DEBUG_EP0("%s: finished, waiting for status\n", __func__);
usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR);
dev->ep0state = WAIT_FOR_SETUP;
} else {
DEBUG_EP0("%s: not finished\n", __func__);
usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR);
}
if (need_zlp) {
DEBUG_EP0("%s: Need ZLP!\n", __func__);
usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR);
dev->ep0state = DATA_STATE_NEED_ZLP;
}
return 1;
}
static int lh7a40x_handle_get_status(struct lh7a40x_udc *dev,
struct usb_ctrlrequest *ctrl)
{
struct lh7a40x_ep *ep0 = &dev->ep[0];
struct lh7a40x_ep *qep;
int reqtype = (ctrl->bRequestType & USB_RECIP_MASK);
u16 val = 0;
if (reqtype == USB_RECIP_INTERFACE) {
/* This is not supported.
* And according to the USB spec, this one does nothing..
* Just return 0
*/
DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n");
} else if (reqtype == USB_RECIP_DEVICE) {
DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n");
val |= (1 << 0); /* Self powered */
/*val |= (1<<1); *//* Remote wakeup */
} else if (reqtype == USB_RECIP_ENDPOINT) {
int ep_num = (ctrl->wIndex & ~USB_DIR_IN);
DEBUG_SETUP
("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n",
ep_num, ctrl->wLength);
if (ctrl->wLength > 2 || ep_num > 3)
return -EOPNOTSUPP;
qep = &dev->ep[ep_num];
if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0)
&& ep_index(qep) != 0) {
return -EOPNOTSUPP;
}
usb_set_index(ep_index(qep));
/* Return status on next IN token */
switch (qep->ep_type) {
case ep_control:
val =
(usb_read(qep->csr1) & EP0_SEND_STALL) ==
EP0_SEND_STALL;
break;
case ep_bulk_in:
case ep_interrupt:
val =
(usb_read(qep->csr1) & USB_IN_CSR1_SEND_STALL) ==
USB_IN_CSR1_SEND_STALL;
break;
case ep_bulk_out:
val =
(usb_read(qep->csr1) & USB_OUT_CSR1_SEND_STALL) ==
USB_OUT_CSR1_SEND_STALL;
break;
}
/* Back to EP0 index */
usb_set_index(0);
DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num,
ctrl->wIndex, val);
} else {
DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype);
return -EOPNOTSUPP;
}
/* Clear "out packet ready" */
usb_set((EP0_CLR_OUT), USB_EP0_CSR);
/* Put status to FIFO */
lh7a40x_fifo_write(ep0, (u8 *) & val, sizeof(val));
/* Issue "In packet ready" */
usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR);
return 0;
}
/*
* WAIT_FOR_SETUP (OUT_PKT_RDY)
* - read data packet from EP0 FIFO
* - decode command
* - if error
* set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits
* - else
* set EP0_CLR_OUT | EP0_DATA_END bits
*/
static void lh7a40x_ep0_setup(struct lh7a40x_udc *dev, u32 csr)
{
struct lh7a40x_ep *ep = &dev->ep[0];
struct usb_ctrlrequest ctrl;
int i, bytes, is_in;
DEBUG_SETUP("%s: %x\n", __func__, csr);
/* Nuke all previous transfers */
nuke(ep, -EPROTO);
/* read control req from fifo (8 bytes) */
bytes = lh7a40x_fifo_read(ep, (unsigned char *)&ctrl, 8);
DEBUG_SETUP("Read CTRL REQ %d bytes\n", bytes);
DEBUG_SETUP("CTRL.bRequestType = %d (is_in %d)\n", ctrl.bRequestType,
ctrl.bRequestType == USB_DIR_IN);
DEBUG_SETUP("CTRL.bRequest = %d\n", ctrl.bRequest);
DEBUG_SETUP("CTRL.wLength = %d\n", ctrl.wLength);
DEBUG_SETUP("CTRL.wValue = %d (%d)\n", ctrl.wValue, ctrl.wValue >> 8);
DEBUG_SETUP("CTRL.wIndex = %d\n", ctrl.wIndex);
/* Set direction of EP0 */
if (likely(ctrl.bRequestType & USB_DIR_IN)) {
ep->bEndpointAddress |= USB_DIR_IN;
is_in = 1;
} else {
ep->bEndpointAddress &= ~USB_DIR_IN;
is_in = 0;
}
dev->req_pending = 1;
/* Handle some SETUP packets ourselves */
switch (ctrl.bRequest) {
case USB_REQ_SET_ADDRESS:
if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
break;
DEBUG_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue);
udc_set_address(dev, ctrl.wValue);
usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR);
return;
case USB_REQ_GET_STATUS:{
if (lh7a40x_handle_get_status(dev, &ctrl) == 0)
return;
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
if (ctrl.bRequestType == USB_RECIP_ENDPOINT) {
struct lh7a40x_ep *qep;
int ep_num = (ctrl.wIndex & 0x0f);
/* Support only HALT feature */
if (ctrl.wValue != 0 || ctrl.wLength != 0
|| ep_num > 3 || ep_num < 1)
break;
qep = &dev->ep[ep_num];
spin_unlock(&dev->lock);
if (ctrl.bRequest == USB_REQ_SET_FEATURE) {
DEBUG_SETUP("SET_FEATURE (%d)\n",
ep_num);
lh7a40x_set_halt(&qep->ep, 1);
} else {
DEBUG_SETUP("CLR_FEATURE (%d)\n",
ep_num);
lh7a40x_set_halt(&qep->ep, 0);
}
spin_lock(&dev->lock);
usb_set_index(0);
/* Reply with a ZLP on next IN token */
usb_set((EP0_CLR_OUT | EP0_DATA_END),
USB_EP0_CSR);
return;
}
break;
}
default:
break;
}
if (likely(dev->driver)) {
/* device-2-host (IN) or no data setup command, process immediately */
spin_unlock(&dev->lock);
i = dev->driver->setup(&dev->gadget, &ctrl);
spin_lock(&dev->lock);
if (i < 0) {
/* setup processing failed, force stall */
DEBUG_SETUP
(" --> ERROR: gadget setup FAILED (stalling), setup returned %d\n",
i);
usb_set_index(0);
usb_set((EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL),
USB_EP0_CSR);
/* ep->stopped = 1; */
dev->ep0state = WAIT_FOR_SETUP;
}
}
}
/*
* DATA_STATE_NEED_ZLP
*/
static void lh7a40x_ep0_in_zlp(struct lh7a40x_udc *dev, u32 csr)
{
DEBUG_EP0("%s: %x\n", __func__, csr);
/* c.f. Table 15-14 */
usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR);
dev->ep0state = WAIT_FOR_SETUP;
}
/*
* handle ep0 interrupt
*/
static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr)
{
struct lh7a40x_ep *ep = &dev->ep[0];
u32 csr;
/* Set index 0 */
usb_set_index(0);
csr = usb_read(USB_EP0_CSR);
DEBUG_EP0("%s: csr = %x\n", __func__, csr);
/*
* For overview of what we should be doing see c.f. Chapter 18.1.2.4
* We will follow that outline here modified by our own global state
* indication which provides hints as to what we think should be
* happening..
*/
/*
* if SENT_STALL is set
* - clear the SENT_STALL bit
*/
if (csr & EP0_SENT_STALL) {
DEBUG_EP0("%s: EP0_SENT_STALL is set: %x\n", __func__, csr);
usb_clear((EP0_SENT_STALL | EP0_SEND_STALL), USB_EP0_CSR);
nuke(ep, -ECONNABORTED);
dev->ep0state = WAIT_FOR_SETUP;
return;
}
/*
* if a transfer is in progress && IN_PKT_RDY and OUT_PKT_RDY are clear
* - fill EP0 FIFO
* - if last packet
* - set IN_PKT_RDY | DATA_END
* - else
* set IN_PKT_RDY
*/
if (!(csr & (EP0_IN_PKT_RDY | EP0_OUT_PKT_RDY))) {
DEBUG_EP0("%s: IN_PKT_RDY and OUT_PKT_RDY are clear\n",
__func__);
switch (dev->ep0state) {
case DATA_STATE_XMIT:
DEBUG_EP0("continue with DATA_STATE_XMIT\n");
lh7a40x_ep0_in(dev, csr);
return;
case DATA_STATE_NEED_ZLP:
DEBUG_EP0("continue with DATA_STATE_NEED_ZLP\n");
lh7a40x_ep0_in_zlp(dev, csr);
return;
default:
/* Stall? */
DEBUG_EP0("Odd state!! state = %s\n",
state_names[dev->ep0state]);
dev->ep0state = WAIT_FOR_SETUP;
/* nuke(ep, 0); */
/* usb_set(EP0_SEND_STALL, ep->csr1); */
break;
}
}
/*
* if SETUP_END is set
* - abort the last transfer
* - set SERVICED_SETUP_END_BIT
*/
if (csr & EP0_SETUP_END) {
DEBUG_EP0("%s: EP0_SETUP_END is set: %x\n", __func__, csr);
usb_set(EP0_CLR_SETUP_END, USB_EP0_CSR);
nuke(ep, 0);
dev->ep0state = WAIT_FOR_SETUP;
}
/*
* if EP0_OUT_PKT_RDY is set
* - read data packet from EP0 FIFO
* - decode command
* - if error
* set SERVICED_OUT_PKT_RDY | DATA_END bits | SEND_STALL
* - else
* set SERVICED_OUT_PKT_RDY | DATA_END bits
*/
if (csr & EP0_OUT_PKT_RDY) {
DEBUG_EP0("%s: EP0_OUT_PKT_RDY is set: %x\n", __func__,
csr);
switch (dev->ep0state) {
case WAIT_FOR_SETUP:
DEBUG_EP0("WAIT_FOR_SETUP\n");
lh7a40x_ep0_setup(dev, csr);
break;
case DATA_STATE_RECV:
DEBUG_EP0("DATA_STATE_RECV\n");
lh7a40x_ep0_out(dev, csr);
break;
default:
/* send stall? */
DEBUG_EP0("strange state!! 2. send stall? state = %d\n",
dev->ep0state);
break;
}
}
}
static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep)
{
u32 csr;
usb_set_index(0);
csr = usb_read(USB_EP0_CSR);
DEBUG_EP0("%s: %x\n", __func__, csr);
/* Clear "out packet ready" */
usb_set(EP0_CLR_OUT, USB_EP0_CSR);
if (ep_is_in(ep)) {
dev->ep0state = DATA_STATE_XMIT;
lh7a40x_ep0_in(dev, csr);
} else {
dev->ep0state = DATA_STATE_RECV;
lh7a40x_ep0_out(dev, csr);
}
}
/* ---------------------------------------------------------------------------
* device-scoped parts of the api to the usb controller hardware
* ---------------------------------------------------------------------------
*/
static int lh7a40x_udc_get_frame(struct usb_gadget *_gadget)
{
u32 frame1 = usb_read(USB_FRM_NUM1); /* Least significant 8 bits */
u32 frame2 = usb_read(USB_FRM_NUM2); /* Most significant 3 bits */
DEBUG("%s, %p\n", __func__, _gadget);
return ((frame2 & 0x07) << 8) | (frame1 & 0xff);
}
static int lh7a40x_udc_wakeup(struct usb_gadget *_gadget)
{
/* host may not have enabled remote wakeup */
/*if ((UDCCS0 & UDCCS0_DRWF) == 0)
return -EHOSTUNREACH;
udc_set_mask_UDCCR(UDCCR_RSM); */
return -ENOTSUPP;
}
static const struct usb_gadget_ops lh7a40x_udc_ops = {
.get_frame = lh7a40x_udc_get_frame,
.wakeup = lh7a40x_udc_wakeup,
/* current versions must always be self-powered */
};
static void nop_release(struct device *dev)
{
DEBUG("%s %s\n", __func__, dev_name(dev));
}
static struct lh7a40x_udc memory = {
.usb_address = 0,
.gadget = {
.ops = &lh7a40x_udc_ops,
.ep0 = &memory.ep[0].ep,
.name = driver_name,
.dev = {
.init_name = "gadget",
.release = nop_release,
},
},
/* control endpoint */
.ep[0] = {
.ep = {
.name = ep0name,
.ops = &lh7a40x_ep_ops,
.maxpacket = EP0_PACKETSIZE,
},
.dev = &memory,
.bEndpointAddress = 0,
.bmAttributes = 0,
.ep_type = ep_control,
.fifo = io_p2v(USB_EP0_FIFO),
.csr1 = USB_EP0_CSR,
.csr2 = USB_EP0_CSR,
},
/* first group of endpoints */
.ep[1] = {
.ep = {
.name = "ep1in-bulk",
.ops = &lh7a40x_ep_ops,
.maxpacket = 64,
},
.dev = &memory,
.bEndpointAddress = USB_DIR_IN | 1,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.ep_type = ep_bulk_in,
.fifo = io_p2v(USB_EP1_FIFO),
.csr1 = USB_IN_CSR1,
.csr2 = USB_IN_CSR2,
},
.ep[2] = {
.ep = {
.name = "ep2out-bulk",
.ops = &lh7a40x_ep_ops,
.maxpacket = 64,
},
.dev = &memory,
.bEndpointAddress = 2,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.ep_type = ep_bulk_out,
.fifo = io_p2v(USB_EP2_FIFO),
.csr1 = USB_OUT_CSR1,
.csr2 = USB_OUT_CSR2,
},
.ep[3] = {
.ep = {
.name = "ep3in-int",
.ops = &lh7a40x_ep_ops,
.maxpacket = 64,
},
.dev = &memory,
.bEndpointAddress = USB_DIR_IN | 3,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.ep_type = ep_interrupt,
.fifo = io_p2v(USB_EP3_FIFO),
.csr1 = USB_IN_CSR1,
.csr2 = USB_IN_CSR2,
},
};
/*
* probe - binds to the platform device
*/
static int lh7a40x_udc_probe(struct platform_device *pdev)
{
struct lh7a40x_udc *dev = &memory;
int retval;
DEBUG("%s: %p\n", __func__, pdev);
spin_lock_init(&dev->lock);
dev->dev = &pdev->dev;
device_initialize(&dev->gadget.dev);
dev->gadget.dev.parent = &pdev->dev;
the_controller = dev;
platform_set_drvdata(pdev, dev);
udc_disable(dev);
udc_reinit(dev);
/* irq setup after old hardware state is cleaned up */
retval =
request_irq(IRQ_USBINTR, lh7a40x_udc_irq, IRQF_DISABLED, driver_name,
dev);
if (retval != 0) {
DEBUG(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name,
IRQ_USBINTR, retval);
return -EBUSY;
}
create_proc_files();
return retval;
}
static int lh7a40x_udc_remove(struct platform_device *pdev)
{
struct lh7a40x_udc *dev = platform_get_drvdata(pdev);
DEBUG("%s: %p\n", __func__, pdev);
if (dev->driver)
return -EBUSY;
udc_disable(dev);
remove_proc_files();
free_irq(IRQ_USBINTR, dev);
platform_set_drvdata(pdev, 0);
the_controller = 0;
return 0;
}
/*-------------------------------------------------------------------------*/
static struct platform_driver udc_driver = {
.probe = lh7a40x_udc_probe,
.remove = lh7a40x_udc_remove,
/* FIXME power management support */
/* .suspend = ... disable UDC */
/* .resume = ... re-enable UDC */
.driver = {
.name = (char *)driver_name,
.owner = THIS_MODULE,
},
};
static int __init udc_init(void)
{
DEBUG("%s: %s version %s\n", __func__, driver_name, DRIVER_VERSION);
return platform_driver_register(&udc_driver);
}
static void __exit udc_exit(void)
{
platform_driver_unregister(&udc_driver);
}
module_init(udc_init);
module_exit(udc_exit);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Mikko Lahteenmaki, Bo Henriksen");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:lh7a40x_udc");
/*
* linux/drivers/usb/gadget/lh7a40x_udc.h
* Sharp LH7A40x on-chip full speed USB device controllers
*
* Copyright (C) 2004 Mikko Lahteenmaki, Nordic ID
* Copyright (C) 2004 Bo Henriksen, Nordic ID
*
* 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
*
*/
#ifndef __LH7A40X_H_
#define __LH7A40X_H_
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <asm/byteorder.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <mach/hardware.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
/*
* Memory map
*/
#define USB_FA 0x80000200 // function address register
#define USB_PM 0x80000204 // power management register
#define USB_IN_INT 0x80000208 // IN interrupt register bank (EP0-EP3)
#define USB_OUT_INT 0x80000210 // OUT interrupt register bank (EP2)
#define USB_INT 0x80000218 // interrupt register bank
#define USB_IN_INT_EN 0x8000021C // IN interrupt enable register bank
#define USB_OUT_INT_EN 0x80000224 // OUT interrupt enable register bank
#define USB_INT_EN 0x8000022C // USB interrupt enable register bank
#define USB_FRM_NUM1 0x80000230 // Frame number1 register
#define USB_FRM_NUM2 0x80000234 // Frame number2 register
#define USB_INDEX 0x80000238 // index register
#define USB_IN_MAXP 0x80000240 // IN MAXP register
#define USB_IN_CSR1 0x80000244 // IN CSR1 register/EP0 CSR register
#define USB_EP0_CSR 0x80000244 // IN CSR1 register/EP0 CSR register
#define USB_IN_CSR2 0x80000248 // IN CSR2 register
#define USB_OUT_MAXP 0x8000024C // OUT MAXP register
#define USB_OUT_CSR1 0x80000250 // OUT CSR1 register
#define USB_OUT_CSR2 0x80000254 // OUT CSR2 register
#define USB_OUT_FIFO_WC1 0x80000258 // OUT FIFO write count1 register
#define USB_OUT_FIFO_WC2 0x8000025C // OUT FIFO write count2 register
#define USB_RESET 0x8000044C // USB reset register
#define USB_EP0_FIFO 0x80000280
#define USB_EP1_FIFO 0x80000284
#define USB_EP2_FIFO 0x80000288
#define USB_EP3_FIFO 0x8000028c
/*
* USB reset register
*/
#define USB_RESET_APB (1<<1) //resets USB APB control side WRITE
#define USB_RESET_IO (1<<0) //resets USB IO side WRITE
/*
* USB function address register
*/
#define USB_FA_ADDR_UPDATE (1<<7)
#define USB_FA_FUNCTION_ADDR (0x7F)
/*
* Power Management register
*/
#define PM_USB_DCP (1<<5)
#define PM_USB_ENABLE (1<<4)
#define PM_USB_RESET (1<<3)
#define PM_UC_RESUME (1<<2)
#define PM_SUSPEND_MODE (1<<1)
#define PM_ENABLE_SUSPEND (1<<0)
/*
* IN interrupt register
*/
#define USB_IN_INT_EP3 (1<<3)
#define USB_IN_INT_EP1 (1<<1)
#define USB_IN_INT_EP0 (1<<0)
/*
* OUT interrupt register
*/
#define USB_OUT_INT_EP2 (1<<2)
/*
* USB interrupt register
*/
#define USB_INT_RESET_INT (1<<2)
#define USB_INT_RESUME_INT (1<<1)
#define USB_INT_SUSPEND_INT (1<<0)
/*
* USB interrupt enable register
*/
#define USB_INT_EN_USB_RESET_INTER (1<<2)
#define USB_INT_EN_RESUME_INTER (1<<1)
#define USB_INT_EN_SUSPEND_INTER (1<<0)
/*
* INCSR1 register
*/
#define USB_IN_CSR1_CLR_DATA_TOGGLE (1<<6)
#define USB_IN_CSR1_SENT_STALL (1<<5)
#define USB_IN_CSR1_SEND_STALL (1<<4)
#define USB_IN_CSR1_FIFO_FLUSH (1<<3)
#define USB_IN_CSR1_FIFO_NOT_EMPTY (1<<1)
#define USB_IN_CSR1_IN_PKT_RDY (1<<0)
/*
* INCSR2 register
*/
#define USB_IN_CSR2_AUTO_SET (1<<7)
#define USB_IN_CSR2_USB_DMA_EN (1<<4)
/*
* OUT CSR1 register
*/
#define USB_OUT_CSR1_CLR_DATA_REG (1<<7)
#define USB_OUT_CSR1_SENT_STALL (1<<6)
#define USB_OUT_CSR1_SEND_STALL (1<<5)
#define USB_OUT_CSR1_FIFO_FLUSH (1<<4)
#define USB_OUT_CSR1_FIFO_FULL (1<<1)
#define USB_OUT_CSR1_OUT_PKT_RDY (1<<0)
/*
* OUT CSR2 register
*/
#define USB_OUT_CSR2_AUTO_CLR (1<<7)
#define USB_OUT_CSR2_USB_DMA_EN (1<<4)
/*
* EP0 CSR
*/
#define EP0_CLR_SETUP_END (1<<7) /* Clear "Setup Ends" Bit (w) */
#define EP0_CLR_OUT (1<<6) /* Clear "Out packet ready" Bit (w) */
#define EP0_SEND_STALL (1<<5) /* Send STALL Handshake (rw) */
#define EP0_SETUP_END (1<<4) /* Setup Ends (r) */
#define EP0_DATA_END (1<<3) /* Data end (rw) */
#define EP0_SENT_STALL (1<<2) /* Sent Stall Handshake (r) */
#define EP0_IN_PKT_RDY (1<<1) /* In packet ready (rw) */
#define EP0_OUT_PKT_RDY (1<<0) /* Out packet ready (r) */
/* general CSR */
#define OUT_PKT_RDY (1<<0)
#define IN_PKT_RDY (1<<0)
/*
* IN/OUT MAXP register
*/
#define USB_OUT_MAXP_MAXP (0xF)
#define USB_IN_MAXP_MAXP (0xF)
// Max packet size
//#define EP0_PACKETSIZE 0x10
#define EP0_PACKETSIZE 0x8
#define EP0_MAXPACKETSIZE 0x10
#define UDC_MAX_ENDPOINTS 4
#define WAIT_FOR_SETUP 0
#define DATA_STATE_XMIT 1
#define DATA_STATE_NEED_ZLP 2
#define WAIT_FOR_OUT_STATUS 3
#define DATA_STATE_RECV 4
/* ********************************************************************************************* */
/* IO
*/
typedef enum ep_type {
ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt
} ep_type_t;
struct lh7a40x_ep {
struct usb_ep ep;
struct lh7a40x_udc *dev;
const struct usb_endpoint_descriptor *desc;
struct list_head queue;
unsigned long pio_irqs;
u8 stopped;
u8 bEndpointAddress;
u8 bmAttributes;
ep_type_t ep_type;
u32 fifo;
u32 csr1;
u32 csr2;
};
struct lh7a40x_request {
struct usb_request req;
struct list_head queue;
};
struct lh7a40x_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct device *dev;
spinlock_t lock;
int ep0state;
struct lh7a40x_ep ep[UDC_MAX_ENDPOINTS];
unsigned char usb_address;
unsigned req_pending:1, req_std:1, req_config:1;
};
extern struct lh7a40x_udc *the_controller;
#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN)==USB_DIR_IN)
#define ep_index(EP) ((EP)->bEndpointAddress&0xF)
#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
#endif
...@@ -1023,11 +1023,6 @@ MODULE_LICENSE ("GPL"); ...@@ -1023,11 +1023,6 @@ MODULE_LICENSE ("GPL");
#define OMAP3_PLATFORM_DRIVER ohci_hcd_omap3_driver #define OMAP3_PLATFORM_DRIVER ohci_hcd_omap3_driver
#endif #endif
#ifdef CONFIG_ARCH_LH7A404
#include "ohci-lh7a404.c"
#define PLATFORM_DRIVER ohci_hcd_lh7a404_driver
#endif
#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) #if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
#include "ohci-pxa27x.c" #include "ohci-pxa27x.c"
#define PLATFORM_DRIVER ohci_hcd_pxa27x_driver #define PLATFORM_DRIVER ohci_hcd_pxa27x_driver
......
/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
* (C) Copyright 2002 Hewlett-Packard Company
*
* Bus Glue for Sharp LH7A404
*
* Written by Christopher Hoover <ch@hpl.hp.com>
* Based on fragments of previous driver by Russell King et al.
*
* Modified for LH7A404 from ohci-sa1111.c
* by Durgesh Pattamatta <pattamattad@sharpsec.com>
*
* This file is licenced under the GPL.
*/
#include <linux/platform_device.h>
#include <linux/signal.h>
#include <mach/hardware.h>
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
static void lh7a404_start_hc(struct platform_device *dev)
{
printk(KERN_DEBUG "%s: starting LH7A404 OHCI USB Controller\n",
__FILE__);
/*
* Now, carefully enable the USB clock, and take
* the USB host controller out of reset.
*/
CSC_PWRCNT |= CSC_PWRCNT_USBH_EN; /* Enable clock */
udelay(1000);
USBH_CMDSTATUS = OHCI_HCR;
printk(KERN_DEBUG "%s: Clock to USB host has been enabled \n", __FILE__);
}
static void lh7a404_stop_hc(struct platform_device *dev)
{
printk(KERN_DEBUG "%s: stopping LH7A404 OHCI USB Controller\n",
__FILE__);
CSC_PWRCNT &= ~CSC_PWRCNT_USBH_EN; /* Disable clock */
}
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_hcd_lh7a404_probe - initialize LH7A404-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
*/
int usb_hcd_lh7a404_probe (const struct hc_driver *driver,
struct platform_device *dev)
{
int retval;
struct usb_hcd *hcd;
if (dev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
}
hcd = usb_create_hcd(driver, &dev->dev, "lh7a404");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start;
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed");
retval = -ENOMEM;
goto err2;
}
lh7a404_start_hc(dev);
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);
if (retval == 0)
return retval;
lh7a404_stop_hc(dev);
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_lh7a404_remove - shutdown processing for LH7A404-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_lh7a404_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_hcd_lh7a404_remove (struct usb_hcd *hcd, struct platform_device *dev)
{
usb_remove_hcd(hcd);
lh7a404_stop_hc(dev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
/*-------------------------------------------------------------------------*/
static int __devinit
ohci_lh7a404_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
ohci_dbg (ohci, "ohci_lh7a404_start, ohci:%p", ohci);
if ((ret = ohci_init(ohci)) < 0)
return ret;
if ((ret = ohci_run (ohci)) < 0) {
err ("can't start %s", hcd->self.bus_name);
ohci_stop (hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_lh7a404_hc_driver = {
.description = hcd_name,
.product_desc = "LH7A404 OHCI",
.hcd_priv_size = sizeof(struct ohci_hcd),
/*
* generic hardware linkage
*/
.irq = ohci_irq,
.flags = HCD_USB11 | HCD_MEMORY,
/*
* basic lifecycle operations
*/
.start = ohci_lh7a404_start,
.stop = ohci_stop,
.shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ohci_get_frame,
/*
* root hub support
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
#endif
.start_port_reset = ohci_start_port_reset,
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_lh7a404_drv_probe(struct platform_device *pdev)
{
int ret;
pr_debug ("In ohci_hcd_lh7a404_drv_probe");
if (usb_disabled())
return -ENODEV;
ret = usb_hcd_lh7a404_probe(&ohci_lh7a404_hc_driver, pdev);
return ret;
}
static int ohci_hcd_lh7a404_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_hcd_lh7a404_remove(hcd, pdev);
return 0;
}
/*TBD*/
/*static int ohci_hcd_lh7a404_drv_suspend(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
return 0;
}
static int ohci_hcd_lh7a404_drv_resume(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
return 0;
}
*/
static struct platform_driver ohci_hcd_lh7a404_driver = {
.probe = ohci_hcd_lh7a404_drv_probe,
.remove = ohci_hcd_lh7a404_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
/*.suspend = ohci_hcd_lh7a404_drv_suspend, */
/*.resume = ohci_hcd_lh7a404_drv_resume, */
.driver = {
.name = "lh7a404-ohci",
.owner = THIS_MODULE,
},
};
MODULE_ALIAS("platform:lh7a404-ohci");
...@@ -575,18 +575,8 @@ static inline void _ohci_writel (const struct ohci_hcd *ohci, ...@@ -575,18 +575,8 @@ static inline void _ohci_writel (const struct ohci_hcd *ohci,
#endif #endif
} }
#ifdef CONFIG_ARCH_LH7A404
/* Marc Singer: at the time this code was written, the LH7A404
* had a problem reading the USB host registers. This
* implementation of the ohci_readl function performs the read
* twice as a work-around.
*/
#define ohci_readl(o,r) (_ohci_readl(o,r),_ohci_readl(o,r))
#define ohci_writel(o,v,r) _ohci_writel(o,v,r)
#else
#define ohci_readl(o,r) _ohci_readl(o,r) #define ohci_readl(o,r) _ohci_readl(o,r)
#define ohci_writel(o,v,r) _ohci_writel(o,v,r) #define ohci_writel(o,v,r) _ohci_writel(o,v,r)
#endif
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
...@@ -322,69 +322,6 @@ config FB_ARMCLCD ...@@ -322,69 +322,6 @@ config FB_ARMCLCD
here and read <file:Documentation/kbuild/modules.txt>. The module here and read <file:Documentation/kbuild/modules.txt>. The module
will be called amba-clcd. will be called amba-clcd.
choice
depends on FB_ARMCLCD && (ARCH_LH7A40X || ARCH_LH7952X)
prompt "LCD Panel"
default FB_ARMCLCD_SHARP_LQ035Q7DB02
config FB_ARMCLCD_SHARP_LQ035Q7DB02_HRTFT
bool "LogicPD LCD 3.5\" QVGA w/HRTFT IC"
help
This is an implementation of the Sharp LQ035Q7DB02, a 3.5"
color QVGA, HRTFT panel. The LogicPD device includes
an integrated HRTFT controller IC.
The native resolution is 240x320.
config FB_ARMCLCD_SHARP_LQ057Q3DC02
bool "LogicPD LCD 5.7\" QVGA"
help
This is an implementation of the Sharp LQ057Q3DC02, a 5.7"
color QVGA, TFT panel. The LogicPD device includes an
The native resolution is 320x240.
config FB_ARMCLCD_SHARP_LQ64D343
bool "LogicPD LCD 6.4\" VGA"
help
This is an implementation of the Sharp LQ64D343, a 6.4"
color VGA, TFT panel. The LogicPD device includes an
The native resolution is 640x480.
config FB_ARMCLCD_SHARP_LQ10D368
bool "LogicPD LCD 10.4\" VGA"
help
This is an implementation of the Sharp LQ10D368, a 10.4"
color VGA, TFT panel. The LogicPD device includes an
The native resolution is 640x480.
config FB_ARMCLCD_SHARP_LQ121S1DG41
bool "LogicPD LCD 12.1\" SVGA"
help
This is an implementation of the Sharp LQ121S1DG41, a 12.1"
color SVGA, TFT panel. The LogicPD device includes an
The native resolution is 800x600.
This panel requires a clock rate may be an integer fraction
of the base LCDCLK frequency. The driver will select the
highest frequency available that is lower than the maximum
allowed. The panel may flicker if the clock rate is
slower than the recommended minimum.
config FB_ARMCLCD_AUO_A070VW01_WIDE
bool "AU Optronics A070VW01 LCD 7.0\" WIDE"
help
This is an implementation of the AU Optronics, a 7.0"
WIDE Color. The native resolution is 234x480.
config FB_ARMCLCD_HITACHI
bool "Hitachi Wide Screen 800x480"
help
This is an implementation of the Hitachi 800x480.
endchoice
config FB_ACORN config FB_ACORN
bool "Acorn VIDC support" bool "Acorn VIDC support"
depends on (FB = y) && ARM && ARCH_ACORN depends on (FB = y) && ARM && ARCH_ACORN
......
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