1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter.git synced 2025-03-09 15:40:20 +00:00

Fix for RUTX platform

This commit is contained in:
Ycarus (Yannick Chabanois) 2022-03-28 18:17:07 +02:00
parent ccdb64ad45
commit 59bc57d5d5
7254 changed files with 1810270 additions and 7 deletions

View file

@ -0,0 +1,39 @@
include $(TOPDIR)/config.mk
LIB := $(obj)libatibiosemu.o
X86DIR = x86emu
$(shell mkdir -p $(obj)$(X86DIR))
COBJS-$(CONFIG_BIOSEMU) = atibios.o biosemu.o besys.o bios.o \
$(X86DIR)/decode.o \
$(X86DIR)/ops2.o \
$(X86DIR)/ops.o \
$(X86DIR)/prim_ops.o \
$(X86DIR)/sys.o \
$(X86DIR)/debug.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
EXTRA_CFLAGS += -I. -I./include -I$(TOPDIR)/include \
-D__PPC__ -D__BIG_ENDIAN__
CFLAGS += $(EXTRA_CFLAGS)
HOSTCFLAGS += $(EXTRA_CFLAGS)
CPPFLAGS += $(EXTRA_CFLAGS)
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,338 @@
/****************************************************************************
*
* Video BOOT Graphics Card POST Module
*
* ========================================================================
* Copyright (C) 2007 Freescale Semiconductor, Inc.
* Jason Jin <Jason.jin@freescale.com>
*
* Copyright (C) 1991-2004 SciTech Software, Inc. All rights reserved.
*
* This file may be distributed and/or modified under the terms of the
* GNU General Public License version 2.0 as published by the Free
* Software Foundation and appearing in the file LICENSE.GPL included
* in the packaging of this file.
*
* Licensees holding a valid Commercial License for this product from
* SciTech Software, Inc. may use this file in accordance with the
* Commercial License Agreement provided with the Software.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
* THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE.
*
* See http://www.scitechsoft.com/license/ for information about
* the licensing options available and how to purchase a Commercial
* License Agreement.
*
* Contact license@scitechsoft.com if any conditions of this licensing
* are not clear to you, or you have questions about licensing options.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Linux Kernel
* Developer: Kendall Bennett
*
* Description: Module to implement booting PCI/AGP controllers on the
* bus. We use the x86 real mode emulator to run the BIOS on
* graphics controllers to bring the cards up.
*
* Note that at present this module does *not* support
* multiple controllers.
*
* The orignal name of this file is warmboot.c.
* Jason ported this file to u-boot to run the ATI video card
* BIOS in u-boot.
****************************************************************************/
#include <common.h>
#include "biosemui.h"
#include <malloc.h>
/* Length of the BIOS image */
#define MAX_BIOSLEN (128 * 1024L)
/* Define some useful types and macros */
#define true 1
#define false 0
/* Place to save PCI BAR's that we change and later restore */
static u32 saveROMBaseAddress;
static u32 saveBaseAddress10;
static u32 saveBaseAddress14;
static u32 saveBaseAddress18;
static u32 saveBaseAddress20;
/****************************************************************************
PARAMETERS:
pcidev - PCI device info for the video card on the bus to boot
VGAInfo - BIOS emulator VGA info structure
REMARKS:
This function executes the BIOS POST code on the controller. We assume that
at this stage the controller has its I/O and memory space enabled and
that all other controllers are in a disabled state.
****************************************************************************/
static void PCI_doBIOSPOST(pci_dev_t pcidev, BE_VGAInfo * VGAInfo)
{
RMREGS regs;
RMSREGS sregs;
/* Determine the value to store in AX for BIOS POST. Per the PCI specs,
AH must contain the bus and AL must contain the devfn, encoded as
(dev << 3) | fn
*/
memset(&regs, 0, sizeof(regs));
memset(&sregs, 0, sizeof(sregs));
regs.x.ax = ((int)PCI_BUS(pcidev) << 8) |
((int)PCI_DEV(pcidev) << 3) | (int)PCI_FUNC(pcidev);
/*Setup the X86 emulator for the VGA BIOS*/
BE_setVGA(VGAInfo);
/*Execute the BIOS POST code*/
BE_callRealMode(0xC000, 0x0003, &regs, &sregs);
/*Cleanup and exit*/
BE_getVGA(VGAInfo);
}
/****************************************************************************
PARAMETERS:
pcidev - PCI device info for the video card on the bus
bar - Place to return the base address register offset to use
RETURNS:
The address to use to map the secondary BIOS (AGP devices)
REMARKS:
Searches all the PCI base address registers for the device looking for a
memory mapping that is large enough to hold our ROM BIOS. We usually end up
finding the framebuffer mapping (usually BAR 0x10), and we use this mapping
to map the BIOS for the device into. We use a mapping that is already
assigned to the device to ensure the memory range will be passed through
by any PCI->PCI or AGP->PCI bridge that may be present.
NOTE: Usually this function is only used for AGP devices, but it may be
used for PCI devices that have already been POST'ed and the BIOS
ROM base address has been zero'ed out.
NOTE: This function leaves the original memory aperture disabled by leaving
it programmed to all 1's. It must be restored to the correct value
later.
****************************************************************************/
static u32 PCI_findBIOSAddr(pci_dev_t pcidev, int *bar)
{
u32 base, size;
for (*bar = 0x10; *bar <= 0x14; (*bar) += 4) {
pci_read_config_dword(pcidev, *bar, &base);
if (!(base & 0x1)) {
pci_write_config_dword(pcidev, *bar, 0xFFFFFFFF);
pci_read_config_dword(pcidev, *bar, &size);
size = ~(size & ~0xFF) + 1;
if (size >= MAX_BIOSLEN)
return base & ~0xFF;
}
}
return 0;
}
/****************************************************************************
REMARKS:
Some non-x86 Linux kernels map PCI relocateable I/O to values that
are above 64K, which will not work with the BIOS image that requires
the offset for the I/O ports to be a maximum of 16-bits. Ideally
someone should fix the kernel to map the I/O ports for VGA compatible
devices to a different location (or just all I/O ports since it is
unlikely you can have enough devices in the machine to use up all
64K of the I/O space - a total of more than 256 cards would be
necessary).
Anyway to fix this we change all I/O mapped base registers and
chop off the top bits.
****************************************************************************/
static void PCI_fixupIObase(pci_dev_t pcidev, int reg, u32 * base)
{
if ((*base & 0x1) && (*base > 0xFFFE)) {
*base &= 0xFFFF;
pci_write_config_dword(pcidev, reg, *base);
}
}
/****************************************************************************
PARAMETERS:
pcidev - PCI device info for the video card on the bus
RETURNS:
Pointers to the mapped BIOS image
REMARKS:
Maps a pointer to the BIOS image on the graphics card on the PCI bus.
****************************************************************************/
void *PCI_mapBIOSImage(pci_dev_t pcidev)
{
u32 BIOSImageBus;
int BIOSImageBAR;
u8 *BIOSImage;
/*Save PCI BAR registers that might get changed*/
pci_read_config_dword(pcidev, PCI_ROM_ADDRESS, &saveROMBaseAddress);
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &saveBaseAddress10);
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_1, &saveBaseAddress14);
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_2, &saveBaseAddress18);
pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_4, &saveBaseAddress20);
/*Fix up I/O base registers to less than 64K */
if(saveBaseAddress14 != 0)
PCI_fixupIObase(pcidev, PCI_BASE_ADDRESS_1, &saveBaseAddress14);
else
PCI_fixupIObase(pcidev, PCI_BASE_ADDRESS_4, &saveBaseAddress20);
/* Some cards have problems that stop us from being able to read the
BIOS image from the ROM BAR. To fix this we have to do some chipset
specific programming for different cards to solve this problem.
*/
BIOSImageBus = PCI_findBIOSAddr(pcidev, &BIOSImageBAR);
if (BIOSImageBus == 0) {
printf("Find bios addr error\n");
return NULL;
}
BIOSImage = pci_bus_to_virt(pcidev, BIOSImageBus,
PCI_REGION_MEM, 0, MAP_NOCACHE);
/*Change the PCI BAR registers to map it onto the bus.*/
pci_write_config_dword(pcidev, BIOSImageBAR, 0);
pci_write_config_dword(pcidev, PCI_ROM_ADDRESS, BIOSImageBus | 0x1);
udelay(1);
/*Check that the BIOS image is valid. If not fail, or return the
compiled in BIOS image if that option was enabled
*/
if (BIOSImage[0] != 0x55 || BIOSImage[1] != 0xAA || BIOSImage[2] == 0) {
return NULL;
}
return BIOSImage;
}
/****************************************************************************
PARAMETERS:
pcidev - PCI device info for the video card on the bus
REMARKS:
Unmaps the BIOS image for the device and restores framebuffer mappings
****************************************************************************/
void PCI_unmapBIOSImage(pci_dev_t pcidev, void *BIOSImage)
{
pci_write_config_dword(pcidev, PCI_ROM_ADDRESS, saveROMBaseAddress);
pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_0, saveBaseAddress10);
pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_1, saveBaseAddress14);
pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_2, saveBaseAddress18);
pci_write_config_dword(pcidev, PCI_BASE_ADDRESS_4, saveBaseAddress20);
}
/****************************************************************************
PARAMETERS:
pcidev - PCI device info for the video card on the bus to boot
VGAInfo - BIOS emulator VGA info structure
RETURNS:
True if successfully initialised, false if not.
REMARKS:
Loads and POST's the display controllers BIOS, directly from the BIOS
image we can extract over the PCI bus.
****************************************************************************/
static int PCI_postController(pci_dev_t pcidev, BE_VGAInfo * VGAInfo)
{
u32 BIOSImageLen;
uchar *mappedBIOS;
uchar *copyOfBIOS;
/*Allocate memory to store copy of BIOS from display controller*/
if ((mappedBIOS = PCI_mapBIOSImage(pcidev)) == NULL) {
printf("videoboot: Video ROM failed to map!\n");
return false;
}
BIOSImageLen = mappedBIOS[2] * 512;
if ((copyOfBIOS = malloc(BIOSImageLen)) == NULL) {
printf("videoboot: Out of memory!\n");
return false;
}
memcpy(copyOfBIOS, mappedBIOS, BIOSImageLen);
PCI_unmapBIOSImage(pcidev, mappedBIOS);
/*Save information in VGAInfo structure*/
VGAInfo->function = PCI_FUNC(pcidev);
VGAInfo->device = PCI_DEV(pcidev);
VGAInfo->bus = PCI_BUS(pcidev);
VGAInfo->pcidev = pcidev;
VGAInfo->BIOSImage = copyOfBIOS;
VGAInfo->BIOSImageLen = BIOSImageLen;
/*Now execute the BIOS POST for the device*/
if (copyOfBIOS[0] != 0x55 || copyOfBIOS[1] != 0xAA) {
printf("videoboot: Video ROM image is invalid!\n");
return false;
}
PCI_doBIOSPOST(pcidev, VGAInfo);
/*Reset the size of the BIOS image to the final size*/
VGAInfo->BIOSImageLen = copyOfBIOS[2] * 512;
return true;
}
/****************************************************************************
PARAMETERS:
pcidev - PCI device info for the video card on the bus to boot
pVGAInfo - Place to return VGA info structure is requested
cleanUp - True to clean up on exit, false to leave emulator active
REMARKS:
Boots the PCI/AGP video card on the bus using the Video ROM BIOS image
and the X86 BIOS emulator module.
****************************************************************************/
int BootVideoCardBIOS(pci_dev_t pcidev, BE_VGAInfo ** pVGAInfo, int cleanUp)
{
BE_VGAInfo *VGAInfo;
printf("videoboot: Booting PCI video card bus %d, function %d, device %d\n",
PCI_BUS(pcidev), PCI_FUNC(pcidev), PCI_DEV(pcidev));
/*Initialise the x86 BIOS emulator*/
if ((VGAInfo = malloc(sizeof(*VGAInfo))) == NULL) {
printf("videoboot: Out of memory!\n");
return false;
}
memset(VGAInfo, 0, sizeof(*VGAInfo));
BE_init(0, 65536, VGAInfo, 0);
/*Post all the display controller BIOS'es*/
if (!PCI_postController(pcidev, VGAInfo))
return false;
/*Cleanup and exit the emulator if requested. If the BIOS emulator
is needed after booting the card, we will not call BE_exit and
leave it enabled for further use (ie: VESA driver etc).
*/
if (cleanUp) {
BE_exit();
if (VGAInfo->BIOSImage)
free(VGAInfo->BIOSImage);
free(VGAInfo);
VGAInfo = NULL;
}
/*Return VGA info pointer if the caller requested it*/
if (pVGAInfo)
*pVGAInfo = VGAInfo;
return true;
}

View file

@ -0,0 +1,720 @@
/****************************************************************************
*
* BIOS emulator and interface
* to Realmode X86 Emulator Library
*
* ========================================================================
*
* Copyright (C) 2007 Freescale Semiconductor, Inc.
* Jason Jin<Jason.jin@freescale.com>
*
* Copyright (C) 1991-2004 SciTech Software, Inc. All rights reserved.
*
* This file may be distributed and/or modified under the terms of the
* GNU General Public License version 2.0 as published by the Free
* Software Foundation and appearing in the file LICENSE.GPL included
* in the packaging of this file.
*
* Licensees holding a valid Commercial License for this product from
* SciTech Software, Inc. may use this file in accordance with the
* Commercial License Agreement provided with the Software.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
* THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE.
*
* See http://www.scitechsoft.com/license/ for information about
* the licensing options available and how to purchase a Commercial
* License Agreement.
*
* Contact license@scitechsoft.com if any conditions of this licensing
* are not clear to you, or you have questions about licensing options.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: This file includes BIOS emulator I/O and memory access
* functions.
*
* Jason ported this file to u-boot to run the ATI video card
* BIOS in u-boot. Removed some emulate functions such as the
* timer port access. Made all the VGA port except reading 0x3c3
* be emulated. Seems like reading 0x3c3 should return the high
* 16 bit of the io port.
*
****************************************************************************/
#include <common.h>
#include "biosemui.h"
/*------------------------- Global Variables ------------------------------*/
#ifndef __i386__
static char *BE_biosDate = "08/14/99";
static u8 BE_model = 0xFC;
static u8 BE_submodel = 0x00;
#endif
/*----------------------------- Implementation ----------------------------*/
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to convert
RETURNS:
Actual memory address to read or write the data
REMARKS:
This function converts an emulator memory address in a 32-bit range to
a real memory address that we wish to access. It handles splitting up the
memory address space appropriately to access the emulator BIOS image, video
memory and system BIOS etc.
****************************************************************************/
static u8 *BE_memaddr(u32 addr)
{
if (addr >= 0xC0000 && addr <= _BE_env.biosmem_limit) {
return (u8*)(_BE_env.biosmem_base + addr - 0xC0000);
} else if (addr > _BE_env.biosmem_limit && addr < 0xD0000) {
DB(printf("BE_memaddr: address %#lx may be invalid!\n", addr);)
return M.mem_base;
} else if (addr >= 0xA0000 && addr <= 0xBFFFF) {
return (u8*)(_BE_env.busmem_base + addr - 0xA0000);
}
#ifdef __i386__
else if (addr >= 0xD0000 && addr <= 0xFFFFF) {
/* We map the real System BIOS directly on real PC's */
DB(printf("BE_memaddr: System BIOS address %#lx\n", addr);)
return _BE_env.busmem_base + addr - 0xA0000;
}
#else
else if (addr >= 0xFFFF5 && addr < 0xFFFFE) {
/* Return a faked BIOS date string for non-x86 machines */
DB(printf("BE_memaddr - Returning BIOS date\n");)
return (u8 *)(BE_biosDate + addr - 0xFFFF5);
} else if (addr == 0xFFFFE) {
/* Return system model identifier for non-x86 machines */
DB(printf("BE_memaddr - Returning model\n");)
return &BE_model;
} else if (addr == 0xFFFFF) {
/* Return system submodel identifier for non-x86 machines */
DB(printf("BE_memaddr - Returning submodel\n");)
return &BE_submodel;
}
#endif
else if (addr > M.mem_size - 1) {
HALT_SYS();
return M.mem_base;
}
return M.mem_base + addr;
}
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
RETURNS:
Byte value read from emulator memory.
REMARKS:
Reads a byte value from the emulator memory. We have three distinct memory
regions that are handled differently, which this function handles.
****************************************************************************/
u8 X86API BE_rdb(u32 addr)
{
if (_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)
return 0;
else {
u8 val = readb_le(BE_memaddr(addr));
return val;
}
}
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
RETURNS:
Word value read from emulator memory.
REMARKS:
Reads a word value from the emulator memory. We have three distinct memory
regions that are handled differently, which this function handles.
****************************************************************************/
u16 X86API BE_rdw(u32 addr)
{
if (_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)
return 0;
else {
u8 *base = BE_memaddr(addr);
u16 val = readw_le(base);
return val;
}
}
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
RETURNS:
Long value read from emulator memory.
REMARKS:
Reads a 32-bit value from the emulator memory. We have three distinct memory
regions that are handled differently, which this function handles.
****************************************************************************/
u32 X86API BE_rdl(u32 addr)
{
if (_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)
return 0;
else {
u8 *base = BE_memaddr(addr);
u32 val = readl_le(base);
return val;
}
}
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
val - Value to store
REMARKS:
Writes a byte value to emulator memory. We have three distinct memory
regions that are handled differently, which this function handles.
****************************************************************************/
void X86API BE_wrb(u32 addr, u8 val)
{
if (!(_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)) {
writeb_le(BE_memaddr(addr), val);
}
}
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
val - Value to store
REMARKS:
Writes a word value to emulator memory. We have three distinct memory
regions that are handled differently, which this function handles.
****************************************************************************/
void X86API BE_wrw(u32 addr, u16 val)
{
if (!(_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)) {
u8 *base = BE_memaddr(addr);
writew_le(base, val);
}
}
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
val - Value to store
REMARKS:
Writes a 32-bit value to emulator memory. We have three distinct memory
regions that are handled differently, which this function handles.
****************************************************************************/
void X86API BE_wrl(u32 addr, u32 val)
{
if (!(_BE_env.emulateVGA && addr >= 0xA0000 && addr <= 0xBFFFF)) {
u8 *base = BE_memaddr(addr);
writel_le(base, val);
}
}
#if defined(DEBUG) || !defined(__i386__)
/* For Non-Intel machines we may need to emulate some I/O port accesses that
* the BIOS may try to access, such as the PCI config registers.
*/
#define IS_TIMER_PORT(port) (0x40 <= port && port <= 0x43)
#define IS_CMOS_PORT(port) (0x70 <= port && port <= 0x71)
/*#define IS_VGA_PORT(port) (_BE_env.emulateVGA && 0x3C0 <= port && port <= 0x3DA)*/
#define IS_VGA_PORT(port) (0x3C0 <= port && port <= 0x3DA)
#define IS_PCI_PORT(port) (0xCF8 <= port && port <= 0xCFF)
#define IS_SPKR_PORT(port) (port == 0x61)
/****************************************************************************
PARAMETERS:
port - Port to read from
type - Type of access to perform
REMARKS:
Performs an emulated read from the Standard VGA I/O ports. If the target
hardware does not support mapping the VGA I/O and memory (such as some
PowerPC systems), we emulate the VGA so that the BIOS will still be able to
set NonVGA display modes such as on ATI hardware.
****************************************************************************/
static u8 VGA_inpb (const int port)
{
u8 val = 0xff;
switch (port) {
case 0x3C0:
/* 3C0 has funky characteristics because it can act as either
a data register or index register depending on the state
of an internal flip flop in the hardware. Hence we have
to emulate that functionality in here. */
if (_BE_env.flipFlop3C0 == 0) {
/* Access 3C0 as index register */
val = _BE_env.emu3C0;
} else {
/* Access 3C0 as data register */
if (_BE_env.emu3C0 < ATT_C)
val = _BE_env.emu3C1[_BE_env.emu3C0];
}
_BE_env.flipFlop3C0 ^= 1;
break;
case 0x3C1:
if (_BE_env.emu3C0 < ATT_C)
return _BE_env.emu3C1[_BE_env.emu3C0];
break;
case 0x3CC:
return _BE_env.emu3C2;
case 0x3C4:
return _BE_env.emu3C4;
case 0x3C5:
if (_BE_env.emu3C4 < ATT_C)
return _BE_env.emu3C5[_BE_env.emu3C4];
break;
case 0x3C6:
return _BE_env.emu3C6;
case 0x3C7:
return _BE_env.emu3C7;
case 0x3C8:
return _BE_env.emu3C8;
case 0x3C9:
if (_BE_env.emu3C7 < PAL_C)
return _BE_env.emu3C9[_BE_env.emu3C7++];
break;
case 0x3CE:
return _BE_env.emu3CE;
case 0x3CF:
if (_BE_env.emu3CE < GRA_C)
return _BE_env.emu3CF[_BE_env.emu3CE];
break;
case 0x3D4:
if (_BE_env.emu3C2 & 0x1)
return _BE_env.emu3D4;
break;
case 0x3D5:
if ((_BE_env.emu3C2 & 0x1) && (_BE_env.emu3D4 < CRT_C))
return _BE_env.emu3D5[_BE_env.emu3D4];
break;
case 0x3DA:
_BE_env.flipFlop3C0 = 0;
val = _BE_env.emu3DA;
_BE_env.emu3DA ^= 0x9;
break;
}
return val;
}
/****************************************************************************
PARAMETERS:
port - Port to write to
type - Type of access to perform
REMARKS:
Performs an emulated write to one of the 8253 timer registers. For now
we only emulate timer 0 which is the only timer that the BIOS code appears
to use.
****************************************************************************/
static void VGA_outpb (int port, u8 val)
{
switch (port) {
case 0x3C0:
/* 3C0 has funky characteristics because it can act as either
a data register or index register depending on the state
of an internal flip flop in the hardware. Hence we have
to emulate that functionality in here. */
if (_BE_env.flipFlop3C0 == 0) {
/* Access 3C0 as index register */
_BE_env.emu3C0 = val;
} else {
/* Access 3C0 as data register */
if (_BE_env.emu3C0 < ATT_C)
_BE_env.emu3C1[_BE_env.emu3C0] = val;
}
_BE_env.flipFlop3C0 ^= 1;
break;
case 0x3C2:
_BE_env.emu3C2 = val;
break;
case 0x3C4:
_BE_env.emu3C4 = val;
break;
case 0x3C5:
if (_BE_env.emu3C4 < ATT_C)
_BE_env.emu3C5[_BE_env.emu3C4] = val;
break;
case 0x3C6:
_BE_env.emu3C6 = val;
break;
case 0x3C7:
_BE_env.emu3C7 = (int) val *3;
break;
case 0x3C8:
_BE_env.emu3C8 = (int) val *3;
break;
case 0x3C9:
if (_BE_env.emu3C8 < PAL_C)
_BE_env.emu3C9[_BE_env.emu3C8++] = val;
break;
case 0x3CE:
_BE_env.emu3CE = val;
break;
case 0x3CF:
if (_BE_env.emu3CE < GRA_C)
_BE_env.emu3CF[_BE_env.emu3CE] = val;
break;
case 0x3D4:
if (_BE_env.emu3C2 & 0x1)
_BE_env.emu3D4 = val;
break;
case 0x3D5:
if ((_BE_env.emu3C2 & 0x1) && (_BE_env.emu3D4 < CRT_C))
_BE_env.emu3D5[_BE_env.emu3D4] = val;
break;
}
}
/****************************************************************************
PARAMETERS:
regOffset - Offset into register space for non-DWORD accesses
value - Value to write to register for PCI_WRITE_* operations
func - Function to perform (PCIAccessRegFlags)
RETURNS:
Value read from configuration register for PCI_READ_* operations
REMARKS:
Accesses a PCI configuration space register by decoding the value currently
stored in the _BE_env.configAddress variable and passing it through to the
portable PCI_accessReg function.
****************************************************************************/
static u32 BE_accessReg(int regOffset, u32 value, int func)
{
#ifdef __KERNEL__
int function, device, bus;
u8 val8;
u16 val16;
u32 val32;
/* Decode the configuration register values for the register we wish to
* access
*/
regOffset += (_BE_env.configAddress & 0xFF);
function = (_BE_env.configAddress >> 8) & 0x7;
device = (_BE_env.configAddress >> 11) & 0x1F;
bus = (_BE_env.configAddress >> 16) & 0xFF;
/* Ignore accesses to all devices other than the one we're POSTing */
if ((function == _BE_env.vgaInfo.function) &&
(device == _BE_env.vgaInfo.device) &&
(bus == _BE_env.vgaInfo.bus)) {
switch (func) {
case REG_READ_BYTE:
pci_read_config_byte(_BE_env.vgaInfo.pcidev, regOffset,
&val8);
return val8;
case REG_READ_WORD:
pci_read_config_word(_BE_env.vgaInfo.pcidev, regOffset,
&val16);
return val16;
case REG_READ_DWORD:
pci_read_config_dword(_BE_env.vgaInfo.pcidev, regOffset,
&val32);
return val32;
case REG_WRITE_BYTE:
pci_write_config_byte(_BE_env.vgaInfo.pcidev, regOffset,
value);
return 0;
case REG_WRITE_WORD:
pci_write_config_word(_BE_env.vgaInfo.pcidev, regOffset,
value);
return 0;
case REG_WRITE_DWORD:
pci_write_config_dword(_BE_env.vgaInfo.pcidev,
regOffset, value);
return 0;
}
}
return 0;
#else
PCIDeviceInfo pciInfo;
pciInfo.mech1 = 1;
pciInfo.slot.i = 0;
pciInfo.slot.p.Function = (_BE_env.configAddress >> 8) & 0x7;
pciInfo.slot.p.Device = (_BE_env.configAddress >> 11) & 0x1F;
pciInfo.slot.p.Bus = (_BE_env.configAddress >> 16) & 0xFF;
pciInfo.slot.p.Enable = 1;
/* Ignore accesses to all devices other than the one we're POSTing */
if ((pciInfo.slot.p.Function ==
_BE_env.vgaInfo.pciInfo->slot.p.Function)
&& (pciInfo.slot.p.Device == _BE_env.vgaInfo.pciInfo->slot.p.Device)
&& (pciInfo.slot.p.Bus == _BE_env.vgaInfo.pciInfo->slot.p.Bus))
return PCI_accessReg((_BE_env.configAddress & 0xFF) + regOffset,
value, func, &pciInfo);
return 0;
#endif
}
/****************************************************************************
PARAMETERS:
port - Port to read from
type - Type of access to perform
REMARKS:
Performs an emulated read from one of the PCI configuration space registers.
We emulate this using our PCI_accessReg function which will access the PCI
configuration space registers in a portable fashion.
****************************************************************************/
static u32 PCI_inp(int port, int type)
{
switch (type) {
case REG_READ_BYTE:
if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port
&& port <= 0xCFF)
return BE_accessReg(port - 0xCFC, 0, REG_READ_BYTE);
break;
case REG_READ_WORD:
if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port
&& port <= 0xCFF)
return BE_accessReg(port - 0xCFC, 0, REG_READ_WORD);
break;
case REG_READ_DWORD:
if (port == 0xCF8)
return _BE_env.configAddress;
else if ((_BE_env.configAddress & 0x80000000) && port == 0xCFC)
return BE_accessReg(0, 0, REG_READ_DWORD);
break;
}
return 0;
}
/****************************************************************************
PARAMETERS:
port - Port to write to
type - Type of access to perform
REMARKS:
Performs an emulated write to one of the PCI control registers.
****************************************************************************/
static void PCI_outp(int port, u32 val, int type)
{
switch (type) {
case REG_WRITE_BYTE:
if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port
&& port <= 0xCFF)
BE_accessReg(port - 0xCFC, val, REG_WRITE_BYTE);
break;
case REG_WRITE_WORD:
if ((_BE_env.configAddress & 0x80000000) && 0xCFC <= port
&& port <= 0xCFF)
BE_accessReg(port - 0xCFC, val, REG_WRITE_WORD);
break;
case REG_WRITE_DWORD:
if (port == 0xCF8)
{
_BE_env.configAddress = val & 0x80FFFFFC;
}
else if ((_BE_env.configAddress & 0x80000000) && port == 0xCFC)
BE_accessReg(0, val, REG_WRITE_DWORD);
break;
}
}
#endif
/****************************************************************************
PARAMETERS:
port - Port to write to
RETURNS:
Value read from the I/O port
REMARKS:
Performs an emulated 8-bit read from an I/O port. We handle special cases
that we need to emulate in here, and fall through to reflecting the write
through to the real hardware if we don't need to special case it.
****************************************************************************/
u8 X86API BE_inb(X86EMU_pioAddr port)
{
u8 val = 0;
#if defined(DEBUG) || !defined(__i386__)
if (IS_VGA_PORT(port)){
/*seems reading port 0x3c3 return the high 16 bit of io port*/
if(port == 0x3c3)
val = LOG_inpb(port);
else
val = VGA_inpb(port);
}
else if (IS_TIMER_PORT(port))
DB(printf("Can not interept TIMER port now!\n");)
else if (IS_SPKR_PORT(port))
DB(printf("Can not interept SPEAKER port now!\n");)
else if (IS_CMOS_PORT(port))
DB(printf("Can not interept CMOS port now!\n");)
else if (IS_PCI_PORT(port))
val = PCI_inp(port, REG_READ_BYTE);
else if (port < 0x100) {
DB(printf("WARN: INVALID inb.%04X -> %02X\n", (u16) port, val);)
val = LOG_inpb(port);
} else
#endif
val = LOG_inpb(port);
return val;
}
/****************************************************************************
PARAMETERS:
port - Port to write to
RETURNS:
Value read from the I/O port
REMARKS:
Performs an emulated 16-bit read from an I/O port. We handle special cases
that we need to emulate in here, and fall through to reflecting the write
through to the real hardware if we don't need to special case it.
****************************************************************************/
u16 X86API BE_inw(X86EMU_pioAddr port)
{
u16 val = 0;
#if defined(DEBUG) || !defined(__i386__)
if (IS_PCI_PORT(port))
val = PCI_inp(port, REG_READ_WORD);
else if (port < 0x100) {
DB(printf("WARN: Maybe INVALID inw.%04X -> %04X\n", (u16) port, val);)
val = LOG_inpw(port);
} else
#endif
val = LOG_inpw(port);
return val;
}
/****************************************************************************
PARAMETERS:
port - Port to write to
RETURNS:
Value read from the I/O port
REMARKS:
Performs an emulated 32-bit read from an I/O port. We handle special cases
that we need to emulate in here, and fall through to reflecting the write
through to the real hardware if we don't need to special case it.
****************************************************************************/
u32 X86API BE_inl(X86EMU_pioAddr port)
{
u32 val = 0;
#if defined(DEBUG) || !defined(__i386__)
if (IS_PCI_PORT(port))
val = PCI_inp(port, REG_READ_DWORD);
else if (port < 0x100) {
val = LOG_inpd(port);
} else
#endif
val = LOG_inpd(port);
return val;
}
/****************************************************************************
PARAMETERS:
port - Port to write to
val - Value to write to port
REMARKS:
Performs an emulated 8-bit write to an I/O port. We handle special cases
that we need to emulate in here, and fall through to reflecting the write
through to the real hardware if we don't need to special case it.
****************************************************************************/
void X86API BE_outb(X86EMU_pioAddr port, u8 val)
{
#if defined(DEBUG) || !defined(__i386__)
if (IS_VGA_PORT(port))
VGA_outpb(port, val);
else if (IS_TIMER_PORT(port))
DB(printf("Can not interept TIMER port now!\n");)
else if (IS_SPKR_PORT(port))
DB(printf("Can not interept SPEAKER port now!\n");)
else if (IS_CMOS_PORT(port))
DB(printf("Can not interept CMOS port now!\n");)
else if (IS_PCI_PORT(port))
PCI_outp(port, val, REG_WRITE_BYTE);
else if (port < 0x100) {
DB(printf("WARN:Maybe INVALID outb.%04X <- %02X\n", (u16) port, val);)
LOG_outpb(port, val);
} else
#endif
LOG_outpb(port, val);
}
/****************************************************************************
PARAMETERS:
port - Port to write to
val - Value to write to port
REMARKS:
Performs an emulated 16-bit write to an I/O port. We handle special cases
that we need to emulate in here, and fall through to reflecting the write
through to the real hardware if we don't need to special case it.
****************************************************************************/
void X86API BE_outw(X86EMU_pioAddr port, u16 val)
{
#if defined(DEBUG) || !defined(__i386__)
if (IS_VGA_PORT(port)) {
VGA_outpb(port, val);
VGA_outpb(port + 1, val >> 8);
} else if (IS_PCI_PORT(port))
PCI_outp(port, val, REG_WRITE_WORD);
else if (port < 0x100) {
DB(printf("WARN: MAybe INVALID outw.%04X <- %04X\n", (u16) port,
val);)
LOG_outpw(port, val);
} else
#endif
LOG_outpw(port, val);
}
/****************************************************************************
PARAMETERS:
port - Port to write to
val - Value to write to port
REMARKS:
Performs an emulated 32-bit write to an I/O port. We handle special cases
that we need to emulate in here, and fall through to reflecting the write
through to the real hardware if we don't need to special case it.
****************************************************************************/
void X86API BE_outl(X86EMU_pioAddr port, u32 val)
{
#if defined(DEBUG) || !defined(__i386__)
if (IS_PCI_PORT(port))
PCI_outp(port, val, REG_WRITE_DWORD);
else if (port < 0x100) {
DB(printf("WARN: INVALID outl.%04X <- %08X\n", (u16) port,val);)
LOG_outpd(port, val);
} else
#endif
LOG_outpd(port, val);
}

View file

@ -0,0 +1,322 @@
/****************************************************************************
*
* BIOS emulator and interface
* to Realmode X86 Emulator Library
*
* Copyright (C) 2007 Freescale Semiconductor, Inc.
* Jason Jin <Jason.jin@freescale.com>
*
* Copyright (C) 1996-1999 SciTech Software, Inc.
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Module implementing the BIOS specific functions.
*
* Jason ported this file to u-boot to run the ATI video card
* video BIOS.
*
****************************************************************************/
#include <common.h>
#include "biosemui.h"
/*----------------------------- Implementation ----------------------------*/
/****************************************************************************
PARAMETERS:
intno - Interrupt number being serviced
REMARKS:
Handler for undefined interrupts.
****************************************************************************/
static void X86API undefined_intr(int intno)
{
if (BE_rdw(intno * 4 + 2) == BIOS_SEG) {
DB(printf("biosEmu: undefined interrupt %xh called!\n", intno);)
} else
X86EMU_prepareForInt(intno);
}
/****************************************************************************
PARAMETERS:
intno - Interrupt number being serviced
REMARKS:
This function handles the default system BIOS Int 10h (the default is stored
in the Int 42h vector by the system BIOS at bootup). We only need to handle
a small number of special functions used by the BIOS during POST time.
****************************************************************************/
static void X86API int42(int intno)
{
if (M.x86.R_AH == 0x12 && M.x86.R_BL == 0x32) {
if (M.x86.R_AL == 0) {
/* Enable CPU accesses to video memory */
PM_outpb(0x3c2, PM_inpb(0x3cc) | (u8) 0x02);
return;
} else if (M.x86.R_AL == 1) {
/* Disable CPU accesses to video memory */
PM_outpb(0x3c2, PM_inpb(0x3cc) & (u8) ~ 0x02);
return;
}
#ifdef DEBUG
else {
printf("int42: unknown function AH=0x12, BL=0x32, AL=%#02x\n",
M.x86.R_AL);
}
#endif
}
#ifdef DEBUG
else {
printf("int42: unknown function AH=%#02x, AL=%#02x, BL=%#02x\n",
M.x86.R_AH, M.x86.R_AL, M.x86.R_BL);
}
#endif
}
/****************************************************************************
PARAMETERS:
intno - Interrupt number being serviced
REMARKS:
This function handles the default system BIOS Int 10h. If the POST code
has not yet re-vectored the Int 10h BIOS interrupt vector, we handle this
by simply calling the int42 interrupt handler above. Very early in the
BIOS POST process, the vector gets replaced and we simply let the real
mode interrupt handler process the interrupt.
****************************************************************************/
static void X86API int10(int intno)
{
if (BE_rdw(intno * 4 + 2) == BIOS_SEG)
int42(intno);
else
X86EMU_prepareForInt(intno);
}
/* Result codes returned by the PCI BIOS */
#define SUCCESSFUL 0x00
#define FUNC_NOT_SUPPORT 0x81
#define BAD_VENDOR_ID 0x83
#define DEVICE_NOT_FOUND 0x86
#define BAD_REGISTER_NUMBER 0x87
#define SET_FAILED 0x88
#define BUFFER_TOO_SMALL 0x89
/****************************************************************************
PARAMETERS:
intno - Interrupt number being serviced
REMARKS:
This function handles the default Int 1Ah interrupt handler for the real
mode code, which provides support for the PCI BIOS functions. Since we only
want to allow the real mode BIOS code *only* see the PCI config space for
its own device, we only return information for the specific PCI config
space that we have passed in to the init function. This solves problems
when using the BIOS to warm boot a secondary adapter when there is an
identical adapter before it on the bus (some BIOS'es get confused in this
case).
****************************************************************************/
static void X86API int1A(int unused)
{
u16 pciSlot;
#ifdef __KERNEL__
u8 interface, subclass, baseclass;
/* Initialise the PCI slot number */
pciSlot = ((int)_BE_env.vgaInfo.bus << 8) |
((int)_BE_env.vgaInfo.device << 3) | (int)_BE_env.vgaInfo.function;
#else
/* Fail if no PCI device information has been registered */
if (!_BE_env.vgaInfo.pciInfo)
return;
pciSlot = (u16) (_BE_env.vgaInfo.pciInfo->slot.i >> 8);
#endif
switch (M.x86.R_AX) {
case 0xB101: /* PCI bios present? */
M.x86.R_AL = 0x00; /* no config space/special cycle generation support */
M.x86.R_EDX = 0x20494350; /* " ICP" */
M.x86.R_BX = 0x0210; /* Version 2.10 */
M.x86.R_CL = 0; /* Max bus number in system */
CLEAR_FLAG(F_CF);
break;
case 0xB102: /* Find PCI device */
M.x86.R_AH = DEVICE_NOT_FOUND;
#ifdef __KERNEL__
if (M.x86.R_DX == _BE_env.vgaInfo.VendorID &&
M.x86.R_CX == _BE_env.vgaInfo.DeviceID && M.x86.R_SI == 0) {
#else
if (M.x86.R_DX == _BE_env.vgaInfo.pciInfo->VendorID &&
M.x86.R_CX == _BE_env.vgaInfo.pciInfo->DeviceID &&
M.x86.R_SI == 0) {
#endif
M.x86.R_AH = SUCCESSFUL;
M.x86.R_BX = pciSlot;
}
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
break;
case 0xB103: /* Find PCI class code */
M.x86.R_AH = DEVICE_NOT_FOUND;
#ifdef __KERNEL__
pci_read_config_byte(_BE_env.vgaInfo.pcidev, PCI_CLASS_PROG,
&interface);
pci_read_config_byte(_BE_env.vgaInfo.pcidev, PCI_CLASS_DEVICE,
&subclass);
pci_read_config_byte(_BE_env.vgaInfo.pcidev,
PCI_CLASS_DEVICE + 1, &baseclass);
if (M.x86.R_CL == interface && M.x86.R_CH == subclass
&& (u8) (M.x86.R_ECX >> 16) == baseclass) {
#else
if (M.x86.R_CL == _BE_env.vgaInfo.pciInfo->Interface &&
M.x86.R_CH == _BE_env.vgaInfo.pciInfo->SubClass &&
(u8) (M.x86.R_ECX >> 16) ==
_BE_env.vgaInfo.pciInfo->BaseClass) {
#endif
M.x86.R_AH = SUCCESSFUL;
M.x86.R_BX = pciSlot;
}
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
break;
case 0xB108: /* Read configuration byte */
M.x86.R_AH = BAD_REGISTER_NUMBER;
if (M.x86.R_BX == pciSlot) {
M.x86.R_AH = SUCCESSFUL;
#ifdef __KERNEL__
pci_read_config_byte(_BE_env.vgaInfo.pcidev, M.x86.R_DI,
&M.x86.R_CL);
#else
M.x86.R_CL =
(u8) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_BYTE,
_BE_env.vgaInfo.pciInfo);
#endif
}
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
break;
case 0xB109: /* Read configuration word */
M.x86.R_AH = BAD_REGISTER_NUMBER;
if (M.x86.R_BX == pciSlot) {
M.x86.R_AH = SUCCESSFUL;
#ifdef __KERNEL__
pci_read_config_word(_BE_env.vgaInfo.pcidev, M.x86.R_DI,
&M.x86.R_CX);
#else
M.x86.R_CX =
(u16) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_WORD,
_BE_env.vgaInfo.pciInfo);
#endif
}
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
break;
case 0xB10A: /* Read configuration dword */
M.x86.R_AH = BAD_REGISTER_NUMBER;
if (M.x86.R_BX == pciSlot) {
M.x86.R_AH = SUCCESSFUL;
#ifdef __KERNEL__
pci_read_config_dword(_BE_env.vgaInfo.pcidev,
M.x86.R_DI, &M.x86.R_ECX);
#else
M.x86.R_ECX =
(u32) PCI_accessReg(M.x86.R_DI, 0, PCI_READ_DWORD,
_BE_env.vgaInfo.pciInfo);
#endif
}
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
break;
case 0xB10B: /* Write configuration byte */
M.x86.R_AH = BAD_REGISTER_NUMBER;
if (M.x86.R_BX == pciSlot) {
M.x86.R_AH = SUCCESSFUL;
#ifdef __KERNEL__
pci_write_config_byte(_BE_env.vgaInfo.pcidev,
M.x86.R_DI, M.x86.R_CL);
#else
PCI_accessReg(M.x86.R_DI, M.x86.R_CL, PCI_WRITE_BYTE,
_BE_env.vgaInfo.pciInfo);
#endif
}
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
break;
case 0xB10C: /* Write configuration word */
M.x86.R_AH = BAD_REGISTER_NUMBER;
if (M.x86.R_BX == pciSlot) {
M.x86.R_AH = SUCCESSFUL;
#ifdef __KERNEL__
pci_write_config_word(_BE_env.vgaInfo.pcidev,
M.x86.R_DI, M.x86.R_CX);
#else
PCI_accessReg(M.x86.R_DI, M.x86.R_CX, PCI_WRITE_WORD,
_BE_env.vgaInfo.pciInfo);
#endif
}
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
break;
case 0xB10D: /* Write configuration dword */
M.x86.R_AH = BAD_REGISTER_NUMBER;
if (M.x86.R_BX == pciSlot) {
M.x86.R_AH = SUCCESSFUL;
#ifdef __KERNEL__
pci_write_config_dword(_BE_env.vgaInfo.pcidev,
M.x86.R_DI, M.x86.R_ECX);
#else
PCI_accessReg(M.x86.R_DI, M.x86.R_ECX, PCI_WRITE_DWORD,
_BE_env.vgaInfo.pciInfo);
#endif
}
CONDITIONAL_SET_FLAG((M.x86.R_AH != SUCCESSFUL), F_CF);
break;
default:
printf("biosEmu/bios.int1a: unknown function AX=%#04x\n",
M.x86.R_AX);
}
}
/****************************************************************************
REMARKS:
This function initialises the BIOS emulation functions for the specific
PCI display device. We insulate the real mode BIOS from any other devices
on the bus, so that it will work correctly thinking that it is the only
device present on the bus (ie: avoiding any adapters present in from of
the device we are trying to control).
****************************************************************************/
#define BE_constLE_32(v) ((((((v)&0xff00)>>8)|(((v)&0xff)<<8))<<16)|(((((v)&0xff000000)>>8)|(((v)&0x00ff0000)<<8))>>16))
void _BE_bios_init(u32 * intrTab)
{
int i;
X86EMU_intrFuncs bios_intr_tab[256];
for (i = 0; i < 256; ++i) {
intrTab[i] = BE_constLE_32(BIOS_SEG << 16);
bios_intr_tab[i] = undefined_intr;
}
bios_intr_tab[0x10] = int10;
bios_intr_tab[0x1A] = int1A;
bios_intr_tab[0x42] = int42;
bios_intr_tab[0x6D] = int10;
X86EMU_setupIntrFuncs(bios_intr_tab);
}

View file

@ -0,0 +1,372 @@
/****************************************************************************
*
* BIOS emulator and interface
* to Realmode X86 Emulator Library
*
* Copyright (C) 2007 Freescale Semiconductor, Inc.
* Jason Jin <Jason.jin@freescale.com>
*
* Copyright (C) 1996-1999 SciTech Software, Inc.
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Module implementing the system specific functions. This
* module is always compiled and linked in the OS depedent
* libraries, and never in a binary portable driver.
*
* Jason ported this file to u-boot to run the ATI video card BIOS
* in u-boot. Made all the video memory be emulated during the
* BIOS runing process which may affect the VGA function but the
* frambuffer function can work after run the BIOS.
*
****************************************************************************/
#include <malloc.h>
#include <common.h>
#include "biosemui.h"
BE_sysEnv _BE_env = {{0}};
static X86EMU_memFuncs _BE_mem __attribute__((section(GOT2_TYPE))) = {
BE_rdb,
BE_rdw,
BE_rdl,
BE_wrb,
BE_wrw,
BE_wrl,
};
static X86EMU_pioFuncs _BE_pio __attribute__((section(GOT2_TYPE))) = {
BE_inb,
BE_inw,
BE_inl,
BE_outb,
BE_outw,
BE_outl,
};
#define OFF(addr) (u16)(((addr) >> 0) & 0xffff)
#define SEG(addr) (u16)(((addr) >> 4) & 0xf000)
/****************************************************************************
PARAMETERS:
debugFlags - Flags to enable debugging options (debug builds only)
memSize - Amount of memory to allocate for real mode machine
info - Pointer to default VGA device information
REMARKS:
This functions initialises the BElib, and uses the passed in
BIOS image as the BIOS that is used and emulated at 0xC0000.
****************************************************************************/
int X86API BE_init(u32 debugFlags, int memSize, BE_VGAInfo * info, int shared)
{
#if !defined(__DRIVER__) && !defined(__KERNEL__)
PM_init();
#endif
memset(&M, 0, sizeof(M));
if (memSize < 20480){
printf("Emulator requires at least 20Kb of memory!\n");
return 0;
}
M.mem_base = malloc(memSize);
if (M.mem_base == NULL){
printf("Biosemu:Out of memory!");
return 0;
}
M.mem_size = memSize;
_BE_env.emulateVGA = 0;
_BE_env.busmem_base = (unsigned long)malloc(128 * 1024);
if ((void *)_BE_env.busmem_base == NULL){
printf("Biosemu:Out of memory!");
return 0;
}
M.x86.debug = debugFlags;
_BE_bios_init((u32*)info->LowMem);
X86EMU_setupMemFuncs(&_BE_mem);
X86EMU_setupPioFuncs(&_BE_pio);
BE_setVGA(info);
return 1;
}
/****************************************************************************
PARAMETERS:
info - Pointer to VGA device information to make current
REMARKS:
This function sets the VGA BIOS functions in the emulator to point to the
specific VGA BIOS in use. This includes swapping the BIOS interrupt
vectors, BIOS image and BIOS data area to the new BIOS. This allows the
real mode BIOS to be swapped without resetting the entire emulator.
****************************************************************************/
void X86API BE_setVGA(BE_VGAInfo * info)
{
#ifdef __KERNEL__
_BE_env.vgaInfo.function = info->function;
_BE_env.vgaInfo.device = info->device;
_BE_env.vgaInfo.bus = info->bus;
_BE_env.vgaInfo.pcidev = info->pcidev;
#else
_BE_env.vgaInfo.pciInfo = info->pciInfo;
#endif
_BE_env.vgaInfo.BIOSImage = info->BIOSImage;
if (info->BIOSImage) {
_BE_env.biosmem_base = (ulong) info->BIOSImage;
_BE_env.biosmem_limit = 0xC0000 + info->BIOSImageLen - 1;
} else {
_BE_env.biosmem_base = _BE_env.busmem_base + 0x20000;
_BE_env.biosmem_limit = 0xC7FFF;
}
if ((info->LowMem[0] == 0) && (info->LowMem[1] == 0) &&
(info->LowMem[2] == 0) && (info->LowMem[3] == 0))
_BE_bios_init((u32 *) info->LowMem);
memcpy((u8 *) M.mem_base, info->LowMem, sizeof(info->LowMem));
}
/****************************************************************************
PARAMETERS:
info - Pointer to VGA device information to retrieve current
REMARKS:
This function returns the VGA BIOS functions currently active in the
emulator, so they can be restored at a later date.
****************************************************************************/
void X86API BE_getVGA(BE_VGAInfo * info)
{
#ifdef __KERNEL__
info->function = _BE_env.vgaInfo.function;
info->device = _BE_env.vgaInfo.device;
info->bus = _BE_env.vgaInfo.bus;
info->pcidev = _BE_env.vgaInfo.pcidev;
#else
info->pciInfo = _BE_env.vgaInfo.pciInfo;
#endif
info->BIOSImage = _BE_env.vgaInfo.BIOSImage;
memcpy(info->LowMem, (u8 *) M.mem_base, sizeof(info->LowMem));
}
/****************************************************************************
PARAMETERS:
r_seg - Segment for pointer to convert
r_off - Offset for pointer to convert
REMARKS:
This function maps a real mode pointer in the emulator memory to a protected
mode pointer that can be used to directly access the memory.
NOTE: The memory is *always* in little endian format, son on non-x86
systems you will need to do endian translations to access this
memory.
****************************************************************************/
void *X86API BE_mapRealPointer(uint r_seg, uint r_off)
{
u32 addr = ((u32) r_seg << 4) + r_off;
if (addr >= 0xC0000 && addr <= _BE_env.biosmem_limit) {
return (void *)(_BE_env.biosmem_base + addr - 0xC0000);
} else if (addr >= 0xA0000 && addr <= 0xFFFFF) {
return (void *)(_BE_env.busmem_base + addr - 0xA0000);
}
return (void *)(M.mem_base + addr);
}
/****************************************************************************
PARAMETERS:
len - Return the length of the VESA buffer
rseg - Place to store VESA buffer segment
roff - Place to store VESA buffer offset
REMARKS:
This function returns the address of the VESA transfer buffer in real
_BE_piomode emulator memory. The VESA transfer buffer is always 1024 bytes long,
and located at 15Kb into the start of the real mode memory (16Kb is where
we put the real mode code we execute for issuing interrupts).
NOTE: The memory is *always* in little endian format, son on non-x86
systems you will need to do endian translations to access this
memory.
****************************************************************************/
void *X86API BE_getVESABuf(uint * len, uint * rseg, uint * roff)
{
*len = 1024;
*rseg = SEG(0x03C00);
*roff = OFF(0x03C00);
return (void *)(M.mem_base + ((u32) * rseg << 4) + *roff);
}
/****************************************************************************
REMARKS:
Cleans up and exits the emulator.
****************************************************************************/
void X86API BE_exit(void)
{
free(M.mem_base);
free((void *)_BE_env.busmem_base);
}
/****************************************************************************
PARAMETERS:
seg - Segment of code to call
off - Offset of code to call
regs - Real mode registers to load
sregs - Real mode segment registers to load
REMARKS:
This functions calls a real mode far function at the specified address,
and loads all the x86 registers from the passed in registers structure.
On exit the registers returned from the call are returned in the same
structures.
****************************************************************************/
void X86API BE_callRealMode(uint seg, uint off, RMREGS * regs, RMSREGS * sregs)
{
M.x86.R_EAX = regs->e.eax;
M.x86.R_EBX = regs->e.ebx;
M.x86.R_ECX = regs->e.ecx;
M.x86.R_EDX = regs->e.edx;
M.x86.R_ESI = regs->e.esi;
M.x86.R_EDI = regs->e.edi;
M.x86.R_DS = sregs->ds;
M.x86.R_ES = sregs->es;
M.x86.R_FS = sregs->fs;
M.x86.R_GS = sregs->gs;
((u8 *) M.mem_base)[0x4000] = 0x9A;
((u8 *) M.mem_base)[0x4001] = (u8) off;
((u8 *) M.mem_base)[0x4002] = (u8) (off >> 8);
((u8 *) M.mem_base)[0x4003] = (u8) seg;
((u8 *) M.mem_base)[0x4004] = (u8) (seg >> 8);
((u8 *) M.mem_base)[0x4005] = 0xF1; /* Illegal op-code */
M.x86.R_CS = SEG(0x04000);
M.x86.R_IP = OFF(0x04000);
M.x86.R_SS = SEG(M.mem_size - 2);
M.x86.R_SP = OFF(M.mem_size - 2) + 2;
X86EMU_exec();
regs->e.cflag = M.x86.R_EFLG & F_CF;
regs->e.eax = M.x86.R_EAX;
regs->e.ebx = M.x86.R_EBX;
regs->e.ecx = M.x86.R_ECX;
regs->e.edx = M.x86.R_EDX;
regs->e.esi = M.x86.R_ESI;
regs->e.edi = M.x86.R_EDI;
sregs->ds = M.x86.R_DS;
sregs->es = M.x86.R_ES;
sregs->fs = M.x86.R_FS;
sregs->gs = M.x86.R_GS;
}
/****************************************************************************
PARAMETERS:
intno - Interrupt number to execute
in - Real mode registers to load
out - Place to store resulting real mode registers
REMARKS:
This functions calls a real mode interrupt function at the specified address,
and loads all the x86 registers from the passed in registers structure.
On exit the registers returned from the call are returned in out stucture.
****************************************************************************/
int X86API BE_int86(int intno, RMREGS * in, RMREGS * out)
{
M.x86.R_EAX = in->e.eax;
M.x86.R_EBX = in->e.ebx;
M.x86.R_ECX = in->e.ecx;
M.x86.R_EDX = in->e.edx;
M.x86.R_ESI = in->e.esi;
M.x86.R_EDI = in->e.edi;
((u8 *) M.mem_base)[0x4000] = 0xCD;
((u8 *) M.mem_base)[0x4001] = (u8) intno;
((u8 *) M.mem_base)[0x4002] = 0xF1;
M.x86.R_CS = SEG(0x04000);
M.x86.R_IP = OFF(0x04000);
M.x86.R_SS = SEG(M.mem_size - 1);
M.x86.R_SP = OFF(M.mem_size - 1) - 1;
X86EMU_exec();
out->e.cflag = M.x86.R_EFLG & F_CF;
out->e.eax = M.x86.R_EAX;
out->e.ebx = M.x86.R_EBX;
out->e.ecx = M.x86.R_ECX;
out->e.edx = M.x86.R_EDX;
out->e.esi = M.x86.R_ESI;
out->e.edi = M.x86.R_EDI;
return out->x.ax;
}
/****************************************************************************
PARAMETERS:
intno - Interrupt number to execute
in - Real mode registers to load
out - Place to store resulting real mode registers
sregs - Real mode segment registers to load
REMARKS:
This functions calls a real mode interrupt function at the specified address,
and loads all the x86 registers from the passed in registers structure.
On exit the registers returned from the call are returned in out stucture.
****************************************************************************/
int X86API BE_int86x(int intno, RMREGS * in, RMREGS * out, RMSREGS * sregs)
{
M.x86.R_EAX = in->e.eax;
M.x86.R_EBX = in->e.ebx;
M.x86.R_ECX = in->e.ecx;
M.x86.R_EDX = in->e.edx;
M.x86.R_ESI = in->e.esi;
M.x86.R_EDI = in->e.edi;
M.x86.R_DS = sregs->ds;
M.x86.R_ES = sregs->es;
M.x86.R_FS = sregs->fs;
M.x86.R_GS = sregs->gs;
((u8 *) M.mem_base)[0x4000] = 0xCD;
((u8 *) M.mem_base)[0x4001] = (u8) intno;
((u8 *) M.mem_base)[0x4002] = 0xF1;
M.x86.R_CS = SEG(0x04000);
M.x86.R_IP = OFF(0x04000);
M.x86.R_SS = SEG(M.mem_size - 1);
M.x86.R_SP = OFF(M.mem_size - 1) - 1;
X86EMU_exec();
out->e.cflag = M.x86.R_EFLG & F_CF;
out->e.eax = M.x86.R_EAX;
out->e.ebx = M.x86.R_EBX;
out->e.ecx = M.x86.R_ECX;
out->e.edx = M.x86.R_EDX;
out->e.esi = M.x86.R_ESI;
out->e.edi = M.x86.R_EDI;
sregs->ds = M.x86.R_DS;
sregs->es = M.x86.R_ES;
sregs->fs = M.x86.R_FS;
sregs->gs = M.x86.R_GS;
return out->x.ax;
}

View file

@ -0,0 +1,169 @@
/****************************************************************************
*
* BIOS emulator and interface
* to Realmode X86 Emulator Library
*
* Copyright (C) 2007 Freescale Semiconductor, Inc.
* Jason Jin <Jason.jin@freescale.com>
*
* Copyright (C) 1996-1999 SciTech Software, Inc.
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Internal header file for the BIOS emulator library.
*
* Jason ported this file to u-boot, Added some architecture
* related Macro.
*
****************************************************************************/
#ifndef __BIOSEMUI_H
#define __BIOSEMUI_H
#include "biosemu.h"
#include <asm/io.h>
/*---------------------- Macros and type definitions ----------------------*/
#ifdef DEBUG
#define DB(x) x
#else
#define DB(x) do{}while(0);
#endif
#define BIOS_SEG 0xfff0
extern X86EMU_sysEnv _X86EMU_env;
#define M _X86EMU_env
/* Macros to read and write values to x86 emulator memory. Memory is always
* considered to be little endian, so we use macros to do endian swapping
* where necessary.
*/
#ifdef __BIG_ENDIAN__
#define readb_le(base) *((u8*)(base))
#define readw_le(base) ((u16)readb_le(base) | ((u16)readb_le((base) + 1) << 8))
#define readl_le(base) ((u32)readb_le((base) + 0) | ((u32)readb_le((base) + 1) << 8) | \
((u32)readb_le((base) + 2) << 16) | ((u32)readb_le((base) + 3) << 24))
#define writeb_le(base, v) *((u8*)(base)) = (v)
#define writew_le(base, v) writeb_le(base + 0, (v >> 0) & 0xff), \
writeb_le(base + 1, (v >> 8) & 0xff)
#define writel_le(base, v) writeb_le(base + 0, (v >> 0) & 0xff), \
writeb_le(base + 1, (v >> 8) & 0xff), \
writeb_le(base + 2, (v >> 16) & 0xff), \
writeb_le(base + 3, (v >> 24) & 0xff)
#else
#define readb_le(base) *((u8*)(base))
#define readw_le(base) *((u16*)(base))
#define readl_le(base) *((u32*)(base))
#define writeb_le(base, v) *((u8*)(base)) = (v)
#define writew_le(base, v) *((u16*)(base)) = (v)
#define writel_le(base, v) *((u32*)(base)) = (v)
#endif
/****************************************************************************
REMARKS:
Function codes passed to the emulated I/O port functions to determine the
type of operation to perform.
****************************************************************************/
typedef enum {
REG_READ_BYTE = 0,
REG_READ_WORD = 1,
REG_READ_DWORD = 2,
REG_WRITE_BYTE = 3,
REG_WRITE_WORD = 4,
REG_WRITE_DWORD = 5
} RegisterFlags;
/****************************************************************************
REMARKS:
Function codes passed to the emulated I/O port functions to determine the
type of operation to perform.
****************************************************************************/
typedef enum {
PORT_BYTE = 1,
PORT_WORD = 2,
PORT_DWORD = 3,
} PortInfoFlags;
/****************************************************************************
REMARKS:
Data structure used to describe the details for the BIOS emulator system
environment as used by the X86 emulator library.
HEADER:
biosemu.h
MEMBERS:
type - Type of port access (1 = byte, 2 = word, 3 = dword)
defVal - Default power on value
finalVal - Final value
****************************************************************************/
typedef struct {
u8 type;
u32 defVal;
u32 finalVal;
} BE_portInfo;
#define PM_inpb(port) inb(port+VIDEO_IO_OFFSET)
#define PM_inpw(port) inw(port+VIDEO_IO_OFFSET)
#define PM_inpd(port) inl(port+VIDEO_IO_OFFSET)
#define PM_outpb(port,val) outb(val,port+VIDEO_IO_OFFSET)
#define PM_outpw(port,val) outw(val,port+VIDEO_IO_OFFSET)
#define PM_outpd(port,val) outl(val,port+VIDEO_IO_OFFSET)
#define LOG_inpb(port) PM_inpb(port)
#define LOG_inpw(port) PM_inpw(port)
#define LOG_inpd(port) PM_inpd(port)
#define LOG_outpb(port,val) PM_outpb(port,val)
#define LOG_outpw(port,val) PM_outpw(port,val)
#define LOG_outpd(port,val) PM_outpd(port,val)
/*-------------------------- Function Prototypes --------------------------*/
/* bios.c */
void _BE_bios_init(u32 * intrTab);
void _BE_setup_funcs(void);
/* besys.c */
#define DEBUG_IO() (M.x86.debug & DEBUG_IO_TRACE_F)
u8 X86API BE_rdb(u32 addr);
u16 X86API BE_rdw(u32 addr);
u32 X86API BE_rdl(u32 addr);
void X86API BE_wrb(u32 addr, u8 val);
void X86API BE_wrw(u32 addr, u16 val);
void X86API BE_wrl(u32 addr, u32 val);
u8 X86API BE_inb(X86EMU_pioAddr port);
u16 X86API BE_inw(X86EMU_pioAddr port);
u32 X86API BE_inl(X86EMU_pioAddr port);
void X86API BE_outb(X86EMU_pioAddr port, u8 val);
void X86API BE_outw(X86EMU_pioAddr port, u16 val);
void X86API BE_outl(X86EMU_pioAddr port, u32 val);
#endif
/* __BIOSEMUI_H */

View file

@ -0,0 +1,392 @@
/****************************************************************************
*
* BIOS emulator and interface
* to Realmode X86 Emulator Library
*
* Copyright (C) 1996-1999 SciTech Software, Inc.
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Header file for the real mode x86 BIOS emulator, which is
* used to warmboot any number of VGA compatible PCI/AGP
* controllers under any OS, on any processor family that
* supports PCI. We also allow the user application to call
* real mode BIOS functions and Int 10h functions (including
* the VESA BIOS).
*
****************************************************************************/
#ifndef __BIOSEMU_H
#define __BIOSEMU_H
#ifdef __KERNEL__
#include "x86emu.h"
#else
#include "x86emu.h"
#include "pmapi.h"
#include "pcilib.h"
#endif
/*---------------------- Macros and type definitions ----------------------*/
#pragma pack(1)
#ifndef __KERNEL__
/****************************************************************************
REMARKS:
Data structure used to describe the details specific to a particular VGA
controller. This information is used to allow the VGA controller to be
swapped on the fly within the BIOS emulator.
HEADER:
biosemu.h
MEMBERS:
pciInfo - PCI device information block for the controller
BIOSImage - Pointer to a read/write copy of the BIOS image
BIOSImageLen - Length of the BIOS image
LowMem - Copy of key low memory areas
****************************************************************************/
typedef struct {
PCIDeviceInfo *pciInfo;
void *BIOSImage;
ulong BIOSImageLen;
uchar LowMem[1536];
} BE_VGAInfo;
#else
/****************************************************************************
REMARKS:
Data structure used to describe the details for the BIOS emulator system
environment as used by the X86 emulator library.
HEADER:
biosemu.h
MEMBERS:
vgaInfo - VGA BIOS information structure
biosmem_base - Base of the BIOS image
biosmem_limit - Limit of the BIOS image
busmem_base - Base of the VGA bus memory
****************************************************************************/
typedef struct {
int function;
int device;
int bus;
u32 VendorID;
u32 DeviceID;
pci_dev_t pcidev;
void *BIOSImage;
u32 BIOSImageLen;
u8 LowMem[1536];
} BE_VGAInfo;
#endif /* __KERNEL__ */
#define CRT_C 24 /* 24 CRT Controller Registers */
#define ATT_C 21 /* 21 Attribute Controller Registers */
#define GRA_C 9 /* 9 Graphics Controller Registers */
#define SEQ_C 5 /* 5 Sequencer Registers */
#define PAL_C 768 /* 768 Palette Registers */
/****************************************************************************
REMARKS:
Data structure used to describe the details for the BIOS emulator system
environment as used by the X86 emulator library.
HEADER:
biosemu.h
MEMBERS:
vgaInfo - VGA BIOS information structure
biosmem_base - Base of the BIOS image
biosmem_limit - Limit of the BIOS image
busmem_base - Base of the VGA bus memory
timer - Timer used to emulate PC timer ports
timer0 - Latched value for timer 0
timer0Latched - True if timer 0 value was just latched
timer2 - Current value for timer 2
emulateVGA - True to emulate VGA I/O and memory accesses
****************************************************************************/
typedef struct {
BE_VGAInfo vgaInfo;
ulong biosmem_base;
ulong biosmem_limit;
ulong busmem_base;
u32 timer0;
int timer0Latched;
u32 timer1;
int timer1Latched;
u32 timer2;
int timer2Latched;
int emulateVGA;
u8 emu61;
u8 emu70;
int flipFlop3C0;
u32 configAddress;
u8 emu3C0;
u8 emu3C1[ATT_C];
u8 emu3C2;
u8 emu3C4;
u8 emu3C5[SEQ_C];
u8 emu3C6;
uint emu3C7;
uint emu3C8;
u8 emu3C9[PAL_C];
u8 emu3CE;
u8 emu3CF[GRA_C];
u8 emu3D4;
u8 emu3D5[CRT_C];
u8 emu3DA;
} BE_sysEnv;
#ifdef __KERNEL__
/* Define some types when compiling for the Linux kernel that normally
* come from the SciTech PM library.
*/
/****************************************************************************
REMARKS:
Structure describing the 32-bit extended x86 CPU registers
HEADER:
pmapi.h
MEMBERS:
eax - Value of the EAX register
ebx - Value of the EBX register
ecx - Value of the ECX register
edx - Value of the EDX register
esi - Value of the ESI register
edi - Value of the EDI register
cflag - Value of the carry flag
****************************************************************************/
typedef struct {
u32 eax;
u32 ebx;
u32 ecx;
u32 edx;
u32 esi;
u32 edi;
u32 cflag;
} RMDWORDREGS;
/****************************************************************************
REMARKS:
Structure describing the 16-bit x86 CPU registers
HEADER:
pmapi.h
MEMBERS:
ax - Value of the AX register
bx - Value of the BX register
cx - Value of the CX register
dx - Value of the DX register
si - Value of the SI register
di - Value of the DI register
cflag - Value of the carry flag
****************************************************************************/
#ifdef __BIG_ENDIAN__
typedef struct {
u16 ax_hi, ax;
u16 bx_hi, bx;
u16 cx_hi, cx;
u16 dx_hi, dx;
u16 si_hi, si;
u16 di_hi, di;
u16 cflag_hi, cflag;
} RMWORDREGS;
#else
typedef struct {
u16 ax, ax_hi;
u16 bx, bx_hi;
u16 cx, cx_hi;
u16 dx, dx_hi;
u16 si, si_hi;
u16 di, di_hi;
u16 cflag, cflag_hi;
} RMWORDREGS;
#endif
/****************************************************************************
REMARKS:
Structure describing the 8-bit x86 CPU registers
HEADER:
pmapi.h
MEMBERS:
al - Value of the AL register
ah - Value of the AH register
bl - Value of the BL register
bh - Value of the BH register
cl - Value of the CL register
ch - Value of the CH register
dl - Value of the DL register
dh - Value of the DH register
****************************************************************************/
#ifdef __BIG_ENDIAN__
typedef struct {
u16 ax_hi;
u8 ah, al;
u16 bx_hi;
u8 bh, bl;
u16 cx_hi;
u8 ch, cl;
u16 dx_hi;
u8 dh, dl;
} RMBYTEREGS;
#else
typedef struct {
u8 al;
u8 ah;
u16 ax_hi;
u8 bl;
u8 bh;
u16 bx_hi;
u8 cl;
u8 ch;
u16 cx_hi;
u8 dl;
u8 dh;
u16 dx_hi;
} RMBYTEREGS;
#endif
/****************************************************************************
REMARKS:
Structure describing all the x86 CPU registers
HEADER:
pmapi.h
MEMBERS:
e - Member to access registers as 32-bit values
x - Member to access registers as 16-bit values
h - Member to access registers as 8-bit values
****************************************************************************/
typedef union {
RMDWORDREGS e;
RMWORDREGS x;
RMBYTEREGS h;
} RMREGS;
/****************************************************************************
REMARKS:
Structure describing all the x86 segment registers
HEADER:
pmapi.h
MEMBERS:
es - ES segment register
cs - CS segment register
ss - SS segment register
ds - DS segment register
fs - FS segment register
gs - GS segment register
****************************************************************************/
typedef struct {
u16 es;
u16 cs;
u16 ss;
u16 ds;
u16 fs;
u16 gs;
} RMSREGS;
#endif /* __KERNEL__ */
#ifndef __KERNEL__
/****************************************************************************
REMARKS:
Structure defining all the BIOS Emulator API functions as exported from
the Binary Portable DLL.
{secret}
****************************************************************************/
typedef struct {
ulong dwSize;
ibool(PMAPIP BE_init) (u32 debugFlags, int memSize, BE_VGAInfo * info);
void (PMAPIP BE_setVGA) (BE_VGAInfo * info);
void (PMAPIP BE_getVGA) (BE_VGAInfo * info);
void *(PMAPIP BE_mapRealPointer) (uint r_seg, uint r_off);
void *(PMAPIP BE_getVESABuf) (uint * len, uint * rseg, uint * roff);
void (PMAPIP BE_callRealMode) (uint seg, uint off, RMREGS * regs,
RMSREGS * sregs);
int (PMAPIP BE_int86) (int intno, RMREGS * in, RMREGS * out);
int (PMAPIP BE_int86x) (int intno, RMREGS * in, RMREGS * out,
RMSREGS * sregs);
void *reserved1;
void (PMAPIP BE_exit) (void);
} BE_exports;
/****************************************************************************
REMARKS:
Function pointer type for the Binary Portable DLL initialisation entry point.
{secret}
****************************************************************************/
typedef BE_exports *(PMAPIP BE_initLibrary_t) (PM_imports * PMImp);
#endif
#pragma pack()
/*---------------------------- Global variables ---------------------------*/
#ifdef __cplusplus
extern "C" { /* Use "C" linkage when in C++ mode */
#endif
/* {secret} Global BIOS emulator system environment */
extern BE_sysEnv _BE_env;
/*-------------------------- Function Prototypes --------------------------*/
/* BIOS emulator library entry points */
int X86API BE_init(u32 debugFlags, int memSize, BE_VGAInfo * info,
int shared);
void X86API BE_setVGA(BE_VGAInfo * info);
void X86API BE_getVGA(BE_VGAInfo * info);
void X86API BE_setDebugFlags(u32 debugFlags);
void *X86API BE_mapRealPointer(uint r_seg, uint r_off);
void *X86API BE_getVESABuf(uint * len, uint * rseg, uint * roff);
void X86API BE_callRealMode(uint seg, uint off, RMREGS * regs,
RMSREGS * sregs);
int X86API BE_int86(int intno, RMREGS * in, RMREGS * out);
int X86API BE_int86x(int intno, RMREGS * in, RMREGS * out,
RMSREGS * sregs);
void X86API BE_exit(void);
#ifdef __cplusplus
} /* End of "C" linkage for C++ */
#endif
#endif /* __BIOSEMU_H */

View file

@ -0,0 +1,201 @@
/****************************************************************************
*
* Realmode X86 Emulator Library
*
* Copyright (C) 1996-1999 SciTech Software, Inc.
* Copyright (C) David Mosberger-Tang
* Copyright (C) 1999 Egbert Eich
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Header file for public specific functions.
* Any application linking against us should only
* include this header
*
****************************************************************************/
#ifndef __X86EMU_X86EMU_H
#define __X86EMU_X86EMU_H
#include <asm/types.h>
#include <common.h>
#include <pci.h>
#include <asm/io.h>
#define X86API
#define X86APIP *
typedef u16 X86EMU_pioAddr;
#include "x86emu/regs.h"
/*---------------------- Macros and type definitions ----------------------*/
#if defined (CONFIG_ARM)
#define GAS_LINE_COMMENT "@"
#elif defined(CONFIG_MIPS) || defined(CONFIG_PPC)
#define GAS_LINE_COMMENT "#"
#elif defined (CONFIG_SH)
#define GAS_LINE_COMMENT "!"
#endif
#define GOT2_TYPE ".got2,\"aw\"\t"GAS_LINE_COMMENT
#pragma pack(1)
/****************************************************************************
REMARKS:
Data structure containing ponters to programmed I/O functions used by the
emulator. This is used so that the user program can hook all programmed
I/O for the emulator to handled as necessary by the user program. By
default the emulator contains simple functions that do not do access the
hardware in any way. To allow the emualtor access the hardware, you will
need to override the programmed I/O functions using the X86EMU_setupPioFuncs
function.
HEADER:
x86emu.h
MEMBERS:
inb - Function to read a byte from an I/O port
inw - Function to read a word from an I/O port
inl - Function to read a dword from an I/O port
outb - Function to write a byte to an I/O port
outw - Function to write a word to an I/O port
outl - Function to write a dword to an I/O port
****************************************************************************/
typedef struct {
u8(X86APIP inb) (X86EMU_pioAddr addr);
u16(X86APIP inw) (X86EMU_pioAddr addr);
u32(X86APIP inl) (X86EMU_pioAddr addr);
void (X86APIP outb) (X86EMU_pioAddr addr, u8 val);
void (X86APIP outw) (X86EMU_pioAddr addr, u16 val);
void (X86APIP outl) (X86EMU_pioAddr addr, u32 val);
} X86EMU_pioFuncs;
/****************************************************************************
REMARKS:
Data structure containing ponters to memory access functions used by the
emulator. This is used so that the user program can hook all memory
access functions as necessary for the emulator. By default the emulator
contains simple functions that only access the internal memory of the
emulator. If you need specialised functions to handle access to different
types of memory (ie: hardware framebuffer accesses and BIOS memory access
etc), you will need to override this using the X86EMU_setupMemFuncs
function.
HEADER:
x86emu.h
MEMBERS:
rdb - Function to read a byte from an address
rdw - Function to read a word from an address
rdl - Function to read a dword from an address
wrb - Function to write a byte to an address
wrw - Function to write a word to an address
wrl - Function to write a dword to an address
****************************************************************************/
typedef struct {
u8(X86APIP rdb) (u32 addr);
u16(X86APIP rdw) (u32 addr);
u32(X86APIP rdl) (u32 addr);
void (X86APIP wrb) (u32 addr, u8 val);
void (X86APIP wrw) (u32 addr, u16 val);
void (X86APIP wrl) (u32 addr, u32 val);
} X86EMU_memFuncs;
/****************************************************************************
Here are the default memory read and write
function in case they are needed as fallbacks.
***************************************************************************/
extern u8 X86API rdb(u32 addr);
extern u16 X86API rdw(u32 addr);
extern u32 X86API rdl(u32 addr);
extern void X86API wrb(u32 addr, u8 val);
extern void X86API wrw(u32 addr, u16 val);
extern void X86API wrl(u32 addr, u32 val);
#pragma pack()
/*--------------------- type definitions -----------------------------------*/
typedef void (X86APIP X86EMU_intrFuncs) (int num);
extern X86EMU_intrFuncs _X86EMU_intrTab[256];
/*-------------------------- Function Prototypes --------------------------*/
#ifdef __cplusplus
extern "C" { /* Use "C" linkage when in C++ mode */
#endif
void X86EMU_setupMemFuncs(X86EMU_memFuncs * funcs);
void X86EMU_setupPioFuncs(X86EMU_pioFuncs * funcs);
void X86EMU_setupIntrFuncs(X86EMU_intrFuncs funcs[]);
void X86EMU_prepareForInt(int num);
/* decode.c */
void X86EMU_exec(void);
void X86EMU_halt_sys(void);
#ifdef DEBUG
#define HALT_SYS() \
printf("halt_sys: file %s, line %d\n", __FILE__, __LINE__), \
X86EMU_halt_sys()
#else
#define HALT_SYS() X86EMU_halt_sys()
#endif
/* Debug options */
#define DEBUG_DECODE_F 0x0001 /* print decoded instruction */
#define DEBUG_TRACE_F 0x0002 /* dump regs before/after execution */
#define DEBUG_STEP_F 0x0004
#define DEBUG_DISASSEMBLE_F 0x0008
#define DEBUG_BREAK_F 0x0010
#define DEBUG_SVC_F 0x0020
#define DEBUG_SAVE_CS_IP 0x0040
#define DEBUG_FS_F 0x0080
#define DEBUG_PROC_F 0x0100
#define DEBUG_SYSINT_F 0x0200 /* bios system interrupts. */
#define DEBUG_TRACECALL_F 0x0400
#define DEBUG_INSTRUMENT_F 0x0800
#define DEBUG_MEM_TRACE_F 0x1000
#define DEBUG_IO_TRACE_F 0x2000
#define DEBUG_TRACECALL_REGS_F 0x4000
#define DEBUG_DECODE_NOPRINT_F 0x8000
#define DEBUG_EXIT 0x10000
#define DEBUG_SYS_F (DEBUG_SVC_F|DEBUG_FS_F|DEBUG_PROC_F)
void X86EMU_trace_regs(void);
void X86EMU_trace_xregs(void);
void X86EMU_dump_memory(u16 seg, u16 off, u32 amt);
int X86EMU_trace_on(void);
int X86EMU_trace_off(void);
#ifdef __cplusplus
} /* End of "C" linkage for C++ */
#endif
#endif /* __X86EMU_X86EMU_H */

View file

@ -0,0 +1,209 @@
/****************************************************************************
*
* Realmode X86 Emulator Library
*
* Copyright (C) 1991-2004 SciTech Software, Inc.
* Copyright (C) David Mosberger-Tang
* Copyright (C) 1999 Egbert Eich
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Header file for debug definitions.
*
****************************************************************************/
#ifndef __X86EMU_DEBUG_H
#define __X86EMU_DEBUG_H
/*---------------------- Macros and type definitions ----------------------*/
/* checks to be enabled for "runtime" */
#define CHECK_IP_FETCH_F 0x1
#define CHECK_SP_ACCESS_F 0x2
#define CHECK_MEM_ACCESS_F 0x4 /*using regular linear pointer */
#define CHECK_DATA_ACCESS_F 0x8 /*using segment:offset */
#ifdef DEBUG
# define CHECK_IP_FETCH() (M.x86.check & CHECK_IP_FETCH_F)
# define CHECK_SP_ACCESS() (M.x86.check & CHECK_SP_ACCESS_F)
# define CHECK_MEM_ACCESS() (M.x86.check & CHECK_MEM_ACCESS_F)
# define CHECK_DATA_ACCESS() (M.x86.check & CHECK_DATA_ACCESS_F)
#else
# define CHECK_IP_FETCH()
# define CHECK_SP_ACCESS()
# define CHECK_MEM_ACCESS()
# define CHECK_DATA_ACCESS()
#endif
#ifdef DEBUG
# define DEBUG_INSTRUMENT() (M.x86.debug & DEBUG_INSTRUMENT_F)
# define DEBUG_DECODE() (M.x86.debug & DEBUG_DECODE_F)
# define DEBUG_TRACE() (M.x86.debug & DEBUG_TRACE_F)
# define DEBUG_STEP() (M.x86.debug & DEBUG_STEP_F)
# define DEBUG_DISASSEMBLE() (M.x86.debug & DEBUG_DISASSEMBLE_F)
# define DEBUG_BREAK() (M.x86.debug & DEBUG_BREAK_F)
# define DEBUG_SVC() (M.x86.debug & DEBUG_SVC_F)
# define DEBUG_SAVE_IP_CS() (M.x86.debug & DEBUG_SAVE_CS_IP)
# define DEBUG_FS() (M.x86.debug & DEBUG_FS_F)
# define DEBUG_PROC() (M.x86.debug & DEBUG_PROC_F)
# define DEBUG_SYSINT() (M.x86.debug & DEBUG_SYSINT_F)
# define DEBUG_TRACECALL() (M.x86.debug & DEBUG_TRACECALL_F)
# define DEBUG_TRACECALLREGS() (M.x86.debug & DEBUG_TRACECALL_REGS_F)
# define DEBUG_SYS() (M.x86.debug & DEBUG_SYS_F)
# define DEBUG_MEM_TRACE() (M.x86.debug & DEBUG_MEM_TRACE_F)
# define DEBUG_IO_TRACE() (M.x86.debug & DEBUG_IO_TRACE_F)
# define DEBUG_DECODE_NOPRINT() (M.x86.debug & DEBUG_DECODE_NOPRINT_F)
#else
# define DEBUG_INSTRUMENT() 0
# define DEBUG_DECODE() 0
# define DEBUG_TRACE() 0
# define DEBUG_STEP() 0
# define DEBUG_DISASSEMBLE() 0
# define DEBUG_BREAK() 0
# define DEBUG_SVC() 0
# define DEBUG_SAVE_IP_CS() 0
# define DEBUG_FS() 0
# define DEBUG_PROC() 0
# define DEBUG_SYSINT() 0
# define DEBUG_TRACECALL() 0
# define DEBUG_TRACECALLREGS() 0
# define DEBUG_SYS() 0
# define DEBUG_MEM_TRACE() 0
# define DEBUG_IO_TRACE() 0
# define DEBUG_DECODE_NOPRINT() 0
#endif
#ifdef DEBUG
# define DECODE_PRINTF(x) if (DEBUG_DECODE()) \
x86emu_decode_printf(x)
# define DECODE_PRINTF2(x,y) if (DEBUG_DECODE()) \
x86emu_decode_printf2(x,y)
/*
* The following allow us to look at the bytes of an instruction. The
* first INCR_INSTRN_LEN, is called everytime bytes are consumed in
* the decoding process. The SAVE_IP_CS is called initially when the
* major opcode of the instruction is accessed.
*/
#define INC_DECODED_INST_LEN(x) \
if (DEBUG_DECODE()) \
x86emu_inc_decoded_inst_len(x)
#define SAVE_IP_CS(x,y) \
if (DEBUG_DECODE() | DEBUG_TRACECALL() | DEBUG_BREAK() \
| DEBUG_IO_TRACE() | DEBUG_SAVE_IP_CS()) { \
M.x86.saved_cs = x; \
M.x86.saved_ip = y; \
}
#else
# define INC_DECODED_INST_LEN(x)
# define DECODE_PRINTF(x)
# define DECODE_PRINTF2(x,y)
# define SAVE_IP_CS(x,y)
#endif
#ifdef DEBUG
#define TRACE_REGS() \
if (DEBUG_DISASSEMBLE()) { \
x86emu_just_disassemble(); \
goto EndOfTheInstructionProcedure; \
} \
if (DEBUG_TRACE() || DEBUG_DECODE()) X86EMU_trace_regs()
#else
# define TRACE_REGS()
#endif
#ifdef DEBUG
# define SINGLE_STEP() if (DEBUG_STEP()) x86emu_single_step()
#else
# define SINGLE_STEP()
#endif
#define TRACE_AND_STEP() \
TRACE_REGS(); \
SINGLE_STEP()
#ifdef DEBUG
# define START_OF_INSTR()
# define END_OF_INSTR() EndOfTheInstructionProcedure: x86emu_end_instr();
# define END_OF_INSTR_NO_TRACE() x86emu_end_instr();
#else
# define START_OF_INSTR()
# define END_OF_INSTR()
# define END_OF_INSTR_NO_TRACE()
#endif
#ifdef DEBUG
# define CALL_TRACE(u,v,w,x,s) \
if (DEBUG_TRACECALLREGS()) \
x86emu_dump_regs(); \
if (DEBUG_TRACECALL()) \
printk("%04x:%04x: CALL %s%04x:%04x\n", u , v, s, w, x);
# define RETURN_TRACE(n,u,v) \
if (DEBUG_TRACECALLREGS()) \
x86emu_dump_regs(); \
if (DEBUG_TRACECALL()) \
printk("%04x:%04x: %s\n",u,v,n);
#else
# define CALL_TRACE(u,v,w,x,s)
# define RETURN_TRACE(n,u,v)
#endif
#ifdef DEBUG
#define DB(x) x
#else
#define DB(x)
#endif
/*-------------------------- Function Prototypes --------------------------*/
#ifdef __cplusplus
extern "C" { /* Use "C" linkage when in C++ mode */
#endif
extern void x86emu_inc_decoded_inst_len(int x);
extern void x86emu_decode_printf(char *x);
extern void x86emu_decode_printf2(char *x, int y);
extern void x86emu_just_disassemble(void);
extern void x86emu_single_step(void);
extern void x86emu_end_instr(void);
extern void x86emu_dump_regs(void);
extern void x86emu_dump_xregs(void);
extern void x86emu_print_int_vect(u16 iv);
extern void x86emu_instrument_instruction(void);
extern void x86emu_check_ip_access(void);
extern void x86emu_check_sp_access(void);
extern void x86emu_check_mem_access(u32 p);
extern void x86emu_check_data_access(uint s, uint o);
#ifdef __cplusplus
} /* End of "C" linkage for C++ */
#endif
#endif /* __X86EMU_DEBUG_H */

View file

@ -0,0 +1,88 @@
/****************************************************************************
*
* Realmode X86 Emulator Library
*
* Copyright (C) 1991-2004 SciTech Software, Inc.
* Copyright (C) David Mosberger-Tang
* Copyright (C) 1999 Egbert Eich
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Header file for instruction decoding logic.
*
****************************************************************************/
#ifndef __X86EMU_DECODE_H
#define __X86EMU_DECODE_H
/*---------------------- Macros and type definitions ----------------------*/
/* Instruction Decoding Stuff */
#define FETCH_DECODE_MODRM(mod,rh,rl) fetch_decode_modrm(&mod,&rh,&rl)
#define DECODE_RM_BYTE_REGISTER(r) decode_rm_byte_register(r)
#define DECODE_RM_WORD_REGISTER(r) decode_rm_word_register(r)
#define DECODE_RM_LONG_REGISTER(r) decode_rm_long_register(r)
#define DECODE_CLEAR_SEGOVR() M.x86.mode &= ~SYSMODE_CLRMASK
/*-------------------------- Function Prototypes --------------------------*/
#ifdef __cplusplus
extern "C" { /* Use "C" linkage when in C++ mode */
#endif
void x86emu_intr_raise (u8 type);
void fetch_decode_modrm (int *mod,int *regh,int *regl);
u8 fetch_byte_imm (void);
u16 fetch_word_imm (void);
u32 fetch_long_imm (void);
u8 fetch_data_byte (uint offset);
u8 fetch_data_byte_abs (uint segment, uint offset);
u16 fetch_data_word (uint offset);
u16 fetch_data_word_abs (uint segment, uint offset);
u32 fetch_data_long (uint offset);
u32 fetch_data_long_abs (uint segment, uint offset);
void store_data_byte (uint offset, u8 val);
void store_data_byte_abs (uint segment, uint offset, u8 val);
void store_data_word (uint offset, u16 val);
void store_data_word_abs (uint segment, uint offset, u16 val);
void store_data_long (uint offset, u32 val);
void store_data_long_abs (uint segment, uint offset, u32 val);
u8* decode_rm_byte_register(int reg);
u16* decode_rm_word_register(int reg);
u32* decode_rm_long_register(int reg);
u16* decode_rm_seg_register(int reg);
unsigned decode_rm00_address(int rm);
unsigned decode_rm01_address(int rm);
unsigned decode_rm10_address(int rm);
unsigned decode_rmXX_address(int mod, int rm);
#ifdef __cplusplus
} /* End of "C" linkage for C++ */
#endif
#endif /* __X86EMU_DECODE_H */

View file

@ -0,0 +1,45 @@
/****************************************************************************
*
* Realmode X86 Emulator Library
*
* Copyright (C) 1991-2004 SciTech Software, Inc.
* Copyright (C) David Mosberger-Tang
* Copyright (C) 1999 Egbert Eich
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Header file for operand decoding functions.
*
****************************************************************************/
#ifndef __X86EMU_OPS_H
#define __X86EMU_OPS_H
extern void (*x86emu_optab[0x100])(u8 op1);
extern void (*x86emu_optab2[0x100])(u8 op2);
#endif /* __X86EMU_OPS_H */

View file

@ -0,0 +1,970 @@
/****************************************************************************
*
* Realmode X86 Emulator Library
*
* Copyright (C) 1991-2004 SciTech Software, Inc.
* Copyright (C) David Mosberger-Tang
* Copyright (C) 1999 Egbert Eich
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: Watcom C++ 10.6 or later
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Inline assembler versions of the primitive operand
* functions for faster performance. At the moment this is
* x86 inline assembler, but these functions could be replaced
* with native inline assembler for each supported processor
* platform.
*
****************************************************************************/
#ifndef __X86EMU_PRIM_ASM_H
#define __X86EMU_PRIM_ASM_H
#ifdef __WATCOMC__
#ifndef VALIDATE
#define __HAVE_INLINE_ASSEMBLER__
#endif
u32 get_flags_asm(void);
#pragma aux get_flags_asm = \
"pushf" \
"pop eax" \
value [eax] \
modify exact [eax];
u16 aaa_word_asm(u32 * flags, u16 d);
#pragma aux aaa_word_asm = \
"push [edi]" \
"popf" \
"aaa" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] \
value [ax] \
modify exact [ax];
u16 aas_word_asm(u32 * flags, u16 d);
#pragma aux aas_word_asm = \
"push [edi]" \
"popf" \
"aas" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] \
value [ax] \
modify exact [ax];
u16 aad_word_asm(u32 * flags, u16 d);
#pragma aux aad_word_asm = \
"push [edi]" \
"popf" \
"aad" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] \
value [ax] \
modify exact [ax];
u16 aam_word_asm(u32 * flags, u8 d);
#pragma aux aam_word_asm = \
"push [edi]" \
"popf" \
"aam" \
"pushf" \
"pop [edi]" \
parm [edi] [al] \
value [ax] \
modify exact [ax];
u8 adc_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux adc_byte_asm = \
"push [edi]" \
"popf" \
"adc al,bl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [bl] \
value [al] \
modify exact [al bl];
u16 adc_word_asm(u32 * flags, u16 d, u16 s);
#pragma aux adc_word_asm = \
"push [edi]" \
"popf" \
"adc ax,bx" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [bx] \
value [ax] \
modify exact [ax bx];
u32 adc_long_asm(u32 * flags, u32 d, u32 s);
#pragma aux adc_long_asm = \
"push [edi]" \
"popf" \
"adc eax,ebx" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [ebx] \
value [eax] \
modify exact [eax ebx];
u8 add_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux add_byte_asm = \
"push [edi]" \
"popf" \
"add al,bl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [bl] \
value [al] \
modify exact [al bl];
u16 add_word_asm(u32 * flags, u16 d, u16 s);
#pragma aux add_word_asm = \
"push [edi]" \
"popf" \
"add ax,bx" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [bx] \
value [ax] \
modify exact [ax bx];
u32 add_long_asm(u32 * flags, u32 d, u32 s);
#pragma aux add_long_asm = \
"push [edi]" \
"popf" \
"add eax,ebx" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [ebx] \
value [eax] \
modify exact [eax ebx];
u8 and_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux and_byte_asm = \
"push [edi]" \
"popf" \
"and al,bl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [bl] \
value [al] \
modify exact [al bl];
u16 and_word_asm(u32 * flags, u16 d, u16 s);
#pragma aux and_word_asm = \
"push [edi]" \
"popf" \
"and ax,bx" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [bx] \
value [ax] \
modify exact [ax bx];
u32 and_long_asm(u32 * flags, u32 d, u32 s);
#pragma aux and_long_asm = \
"push [edi]" \
"popf" \
"and eax,ebx" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [ebx] \
value [eax] \
modify exact [eax ebx];
u8 cmp_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux cmp_byte_asm = \
"push [edi]" \
"popf" \
"cmp al,bl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [bl] \
value [al] \
modify exact [al bl];
u16 cmp_word_asm(u32 * flags, u16 d, u16 s);
#pragma aux cmp_word_asm = \
"push [edi]" \
"popf" \
"cmp ax,bx" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [bx] \
value [ax] \
modify exact [ax bx];
u32 cmp_long_asm(u32 * flags, u32 d, u32 s);
#pragma aux cmp_long_asm = \
"push [edi]" \
"popf" \
"cmp eax,ebx" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [ebx] \
value [eax] \
modify exact [eax ebx];
u8 daa_byte_asm(u32 * flags, u8 d);
#pragma aux daa_byte_asm = \
"push [edi]" \
"popf" \
"daa" \
"pushf" \
"pop [edi]" \
parm [edi] [al] \
value [al] \
modify exact [al];
u8 das_byte_asm(u32 * flags, u8 d);
#pragma aux das_byte_asm = \
"push [edi]" \
"popf" \
"das" \
"pushf" \
"pop [edi]" \
parm [edi] [al] \
value [al] \
modify exact [al];
u8 dec_byte_asm(u32 * flags, u8 d);
#pragma aux dec_byte_asm = \
"push [edi]" \
"popf" \
"dec al" \
"pushf" \
"pop [edi]" \
parm [edi] [al] \
value [al] \
modify exact [al];
u16 dec_word_asm(u32 * flags, u16 d);
#pragma aux dec_word_asm = \
"push [edi]" \
"popf" \
"dec ax" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] \
value [ax] \
modify exact [ax];
u32 dec_long_asm(u32 * flags, u32 d);
#pragma aux dec_long_asm = \
"push [edi]" \
"popf" \
"dec eax" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] \
value [eax] \
modify exact [eax];
u8 inc_byte_asm(u32 * flags, u8 d);
#pragma aux inc_byte_asm = \
"push [edi]" \
"popf" \
"inc al" \
"pushf" \
"pop [edi]" \
parm [edi] [al] \
value [al] \
modify exact [al];
u16 inc_word_asm(u32 * flags, u16 d);
#pragma aux inc_word_asm = \
"push [edi]" \
"popf" \
"inc ax" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] \
value [ax] \
modify exact [ax];
u32 inc_long_asm(u32 * flags, u32 d);
#pragma aux inc_long_asm = \
"push [edi]" \
"popf" \
"inc eax" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] \
value [eax] \
modify exact [eax];
u8 or_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux or_byte_asm = \
"push [edi]" \
"popf" \
"or al,bl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [bl] \
value [al] \
modify exact [al bl];
u16 or_word_asm(u32 * flags, u16 d, u16 s);
#pragma aux or_word_asm = \
"push [edi]" \
"popf" \
"or ax,bx" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [bx] \
value [ax] \
modify exact [ax bx];
u32 or_long_asm(u32 * flags, u32 d, u32 s);
#pragma aux or_long_asm = \
"push [edi]" \
"popf" \
"or eax,ebx" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [ebx] \
value [eax] \
modify exact [eax ebx];
u8 neg_byte_asm(u32 * flags, u8 d);
#pragma aux neg_byte_asm = \
"push [edi]" \
"popf" \
"neg al" \
"pushf" \
"pop [edi]" \
parm [edi] [al] \
value [al] \
modify exact [al];
u16 neg_word_asm(u32 * flags, u16 d);
#pragma aux neg_word_asm = \
"push [edi]" \
"popf" \
"neg ax" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] \
value [ax] \
modify exact [ax];
u32 neg_long_asm(u32 * flags, u32 d);
#pragma aux neg_long_asm = \
"push [edi]" \
"popf" \
"neg eax" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] \
value [eax] \
modify exact [eax];
u8 not_byte_asm(u32 * flags, u8 d);
#pragma aux not_byte_asm = \
"push [edi]" \
"popf" \
"not al" \
"pushf" \
"pop [edi]" \
parm [edi] [al] \
value [al] \
modify exact [al];
u16 not_word_asm(u32 * flags, u16 d);
#pragma aux not_word_asm = \
"push [edi]" \
"popf" \
"not ax" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] \
value [ax] \
modify exact [ax];
u32 not_long_asm(u32 * flags, u32 d);
#pragma aux not_long_asm = \
"push [edi]" \
"popf" \
"not eax" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] \
value [eax] \
modify exact [eax];
u8 rcl_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux rcl_byte_asm = \
"push [edi]" \
"popf" \
"rcl al,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [cl] \
value [al] \
modify exact [al cl];
u16 rcl_word_asm(u32 * flags, u16 d, u8 s);
#pragma aux rcl_word_asm = \
"push [edi]" \
"popf" \
"rcl ax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [cl] \
value [ax] \
modify exact [ax cl];
u32 rcl_long_asm(u32 * flags, u32 d, u8 s);
#pragma aux rcl_long_asm = \
"push [edi]" \
"popf" \
"rcl eax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [cl] \
value [eax] \
modify exact [eax cl];
u8 rcr_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux rcr_byte_asm = \
"push [edi]" \
"popf" \
"rcr al,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [cl] \
value [al] \
modify exact [al cl];
u16 rcr_word_asm(u32 * flags, u16 d, u8 s);
#pragma aux rcr_word_asm = \
"push [edi]" \
"popf" \
"rcr ax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [cl] \
value [ax] \
modify exact [ax cl];
u32 rcr_long_asm(u32 * flags, u32 d, u8 s);
#pragma aux rcr_long_asm = \
"push [edi]" \
"popf" \
"rcr eax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [cl] \
value [eax] \
modify exact [eax cl];
u8 rol_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux rol_byte_asm = \
"push [edi]" \
"popf" \
"rol al,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [cl] \
value [al] \
modify exact [al cl];
u16 rol_word_asm(u32 * flags, u16 d, u8 s);
#pragma aux rol_word_asm = \
"push [edi]" \
"popf" \
"rol ax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [cl] \
value [ax] \
modify exact [ax cl];
u32 rol_long_asm(u32 * flags, u32 d, u8 s);
#pragma aux rol_long_asm = \
"push [edi]" \
"popf" \
"rol eax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [cl] \
value [eax] \
modify exact [eax cl];
u8 ror_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux ror_byte_asm = \
"push [edi]" \
"popf" \
"ror al,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [cl] \
value [al] \
modify exact [al cl];
u16 ror_word_asm(u32 * flags, u16 d, u8 s);
#pragma aux ror_word_asm = \
"push [edi]" \
"popf" \
"ror ax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [cl] \
value [ax] \
modify exact [ax cl];
u32 ror_long_asm(u32 * flags, u32 d, u8 s);
#pragma aux ror_long_asm = \
"push [edi]" \
"popf" \
"ror eax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [cl] \
value [eax] \
modify exact [eax cl];
u8 shl_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux shl_byte_asm = \
"push [edi]" \
"popf" \
"shl al,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [cl] \
value [al] \
modify exact [al cl];
u16 shl_word_asm(u32 * flags, u16 d, u8 s);
#pragma aux shl_word_asm = \
"push [edi]" \
"popf" \
"shl ax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [cl] \
value [ax] \
modify exact [ax cl];
u32 shl_long_asm(u32 * flags, u32 d, u8 s);
#pragma aux shl_long_asm = \
"push [edi]" \
"popf" \
"shl eax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [cl] \
value [eax] \
modify exact [eax cl];
u8 shr_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux shr_byte_asm = \
"push [edi]" \
"popf" \
"shr al,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [cl] \
value [al] \
modify exact [al cl];
u16 shr_word_asm(u32 * flags, u16 d, u8 s);
#pragma aux shr_word_asm = \
"push [edi]" \
"popf" \
"shr ax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [cl] \
value [ax] \
modify exact [ax cl];
u32 shr_long_asm(u32 * flags, u32 d, u8 s);
#pragma aux shr_long_asm = \
"push [edi]" \
"popf" \
"shr eax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [cl] \
value [eax] \
modify exact [eax cl];
u8 sar_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux sar_byte_asm = \
"push [edi]" \
"popf" \
"sar al,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [cl] \
value [al] \
modify exact [al cl];
u16 sar_word_asm(u32 * flags, u16 d, u8 s);
#pragma aux sar_word_asm = \
"push [edi]" \
"popf" \
"sar ax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [cl] \
value [ax] \
modify exact [ax cl];
u32 sar_long_asm(u32 * flags, u32 d, u8 s);
#pragma aux sar_long_asm = \
"push [edi]" \
"popf" \
"sar eax,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [cl] \
value [eax] \
modify exact [eax cl];
u16 shld_word_asm(u32 * flags, u16 d, u16 fill, u8 s);
#pragma aux shld_word_asm = \
"push [edi]" \
"popf" \
"shld ax,dx,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [dx] [cl] \
value [ax] \
modify exact [ax dx cl];
u32 shld_long_asm(u32 * flags, u32 d, u32 fill, u8 s);
#pragma aux shld_long_asm = \
"push [edi]" \
"popf" \
"shld eax,edx,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [edx] [cl] \
value [eax] \
modify exact [eax edx cl];
u16 shrd_word_asm(u32 * flags, u16 d, u16 fill, u8 s);
#pragma aux shrd_word_asm = \
"push [edi]" \
"popf" \
"shrd ax,dx,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [dx] [cl] \
value [ax] \
modify exact [ax dx cl];
u32 shrd_long_asm(u32 * flags, u32 d, u32 fill, u8 s);
#pragma aux shrd_long_asm = \
"push [edi]" \
"popf" \
"shrd eax,edx,cl" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [edx] [cl] \
value [eax] \
modify exact [eax edx cl];
u8 sbb_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux sbb_byte_asm = \
"push [edi]" \
"popf" \
"sbb al,bl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [bl] \
value [al] \
modify exact [al bl];
u16 sbb_word_asm(u32 * flags, u16 d, u16 s);
#pragma aux sbb_word_asm = \
"push [edi]" \
"popf" \
"sbb ax,bx" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [bx] \
value [ax] \
modify exact [ax bx];
u32 sbb_long_asm(u32 * flags, u32 d, u32 s);
#pragma aux sbb_long_asm = \
"push [edi]" \
"popf" \
"sbb eax,ebx" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [ebx] \
value [eax] \
modify exact [eax ebx];
u8 sub_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux sub_byte_asm = \
"push [edi]" \
"popf" \
"sub al,bl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [bl] \
value [al] \
modify exact [al bl];
u16 sub_word_asm(u32 * flags, u16 d, u16 s);
#pragma aux sub_word_asm = \
"push [edi]" \
"popf" \
"sub ax,bx" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [bx] \
value [ax] \
modify exact [ax bx];
u32 sub_long_asm(u32 * flags, u32 d, u32 s);
#pragma aux sub_long_asm = \
"push [edi]" \
"popf" \
"sub eax,ebx" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [ebx] \
value [eax] \
modify exact [eax ebx];
void test_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux test_byte_asm = \
"push [edi]" \
"popf" \
"test al,bl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [bl] \
modify exact [al bl];
void test_word_asm(u32 * flags, u16 d, u16 s);
#pragma aux test_word_asm = \
"push [edi]" \
"popf" \
"test ax,bx" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [bx] \
modify exact [ax bx];
void test_long_asm(u32 * flags, u32 d, u32 s);
#pragma aux test_long_asm = \
"push [edi]" \
"popf" \
"test eax,ebx" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [ebx] \
modify exact [eax ebx];
u8 xor_byte_asm(u32 * flags, u8 d, u8 s);
#pragma aux xor_byte_asm = \
"push [edi]" \
"popf" \
"xor al,bl" \
"pushf" \
"pop [edi]" \
parm [edi] [al] [bl] \
value [al] \
modify exact [al bl];
u16 xor_word_asm(u32 * flags, u16 d, u16 s);
#pragma aux xor_word_asm = \
"push [edi]" \
"popf" \
"xor ax,bx" \
"pushf" \
"pop [edi]" \
parm [edi] [ax] [bx] \
value [ax] \
modify exact [ax bx];
u32 xor_long_asm(u32 * flags, u32 d, u32 s);
#pragma aux xor_long_asm = \
"push [edi]" \
"popf" \
"xor eax,ebx" \
"pushf" \
"pop [edi]" \
parm [edi] [eax] [ebx] \
value [eax] \
modify exact [eax ebx];
void imul_byte_asm(u32 * flags, u16 * ax, u8 d, u8 s);
#pragma aux imul_byte_asm = \
"push [edi]" \
"popf" \
"imul bl" \
"pushf" \
"pop [edi]" \
"mov [esi],ax" \
parm [edi] [esi] [al] [bl] \
modify exact [esi ax bl];
void imul_word_asm(u32 * flags, u16 * ax, u16 * dx, u16 d, u16 s);
#pragma aux imul_word_asm = \
"push [edi]" \
"popf" \
"imul bx" \
"pushf" \
"pop [edi]" \
"mov [esi],ax" \
"mov [ecx],dx" \
parm [edi] [esi] [ecx] [ax] [bx]\
modify exact [esi edi ax bx dx];
void imul_long_asm(u32 * flags, u32 * eax, u32 * edx, u32 d, u32 s);
#pragma aux imul_long_asm = \
"push [edi]" \
"popf" \
"imul ebx" \
"pushf" \
"pop [edi]" \
"mov [esi],eax" \
"mov [ecx],edx" \
parm [edi] [esi] [ecx] [eax] [ebx] \
modify exact [esi edi eax ebx edx];
void mul_byte_asm(u32 * flags, u16 * ax, u8 d, u8 s);
#pragma aux mul_byte_asm = \
"push [edi]" \
"popf" \
"mul bl" \
"pushf" \
"pop [edi]" \
"mov [esi],ax" \
parm [edi] [esi] [al] [bl] \
modify exact [esi ax bl];
void mul_word_asm(u32 * flags, u16 * ax, u16 * dx, u16 d, u16 s);
#pragma aux mul_word_asm = \
"push [edi]" \
"popf" \
"mul bx" \
"pushf" \
"pop [edi]" \
"mov [esi],ax" \
"mov [ecx],dx" \
parm [edi] [esi] [ecx] [ax] [bx]\
modify exact [esi edi ax bx dx];
void mul_long_asm(u32 * flags, u32 * eax, u32 * edx, u32 d, u32 s);
#pragma aux mul_long_asm = \
"push [edi]" \
"popf" \
"mul ebx" \
"pushf" \
"pop [edi]" \
"mov [esi],eax" \
"mov [ecx],edx" \
parm [edi] [esi] [ecx] [eax] [ebx] \
modify exact [esi edi eax ebx edx];
void idiv_byte_asm(u32 * flags, u8 * al, u8 * ah, u16 d, u8 s);
#pragma aux idiv_byte_asm = \
"push [edi]" \
"popf" \
"idiv bl" \
"pushf" \
"pop [edi]" \
"mov [esi],al" \
"mov [ecx],ah" \
parm [edi] [esi] [ecx] [ax] [bl]\
modify exact [esi edi ax bl];
void idiv_word_asm(u32 * flags, u16 * ax, u16 * dx, u16 dlo, u16 dhi, u16 s);
#pragma aux idiv_word_asm = \
"push [edi]" \
"popf" \
"idiv bx" \
"pushf" \
"pop [edi]" \
"mov [esi],ax" \
"mov [ecx],dx" \
parm [edi] [esi] [ecx] [ax] [dx] [bx]\
modify exact [esi edi ax dx bx];
void idiv_long_asm(u32 * flags, u32 * eax, u32 * edx, u32 dlo, u32 dhi, u32 s);
#pragma aux idiv_long_asm = \
"push [edi]" \
"popf" \
"idiv ebx" \
"pushf" \
"pop [edi]" \
"mov [esi],eax" \
"mov [ecx],edx" \
parm [edi] [esi] [ecx] [eax] [edx] [ebx]\
modify exact [esi edi eax edx ebx];
void div_byte_asm(u32 * flags, u8 * al, u8 * ah, u16 d, u8 s);
#pragma aux div_byte_asm = \
"push [edi]" \
"popf" \
"div bl" \
"pushf" \
"pop [edi]" \
"mov [esi],al" \
"mov [ecx],ah" \
parm [edi] [esi] [ecx] [ax] [bl]\
modify exact [esi edi ax bl];
void div_word_asm(u32 * flags, u16 * ax, u16 * dx, u16 dlo, u16 dhi, u16 s);
#pragma aux div_word_asm = \
"push [edi]" \
"popf" \
"div bx" \
"pushf" \
"pop [edi]" \
"mov [esi],ax" \
"mov [ecx],dx" \
parm [edi] [esi] [ecx] [ax] [dx] [bx]\
modify exact [esi edi ax dx bx];
void div_long_asm(u32 * flags, u32 * eax, u32 * edx, u32 dlo, u32 dhi, u32 s);
#pragma aux div_long_asm = \
"push [edi]" \
"popf" \
"div ebx" \
"pushf" \
"pop [edi]" \
"mov [esi],eax" \
"mov [ecx],edx" \
parm [edi] [esi] [ecx] [eax] [edx] [ebx]\
modify exact [esi edi eax edx ebx];
#endif
#endif /* __X86EMU_PRIM_ASM_H */

View file

@ -0,0 +1,141 @@
/****************************************************************************
*
* Realmode X86 Emulator Library
*
* Copyright (C) 1991-2004 SciTech Software, Inc.
* Copyright (C) David Mosberger-Tang
* Copyright (C) 1999 Egbert Eich
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Header file for primitive operation functions.
*
****************************************************************************/
#ifndef __X86EMU_PRIM_OPS_H
#define __X86EMU_PRIM_OPS_H
#ifdef __cplusplus
extern "C" { /* Use "C" linkage when in C++ mode */
#endif
u16 aaa_word (u16 d);
u16 aas_word (u16 d);
u16 aad_word (u16 d);
u16 aam_word (u8 d);
u8 adc_byte (u8 d, u8 s);
u16 adc_word (u16 d, u16 s);
u32 adc_long (u32 d, u32 s);
u8 add_byte (u8 d, u8 s);
u16 add_word (u16 d, u16 s);
u32 add_long (u32 d, u32 s);
u8 and_byte (u8 d, u8 s);
u16 and_word (u16 d, u16 s);
u32 and_long (u32 d, u32 s);
u8 cmp_byte (u8 d, u8 s);
u16 cmp_word (u16 d, u16 s);
u32 cmp_long (u32 d, u32 s);
u8 daa_byte (u8 d);
u8 das_byte (u8 d);
u8 dec_byte (u8 d);
u16 dec_word (u16 d);
u32 dec_long (u32 d);
u8 inc_byte (u8 d);
u16 inc_word (u16 d);
u32 inc_long (u32 d);
u8 or_byte (u8 d, u8 s);
u16 or_word (u16 d, u16 s);
u32 or_long (u32 d, u32 s);
u8 neg_byte (u8 s);
u16 neg_word (u16 s);
u32 neg_long (u32 s);
u8 not_byte (u8 s);
u16 not_word (u16 s);
u32 not_long (u32 s);
u8 rcl_byte (u8 d, u8 s);
u16 rcl_word (u16 d, u8 s);
u32 rcl_long (u32 d, u8 s);
u8 rcr_byte (u8 d, u8 s);
u16 rcr_word (u16 d, u8 s);
u32 rcr_long (u32 d, u8 s);
u8 rol_byte (u8 d, u8 s);
u16 rol_word (u16 d, u8 s);
u32 rol_long (u32 d, u8 s);
u8 ror_byte (u8 d, u8 s);
u16 ror_word (u16 d, u8 s);
u32 ror_long (u32 d, u8 s);
u8 shl_byte (u8 d, u8 s);
u16 shl_word (u16 d, u8 s);
u32 shl_long (u32 d, u8 s);
u8 shr_byte (u8 d, u8 s);
u16 shr_word (u16 d, u8 s);
u32 shr_long (u32 d, u8 s);
u8 sar_byte (u8 d, u8 s);
u16 sar_word (u16 d, u8 s);
u32 sar_long (u32 d, u8 s);
u16 shld_word (u16 d, u16 fill, u8 s);
u32 shld_long (u32 d, u32 fill, u8 s);
u16 shrd_word (u16 d, u16 fill, u8 s);
u32 shrd_long (u32 d, u32 fill, u8 s);
u8 sbb_byte (u8 d, u8 s);
u16 sbb_word (u16 d, u16 s);
u32 sbb_long (u32 d, u32 s);
u8 sub_byte (u8 d, u8 s);
u16 sub_word (u16 d, u16 s);
u32 sub_long (u32 d, u32 s);
void test_byte (u8 d, u8 s);
void test_word (u16 d, u16 s);
void test_long (u32 d, u32 s);
u8 xor_byte (u8 d, u8 s);
u16 xor_word (u16 d, u16 s);
u32 xor_long (u32 d, u32 s);
void imul_byte (u8 s);
void imul_word (u16 s);
void imul_long (u32 s);
void imul_long_direct(u32 *res_lo, u32* res_hi,u32 d, u32 s);
void mul_byte (u8 s);
void mul_word (u16 s);
void mul_long (u32 s);
void idiv_byte (u8 s);
void idiv_word (u16 s);
void idiv_long (u32 s);
void div_byte (u8 s);
void div_word (u16 s);
void div_long (u32 s);
void ins (int size);
void outs (int size);
u16 mem_access_word (int addr);
void push_word (u16 w);
void push_long (u32 w);
u16 pop_word (void);
u32 pop_long (void);
#ifdef __cplusplus
} /* End of "C" linkage for C++ */
#endif
#endif /* __X86EMU_PRIM_OPS_H */

View file

@ -0,0 +1,340 @@
/****************************************************************************
*
* Realmode X86 Emulator Library
*
* Copyright (C) 1991-2004 SciTech Software, Inc.
* Copyright (C) David Mosberger-Tang
* Copyright (C) 1999 Egbert Eich
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Header file for x86 register definitions.
*
****************************************************************************/
#ifndef __X86EMU_REGS_H
#define __X86EMU_REGS_H
/*---------------------- Macros and type definitions ----------------------*/
#pragma pack(1)
/*
* General EAX, EBX, ECX, EDX type registers. Note that for
* portability, and speed, the issue of byte swapping is not addressed
* in the registers. All registers are stored in the default format
* available on the host machine. The only critical issue is that the
* registers should line up EXACTLY in the same manner as they do in
* the 386. That is:
*
* EAX & 0xff === AL
* EAX & 0xffff == AX
*
* etc. The result is that alot of the calculations can then be
* done using the native instruction set fully.
*/
#ifdef __BIG_ENDIAN__
typedef struct {
u32 e_reg;
} I32_reg_t;
typedef struct {
u16 filler0, x_reg;
} I16_reg_t;
typedef struct {
u8 filler0, filler1, h_reg, l_reg;
} I8_reg_t;
#else /* !__BIG_ENDIAN__ */
typedef struct {
u32 e_reg;
} I32_reg_t;
typedef struct {
u16 x_reg;
} I16_reg_t;
typedef struct {
u8 l_reg, h_reg;
} I8_reg_t;
#endif /* BIG_ENDIAN */
typedef union {
I32_reg_t I32_reg;
I16_reg_t I16_reg;
I8_reg_t I8_reg;
} i386_general_register;
struct i386_general_regs {
i386_general_register A, B, C, D;
};
typedef struct i386_general_regs Gen_reg_t;
struct i386_special_regs {
i386_general_register SP, BP, SI, DI, IP;
u32 FLAGS;
};
/*
* Segment registers here represent the 16 bit quantities
* CS, DS, ES, SS.
*/
#undef CS
#undef DS
#undef SS
#undef ES
#undef FS
#undef GS
struct i386_segment_regs {
u16 CS, DS, SS, ES, FS, GS;
};
/* 8 bit registers */
#define R_AH gen.A.I8_reg.h_reg
#define R_AL gen.A.I8_reg.l_reg
#define R_BH gen.B.I8_reg.h_reg
#define R_BL gen.B.I8_reg.l_reg
#define R_CH gen.C.I8_reg.h_reg
#define R_CL gen.C.I8_reg.l_reg
#define R_DH gen.D.I8_reg.h_reg
#define R_DL gen.D.I8_reg.l_reg
/* 16 bit registers */
#define R_AX gen.A.I16_reg.x_reg
#define R_BX gen.B.I16_reg.x_reg
#define R_CX gen.C.I16_reg.x_reg
#define R_DX gen.D.I16_reg.x_reg
/* 32 bit extended registers */
#define R_EAX gen.A.I32_reg.e_reg
#define R_EBX gen.B.I32_reg.e_reg
#define R_ECX gen.C.I32_reg.e_reg
#define R_EDX gen.D.I32_reg.e_reg
/* special registers */
#define R_SP spc.SP.I16_reg.x_reg
#define R_BP spc.BP.I16_reg.x_reg
#define R_SI spc.SI.I16_reg.x_reg
#define R_DI spc.DI.I16_reg.x_reg
#define R_IP spc.IP.I16_reg.x_reg
#define R_FLG spc.FLAGS
/* special registers */
#define R_SP spc.SP.I16_reg.x_reg
#define R_BP spc.BP.I16_reg.x_reg
#define R_SI spc.SI.I16_reg.x_reg
#define R_DI spc.DI.I16_reg.x_reg
#define R_IP spc.IP.I16_reg.x_reg
#define R_FLG spc.FLAGS
/* special registers */
#define R_ESP spc.SP.I32_reg.e_reg
#define R_EBP spc.BP.I32_reg.e_reg
#define R_ESI spc.SI.I32_reg.e_reg
#define R_EDI spc.DI.I32_reg.e_reg
#define R_EIP spc.IP.I32_reg.e_reg
#define R_EFLG spc.FLAGS
/* segment registers */
#define R_CS seg.CS
#define R_DS seg.DS
#define R_SS seg.SS
#define R_ES seg.ES
#define R_FS seg.FS
#define R_GS seg.GS
/* flag conditions */
#define FB_CF 0x0001 /* CARRY flag */
#define FB_PF 0x0004 /* PARITY flag */
#define FB_AF 0x0010 /* AUX flag */
#define FB_ZF 0x0040 /* ZERO flag */
#define FB_SF 0x0080 /* SIGN flag */
#define FB_TF 0x0100 /* TRAP flag */
#define FB_IF 0x0200 /* INTERRUPT ENABLE flag */
#define FB_DF 0x0400 /* DIR flag */
#define FB_OF 0x0800 /* OVERFLOW flag */
/* 80286 and above always have bit#1 set */
#define F_ALWAYS_ON (0x0002) /* flag bits always on */
/*
* Define a mask for only those flag bits we will ever pass back
* (via PUSHF)
*/
#define F_MSK (FB_CF|FB_PF|FB_AF|FB_ZF|FB_SF|FB_TF|FB_IF|FB_DF|FB_OF)
/* following bits masked in to a 16bit quantity */
#define F_CF 0x0001 /* CARRY flag */
#define F_PF 0x0004 /* PARITY flag */
#define F_AF 0x0010 /* AUX flag */
#define F_ZF 0x0040 /* ZERO flag */
#define F_SF 0x0080 /* SIGN flag */
#define F_TF 0x0100 /* TRAP flag */
#define F_IF 0x0200 /* INTERRUPT ENABLE flag */
#define F_DF 0x0400 /* DIR flag */
#define F_OF 0x0800 /* OVERFLOW flag */
#define TOGGLE_FLAG(flag) (M.x86.R_FLG ^= (flag))
#define SET_FLAG(flag) (M.x86.R_FLG |= (flag))
#define CLEAR_FLAG(flag) (M.x86.R_FLG &= ~(flag))
#define ACCESS_FLAG(flag) (M.x86.R_FLG & (flag))
#define CLEARALL_FLAG(m) (M.x86.R_FLG = 0)
#define CONDITIONAL_SET_FLAG(COND,FLAG) \
if (COND) SET_FLAG(FLAG); else CLEAR_FLAG(FLAG)
#define F_PF_CALC 0x010000 /* PARITY flag has been calced */
#define F_ZF_CALC 0x020000 /* ZERO flag has been calced */
#define F_SF_CALC 0x040000 /* SIGN flag has been calced */
#define F_ALL_CALC 0xff0000 /* All have been calced */
/*
* Emulator machine state.
* Segment usage control.
*/
#define SYSMODE_SEG_DS_SS 0x00000001
#define SYSMODE_SEGOVR_CS 0x00000002
#define SYSMODE_SEGOVR_DS 0x00000004
#define SYSMODE_SEGOVR_ES 0x00000008
#define SYSMODE_SEGOVR_FS 0x00000010
#define SYSMODE_SEGOVR_GS 0x00000020
#define SYSMODE_SEGOVR_SS 0x00000040
#define SYSMODE_PREFIX_REPE 0x00000080
#define SYSMODE_PREFIX_REPNE 0x00000100
#define SYSMODE_PREFIX_DATA 0x00000200
#define SYSMODE_PREFIX_ADDR 0x00000400
#define SYSMODE_INTR_PENDING 0x10000000
#define SYSMODE_EXTRN_INTR 0x20000000
#define SYSMODE_HALTED 0x40000000
#define SYSMODE_SEGMASK (SYSMODE_SEG_DS_SS | \
SYSMODE_SEGOVR_CS | \
SYSMODE_SEGOVR_DS | \
SYSMODE_SEGOVR_ES | \
SYSMODE_SEGOVR_FS | \
SYSMODE_SEGOVR_GS | \
SYSMODE_SEGOVR_SS)
#define SYSMODE_CLRMASK (SYSMODE_SEG_DS_SS | \
SYSMODE_SEGOVR_CS | \
SYSMODE_SEGOVR_DS | \
SYSMODE_SEGOVR_ES | \
SYSMODE_SEGOVR_FS | \
SYSMODE_SEGOVR_GS | \
SYSMODE_SEGOVR_SS | \
SYSMODE_PREFIX_DATA | \
SYSMODE_PREFIX_ADDR)
#define INTR_SYNCH 0x1
#define INTR_ASYNCH 0x2
#define INTR_HALTED 0x4
typedef struct {
struct i386_general_regs gen;
struct i386_special_regs spc;
struct i386_segment_regs seg;
/*
* MODE contains information on:
* REPE prefix 2 bits repe,repne
* SEGMENT overrides 5 bits normal,DS,SS,CS,ES
* Delayed flag set 3 bits (zero, signed, parity)
* reserved 6 bits
* interrupt # 8 bits instruction raised interrupt
* BIOS video segregs 4 bits
* Interrupt Pending 1 bits
* Extern interrupt 1 bits
* Halted 1 bits
*/
long mode;
u8 intno;
volatile int intr; /* mask of pending interrupts */
int debug;
#ifdef DEBUG
int check;
u16 saved_ip;
u16 saved_cs;
int enc_pos;
int enc_str_pos;
char decode_buf[32]; /* encoded byte stream */
char decoded_buf[256]; /* disassembled strings */
#endif
} X86EMU_regs;
/****************************************************************************
REMARKS:
Structure maintaining the emulator machine state.
MEMBERS:
x86 - X86 registers
mem_base - Base real mode memory for the emulator
mem_size - Size of the real mode memory block for the emulator
****************************************************************************/
#undef x86
typedef struct {
X86EMU_regs x86;
u8 *mem_base;
u32 mem_size;
void *private;
} X86EMU_sysEnv;
#pragma pack()
/*----------------------------- Global Variables --------------------------*/
#ifdef __cplusplus
extern "C" { /* Use "C" linkage when in C++ mode */
#endif
/* Global emulator machine state.
*
* We keep it global to avoid pointer dereferences in the code for speed.
*/
extern X86EMU_sysEnv _X86EMU_env;
#define M _X86EMU_env
/*-------------------------- Function Prototypes --------------------------*/
/* Function to log information at runtime */
#ifndef __KERNEL__
void printk(const char *fmt, ...);
#endif
#ifdef __cplusplus
} /* End of "C" linkage for C++ */
#endif
#endif /* __X86EMU_REGS_H */

View file

@ -0,0 +1,101 @@
/****************************************************************************
*
* Realmode X86 Emulator Library
*
* Copyright (C) 1991-2004 SciTech Software, Inc.
* Copyright (C) David Mosberger-Tang
* Copyright (C) 1999 Egbert Eich
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: Header file for system specific functions. These functions
* are always compiled and linked in the OS depedent libraries,
* and never in a binary portable driver.
*
****************************************************************************/
#ifndef __X86EMU_X86EMUI_H
#define __X86EMU_X86EMUI_H
/* If we are compiling in C++ mode, we can compile some functions as
* inline to increase performance (however the code size increases quite
* dramatically in this case).
*/
#if defined(__cplusplus) && !defined(_NO_INLINE)
#define _INLINE inline
#else
#define _INLINE static
#endif
/* Get rid of unused parameters in C++ compilation mode */
#ifdef __cplusplus
#define X86EMU_UNUSED(v)
#else
#define X86EMU_UNUSED(v) v
#endif
#include "x86emu.h"
#include "x86emu/regs.h"
#include "x86emu/debug.h"
#include "x86emu/decode.h"
#include "x86emu/ops.h"
#include "x86emu/prim_ops.h"
#ifndef __KERNEL__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif
#define printk printf
/*--------------------------- Inline Functions ----------------------------*/
#ifdef __cplusplus
extern "C" { /* Use "C" linkage when in C++ mode */
#endif
extern u8(X86APIP sys_rdb) (u32 addr);
extern u16(X86APIP sys_rdw) (u32 addr);
extern u32(X86APIP sys_rdl) (u32 addr);
extern void (X86APIP sys_wrb) (u32 addr, u8 val);
extern void (X86APIP sys_wrw) (u32 addr, u16 val);
extern void (X86APIP sys_wrl) (u32 addr, u32 val);
extern u8(X86APIP sys_inb) (X86EMU_pioAddr addr);
extern u16(X86APIP sys_inw) (X86EMU_pioAddr addr);
extern u32(X86APIP sys_inl) (X86EMU_pioAddr addr);
extern void (X86APIP sys_outb) (X86EMU_pioAddr addr, u8 val);
extern void (X86APIP sys_outw) (X86EMU_pioAddr addr, u16 val);
extern void (X86APIP sys_outl) (X86EMU_pioAddr addr, u32 val);
#ifdef __cplusplus
} /* End of "C" linkage for C++ */
#endif
#endif /* __X86EMU_X86EMUI_H */

View file

@ -0,0 +1,463 @@
/****************************************************************************
*
* Realmode X86 Emulator Library
*
* Copyright (C) 1991-2004 SciTech Software, Inc.
* Copyright (C) David Mosberger-Tang
* Copyright (C) 1999 Egbert Eich
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: This file contains the code to handle debugging of the
* emulator.
*
****************************************************************************/
#include <stdarg.h>
#include <common.h>
#include <linux/ctype.h>
#include "x86emu/x86emui.h"
/*----------------------------- Implementation ----------------------------*/
#ifdef DEBUG
static void print_encoded_bytes(u16 s, u16 o);
static void print_decoded_instruction(void);
static int x86emu_parse_line(char *s, int *ps, int *n);
/* should look something like debug's output. */
void X86EMU_trace_regs(void)
{
if (DEBUG_TRACE()) {
x86emu_dump_regs();
}
if (DEBUG_DECODE() && !DEBUG_DECODE_NOPRINT()) {
printk("%04x:%04x ", M.x86.saved_cs, M.x86.saved_ip);
print_encoded_bytes(M.x86.saved_cs, M.x86.saved_ip);
print_decoded_instruction();
}
}
void X86EMU_trace_xregs(void)
{
if (DEBUG_TRACE()) {
x86emu_dump_xregs();
}
}
void x86emu_just_disassemble(void)
{
/*
* This routine called if the flag DEBUG_DISASSEMBLE is set kind
* of a hack!
*/
printk("%04x:%04x ", M.x86.saved_cs, M.x86.saved_ip);
print_encoded_bytes(M.x86.saved_cs, M.x86.saved_ip);
print_decoded_instruction();
}
static void disassemble_forward(u16 seg, u16 off, int n)
{
X86EMU_sysEnv tregs;
int i;
u8 op1;
/*
* hack, hack, hack. What we do is use the exact machinery set up
* for execution, except that now there is an additional state
* flag associated with the "execution", and we are using a copy
* of the register struct. All the major opcodes, once fully
* decoded, have the following two steps: TRACE_REGS(r,m);
* SINGLE_STEP(r,m); which disappear if DEBUG is not defined to
* the preprocessor. The TRACE_REGS macro expands to:
*
* if (debug&DEBUG_DISASSEMBLE)
* {just_disassemble(); goto EndOfInstruction;}
* if (debug&DEBUG_TRACE) trace_regs(r,m);
*
* ...... and at the last line of the routine.
*
* EndOfInstruction: end_instr();
*
* Up to the point where TRACE_REG is expanded, NO modifications
* are done to any register EXCEPT the IP register, for fetch and
* decoding purposes.
*
* This was done for an entirely different reason, but makes a
* nice way to get the system to help debug codes.
*/
tregs = M;
tregs.x86.R_IP = off;
tregs.x86.R_CS = seg;
/* reset the decoding buffers */
tregs.x86.enc_str_pos = 0;
tregs.x86.enc_pos = 0;
/* turn on the "disassemble only, no execute" flag */
tregs.x86.debug |= DEBUG_DISASSEMBLE_F;
/* DUMP NEXT n instructions to screen in straight_line fashion */
/*
* This looks like the regular instruction fetch stream, except
* that when this occurs, each fetched opcode, upon seeing the
* DEBUG_DISASSEMBLE flag set, exits immediately after decoding
* the instruction. XXX --- CHECK THAT MEM IS NOT AFFECTED!!!
* Note the use of a copy of the register structure...
*/
for (i = 0; i < n; i++) {
op1 = (*sys_rdb) (((u32) M.x86.R_CS << 4) + (M.x86.R_IP++));
(x86emu_optab[op1]) (op1);
}
/* end major hack mode. */
}
void x86emu_check_ip_access(void)
{
/* NULL as of now */
}
void x86emu_check_sp_access(void)
{
}
void x86emu_check_mem_access(u32 dummy)
{
/* check bounds, etc */
}
void x86emu_check_data_access(uint dummy1, uint dummy2)
{
/* check bounds, etc */
}
void x86emu_inc_decoded_inst_len(int x)
{
M.x86.enc_pos += x;
}
void x86emu_decode_printf(char *x)
{
sprintf(M.x86.decoded_buf + M.x86.enc_str_pos, "%s", x);
M.x86.enc_str_pos += strlen(x);
}
void x86emu_decode_printf2(char *x, int y)
{
char temp[100];
sprintf(temp, x, y);
sprintf(M.x86.decoded_buf + M.x86.enc_str_pos, "%s", temp);
M.x86.enc_str_pos += strlen(temp);
}
void x86emu_end_instr(void)
{
M.x86.enc_str_pos = 0;
M.x86.enc_pos = 0;
}
static void print_encoded_bytes(u16 s, u16 o)
{
int i;
char buf1[64];
for (i = 0; i < M.x86.enc_pos; i++) {
sprintf(buf1 + 2 * i, "%02x", fetch_data_byte_abs(s, o + i));
}
printk("%-20s", buf1);
}
static void print_decoded_instruction(void)
{
printk("%s", M.x86.decoded_buf);
}
void x86emu_print_int_vect(u16 iv)
{
u16 seg, off;
if (iv > 256)
return;
seg = fetch_data_word_abs(0, iv * 4);
off = fetch_data_word_abs(0, iv * 4 + 2);
printk("%04x:%04x ", seg, off);
}
void X86EMU_dump_memory(u16 seg, u16 off, u32 amt)
{
u32 start = off & 0xfffffff0;
u32 end = (off + 16) & 0xfffffff0;
u32 i;
u32 current;
current = start;
while (end <= off + amt) {
printk("%04x:%04x ", seg, start);
for (i = start; i < off; i++)
printk(" ");
for (; i < end; i++)
printk("%02x ", fetch_data_byte_abs(seg, i));
printk("\n");
start = end;
end = start + 16;
}
}
void x86emu_single_step(void)
{
char s[1024];
int ps[10];
int ntok;
int cmd;
int done;
int segment;
int offset;
static int breakpoint;
static int noDecode = 1;
char *p;
if (DEBUG_BREAK()) {
if (M.x86.saved_ip != breakpoint) {
return;
} else {
M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
M.x86.debug |= DEBUG_TRACE_F;
M.x86.debug &= ~DEBUG_BREAK_F;
print_decoded_instruction();
X86EMU_trace_regs();
}
}
done = 0;
offset = M.x86.saved_ip;
while (!done) {
printk("-");
cmd = x86emu_parse_line(s, ps, &ntok);
switch (cmd) {
case 'u':
disassemble_forward(M.x86.saved_cs, (u16) offset, 10);
break;
case 'd':
if (ntok == 2) {
segment = M.x86.saved_cs;
offset = ps[1];
X86EMU_dump_memory(segment, (u16) offset, 16);
offset += 16;
} else if (ntok == 3) {
segment = ps[1];
offset = ps[2];
X86EMU_dump_memory(segment, (u16) offset, 16);
offset += 16;
} else {
segment = M.x86.saved_cs;
X86EMU_dump_memory(segment, (u16) offset, 16);
offset += 16;
}
break;
case 'c':
M.x86.debug ^= DEBUG_TRACECALL_F;
break;
case 's':
M.x86.debug ^=
DEBUG_SVC_F | DEBUG_SYS_F | DEBUG_SYSINT_F;
break;
case 'r':
X86EMU_trace_regs();
break;
case 'x':
X86EMU_trace_xregs();
break;
case 'g':
if (ntok == 2) {
breakpoint = ps[1];
if (noDecode) {
M.x86.debug |= DEBUG_DECODE_NOPRINT_F;
} else {
M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F;
}
M.x86.debug &= ~DEBUG_TRACE_F;
M.x86.debug |= DEBUG_BREAK_F;
done = 1;
}
break;
case 'q':
M.x86.debug |= DEBUG_EXIT;
return;
case 'P':
noDecode = (noDecode) ? 0 : 1;
printk("Toggled decoding to %s\n",
(noDecode) ? "FALSE" : "TRUE");
break;
case 't':
case 0:
done = 1;
break;
}
}
}
int X86EMU_trace_on(void)
{
return M.x86.debug |= DEBUG_STEP_F | DEBUG_DECODE_F | DEBUG_TRACE_F;
}
int X86EMU_trace_off(void)
{
return M.x86.debug &= ~(DEBUG_STEP_F | DEBUG_DECODE_F | DEBUG_TRACE_F);
}
static int x86emu_parse_line(char *s, int *ps, int *n)
{
int cmd;
*n = 0;
while (isblank(*s))
s++;
ps[*n] = *s;
switch (*s) {
case '\n':
*n += 1;
return 0;
default:
cmd = *s;
*n += 1;
}
while (1) {
while (!isblank(*s) && *s != '\n')
s++;
if (*s == '\n')
return cmd;
while (isblank(*s))
s++;
*n += 1;
}
}
#endif /* DEBUG */
void x86emu_dump_regs(void)
{
printk("\tAX=%04x ", M.x86.R_AX);
printk("BX=%04x ", M.x86.R_BX);
printk("CX=%04x ", M.x86.R_CX);
printk("DX=%04x ", M.x86.R_DX);
printk("SP=%04x ", M.x86.R_SP);
printk("BP=%04x ", M.x86.R_BP);
printk("SI=%04x ", M.x86.R_SI);
printk("DI=%04x\n", M.x86.R_DI);
printk("\tDS=%04x ", M.x86.R_DS);
printk("ES=%04x ", M.x86.R_ES);
printk("SS=%04x ", M.x86.R_SS);
printk("CS=%04x ", M.x86.R_CS);
printk("IP=%04x ", M.x86.R_IP);
if (ACCESS_FLAG(F_OF))
printk("OV "); /* CHECKED... */
else
printk("NV ");
if (ACCESS_FLAG(F_DF))
printk("DN ");
else
printk("UP ");
if (ACCESS_FLAG(F_IF))
printk("EI ");
else
printk("DI ");
if (ACCESS_FLAG(F_SF))
printk("NG ");
else
printk("PL ");
if (ACCESS_FLAG(F_ZF))
printk("ZR ");
else
printk("NZ ");
if (ACCESS_FLAG(F_AF))
printk("AC ");
else
printk("NA ");
if (ACCESS_FLAG(F_PF))
printk("PE ");
else
printk("PO ");
if (ACCESS_FLAG(F_CF))
printk("CY ");
else
printk("NC ");
printk("\n");
}
void x86emu_dump_xregs(void)
{
printk("\tEAX=%08x ", M.x86.R_EAX);
printk("EBX=%08x ", M.x86.R_EBX);
printk("ECX=%08x ", M.x86.R_ECX);
printk("EDX=%08x \n", M.x86.R_EDX);
printk("\tESP=%08x ", M.x86.R_ESP);
printk("EBP=%08x ", M.x86.R_EBP);
printk("ESI=%08x ", M.x86.R_ESI);
printk("EDI=%08x\n", M.x86.R_EDI);
printk("\tDS=%04x ", M.x86.R_DS);
printk("ES=%04x ", M.x86.R_ES);
printk("SS=%04x ", M.x86.R_SS);
printk("CS=%04x ", M.x86.R_CS);
printk("EIP=%08x\n\t", M.x86.R_EIP);
if (ACCESS_FLAG(F_OF))
printk("OV "); /* CHECKED... */
else
printk("NV ");
if (ACCESS_FLAG(F_DF))
printk("DN ");
else
printk("UP ");
if (ACCESS_FLAG(F_IF))
printk("EI ");
else
printk("DI ");
if (ACCESS_FLAG(F_SF))
printk("NG ");
else
printk("PL ");
if (ACCESS_FLAG(F_ZF))
printk("ZR ");
else
printk("NZ ");
if (ACCESS_FLAG(F_AF))
printk("AC ");
else
printk("NA ");
if (ACCESS_FLAG(F_PF))
printk("PE ");
else
printk("PO ");
if (ACCESS_FLAG(F_CF))
printk("CY ");
else
printk("NC ");
printk("\n");
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,323 @@
/****************************************************************************
*
* Realmode X86 Emulator Library
*
* Copyright (C) 1991-2004 SciTech Software, Inc.
* Copyright (C) David Mosberger-Tang
* Copyright (C) 1999 Egbert Eich
*
* ========================================================================
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of the authors not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The authors makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* ========================================================================
*
* Language: ANSI C
* Environment: Any
* Developer: Kendall Bennett
*
* Description: This file includes subroutines which are related to
* programmed I/O and memory access. Included in this module
* are default functions that do nothing. For real uses these
* functions will have to be overriden by the user library.
*
****************************************************************************/
#include <common.h>
#include "x86emu/x86emui.h"
/*------------------------- Global Variables ------------------------------*/
X86EMU_sysEnv _X86EMU_env; /* Global emulator machine state */
X86EMU_intrFuncs _X86EMU_intrTab[256];
int debug_intr;
/*----------------------------- Implementation ----------------------------*/
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
RETURNS:
Byte value read from emulator memory.
REMARKS:
Reads a byte value from the emulator memory.
****************************************************************************/
u8 X86API rdb(u32 addr)
{
return 0;
}
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
RETURNS:
Word value read from emulator memory.
REMARKS:
Reads a word value from the emulator memory.
****************************************************************************/
u16 X86API rdw(u32 addr)
{
return 0;
}
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
RETURNS:
Long value read from emulator memory.
REMARKS:
Reads a long value from the emulator memory.
****************************************************************************/
u32 X86API rdl(u32 addr)
{
return 0;
}
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
val - Value to store
REMARKS:
Writes a byte value to emulator memory.
****************************************************************************/
void X86API wrb(u32 addr, u8 val)
{
}
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
val - Value to store
REMARKS:
Writes a word value to emulator memory.
****************************************************************************/
void X86API wrw(u32 addr, u16 val)
{
}
/****************************************************************************
PARAMETERS:
addr - Emulator memory address to read
val - Value to store
REMARKS:
Writes a long value to emulator memory.
****************************************************************************/
void X86API wrl(u32 addr, u32 val)
{
}
/****************************************************************************
PARAMETERS:
addr - PIO address to read
RETURN:
0
REMARKS:
Default PIO byte read function. Doesn't perform real inb.
****************************************************************************/
static u8 X86API p_inb(X86EMU_pioAddr addr)
{
DB(if (DEBUG_IO_TRACE())
printk("inb %#04x \n", addr);)
return 0;
}
/****************************************************************************
PARAMETERS:
addr - PIO address to read
RETURN:
0
REMARKS:
Default PIO word read function. Doesn't perform real inw.
****************************************************************************/
static u16 X86API p_inw(X86EMU_pioAddr addr)
{
DB(if (DEBUG_IO_TRACE())
printk("inw %#04x \n", addr);)
return 0;
}
/****************************************************************************
PARAMETERS:
addr - PIO address to read
RETURN:
0
REMARKS:
Default PIO long read function. Doesn't perform real inl.
****************************************************************************/
static u32 X86API p_inl(X86EMU_pioAddr addr)
{
DB(if (DEBUG_IO_TRACE())
printk("inl %#04x \n", addr);)
return 0;
}
/****************************************************************************
PARAMETERS:
addr - PIO address to write
val - Value to store
REMARKS:
Default PIO byte write function. Doesn't perform real outb.
****************************************************************************/
static void X86API p_outb(X86EMU_pioAddr addr, u8 val)
{
DB(if (DEBUG_IO_TRACE())
printk("outb %#02x -> %#04x \n", val, addr);)
return;
}
/****************************************************************************
PARAMETERS:
addr - PIO address to write
val - Value to store
REMARKS:
Default PIO word write function. Doesn't perform real outw.
****************************************************************************/
static void X86API p_outw(X86EMU_pioAddr addr, u16 val)
{
DB(if (DEBUG_IO_TRACE())
printk("outw %#04x -> %#04x \n", val, addr);)
return;
}
/****************************************************************************
PARAMETERS:
addr - PIO address to write
val - Value to store
REMARKS:
Default PIO ;ong write function. Doesn't perform real outl.
****************************************************************************/
static void X86API p_outl(X86EMU_pioAddr addr, u32 val)
{
DB(if (DEBUG_IO_TRACE())
printk("outl %#08x -> %#04x \n", val, addr);)
return;
}
/*------------------------- Global Variables ------------------------------*/
u8(X86APIP sys_rdb) (u32 addr) = rdb;
u16(X86APIP sys_rdw) (u32 addr) = rdw;
u32(X86APIP sys_rdl) (u32 addr) = rdl;
void (X86APIP sys_wrb) (u32 addr, u8 val) = wrb;
void (X86APIP sys_wrw) (u32 addr, u16 val) = wrw;
void (X86APIP sys_wrl) (u32 addr, u32 val) = wrl;
u8(X86APIP sys_inb) (X86EMU_pioAddr addr) = p_inb;
u16(X86APIP sys_inw) (X86EMU_pioAddr addr) = p_inw;
u32(X86APIP sys_inl) (X86EMU_pioAddr addr) = p_inl;
void (X86APIP sys_outb) (X86EMU_pioAddr addr, u8 val) = p_outb;
void (X86APIP sys_outw) (X86EMU_pioAddr addr, u16 val) = p_outw;
void (X86APIP sys_outl) (X86EMU_pioAddr addr, u32 val) = p_outl;
/*----------------------------- Setup -------------------------------------*/
/****************************************************************************
PARAMETERS:
funcs - New memory function pointers to make active
REMARKS:
This function is used to set the pointers to functions which access
memory space, allowing the user application to override these functions
and hook them out as necessary for their application.
****************************************************************************/
void X86EMU_setupMemFuncs(X86EMU_memFuncs * funcs)
{
sys_rdb = funcs->rdb;
sys_rdw = funcs->rdw;
sys_rdl = funcs->rdl;
sys_wrb = funcs->wrb;
sys_wrw = funcs->wrw;
sys_wrl = funcs->wrl;
}
/****************************************************************************
PARAMETERS:
funcs - New programmed I/O function pointers to make active
REMARKS:
This function is used to set the pointers to functions which access
I/O space, allowing the user application to override these functions
and hook them out as necessary for their application.
****************************************************************************/
void X86EMU_setupPioFuncs(X86EMU_pioFuncs * funcs)
{
sys_inb = funcs->inb;
sys_inw = funcs->inw;
sys_inl = funcs->inl;
sys_outb = funcs->outb;
sys_outw = funcs->outw;
sys_outl = funcs->outl;
}
/****************************************************************************
PARAMETERS:
funcs - New interrupt vector table to make active
REMARKS:
This function is used to set the pointers to functions which handle
interrupt processing in the emulator, allowing the user application to
hook interrupts as necessary for their application. Any interrupts that
are not hooked by the user application, and reflected and handled internally
in the emulator via the interrupt vector table. This allows the application
to get control when the code being emulated executes specific software
interrupts.
****************************************************************************/
void X86EMU_setupIntrFuncs(X86EMU_intrFuncs funcs[])
{
int i;
for (i = 0; i < 256; i++)
_X86EMU_intrTab[i] = NULL;
if (funcs) {
for (i = 0; i < 256; i++)
_X86EMU_intrTab[i] = funcs[i];
}
}
/****************************************************************************
PARAMETERS:
int - New software interrupt to prepare for
REMARKS:
This function is used to set up the emulator state to exceute a software
interrupt. This can be used by the user application code to allow an
interrupt to be hooked, examined and then reflected back to the emulator
so that the code in the emulator will continue processing the software
interrupt as per normal. This essentially allows system code to actively
hook and handle certain software interrupts as necessary.
****************************************************************************/
void X86EMU_prepareForInt(int num)
{
push_word((u16) M.x86.R_FLG);
CLEAR_FLAG(F_IF);
CLEAR_FLAG(F_TF);
push_word(M.x86.R_CS);
M.x86.R_CS = mem_access_word(num * 4 + 2);
push_word(M.x86.R_IP);
M.x86.R_IP = mem_access_word(num * 4);
M.x86.intr = 0;
}

View file

@ -0,0 +1,60 @@
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# 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 $(TOPDIR)/config.mk
LIB := $(obj)libblock.o
COBJS-$(CONFIG_SCSI_AHCI) += ahci.o
COBJS-$(CONFIG_ATA_PIIX) += ata_piix.o
COBJS-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o
COBJS-$(CONFIG_FSL_SATA) += fsl_sata.o
COBJS-$(CONFIG_IDE_FTIDE020) += ftide020.o
COBJS-$(CONFIG_LIBATA) += libata.o
COBJS-$(CONFIG_MVSATA_IDE) += mvsata_ide.o
COBJS-$(CONFIG_MX51_PATA) += mxc_ata.o
COBJS-$(CONFIG_PATA_BFIN) += pata_bfin.o
COBJS-$(CONFIG_SATA_DWC) += sata_dwc.o
COBJS-$(CONFIG_SATA_SIL3114) += sata_sil3114.o
COBJS-$(CONFIG_SATA_SIL) += sata_sil.o
COBJS-$(CONFIG_IDE_SIL680) += sil680.o
COBJS-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o
COBJS-$(CONFIG_SYSTEMACE) += systemace.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,746 @@
/*
* Copyright (C) Freescale Semiconductor, Inc. 2006.
* Author: Jason Jin<Jason.jin@freescale.com>
* Zhang Wei<wei.zhang@freescale.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
* with the reference on libata and ahci drvier in kernel
*
*/
#include <common.h>
#include <command.h>
#include <pci.h>
#include <asm/processor.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <malloc.h>
#include <scsi.h>
#include <ata.h>
#include <linux/ctype.h>
#include <ahci.h>
struct ahci_probe_ent *probe_ent = NULL;
hd_driveid_t *ataid[AHCI_MAX_PORTS];
#define writel_with_flush(a,b) do { writel(a,b); readl(b); } while (0)
static inline u32 ahci_port_base(u32 base, u32 port)
{
return base + 0x100 + (port * 0x80);
}
static void ahci_setup_port(struct ahci_ioports *port, unsigned long base,
unsigned int port_idx)
{
base = ahci_port_base(base, port_idx);
port->cmd_addr = base;
port->scr_addr = base + PORT_SCR;
}
#define msleep(a) udelay(a * 1000)
#define ssleep(a) msleep(a * 1000)
static int waiting_for_cmd_completed(volatile u8 *offset,
int timeout_msec,
u32 sign)
{
int i;
u32 status;
for (i = 0; ((status = readl(offset)) & sign) && i < timeout_msec; i++)
msleep(1);
return (i < timeout_msec) ? 0 : -1;
}
static int ahci_host_init(struct ahci_probe_ent *probe_ent)
{
#ifndef CONFIG_SCSI_AHCI_PLAT
pci_dev_t pdev = probe_ent->dev;
u16 tmp16;
unsigned short vendor;
#endif
volatile u8 *mmio = (volatile u8 *)probe_ent->mmio_base;
u32 tmp, cap_save;
int i, j;
volatile u8 *port_mmio;
cap_save = readl(mmio + HOST_CAP);
cap_save &= ((1 << 28) | (1 << 17));
cap_save |= (1 << 27);
/* global controller reset */
tmp = readl(mmio + HOST_CTL);
if ((tmp & HOST_RESET) == 0)
writel_with_flush(tmp | HOST_RESET, mmio + HOST_CTL);
/* reset must complete within 1 second, or
* the hardware should be considered fried.
*/
ssleep(1);
tmp = readl(mmio + HOST_CTL);
if (tmp & HOST_RESET) {
debug("controller reset failed (0x%x)\n", tmp);
return -1;
}
writel_with_flush(HOST_AHCI_EN, mmio + HOST_CTL);
writel(cap_save, mmio + HOST_CAP);
writel_with_flush(0xf, mmio + HOST_PORTS_IMPL);
#ifndef CONFIG_SCSI_AHCI_PLAT
pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor);
if (vendor == PCI_VENDOR_ID_INTEL) {
u16 tmp16;
pci_read_config_word(pdev, 0x92, &tmp16);
tmp16 |= 0xf;
pci_write_config_word(pdev, 0x92, tmp16);
}
#endif
probe_ent->cap = readl(mmio + HOST_CAP);
probe_ent->port_map = readl(mmio + HOST_PORTS_IMPL);
probe_ent->n_ports = (probe_ent->cap & 0x1f) + 1;
debug("cap 0x%x port_map 0x%x n_ports %d\n",
probe_ent->cap, probe_ent->port_map, probe_ent->n_ports);
for (i = 0; i < probe_ent->n_ports; i++) {
probe_ent->port[i].port_mmio = ahci_port_base((u32) mmio, i);
port_mmio = (u8 *) probe_ent->port[i].port_mmio;
ahci_setup_port(&probe_ent->port[i], (unsigned long)mmio, i);
/* make sure port is not active */
tmp = readl(port_mmio + PORT_CMD);
if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
PORT_CMD_FIS_RX | PORT_CMD_START)) {
tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
PORT_CMD_FIS_RX | PORT_CMD_START);
writel_with_flush(tmp, port_mmio + PORT_CMD);
/* spec says 500 msecs for each bit, so
* this is slightly incorrect.
*/
msleep(500);
}
writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD);
j = 0;
while (j < 100) {
msleep(10);
tmp = readl(port_mmio + PORT_SCR_STAT);
if ((tmp & 0xf) == 0x3)
break;
j++;
}
tmp = readl(port_mmio + PORT_SCR_ERR);
debug("PORT_SCR_ERR 0x%x\n", tmp);
writel(tmp, port_mmio + PORT_SCR_ERR);
/* ack any pending irq events for this port */
tmp = readl(port_mmio + PORT_IRQ_STAT);
debug("PORT_IRQ_STAT 0x%x\n", tmp);
if (tmp)
writel(tmp, port_mmio + PORT_IRQ_STAT);
writel(1 << i, mmio + HOST_IRQ_STAT);
/* set irq mask (enables interrupts) */
writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
/*register linkup ports */
tmp = readl(port_mmio + PORT_SCR_STAT);
debug("Port %d status: 0x%x\n", i, tmp);
if ((tmp & 0xf) == 0x03)
probe_ent->link_port_map |= (0x01 << i);
}
tmp = readl(mmio + HOST_CTL);
debug("HOST_CTL 0x%x\n", tmp);
writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
tmp = readl(mmio + HOST_CTL);
debug("HOST_CTL 0x%x\n", tmp);
#ifndef CONFIG_SCSI_AHCI_PLAT
pci_read_config_word(pdev, PCI_COMMAND, &tmp16);
tmp |= PCI_COMMAND_MASTER;
pci_write_config_word(pdev, PCI_COMMAND, tmp16);
#endif
return 0;
}
static void ahci_print_info(struct ahci_probe_ent *probe_ent)
{
#ifndef CONFIG_SCSI_AHCI_PLAT
pci_dev_t pdev = probe_ent->dev;
u16 cc;
#endif
volatile u8 *mmio = (volatile u8 *)probe_ent->mmio_base;
u32 vers, cap, impl, speed;
const char *speed_s;
const char *scc_s;
vers = readl(mmio + HOST_VERSION);
cap = probe_ent->cap;
impl = probe_ent->port_map;
speed = (cap >> 20) & 0xf;
if (speed == 1)
speed_s = "1.5";
else if (speed == 2)
speed_s = "3";
else
speed_s = "?";
#ifdef CONFIG_SCSI_AHCI_PLAT
scc_s = "SATA";
#else
pci_read_config_word(pdev, 0x0a, &cc);
if (cc == 0x0101)
scc_s = "IDE";
else if (cc == 0x0106)
scc_s = "SATA";
else if (cc == 0x0104)
scc_s = "RAID";
else
scc_s = "unknown";
#endif
printf("AHCI %02x%02x.%02x%02x "
"%u slots %u ports %s Gbps 0x%x impl %s mode\n",
(vers >> 24) & 0xff,
(vers >> 16) & 0xff,
(vers >> 8) & 0xff,
vers & 0xff,
((cap >> 8) & 0x1f) + 1, (cap & 0x1f) + 1, speed_s, impl, scc_s);
printf("flags: "
"%s%s%s%s%s%s"
"%s%s%s%s%s%s%s\n",
cap & (1 << 31) ? "64bit " : "",
cap & (1 << 30) ? "ncq " : "",
cap & (1 << 28) ? "ilck " : "",
cap & (1 << 27) ? "stag " : "",
cap & (1 << 26) ? "pm " : "",
cap & (1 << 25) ? "led " : "",
cap & (1 << 24) ? "clo " : "",
cap & (1 << 19) ? "nz " : "",
cap & (1 << 18) ? "only " : "",
cap & (1 << 17) ? "pmp " : "",
cap & (1 << 15) ? "pio " : "",
cap & (1 << 14) ? "slum " : "",
cap & (1 << 13) ? "part " : "");
}
#ifndef CONFIG_SCSI_AHCI_PLAT
static int ahci_init_one(pci_dev_t pdev)
{
u16 vendor;
int rc;
memset((void *)ataid, 0, sizeof(hd_driveid_t *) * AHCI_MAX_PORTS);
probe_ent = malloc(sizeof(struct ahci_probe_ent));
memset(probe_ent, 0, sizeof(struct ahci_probe_ent));
probe_ent->dev = pdev;
probe_ent->host_flags = ATA_FLAG_SATA
| ATA_FLAG_NO_LEGACY
| ATA_FLAG_MMIO
| ATA_FLAG_PIO_DMA
| ATA_FLAG_NO_ATAPI;
probe_ent->pio_mask = 0x1f;
probe_ent->udma_mask = 0x7f; /*Fixme,assume to support UDMA6 */
probe_ent->mmio_base = (u32)pci_map_bar(pdev, AHCI_PCI_BAR,
PCI_REGION_MEM);
/* Take from kernel:
* JMicron-specific fixup:
* make sure we're in AHCI mode
*/
pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor);
if (vendor == 0x197b)
pci_write_config_byte(pdev, 0x41, 0xa1);
/* initialize adapter */
rc = ahci_host_init(probe_ent);
if (rc)
goto err_out;
ahci_print_info(probe_ent);
return 0;
err_out:
return rc;
}
#endif
#define MAX_DATA_BYTE_COUNT (4*1024*1024)
static int ahci_fill_sg(u8 port, unsigned char *buf, int buf_len)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
struct ahci_sg *ahci_sg = pp->cmd_tbl_sg;
u32 sg_count;
int i;
sg_count = ((buf_len - 1) / MAX_DATA_BYTE_COUNT) + 1;
if (sg_count > AHCI_MAX_SG) {
printf("Error:Too much sg!\n");
return -1;
}
for (i = 0; i < sg_count; i++) {
ahci_sg->addr =
cpu_to_le32((u32) buf + i * MAX_DATA_BYTE_COUNT);
ahci_sg->addr_hi = 0;
ahci_sg->flags_size = cpu_to_le32(0x3fffff &
(buf_len < MAX_DATA_BYTE_COUNT
? (buf_len - 1)
: (MAX_DATA_BYTE_COUNT - 1)));
ahci_sg++;
buf_len -= MAX_DATA_BYTE_COUNT;
}
return sg_count;
}
static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 opts)
{
pp->cmd_slot->opts = cpu_to_le32(opts);
pp->cmd_slot->status = 0;
pp->cmd_slot->tbl_addr = cpu_to_le32(pp->cmd_tbl & 0xffffffff);
pp->cmd_slot->tbl_addr_hi = 0;
}
static void ahci_set_feature(u8 port)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio;
u32 cmd_fis_len = 5; /* five dwords */
u8 fis[20];
/*set feature */
memset(fis, 0, 20);
fis[0] = 0x27;
fis[1] = 1 << 7;
fis[2] = ATA_CMD_SETF;
fis[3] = SETFEATURES_XFER;
fis[12] = __ilog2(probe_ent->udma_mask + 1) + 0x40 - 0x01;
memcpy((unsigned char *)pp->cmd_tbl, fis, 20);
ahci_fill_cmd_slot(pp, cmd_fis_len);
writel(1, port_mmio + PORT_CMD_ISSUE);
readl(port_mmio + PORT_CMD_ISSUE);
if (waiting_for_cmd_completed(port_mmio + PORT_CMD_ISSUE, 150, 0x1)) {
printf("set feature error!\n");
}
}
static int ahci_port_start(u8 port)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio;
u32 port_status;
u32 mem;
debug("Enter start port: %d\n", port);
port_status = readl(port_mmio + PORT_SCR_STAT);
debug("Port %d status: %x\n", port, port_status);
if ((port_status & 0xf) != 0x03) {
printf("No Link on this port!\n");
return -1;
}
mem = (u32) malloc(AHCI_PORT_PRIV_DMA_SZ + 2048);
if (!mem) {
free(pp);
printf("No mem for table!\n");
return -ENOMEM;
}
mem = (mem + 0x800) & (~0x7ff); /* Aligned to 2048-bytes */
memset((u8 *) mem, 0, AHCI_PORT_PRIV_DMA_SZ);
/*
* First item in chunk of DMA memory: 32-slot command table,
* 32 bytes each in size
*/
pp->cmd_slot = (struct ahci_cmd_hdr *)mem;
debug("cmd_slot = %p\n", pp->cmd_slot);
mem += (AHCI_CMD_SLOT_SZ + 224);
/*
* Second item: Received-FIS area
*/
pp->rx_fis = mem;
mem += AHCI_RX_FIS_SZ;
/*
* Third item: data area for storing a single command
* and its scatter-gather table
*/
pp->cmd_tbl = mem;
debug("cmd_tbl_dma = 0x%x\n", pp->cmd_tbl);
mem += AHCI_CMD_TBL_HDR;
pp->cmd_tbl_sg = (struct ahci_sg *)mem;
writel_with_flush((u32) pp->cmd_slot, port_mmio + PORT_LST_ADDR);
writel_with_flush(pp->rx_fis, port_mmio + PORT_FIS_ADDR);
writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
PORT_CMD_START, port_mmio + PORT_CMD);
debug("Exit start port %d\n", port);
return 0;
}
static int get_ahci_device_data(u8 port, u8 *fis, int fis_len, u8 *buf,
int buf_len)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
volatile u8 *port_mmio = (volatile u8 *)pp->port_mmio;
u32 opts;
u32 port_status;
int sg_count;
debug("Enter get_ahci_device_data: for port %d\n", port);
if (port > probe_ent->n_ports) {
printf("Invaild port number %d\n", port);
return -1;
}
port_status = readl(port_mmio + PORT_SCR_STAT);
if ((port_status & 0xf) != 0x03) {
debug("No Link on port %d!\n", port);
return -1;
}
memcpy((unsigned char *)pp->cmd_tbl, fis, fis_len);
sg_count = ahci_fill_sg(port, buf, buf_len);
opts = (fis_len >> 2) | (sg_count << 16);
ahci_fill_cmd_slot(pp, opts);
writel_with_flush(1, port_mmio + PORT_CMD_ISSUE);
if (waiting_for_cmd_completed(port_mmio + PORT_CMD_ISSUE, 150, 0x1)) {
printf("timeout exit!\n");
return -1;
}
debug("get_ahci_device_data: %d byte transferred.\n",
pp->cmd_slot->status);
return 0;
}
static char *ata_id_strcpy(u16 *target, u16 *src, int len)
{
int i;
for (i = 0; i < len / 2; i++)
target[i] = swab16(src[i]);
return (char *)target;
}
static void dump_ataid(hd_driveid_t *ataid)
{
debug("(49)ataid->capability = 0x%x\n", ataid->capability);
debug("(53)ataid->field_valid =0x%x\n", ataid->field_valid);
debug("(63)ataid->dma_mword = 0x%x\n", ataid->dma_mword);
debug("(64)ataid->eide_pio_modes = 0x%x\n", ataid->eide_pio_modes);
debug("(75)ataid->queue_depth = 0x%x\n", ataid->queue_depth);
debug("(80)ataid->major_rev_num = 0x%x\n", ataid->major_rev_num);
debug("(81)ataid->minor_rev_num = 0x%x\n", ataid->minor_rev_num);
debug("(82)ataid->command_set_1 = 0x%x\n", ataid->command_set_1);
debug("(83)ataid->command_set_2 = 0x%x\n", ataid->command_set_2);
debug("(84)ataid->cfsse = 0x%x\n", ataid->cfsse);
debug("(85)ataid->cfs_enable_1 = 0x%x\n", ataid->cfs_enable_1);
debug("(86)ataid->cfs_enable_2 = 0x%x\n", ataid->cfs_enable_2);
debug("(87)ataid->csf_default = 0x%x\n", ataid->csf_default);
debug("(88)ataid->dma_ultra = 0x%x\n", ataid->dma_ultra);
debug("(93)ataid->hw_config = 0x%x\n", ataid->hw_config);
}
/*
* SCSI INQUIRY command operation.
*/
static int ata_scsiop_inquiry(ccb *pccb)
{
u8 hdr[] = {
0,
0,
0x5, /* claim SPC-3 version compatibility */
2,
95 - 4,
};
u8 fis[20];
u8 *tmpid;
u8 port;
/* Clean ccb data buffer */
memset(pccb->pdata, 0, pccb->datalen);
memcpy(pccb->pdata, hdr, sizeof(hdr));
if (pccb->datalen <= 35)
return 0;
memset(fis, 0, 20);
/* Construct the FIS */
fis[0] = 0x27; /* Host to device FIS. */
fis[1] = 1 << 7; /* Command FIS. */
fis[2] = ATA_CMD_IDENT; /* Command byte. */
/* Read id from sata */
port = pccb->target;
if (!(tmpid = malloc(sizeof(hd_driveid_t))))
return -ENOMEM;
if (get_ahci_device_data(port, (u8 *) & fis, 20,
tmpid, sizeof(hd_driveid_t))) {
debug("scsi_ahci: SCSI inquiry command failure.\n");
return -EIO;
}
if (ataid[port])
free(ataid[port]);
ataid[port] = (hd_driveid_t *) tmpid;
memcpy(&pccb->pdata[8], "ATA ", 8);
ata_id_strcpy((u16 *) &pccb->pdata[16], (u16 *)ataid[port]->model, 16);
ata_id_strcpy((u16 *) &pccb->pdata[32], (u16 *)ataid[port]->fw_rev, 4);
dump_ataid(ataid[port]);
return 0;
}
/*
* SCSI READ10 command operation.
*/
static int ata_scsiop_read10(ccb * pccb)
{
u32 len = 0;
u8 fis[20];
len = (((u32) pccb->cmd[7]) << 8) | ((u32) pccb->cmd[8]);
/* For 10-byte and 16-byte SCSI R/W commands, transfer
* length 0 means transfer 0 block of data.
* However, for ATA R/W commands, sector count 0 means
* 256 or 65536 sectors, not 0 sectors as in SCSI.
*
* WARNING: one or two older ATA drives treat 0 as 0...
*/
if (!len)
return 0;
memset(fis, 0, 20);
/* Construct the FIS */
fis[0] = 0x27; /* Host to device FIS. */
fis[1] = 1 << 7; /* Command FIS. */
fis[2] = ATA_CMD_RD_DMA; /* Command byte. */
/* LBA address, only support LBA28 in this driver */
fis[4] = pccb->cmd[5];
fis[5] = pccb->cmd[4];
fis[6] = pccb->cmd[3];
fis[7] = (pccb->cmd[2] & 0x0f) | 0xe0;
/* Sector Count */
fis[12] = pccb->cmd[8];
fis[13] = pccb->cmd[7];
/* Read from ahci */
if (get_ahci_device_data(pccb->target, (u8 *) & fis, 20,
pccb->pdata, pccb->datalen)) {
debug("scsi_ahci: SCSI READ10 command failure.\n");
return -EIO;
}
return 0;
}
/*
* SCSI READ CAPACITY10 command operation.
*/
static int ata_scsiop_read_capacity10(ccb *pccb)
{
u32 cap;
if (!ataid[pccb->target]) {
printf("scsi_ahci: SCSI READ CAPACITY10 command failure. "
"\tNo ATA info!\n"
"\tPlease run SCSI commmand INQUIRY firstly!\n");
return -EPERM;
}
cap = le32_to_cpu(ataid[pccb->target]->lba_capacity);
memcpy(pccb->pdata, &cap, sizeof(cap));
pccb->pdata[4] = pccb->pdata[5] = 0;
pccb->pdata[6] = 512 >> 8;
pccb->pdata[7] = 512 & 0xff;
return 0;
}
/*
* SCSI TEST UNIT READY command operation.
*/
static int ata_scsiop_test_unit_ready(ccb *pccb)
{
return (ataid[pccb->target]) ? 0 : -EPERM;
}
int scsi_exec(ccb *pccb)
{
int ret;
switch (pccb->cmd[0]) {
case SCSI_READ10:
ret = ata_scsiop_read10(pccb);
break;
case SCSI_RD_CAPAC:
ret = ata_scsiop_read_capacity10(pccb);
break;
case SCSI_TST_U_RDY:
ret = ata_scsiop_test_unit_ready(pccb);
break;
case SCSI_INQUIRY:
ret = ata_scsiop_inquiry(pccb);
break;
default:
printf("Unsupport SCSI command 0x%02x\n", pccb->cmd[0]);
return FALSE;
}
if (ret) {
debug("SCSI command 0x%02x ret errno %d\n", pccb->cmd[0], ret);
return FALSE;
}
return TRUE;
}
void scsi_low_level_init(int busdevfunc)
{
int i;
u32 linkmap;
#ifndef CONFIG_SCSI_AHCI_PLAT
ahci_init_one(busdevfunc);
#endif
linkmap = probe_ent->link_port_map;
for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) {
if (((linkmap >> i) & 0x01)) {
if (ahci_port_start((u8) i)) {
printf("Can not start port %d\n", i);
continue;
}
ahci_set_feature((u8) i);
}
}
}
#ifdef CONFIG_SCSI_AHCI_PLAT
int ahci_init(u32 base)
{
int i, rc = 0;
u32 linkmap;
memset(ataid, 0, sizeof(ataid));
probe_ent = malloc(sizeof(struct ahci_probe_ent));
memset(probe_ent, 0, sizeof(struct ahci_probe_ent));
probe_ent->host_flags = ATA_FLAG_SATA
| ATA_FLAG_NO_LEGACY
| ATA_FLAG_MMIO
| ATA_FLAG_PIO_DMA
| ATA_FLAG_NO_ATAPI;
probe_ent->pio_mask = 0x1f;
probe_ent->udma_mask = 0x7f; /*Fixme,assume to support UDMA6 */
probe_ent->mmio_base = base;
/* initialize adapter */
rc = ahci_host_init(probe_ent);
if (rc)
goto err_out;
ahci_print_info(probe_ent);
linkmap = probe_ent->link_port_map;
for (i = 0; i < CONFIG_SYS_SCSI_MAX_SCSI_ID; i++) {
if (((linkmap >> i) & 0x01)) {
if (ahci_port_start((u8) i)) {
printf("Can not start port %d\n", i);
continue;
}
ahci_set_feature((u8) i);
}
}
err_out:
return rc;
}
#endif
void scsi_bus_reset(void)
{
/*Not implement*/
}
void scsi_print_error(ccb * pccb)
{
/*The ahci error info can be read in the ahci driver*/
}

View file

@ -0,0 +1,756 @@
/*
* Copyright (C) Procsys. All rights reserved.
* Author: Mushtaq Khan <mushtaq_k@procsys.com>
* <mushtaqk_921@yahoo.co.in>
*
* 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
*
* with the reference to ata_piix driver in kernel 2.4.32
*/
/*
* This file contains SATA controller and SATA drive initialization functions
*/
#include <common.h>
#include <asm/io.h>
#include <pci.h>
#include <command.h>
#include <config.h>
#include <asm/byteorder.h>
#include <part.h>
#include <ide.h>
#include <ata.h>
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
extern int sata_curr_device;
#define DEBUG_SATA 0 /*For debug prints set DEBUG_SATA to 1 */
#define SATA_DECL
#define DRV_DECL /*For file specific declarations */
#include "ata_piix.h"
/*Macros realted to PCI*/
#define PCI_SATA_BUS 0x00
#define PCI_SATA_DEV 0x1f
#define PCI_SATA_FUNC 0x02
#define PCI_SATA_BASE1 0x10
#define PCI_SATA_BASE2 0x14
#define PCI_SATA_BASE3 0x18
#define PCI_SATA_BASE4 0x1c
#define PCI_SATA_BASE5 0x20
#define PCI_PMR 0x90
#define PCI_PI 0x09
#define PCI_PCS 0x92
#define PCI_DMA_CTL 0x48
#define PORT_PRESENT (1<<0)
#define PORT_ENABLED (1<<4)
u32 bdf;
u32 iobase1 = 0; /*Primary cmd block */
u32 iobase2 = 0; /*Primary ctl block */
u32 iobase3 = 0; /*Sec cmd block */
u32 iobase4 = 0; /*sec ctl block */
u32 iobase5 = 0; /*BMDMA*/
int
pci_sata_init (void)
{
u32 bus = PCI_SATA_BUS;
u32 dev = PCI_SATA_DEV;
u32 fun = PCI_SATA_FUNC;
u16 cmd = 0;
u8 lat = 0, pcibios_max_latency = 0xff;
u8 pmr; /*Port mapping reg */
u8 pi; /*Prgming Interface reg */
bdf = PCI_BDF (bus, dev, fun);
pci_read_config_dword (bdf, PCI_SATA_BASE1, &iobase1);
pci_read_config_dword (bdf, PCI_SATA_BASE2, &iobase2);
pci_read_config_dword (bdf, PCI_SATA_BASE3, &iobase3);
pci_read_config_dword (bdf, PCI_SATA_BASE4, &iobase4);
pci_read_config_dword (bdf, PCI_SATA_BASE5, &iobase5);
if ((iobase1 == 0xFFFFFFFF) || (iobase2 == 0xFFFFFFFF) ||
(iobase3 == 0xFFFFFFFF) || (iobase4 == 0xFFFFFFFF) ||
(iobase5 == 0xFFFFFFFF)) {
printf ("error no base addr for SATA controller\n");
return 1;
/*ERROR*/}
iobase1 &= 0xFFFFFFFE;
iobase2 &= 0xFFFFFFFE;
iobase3 &= 0xFFFFFFFE;
iobase4 &= 0xFFFFFFFE;
iobase5 &= 0xFFFFFFFE;
/*check for mode */
pci_read_config_byte (bdf, PCI_PMR, &pmr);
if (pmr > 1) {
printf ("combined mode not supported\n");
return 1;
}
pci_read_config_byte (bdf, PCI_PI, &pi);
if ((pi & 0x05) != 0x05) {
printf ("Sata is in Legacy mode\n");
return 1;
} else {
printf ("sata is in Native mode\n");
}
/*MASTER CFG AND IO CFG */
pci_read_config_word (bdf, PCI_COMMAND, &cmd);
cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_IO;
pci_write_config_word (bdf, PCI_COMMAND, cmd);
pci_read_config_byte (dev, PCI_LATENCY_TIMER, &lat);
if (lat < 16)
lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
else if (lat > pcibios_max_latency)
lat = pcibios_max_latency;
pci_write_config_byte (dev, PCI_LATENCY_TIMER, lat);
return 0;
}
int
sata_bus_probe (int port_no)
{
int orig_mask, mask;
u16 pcs;
mask = (PORT_PRESENT << port_no);
pci_read_config_word (bdf, PCI_PCS, &pcs);
orig_mask = (int) pcs & 0xff;
if ((orig_mask & mask) != mask)
return 0;
else
return 1;
}
int
init_sata (int dev)
{
static int done = 0;
u8 i, rv = 0;
if (!done)
done = 1;
else
return 0;
rv = pci_sata_init ();
if (rv == 1) {
printf ("pci initialization failed\n");
return 1;
}
port[0].port_no = 0;
port[0].ioaddr.cmd_addr = iobase1;
port[0].ioaddr.altstatus_addr = port[0].ioaddr.ctl_addr =
iobase2 | ATA_PCI_CTL_OFS;
port[0].ioaddr.bmdma_addr = iobase5;
port[1].port_no = 1;
port[1].ioaddr.cmd_addr = iobase3;
port[1].ioaddr.altstatus_addr = port[1].ioaddr.ctl_addr =
iobase4 | ATA_PCI_CTL_OFS;
port[1].ioaddr.bmdma_addr = iobase5 + 0x8;
for (i = 0; i < CONFIG_SYS_SATA_MAXBUS; i++)
sata_port (&port[i].ioaddr);
for (i = 0; i < CONFIG_SYS_SATA_MAXBUS; i++) {
if (!(sata_bus_probe (i))) {
port[i].port_state = 0;
printf ("SATA#%d port is not present \n", i);
} else {
printf ("SATA#%d port is present\n", i);
if (sata_bus_softreset (i)) {
port[i].port_state = 0;
} else {
port[i].port_state = 1;
}
}
}
for (i = 0; i < CONFIG_SYS_SATA_MAXBUS; i++) {
u8 j, devno;
if (port[i].port_state == 0)
continue;
for (j = 0; j < CONFIG_SYS_SATA_DEVS_PER_BUS; j++) {
sata_identify (i, j);
set_Feature_cmd (i, j);
devno = i * CONFIG_SYS_SATA_DEVS_PER_BUS + j;
if ((sata_dev_desc[devno].lba > 0) &&
(sata_dev_desc[devno].blksz > 0)) {
dev_print (&sata_dev_desc[devno]);
/* initialize partition type */
init_part (&sata_dev_desc[devno]);
if (sata_curr_device < 0)
sata_curr_device =
i * CONFIG_SYS_SATA_DEVS_PER_BUS + j;
}
}
}
return 0;
}
static u8 __inline__
sata_inb (unsigned long ioaddr)
{
return inb (ioaddr);
}
static void __inline__
sata_outb (unsigned char val, unsigned long ioaddr)
{
outb (val, ioaddr);
}
static void
output_data (struct sata_ioports *ioaddr, ulong * sect_buf, int words)
{
outsw (ioaddr->data_addr, sect_buf, words << 1);
}
static int
input_data (struct sata_ioports *ioaddr, ulong * sect_buf, int words)
{
insw (ioaddr->data_addr, sect_buf, words << 1);
return 0;
}
static void
sata_cpy (unsigned char *dst, unsigned char *src, unsigned int len)
{
unsigned char *end, *last;
last = dst;
end = src + len - 1;
/* reserve space for '\0' */
if (len < 2)
goto OUT;
/* skip leading white space */
while ((*src) && (src < end) && (*src == ' '))
++src;
/* copy string, omitting trailing white space */
while ((*src) && (src < end)) {
*dst++ = *src;
if (*src++ != ' ')
last = dst;
}
OUT:
*last = '\0';
}
int
sata_bus_softreset (int num)
{
u8 dev = 0, status = 0, i;
port[num].dev_mask = 0;
for (i = 0; i < CONFIG_SYS_SATA_DEVS_PER_BUS; i++) {
if (!(sata_devchk (&port[num].ioaddr, i))) {
PRINTF ("dev_chk failed for dev#%d\n", i);
} else {
port[num].dev_mask |= (1 << i);
PRINTF ("dev_chk passed for dev#%d\n", i);
}
}
if (!(port[num].dev_mask)) {
printf ("no devices on port%d\n", num);
return 1;
}
dev_select (&port[num].ioaddr, dev);
port[num].ctl_reg = 0x08; /*Default value of control reg */
sata_outb (port[num].ctl_reg, port[num].ioaddr.ctl_addr);
udelay (10);
sata_outb (port[num].ctl_reg | ATA_SRST, port[num].ioaddr.ctl_addr);
udelay (10);
sata_outb (port[num].ctl_reg, port[num].ioaddr.ctl_addr);
/* spec mandates ">= 2ms" before checking status.
* We wait 150ms, because that was the magic delay used for
* ATAPI devices in Hale Landis's ATADRVR, for the period of time
* between when the ATA command register is written, and then
* status is checked. Because waiting for "a while" before
* checking status is fine, post SRST, we perform this magic
* delay here as well.
*/
msleep (150);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 300);
while ((status & ATA_BUSY)) {
msleep (100);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 3);
}
if (status & ATA_BUSY)
printf ("ata%u is slow to respond,plz be patient\n", num);
while ((status & ATA_BUSY)) {
msleep (100);
status = sata_chk_status (&port[num].ioaddr);
}
if (status & ATA_BUSY) {
printf ("ata%u failed to respond : ", num);
printf ("bus reset failed\n");
return 1;
}
return 0;
}
void
sata_identify (int num, int dev)
{
u8 cmd = 0, status = 0, devno = num * CONFIG_SYS_SATA_DEVS_PER_BUS + dev;
u16 iobuf[ATA_SECT_SIZE];
u64 n_sectors = 0;
u8 mask = 0;
memset (iobuf, 0, sizeof (iobuf));
hd_driveid_t *iop = (hd_driveid_t *) iobuf;
if (dev == 0)
mask = 0x01;
else
mask = 0x02;
if (!(port[num].dev_mask & mask)) {
printf ("dev%d is not present on port#%d\n", dev, num);
return;
}
printf ("port=%d dev=%d\n", num, dev);
dev_select (&port[num].ioaddr, dev);
status = 0;
cmd = ATA_CMD_IDENT; /*Device Identify Command */
sata_outb (cmd, port[num].ioaddr.command_addr);
sata_inb (port[num].ioaddr.altstatus_addr);
udelay (10);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 1000);
if (status & ATA_ERR) {
printf ("\ndevice not responding\n");
port[num].dev_mask &= ~mask;
return;
}
input_data (&port[num].ioaddr, (ulong *) iobuf, ATA_SECTORWORDS);
PRINTF ("\nata%u: dev %u cfg 49:%04x 82:%04x 83:%04x 84:%04x85:%04x"
"86:%04x" "87:%04x 88:%04x\n", num, dev, iobuf[49],
iobuf[82], iobuf[83], iobuf[84], iobuf[85], iobuf[86],
iobuf[87], iobuf[88]);
/* we require LBA and DMA support (bits 8 & 9 of word 49) */
if (!ata_id_has_dma (iobuf) || !ata_id_has_lba (iobuf)) {
PRINTF ("ata%u: no dma/lba\n", num);
}
ata_dump_id (iobuf);
if (ata_id_has_lba48 (iobuf)) {
n_sectors = ata_id_u64 (iobuf, 100);
} else {
n_sectors = ata_id_u32 (iobuf, 60);
}
PRINTF ("no. of sectors %u\n", ata_id_u64 (iobuf, 100));
PRINTF ("no. of sectors %u\n", ata_id_u32 (iobuf, 60));
if (n_sectors == 0) {
port[num].dev_mask &= ~mask;
return;
}
sata_cpy ((unsigned char *)sata_dev_desc[devno].revision, iop->fw_rev,
sizeof (sata_dev_desc[devno].revision));
sata_cpy ((unsigned char *)sata_dev_desc[devno].vendor, iop->model,
sizeof (sata_dev_desc[devno].vendor));
sata_cpy ((unsigned char *)sata_dev_desc[devno].product, iop->serial_no,
sizeof (sata_dev_desc[devno].product));
strswab (sata_dev_desc[devno].revision);
strswab (sata_dev_desc[devno].vendor);
if ((iop->config & 0x0080) == 0x0080) {
sata_dev_desc[devno].removable = 1;
} else {
sata_dev_desc[devno].removable = 0;
}
sata_dev_desc[devno].lba = iop->lba_capacity;
PRINTF ("lba=0x%x", sata_dev_desc[devno].lba);
#ifdef CONFIG_LBA48
if (iop->command_set_2 & 0x0400) {
sata_dev_desc[devno].lba48 = 1;
lba = (unsigned long long) iop->lba48_capacity[0] |
((unsigned long long) iop->lba48_capacity[1] << 16) |
((unsigned long long) iop->lba48_capacity[2] << 32) |
((unsigned long long) iop->lba48_capacity[3] << 48);
} else {
sata_dev_desc[devno].lba48 = 0;
}
#endif
/* assuming HD */
sata_dev_desc[devno].type = DEV_TYPE_HARDDISK;
sata_dev_desc[devno].blksz = ATA_BLOCKSIZE;
sata_dev_desc[devno].lun = 0; /* just to fill something in... */
}
void
set_Feature_cmd (int num, int dev)
{
u8 mask = 0x00, status = 0;
if (dev == 0)
mask = 0x01;
else
mask = 0x02;
if (!(port[num].dev_mask & mask)) {
PRINTF ("dev%d is not present on port#%d\n", dev, num);
return;
}
dev_select (&port[num].ioaddr, dev);
sata_outb (SETFEATURES_XFER, port[num].ioaddr.feature_addr);
sata_outb (XFER_PIO_4, port[num].ioaddr.nsect_addr);
sata_outb (0, port[num].ioaddr.lbal_addr);
sata_outb (0, port[num].ioaddr.lbam_addr);
sata_outb (0, port[num].ioaddr.lbah_addr);
sata_outb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
sata_outb (ATA_CMD_SETF, port[num].ioaddr.command_addr);
udelay (50);
msleep (150);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 5000);
if ((status & (ATA_STAT_BUSY | ATA_STAT_ERR))) {
printf ("Error : status 0x%02x\n", status);
port[num].dev_mask &= ~mask;
}
}
void
sata_port (struct sata_ioports *ioport)
{
ioport->data_addr = ioport->cmd_addr + ATA_REG_DATA;
ioport->error_addr = ioport->cmd_addr + ATA_REG_ERR;
ioport->feature_addr = ioport->cmd_addr + ATA_REG_FEATURE;
ioport->nsect_addr = ioport->cmd_addr + ATA_REG_NSECT;
ioport->lbal_addr = ioport->cmd_addr + ATA_REG_LBAL;
ioport->lbam_addr = ioport->cmd_addr + ATA_REG_LBAM;
ioport->lbah_addr = ioport->cmd_addr + ATA_REG_LBAH;
ioport->device_addr = ioport->cmd_addr + ATA_REG_DEVICE;
ioport->status_addr = ioport->cmd_addr + ATA_REG_STATUS;
ioport->command_addr = ioport->cmd_addr + ATA_REG_CMD;
}
int
sata_devchk (struct sata_ioports *ioaddr, int dev)
{
u8 nsect, lbal;
dev_select (ioaddr, dev);
sata_outb (0x55, ioaddr->nsect_addr);
sata_outb (0xaa, ioaddr->lbal_addr);
sata_outb (0xaa, ioaddr->nsect_addr);
sata_outb (0x55, ioaddr->lbal_addr);
sata_outb (0x55, ioaddr->nsect_addr);
sata_outb (0xaa, ioaddr->lbal_addr);
nsect = sata_inb (ioaddr->nsect_addr);
lbal = sata_inb (ioaddr->lbal_addr);
if ((nsect == 0x55) && (lbal == 0xaa))
return 1; /* we found a device */
else
return 0; /* nothing found */
}
void
dev_select (struct sata_ioports *ioaddr, int dev)
{
u8 tmp = 0;
if (dev == 0)
tmp = ATA_DEVICE_OBS;
else
tmp = ATA_DEVICE_OBS | ATA_DEV1;
sata_outb (tmp, ioaddr->device_addr);
sata_inb (ioaddr->altstatus_addr);
udelay (5);
}
u8
sata_busy_wait (struct sata_ioports *ioaddr, int bits, unsigned int max)
{
u8 status;
do {
udelay (1000);
status = sata_chk_status (ioaddr);
max--;
} while ((status & bits) && (max > 0));
return status;
}
u8
sata_chk_status (struct sata_ioports * ioaddr)
{
return sata_inb (ioaddr->status_addr);
}
void
msleep (int count)
{
int i;
for (i = 0; i < count; i++)
udelay (1000);
}
ulong
sata_read (int device, ulong blknr,lbaint_t blkcnt, void * buff)
{
ulong n = 0, *buffer = (ulong *)buff;
u8 dev = 0, num = 0, mask = 0, status = 0;
#ifdef CONFIG_LBA48
unsigned char lba48 = 0;
if (blknr & 0x0000fffff0000000) {
if (!sata_dev_desc[devno].lba48) {
printf ("Drive doesn't support 48-bit addressing\n");
return 0;
}
/* more than 28 bits used, use 48bit mode */
lba48 = 1;
}
#endif
/*Port Number */
num = device / CONFIG_SYS_SATA_DEVS_PER_BUS;
/*dev on the port */
if (device >= CONFIG_SYS_SATA_DEVS_PER_BUS)
dev = device - CONFIG_SYS_SATA_DEVS_PER_BUS;
else
dev = device;
if (dev == 0)
mask = 0x01;
else
mask = 0x02;
if (!(port[num].dev_mask & mask)) {
printf ("dev%d is not present on port#%d\n", dev, num);
return 0;
}
/* Select device */
dev_select (&port[num].ioaddr, dev);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 500);
if (status & ATA_BUSY) {
printf ("ata%u failed to respond\n", port[num].port_no);
return n;
}
while (blkcnt-- > 0) {
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 500);
if (status & ATA_BUSY) {
printf ("ata%u failed to respond\n", 0);
return n;
}
#ifdef CONFIG_LBA48
if (lba48) {
/* write high bits */
sata_outb (0, port[num].ioaddr.nsect_addr);
sata_outb ((blknr >> 24) & 0xFF,
port[num].ioaddr.lbal_addr);
sata_outb ((blknr >> 32) & 0xFF,
port[num].ioaddr.lbam_addr);
sata_outb ((blknr >> 40) & 0xFF,
port[num].ioaddr.lbah_addr);
}
#endif
sata_outb (1, port[num].ioaddr.nsect_addr);
sata_outb (((blknr) >> 0) & 0xFF,
port[num].ioaddr.lbal_addr);
sata_outb ((blknr >> 8) & 0xFF, port[num].ioaddr.lbam_addr);
sata_outb ((blknr >> 16) & 0xFF, port[num].ioaddr.lbah_addr);
#ifdef CONFIG_LBA48
if (lba48) {
sata_outb (ATA_LBA, port[num].ioaddr.device_addr);
sata_outb (ATA_CMD_READ_EXT,
port[num].ioaddr.command_addr);
} else
#endif
{
sata_outb (ATA_LBA | ((blknr >> 24) & 0xF),
port[num].ioaddr.device_addr);
sata_outb (ATA_CMD_READ,
port[num].ioaddr.command_addr);
}
msleep (50);
/*may take up to 4 sec */
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 4000);
if ((status & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR))
!= ATA_STAT_DRQ) {
u8 err = 0;
printf ("Error no DRQ dev %d blk %ld: sts 0x%02x\n",
device, (ulong) blknr, status);
err = sata_inb (port[num].ioaddr.error_addr);
printf ("Error reg = 0x%x\n", err);
return (n);
}
input_data (&port[num].ioaddr, buffer, ATA_SECTORWORDS);
sata_inb (port[num].ioaddr.altstatus_addr);
udelay (50);
++n;
++blknr;
buffer += ATA_SECTORWORDS;
}
return n;
}
ulong
sata_write (int device, ulong blknr,lbaint_t blkcnt, void * buff)
{
ulong n = 0, *buffer = (ulong *)buff;
unsigned char status = 0, num = 0, dev = 0, mask = 0;
#ifdef CONFIG_LBA48
unsigned char lba48 = 0;
if (blknr & 0x0000fffff0000000) {
if (!sata_dev_desc[devno].lba48) {
printf ("Drive doesn't support 48-bit addressing\n");
return 0;
}
/* more than 28 bits used, use 48bit mode */
lba48 = 1;
}
#endif
/*Port Number */
num = device / CONFIG_SYS_SATA_DEVS_PER_BUS;
/*dev on the Port */
if (device >= CONFIG_SYS_SATA_DEVS_PER_BUS)
dev = device - CONFIG_SYS_SATA_DEVS_PER_BUS;
else
dev = device;
if (dev == 0)
mask = 0x01;
else
mask = 0x02;
/* Select device */
dev_select (&port[num].ioaddr, dev);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 500);
if (status & ATA_BUSY) {
printf ("ata%u failed to respond\n", port[num].port_no);
return n;
}
while (blkcnt-- > 0) {
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 500);
if (status & ATA_BUSY) {
printf ("ata%u failed to respond\n",
port[num].port_no);
return n;
}
#ifdef CONFIG_LBA48
if (lba48) {
/* write high bits */
sata_outb (0, port[num].ioaddr.nsect_addr);
sata_outb ((blknr >> 24) & 0xFF,
port[num].ioaddr.lbal_addr);
sata_outb ((blknr >> 32) & 0xFF,
port[num].ioaddr.lbam_addr);
sata_outb ((blknr >> 40) & 0xFF,
port[num].ioaddr.lbah_addr);
}
#endif
sata_outb (1, port[num].ioaddr.nsect_addr);
sata_outb ((blknr >> 0) & 0xFF, port[num].ioaddr.lbal_addr);
sata_outb ((blknr >> 8) & 0xFF, port[num].ioaddr.lbam_addr);
sata_outb ((blknr >> 16) & 0xFF, port[num].ioaddr.lbah_addr);
#ifdef CONFIG_LBA48
if (lba48) {
sata_outb (ATA_LBA, port[num].ioaddr.device_addr);
sata_outb (ATA_CMD_WRITE_EXT,
port[num].ioaddr.command_addr);
} else
#endif
{
sata_outb (ATA_LBA | ((blknr >> 24) & 0xF),
port[num].ioaddr.device_addr);
sata_outb (ATA_CMD_WRITE,
port[num].ioaddr.command_addr);
}
msleep (50);
/*may take up to 4 sec */
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 4000);
if ((status & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR))
!= ATA_STAT_DRQ) {
printf ("Error no DRQ dev %d blk %ld: sts 0x%02x\n",
device, (ulong) blknr, status);
return (n);
}
output_data (&port[num].ioaddr, buffer, ATA_SECTORWORDS);
sata_inb (port[num].ioaddr.altstatus_addr);
udelay (50);
++n;
++blknr;
buffer += ATA_SECTORWORDS;
}
return n;
}
int scan_sata(int dev)
{
return 0;
}

View file

@ -0,0 +1,81 @@
#ifndef __ATA_PIIX_H__
#define __ATA_PIIX_H__
#if (DEBUG_SATA)
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
struct sata_ioports {
unsigned long cmd_addr;
unsigned long data_addr;
unsigned long error_addr;
unsigned long feature_addr;
unsigned long nsect_addr;
unsigned long lbal_addr;
unsigned long lbam_addr;
unsigned long lbah_addr;
unsigned long device_addr;
unsigned long status_addr;
unsigned long command_addr;
unsigned long altstatus_addr;
unsigned long ctl_addr;
unsigned long bmdma_addr;
unsigned long scr_addr;
};
struct sata_port {
unsigned char port_no; /* primary=0, secondary=1 */
struct sata_ioports ioaddr; /* ATA cmd/ctl/dma reg blks */
unsigned char ctl_reg;
unsigned char last_ctl;
unsigned char port_state; /* 1-port is available and */
/* 0-port is not available */
unsigned char dev_mask;
};
/***********SATA LIBRARY SPECIFIC DEFINITIONS AND DECLARATIONS**************/
#ifdef SATA_DECL /*SATA library specific declarations */
inline void
ata_dump_id (u16 * id)
{
PRINTF ("49 = 0x%04x "
"53 = 0x%04x "
"63 = 0x%04x "
"64 = 0x%04x "
"75 = 0x%04x \n", id[49], id[53], id[63], id[64], id[75]);
PRINTF ("80 = 0x%04x "
"81 = 0x%04x "
"82 = 0x%04x "
"83 = 0x%04x "
"84 = 0x%04x \n", id[80], id[81], id[82], id[83], id[84]);
PRINTF ("88 = 0x%04x " "93 = 0x%04x\n", id[88], id[93]);
}
#endif
#ifdef SATA_DECL /*SATA library specific declarations */
int sata_bus_softreset (int num);
void sata_identify (int num, int dev);
void sata_port (struct sata_ioports *ioport);
void set_Feature_cmd (int num, int dev);
int sata_devchk (struct sata_ioports *ioaddr, int dev);
void dev_select (struct sata_ioports *ioaddr, int dev);
u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits, unsigned int max);
u8 sata_chk_status (struct sata_ioports *ioaddr);
ulong sata_read (int device, ulong blknr,lbaint_t blkcnt, void * buffer);
ulong sata_write (int device,ulong blknr, lbaint_t blkcnt, void * buffer);
void msleep (int count);
#endif
/************DRIVER SPECIFIC DEFINITIONS AND DECLARATIONS**************/
#ifdef DRV_DECL /*Driver specific declaration */
int init_sata (int dev);
#endif
#ifdef DRV_DECL /*Defines Driver Specific variables */
struct sata_port port[CONFIG_SYS_SATA_MAXBUS];
#endif
#endif /* __ATA_PIIX_H__ */

View file

@ -0,0 +1,969 @@
/*
* Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
* Terry Lv <r65388@freescale.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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.
*
*/
#include <libata.h>
#include <ahci.h>
#include <fis.h>
#include <common.h>
#include <malloc.h>
#include <linux/ctype.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <linux/bitops.h>
#include <asm/arch/clock.h>
#include "dwc_ahsata.h"
struct sata_port_regs {
u32 clb;
u32 clbu;
u32 fb;
u32 fbu;
u32 is;
u32 ie;
u32 cmd;
u32 res1[1];
u32 tfd;
u32 sig;
u32 ssts;
u32 sctl;
u32 serr;
u32 sact;
u32 ci;
u32 sntf;
u32 res2[1];
u32 dmacr;
u32 res3[1];
u32 phycr;
u32 physr;
};
struct sata_host_regs {
u32 cap;
u32 ghc;
u32 is;
u32 pi;
u32 vs;
u32 ccc_ctl;
u32 ccc_ports;
u32 res1[2];
u32 cap2;
u32 res2[30];
u32 bistafr;
u32 bistcr;
u32 bistfctr;
u32 bistsr;
u32 bistdecr;
u32 res3[2];
u32 oobr;
u32 res4[8];
u32 timer1ms;
u32 res5[1];
u32 gparam1r;
u32 gparam2r;
u32 pparamr;
u32 testr;
u32 versionr;
u32 idr;
};
#define MAX_DATA_BYTES_PER_SG (4 * 1024 * 1024)
#define MAX_BYTES_PER_TRANS (AHCI_MAX_SG * MAX_DATA_BYTES_PER_SG)
#define writel_with_flush(a, b) do { writel(a, b); readl(b); } while (0)
static int is_ready;
static inline u32 ahci_port_base(u32 base, u32 port)
{
return base + 0x100 + (port * 0x80);
}
static int waiting_for_cmd_completed(u8 *offset,
int timeout_msec,
u32 sign)
{
int i;
u32 status;
for (i = 0;
((status = readl(offset)) & sign) && i < timeout_msec;
++i)
mdelay(1);
return (i < timeout_msec) ? 0 : -1;
}
static int ahci_setup_oobr(struct ahci_probe_ent *probe_ent,
int clk)
{
struct sata_host_regs *host_mmio =
(struct sata_host_regs *)probe_ent->mmio_base;
writel(SATA_HOST_OOBR_WE, &(host_mmio->oobr));
writel(0x02060b14, &(host_mmio->oobr));
return 0;
}
static int ahci_host_init(struct ahci_probe_ent *probe_ent)
{
u32 tmp, cap_save, num_ports;
int i, j, timeout = 1000;
struct sata_port_regs *port_mmio = NULL;
struct sata_host_regs *host_mmio =
(struct sata_host_regs *)probe_ent->mmio_base;
int clk = mxc_get_clock(MXC_SATA_CLK);
cap_save = readl(&(host_mmio->cap));
cap_save |= SATA_HOST_CAP_SSS;
/* global controller reset */
tmp = readl(&(host_mmio->ghc));
if ((tmp & SATA_HOST_GHC_HR) == 0)
writel_with_flush(tmp | SATA_HOST_GHC_HR, &(host_mmio->ghc));
while ((readl(&(host_mmio->ghc)) & SATA_HOST_GHC_HR)
&& --timeout)
;
if (timeout <= 0) {
debug("controller reset failed (0x%x)\n", tmp);
return -1;
}
/* Set timer 1ms */
writel(clk / 1000, &(host_mmio->timer1ms));
ahci_setup_oobr(probe_ent, 0);
writel_with_flush(SATA_HOST_GHC_AE, &(host_mmio->ghc));
writel(cap_save, &(host_mmio->cap));
num_ports = (cap_save & SATA_HOST_CAP_NP_MASK) + 1;
writel_with_flush((1 << num_ports) - 1,
&(host_mmio->pi));
/*
* Determine which Ports are implemented by the DWC_ahsata,
* by reading the PI register. This bit map value aids the
* software to determine how many Ports are available and
* which Port registers need to be initialized.
*/
probe_ent->cap = readl(&(host_mmio->cap));
probe_ent->port_map = readl(&(host_mmio->pi));
/* Determine how many command slots the HBA supports */
probe_ent->n_ports =
(probe_ent->cap & SATA_HOST_CAP_NP_MASK) + 1;
debug("cap 0x%x port_map 0x%x n_ports %d\n",
probe_ent->cap, probe_ent->port_map, probe_ent->n_ports);
for (i = 0; i < probe_ent->n_ports; i++) {
probe_ent->port[i].port_mmio =
ahci_port_base((u32)host_mmio, i);
port_mmio =
(struct sata_port_regs *)probe_ent->port[i].port_mmio;
/* Ensure that the DWC_ahsata is in idle state */
tmp = readl(&(port_mmio->cmd));
/*
* When P#CMD.ST, P#CMD.CR, P#CMD.FRE and P#CMD.FR
* are all cleared, the Port is in an idle state.
*/
if (tmp & (SATA_PORT_CMD_CR | SATA_PORT_CMD_FR |
SATA_PORT_CMD_FRE | SATA_PORT_CMD_ST)) {
/*
* System software places a Port into the idle state by
* clearing P#CMD.ST and waiting for P#CMD.CR to return
* 0 when read.
*/
tmp &= ~SATA_PORT_CMD_ST;
writel_with_flush(tmp, &(port_mmio->cmd));
/*
* spec says 500 msecs for each bit, so
* this is slightly incorrect.
*/
mdelay(500);
timeout = 1000;
while ((readl(&(port_mmio->cmd)) & SATA_PORT_CMD_CR)
&& --timeout)
;
if (timeout <= 0) {
debug("port reset failed (0x%x)\n", tmp);
return -1;
}
}
/* Spin-up device */
tmp = readl(&(port_mmio->cmd));
writel((tmp | SATA_PORT_CMD_SUD), &(port_mmio->cmd));
/* Wait for spin-up to finish */
timeout = 1000;
while (!(readl(&(port_mmio->cmd)) | SATA_PORT_CMD_SUD)
&& --timeout)
;
if (timeout <= 0) {
debug("Spin-Up can't finish!\n");
return -1;
}
for (j = 0; j < 100; ++j) {
mdelay(10);
tmp = readl(&(port_mmio->ssts));
if (((tmp & SATA_PORT_SSTS_DET_MASK) == 0x3) ||
((tmp & SATA_PORT_SSTS_DET_MASK) == 0x1))
break;
}
/* Wait for COMINIT bit 26 (DIAG_X) in SERR */
timeout = 1000;
while (!(readl(&(port_mmio->serr)) | SATA_PORT_SERR_DIAG_X)
&& --timeout)
;
if (timeout <= 0) {
debug("Can't find DIAG_X set!\n");
return -1;
}
/*
* For each implemented Port, clear the P#SERR
* register, by writing ones to each implemented\
* bit location.
*/
tmp = readl(&(port_mmio->serr));
debug("P#SERR 0x%x\n",
tmp);
writel(tmp, &(port_mmio->serr));
/* Ack any pending irq events for this port */
tmp = readl(&(host_mmio->is));
debug("IS 0x%x\n", tmp);
if (tmp)
writel(tmp, &(host_mmio->is));
writel(1 << i, &(host_mmio->is));
/* set irq mask (enables interrupts) */
writel(DEF_PORT_IRQ, &(port_mmio->ie));
/* register linkup ports */
tmp = readl(&(port_mmio->ssts));
debug("Port %d status: 0x%x\n", i, tmp);
if ((tmp & SATA_PORT_SSTS_DET_MASK) == 0x03)
probe_ent->link_port_map |= (0x01 << i);
}
tmp = readl(&(host_mmio->ghc));
debug("GHC 0x%x\n", tmp);
writel(tmp | SATA_HOST_GHC_IE, &(host_mmio->ghc));
tmp = readl(&(host_mmio->ghc));
debug("GHC 0x%x\n", tmp);
return 0;
}
static void ahci_print_info(struct ahci_probe_ent *probe_ent)
{
struct sata_host_regs *host_mmio =
(struct sata_host_regs *)probe_ent->mmio_base;
u32 vers, cap, impl, speed;
const char *speed_s;
const char *scc_s;
vers = readl(&(host_mmio->vs));
cap = probe_ent->cap;
impl = probe_ent->port_map;
speed = (cap & SATA_HOST_CAP_ISS_MASK)
>> SATA_HOST_CAP_ISS_OFFSET;
if (speed == 1)
speed_s = "1.5";
else if (speed == 2)
speed_s = "3";
else
speed_s = "?";
scc_s = "SATA";
printf("AHCI %02x%02x.%02x%02x "
"%u slots %u ports %s Gbps 0x%x impl %s mode\n",
(vers >> 24) & 0xff,
(vers >> 16) & 0xff,
(vers >> 8) & 0xff,
vers & 0xff,
((cap >> 8) & 0x1f) + 1,
(cap & 0x1f) + 1,
speed_s,
impl,
scc_s);
printf("flags: "
"%s%s%s%s%s%s"
"%s%s%s%s%s%s%s\n",
cap & (1 << 31) ? "64bit " : "",
cap & (1 << 30) ? "ncq " : "",
cap & (1 << 28) ? "ilck " : "",
cap & (1 << 27) ? "stag " : "",
cap & (1 << 26) ? "pm " : "",
cap & (1 << 25) ? "led " : "",
cap & (1 << 24) ? "clo " : "",
cap & (1 << 19) ? "nz " : "",
cap & (1 << 18) ? "only " : "",
cap & (1 << 17) ? "pmp " : "",
cap & (1 << 15) ? "pio " : "",
cap & (1 << 14) ? "slum " : "",
cap & (1 << 13) ? "part " : "");
}
static int ahci_init_one(int pdev)
{
int rc;
struct ahci_probe_ent *probe_ent = NULL;
probe_ent = malloc(sizeof(struct ahci_probe_ent));
memset(probe_ent, 0, sizeof(struct ahci_probe_ent));
probe_ent->dev = pdev;
probe_ent->host_flags = ATA_FLAG_SATA
| ATA_FLAG_NO_LEGACY
| ATA_FLAG_MMIO
| ATA_FLAG_PIO_DMA
| ATA_FLAG_NO_ATAPI;
probe_ent->mmio_base = CONFIG_DWC_AHSATA_BASE_ADDR;
/* initialize adapter */
rc = ahci_host_init(probe_ent);
if (rc)
goto err_out;
ahci_print_info(probe_ent);
/* Save the private struct to block device struct */
sata_dev_desc[pdev].priv = (void *)probe_ent;
return 0;
err_out:
return rc;
}
static int ahci_fill_sg(struct ahci_probe_ent *probe_ent,
u8 port, unsigned char *buf, int buf_len)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
struct ahci_sg *ahci_sg = pp->cmd_tbl_sg;
u32 sg_count, max_bytes;
int i;
max_bytes = MAX_DATA_BYTES_PER_SG;
sg_count = ((buf_len - 1) / max_bytes) + 1;
if (sg_count > AHCI_MAX_SG) {
printf("Error:Too much sg!\n");
return -1;
}
for (i = 0; i < sg_count; i++) {
ahci_sg->addr =
cpu_to_le32((u32)buf + i * max_bytes);
ahci_sg->addr_hi = 0;
ahci_sg->flags_size = cpu_to_le32(0x3fffff &
(buf_len < max_bytes
? (buf_len - 1)
: (max_bytes - 1)));
ahci_sg++;
buf_len -= max_bytes;
}
return sg_count;
}
static void ahci_fill_cmd_slot(struct ahci_ioports *pp, u32 cmd_slot, u32 opts)
{
struct ahci_cmd_hdr *cmd_hdr = (struct ahci_cmd_hdr *)(pp->cmd_slot +
AHCI_CMD_SLOT_SZ * cmd_slot);
memset(cmd_hdr, 0, AHCI_CMD_SLOT_SZ);
cmd_hdr->opts = cpu_to_le32(opts);
cmd_hdr->status = 0;
cmd_hdr->tbl_addr = cpu_to_le32(pp->cmd_tbl & 0xffffffff);
cmd_hdr->tbl_addr_hi = 0;
}
#define AHCI_GET_CMD_SLOT(c) ((c) ? ffs(c) : 0)
static int ahci_exec_ata_cmd(struct ahci_probe_ent *probe_ent,
u8 port, struct sata_fis_h2d *cfis,
u8 *buf, u32 buf_len, s32 is_write)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
struct sata_port_regs *port_mmio =
(struct sata_port_regs *)pp->port_mmio;
u32 opts;
int sg_count = 0, cmd_slot = 0;
cmd_slot = AHCI_GET_CMD_SLOT(readl(&(port_mmio->ci)));
if (32 == cmd_slot) {
printf("Can't find empty command slot!\n");
return 0;
}
/* Check xfer length */
if (buf_len > MAX_BYTES_PER_TRANS) {
printf("Max transfer length is %dB\n\r",
MAX_BYTES_PER_TRANS);
return 0;
}
memcpy((u8 *)(pp->cmd_tbl), cfis, sizeof(struct sata_fis_h2d));
if (buf && buf_len)
sg_count = ahci_fill_sg(probe_ent, port, buf, buf_len);
opts = (sizeof(struct sata_fis_h2d) >> 2) | (sg_count << 16);
if (is_write)
opts |= 0x40;
ahci_fill_cmd_slot(pp, cmd_slot, opts);
writel_with_flush(1 << cmd_slot, &(port_mmio->ci));
if (waiting_for_cmd_completed((u8 *)&(port_mmio->ci),
10000, 0x1 << cmd_slot)) {
printf("timeout exit!\n");
return -1;
}
debug("ahci_exec_ata_cmd: %d byte transferred.\n",
pp->cmd_slot->status);
return buf_len;
}
static void ahci_set_feature(u8 dev, u8 port)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 1 << 7;
cfis->command = ATA_CMD_SET_FEATURES;
cfis->features = SETFEATURES_XFER;
cfis->sector_count = ffs(probe_ent->udma_mask + 1) + 0x3e;
ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, READ_CMD);
}
static int ahci_port_start(struct ahci_probe_ent *probe_ent,
u8 port)
{
struct ahci_ioports *pp = &(probe_ent->port[port]);
struct sata_port_regs *port_mmio =
(struct sata_port_regs *)pp->port_mmio;
u32 port_status;
u32 mem;
int timeout = 10000000;
debug("Enter start port: %d\n", port);
port_status = readl(&(port_mmio->ssts));
debug("Port %d status: %x\n", port, port_status);
if ((port_status & 0xf) != 0x03) {
printf("No Link on this port!\n");
return -1;
}
mem = (u32)malloc(AHCI_PORT_PRIV_DMA_SZ + 1024);
if (!mem) {
free(pp);
printf("No mem for table!\n");
return -ENOMEM;
}
mem = (mem + 0x400) & (~0x3ff); /* Aligned to 1024-bytes */
memset((u8 *)mem, 0, AHCI_PORT_PRIV_DMA_SZ);
/*
* First item in chunk of DMA memory: 32-slot command table,
* 32 bytes each in size
*/
pp->cmd_slot = (struct ahci_cmd_hdr *)mem;
debug("cmd_slot = 0x%x\n", (unsigned int) pp->cmd_slot);
mem += (AHCI_CMD_SLOT_SZ * DWC_AHSATA_MAX_CMD_SLOTS);
/*
* Second item: Received-FIS area, 256-Byte aligned
*/
pp->rx_fis = mem;
mem += AHCI_RX_FIS_SZ;
/*
* Third item: data area for storing a single command
* and its scatter-gather table
*/
pp->cmd_tbl = mem;
debug("cmd_tbl_dma = 0x%x\n", pp->cmd_tbl);
mem += AHCI_CMD_TBL_HDR;
writel_with_flush(0x00004444, &(port_mmio->dmacr));
pp->cmd_tbl_sg = (struct ahci_sg *)mem;
writel_with_flush((u32)pp->cmd_slot, &(port_mmio->clb));
writel_with_flush(pp->rx_fis, &(port_mmio->fb));
/* Enable FRE */
writel_with_flush((SATA_PORT_CMD_FRE | readl(&(port_mmio->cmd))),
&(port_mmio->cmd));
/* Wait device ready */
while ((readl(&(port_mmio->tfd)) & (SATA_PORT_TFD_STS_ERR |
SATA_PORT_TFD_STS_DRQ | SATA_PORT_TFD_STS_BSY))
&& --timeout)
;
if (timeout <= 0) {
debug("Device not ready for BSY, DRQ and"
"ERR in TFD!\n");
return -1;
}
writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
PORT_CMD_START, &(port_mmio->cmd));
debug("Exit start port %d\n", port);
return 0;
}
int init_sata(int dev)
{
int i;
u32 linkmap;
struct ahci_probe_ent *probe_ent = NULL;
if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1)) {
printf("The sata index %d is out of ranges\n\r", dev);
return -1;
}
ahci_init_one(dev);
probe_ent = (struct ahci_probe_ent *)sata_dev_desc[dev].priv;
linkmap = probe_ent->link_port_map;
if (0 == linkmap) {
printf("No port device detected!\n");
return 1;
}
for (i = 0; i < probe_ent->n_ports; i++) {
if ((linkmap >> i) && ((linkmap >> i) & 0x01)) {
if (ahci_port_start(probe_ent, (u8)i)) {
printf("Can not start port %d\n", i);
return 1;
}
probe_ent->hard_port_no = i;
break;
}
}
return 0;
}
static void dwc_ahsata_print_info(int dev)
{
block_dev_desc_t *pdev = &(sata_dev_desc[dev]);
printf("SATA Device Info:\n\r");
#ifdef CONFIG_SYS_64BIT_LBA
printf("S/N: %s\n\rProduct model number: %s\n\r"
"Firmware version: %s\n\rCapacity: %lld sectors\n\r",
pdev->product, pdev->vendor, pdev->revision, pdev->lba);
#else
printf("S/N: %s\n\rProduct model number: %s\n\r"
"Firmware version: %s\n\rCapacity: %ld sectors\n\r",
pdev->product, pdev->vendor, pdev->revision, pdev->lba);
#endif
}
static void dwc_ahsata_identify(int dev, u16 *id)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_ID_ATA;
ahci_exec_ata_cmd(probe_ent, port, cfis,
(u8 *)id, ATA_ID_WORDS * 2, READ_CMD);
ata_swap_buf_le16(id, ATA_ID_WORDS);
}
static void dwc_ahsata_xfer_mode(int dev, u16 *id)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
probe_ent->pio_mask = id[ATA_ID_PIO_MODES];
probe_ent->udma_mask = id[ATA_ID_UDMA_MODES];
debug("pio %04x, udma %04x\n\r",
probe_ent->pio_mask, probe_ent->udma_mask);
}
static u32 dwc_ahsata_rw_cmd(int dev, u32 start, u32 blkcnt,
u8 *buffer, int is_write)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
u32 block;
block = start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_WRITE : ATA_CMD_READ;
cfis->device = ATA_LBA;
cfis->device |= (block >> 24) & 0xf;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->sector_count = (u8)(blkcnt & 0xff);
if (ahci_exec_ata_cmd(probe_ent, port, cfis,
buffer, ATA_SECT_SIZE * blkcnt, is_write) > 0)
return blkcnt;
else
return 0;
}
void dwc_ahsata_flush_cache(int dev)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_FLUSH;
ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, 0);
}
static u32 dwc_ahsata_rw_cmd_ext(int dev, u32 start, lbaint_t blkcnt,
u8 *buffer, int is_write)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
u64 block;
block = (u64)start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_WRITE_EXT
: ATA_CMD_READ_EXT;
cfis->lba_high_exp = (block >> 40) & 0xff;
cfis->lba_mid_exp = (block >> 32) & 0xff;
cfis->lba_low_exp = (block >> 24) & 0xff;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->device = ATA_LBA;
cfis->sector_count_exp = (blkcnt >> 8) & 0xff;
cfis->sector_count = blkcnt & 0xff;
if (ahci_exec_ata_cmd(probe_ent, port, cfis, buffer,
ATA_SECT_SIZE * blkcnt, is_write) > 0)
return blkcnt;
else
return 0;
}
u32 dwc_ahsata_rw_ncq_cmd(int dev, u32 start, lbaint_t blkcnt,
u8 *buffer, int is_write)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
u64 block;
if (sata_dev_desc[dev].lba48 != 1) {
printf("execute FPDMA command on non-LBA48 hard disk\n\r");
return -1;
}
block = (u64)start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_FPDMA_WRITE
: ATA_CMD_FPDMA_READ;
cfis->lba_high_exp = (block >> 40) & 0xff;
cfis->lba_mid_exp = (block >> 32) & 0xff;
cfis->lba_low_exp = (block >> 24) & 0xff;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->device = ATA_LBA;
cfis->features_exp = (blkcnt >> 8) & 0xff;
cfis->features = blkcnt & 0xff;
/* Use the latest queue */
ahci_exec_ata_cmd(probe_ent, port, cfis,
buffer, ATA_SECT_SIZE * blkcnt, is_write);
return blkcnt;
}
void dwc_ahsata_flush_cache_ext(int dev)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 port = probe_ent->hard_port_no;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_FLUSH_EXT;
ahci_exec_ata_cmd(probe_ent, port, cfis, NULL, 0, 0);
}
static void dwc_ahsata_init_wcache(int dev, u16 *id)
{
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id))
probe_ent->flags |= SATA_FLAG_WCACHE;
if (ata_id_has_flush(id))
probe_ent->flags |= SATA_FLAG_FLUSH;
if (ata_id_has_flush_ext(id))
probe_ent->flags |= SATA_FLAG_FLUSH_EXT;
}
u32 ata_low_level_rw_lba48(int dev, u32 blknr, lbaint_t blkcnt,
void *buffer, int is_write)
{
u32 start, blks;
u8 *addr;
int max_blks;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS_LBA48;
do {
if (blks > max_blks) {
if (max_blks != dwc_ahsata_rw_cmd_ext(dev, start,
max_blks, addr, is_write))
return 0;
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
if (blks != dwc_ahsata_rw_cmd_ext(dev, start,
blks, addr, is_write))
return 0;
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
u32 ata_low_level_rw_lba28(int dev, u32 blknr, lbaint_t blkcnt,
void *buffer, int is_write)
{
u32 start, blks;
u8 *addr;
int max_blks;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS;
do {
if (blks > max_blks) {
if (max_blks != dwc_ahsata_rw_cmd(dev, start,
max_blks, addr, is_write))
return 0;
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
if (blks != dwc_ahsata_rw_cmd(dev, start,
blks, addr, is_write))
return 0;
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
/*
* SATA interface between low level driver and command layer
*/
ulong sata_read(int dev, unsigned long blknr, lbaint_t blkcnt, void *buffer)
{
u32 rc;
if (sata_dev_desc[dev].lba48)
rc = ata_low_level_rw_lba48(dev, blknr, blkcnt,
buffer, READ_CMD);
else
rc = ata_low_level_rw_lba28(dev, blknr, blkcnt,
buffer, READ_CMD);
return rc;
}
ulong sata_write(int dev, unsigned long blknr, lbaint_t blkcnt, void *buffer)
{
u32 rc;
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
u32 flags = probe_ent->flags;
if (sata_dev_desc[dev].lba48) {
rc = ata_low_level_rw_lba48(dev, blknr, blkcnt,
buffer, WRITE_CMD);
if ((flags & SATA_FLAG_WCACHE) &&
(flags & SATA_FLAG_FLUSH_EXT))
dwc_ahsata_flush_cache_ext(dev);
} else {
rc = ata_low_level_rw_lba28(dev, blknr, blkcnt,
buffer, WRITE_CMD);
if ((flags & SATA_FLAG_WCACHE) &&
(flags & SATA_FLAG_FLUSH))
dwc_ahsata_flush_cache(dev);
}
return rc;
}
int scan_sata(int dev)
{
u8 serial[ATA_ID_SERNO_LEN + 1] = { 0 };
u8 firmware[ATA_ID_FW_REV_LEN + 1] = { 0 };
u8 product[ATA_ID_PROD_LEN + 1] = { 0 };
u16 *id;
u64 n_sectors;
struct ahci_probe_ent *probe_ent =
(struct ahci_probe_ent *)sata_dev_desc[dev].priv;
u8 port = probe_ent->hard_port_no;
block_dev_desc_t *pdev = &(sata_dev_desc[dev]);
id = (u16 *)malloc(ATA_ID_WORDS * 2);
if (!id) {
printf("id malloc failed\n\r");
return -1;
}
/* Identify device to get information */
dwc_ahsata_identify(dev, id);
/* Serial number */
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
memcpy(pdev->product, serial, sizeof(serial));
/* Firmware version */
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
memcpy(pdev->revision, firmware, sizeof(firmware));
/* Product model */
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
memcpy(pdev->vendor, product, sizeof(product));
/* Totoal sectors */
n_sectors = ata_id_n_sectors(id);
pdev->lba = (u32)n_sectors;
pdev->type = DEV_TYPE_HARDDISK;
pdev->blksz = ATA_SECT_SIZE;
pdev->lun = 0 ;
/* Check if support LBA48 */
if (ata_id_has_lba48(id)) {
pdev->lba48 = 1;
debug("Device support LBA48\n\r");
}
/* Get the NCQ queue depth from device */
probe_ent->flags &= (~SATA_FLAG_Q_DEP_MASK);
probe_ent->flags |= ata_id_queue_depth(id);
/* Get the xfer mode from device */
dwc_ahsata_xfer_mode(dev, id);
/* Get the write cache status from device */
dwc_ahsata_init_wcache(dev, id);
/* Set the xfer mode to highest speed */
ahci_set_feature(dev, port);
free((void *)id);
dwc_ahsata_print_info(dev);
is_ready = 1;
return 0;
}

View file

@ -0,0 +1,335 @@
/*
* Copyright (C) 2010 Freescale Semiconductor, Inc.
* Terry Lv <r65388@freescale.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef __FSL_SATA_H__
#define __FSL_SATA_H__
#define DWC_AHSATA_MAX_CMD_SLOTS 32
/* Max host controller numbers */
#define SATA_HC_MAX_NUM 4
/* Max command queue depth per host controller */
#define DWC_AHSATA_HC_MAX_CMD 32
/* Max port number per host controller */
#define SATA_HC_MAX_PORT 16
/* Generic Host Register */
/* HBA Capabilities Register */
#define SATA_HOST_CAP_S64A 0x80000000
#define SATA_HOST_CAP_SNCQ 0x40000000
#define SATA_HOST_CAP_SSNTF 0x20000000
#define SATA_HOST_CAP_SMPS 0x10000000
#define SATA_HOST_CAP_SSS 0x08000000
#define SATA_HOST_CAP_SALP 0x04000000
#define SATA_HOST_CAP_SAL 0x02000000
#define SATA_HOST_CAP_SCLO 0x01000000
#define SATA_HOST_CAP_ISS_MASK 0x00f00000
#define SATA_HOST_CAP_ISS_OFFSET 20
#define SATA_HOST_CAP_SNZO 0x00080000
#define SATA_HOST_CAP_SAM 0x00040000
#define SATA_HOST_CAP_SPM 0x00020000
#define SATA_HOST_CAP_PMD 0x00008000
#define SATA_HOST_CAP_SSC 0x00004000
#define SATA_HOST_CAP_PSC 0x00002000
#define SATA_HOST_CAP_NCS 0x00001f00
#define SATA_HOST_CAP_CCCS 0x00000080
#define SATA_HOST_CAP_EMS 0x00000040
#define SATA_HOST_CAP_SXS 0x00000020
#define SATA_HOST_CAP_NP_MASK 0x0000001f
/* Global HBA Control Register */
#define SATA_HOST_GHC_AE 0x80000000
#define SATA_HOST_GHC_IE 0x00000002
#define SATA_HOST_GHC_HR 0x00000001
/* Interrupt Status Register */
/* Ports Implemented Register */
/* AHCI Version Register */
#define SATA_HOST_VS_MJR_MASK 0xffff0000
#define SATA_HOST_VS_MJR_OFFSET 16
#define SATA_HOST_VS_MJR_MNR 0x0000ffff
/* Command Completion Coalescing Control */
#define SATA_HOST_CCC_CTL_TV_MASK 0xffff0000
#define SATA_HOST_CCC_CTL_TV_OFFSET 16
#define SATA_HOST_CCC_CTL_CC_MASK 0x0000ff00
#define SATA_HOST_CCC_CTL_CC_OFFSET 8
#define SATA_HOST_CCC_CTL_INT_MASK 0x000000f8
#define SATA_HOST_CCC_CTL_INT_OFFSET 3
#define SATA_HOST_CCC_CTL_EN 0x00000001
/* Command Completion Coalescing Ports */
/* HBA Capabilities Extended Register */
#define SATA_HOST_CAP2_APST 0x00000004
/* BIST Activate FIS Register */
#define SATA_HOST_BISTAFR_NCP_MASK 0x0000ff00
#define SATA_HOST_BISTAFR_NCP_OFFSET 8
#define SATA_HOST_BISTAFR_PD_MASK 0x000000ff
#define SATA_HOST_BISTAFR_PD_OFFSET 0
/* BIST Control Register */
#define SATA_HOST_BISTCR_FERLB 0x00100000
#define SATA_HOST_BISTCR_TXO 0x00040000
#define SATA_HOST_BISTCR_CNTCLR 0x00020000
#define SATA_HOST_BISTCR_NEALB 0x00010000
#define SATA_HOST_BISTCR_LLC_MASK 0x00000700
#define SATA_HOST_BISTCR_LLC_OFFSET 8
#define SATA_HOST_BISTCR_ERREN 0x00000040
#define SATA_HOST_BISTCR_FLIP 0x00000020
#define SATA_HOST_BISTCR_PV 0x00000010
#define SATA_HOST_BISTCR_PATTERN_MASK 0x0000000f
#define SATA_HOST_BISTCR_PATTERN_OFFSET 0
/* BIST FIS Count Register */
/* BIST Status Register */
#define SATA_HOST_BISTSR_FRAMERR_MASK 0x0000ffff
#define SATA_HOST_BISTSR_FRAMERR_OFFSET 0
#define SATA_HOST_BISTSR_BRSTERR_MASK 0x00ff0000
#define SATA_HOST_BISTSR_BRSTERR_OFFSET 16
/* BIST DWORD Error Count Register */
/* OOB Register*/
#define SATA_HOST_OOBR_WE 0x80000000
#define SATA_HOST_OOBR_cwMin_MASK 0x7f000000
#define SATA_HOST_OOBR_cwMAX_MASK 0x00ff0000
#define SATA_HOST_OOBR_ciMin_MASK 0x0000ff00
#define SATA_HOST_OOBR_ciMax_MASK 0x000000ff
/* Timer 1-ms Register */
/* Global Parameter 1 Register */
#define SATA_HOST_GPARAM1R_ALIGN_M 0x80000000
#define SATA_HOST_GPARAM1R_RX_BUFFER 0x40000000
#define SATA_HOST_GPARAM1R_PHY_DATA_MASK 0x30000000
#define SATA_HOST_GPARAM1R_PHY_RST 0x08000000
#define SATA_HOST_GPARAM1R_PHY_CTRL_MASK 0x07e00000
#define SATA_HOST_GPARAM1R_PHY_STAT_MASK 0x001f8000
#define SATA_HOST_GPARAM1R_LATCH_M 0x00004000
#define SATA_HOST_GPARAM1R_BIST_M 0x00002000
#define SATA_HOST_GPARAM1R_PHY_TYPE 0x00001000
#define SATA_HOST_GPARAM1R_RETURN_ERR 0x00000400
#define SATA_HOST_GPARAM1R_AHB_ENDIAN_MASK 0x00000300
#define SATA_HOST_GPARAM1R_S_HADDR 0X00000080
#define SATA_HOST_GPARAM1R_M_HADDR 0X00000040
/* Global Parameter 2 Register */
#define SATA_HOST_GPARAM2R_DEV_CP 0x00004000
#define SATA_HOST_GPARAM2R_DEV_MP 0x00002000
#define SATA_HOST_GPARAM2R_DEV_ENCODE_M 0x00001000
#define SATA_HOST_GPARAM2R_RXOOB_CLK_M 0x00000800
#define SATA_HOST_GPARAM2R_RXOOB_M 0x00000400
#define SATA_HOST_GPARAM2R_TX_OOB_M 0x00000200
#define SATA_HOST_GPARAM2R_RXOOB_CLK_MASK 0x000001ff
/* Port Parameter Register */
#define SATA_HOST_PPARAMR_TX_MEM_M 0x00000200
#define SATA_HOST_PPARAMR_TX_MEM_S 0x00000100
#define SATA_HOST_PPARAMR_RX_MEM_M 0x00000080
#define SATA_HOST_PPARAMR_RX_MEM_S 0x00000040
#define SATA_HOST_PPARAMR_TXFIFO_DEPTH_MASK 0x00000038
#define SATA_HOST_PPARAMR_RXFIFO_DEPTH_MASK 0x00000007
/* Test Register */
#define SATA_HOST_TESTR_PSEL_MASK 0x00070000
#define SATA_HOST_TESTR_TEST_IF 0x00000001
/* Port Register Descriptions */
/* Port# Command List Base Address Register */
#define SATA_PORT_CLB_CLB_MASK 0xfffffc00
/* Port# Command List Base Address Upper 32-Bits Register */
/* Port# FIS Base Address Register */
#define SATA_PORT_FB_FB_MASK 0xfffffff0
/* Port# FIS Base Address Upper 32-Bits Register */
/* Port# Interrupt Status Register */
#define SATA_PORT_IS_CPDS 0x80000000
#define SATA_PORT_IS_TFES 0x40000000
#define SATA_PORT_IS_HBFS 0x20000000
#define SATA_PORT_IS_HBDS 0x10000000
#define SATA_PORT_IS_IFS 0x08000000
#define SATA_PORT_IS_INFS 0x04000000
#define SATA_PORT_IS_OFS 0x01000000
#define SATA_PORT_IS_IPMS 0x00800000
#define SATA_PORT_IS_PRCS 0x00400000
#define SATA_PORT_IS_DMPS 0x00000080
#define SATA_PORT_IS_PCS 0x00000040
#define SATA_PORT_IS_DPS 0x00000020
#define SATA_PORT_IS_UFS 0x00000010
#define SATA_PORT_IS_SDBS 0x00000008
#define SATA_PORT_IS_DSS 0x00000004
#define SATA_PORT_IS_PSS 0x00000002
#define SATA_PORT_IS_DHRS 0x00000001
/* Port# Interrupt Enable Register */
#define SATA_PORT_IE_CPDE 0x80000000
#define SATA_PORT_IE_TFEE 0x40000000
#define SATA_PORT_IE_HBFE 0x20000000
#define SATA_PORT_IE_HBDE 0x10000000
#define SATA_PORT_IE_IFE 0x08000000
#define SATA_PORT_IE_INFE 0x04000000
#define SATA_PORT_IE_OFE 0x01000000
#define SATA_PORT_IE_IPME 0x00800000
#define SATA_PORT_IE_PRCE 0x00400000
#define SATA_PORT_IE_DMPE 0x00000080
#define SATA_PORT_IE_PCE 0x00000040
#define SATA_PORT_IE_DPE 0x00000020
#define SATA_PORT_IE_UFE 0x00000010
#define SATA_PORT_IE_SDBE 0x00000008
#define SATA_PORT_IE_DSE 0x00000004
#define SATA_PORT_IE_PSE 0x00000002
#define SATA_PORT_IE_DHRE 0x00000001
/* Port# Command Register */
#define SATA_PORT_CMD_ICC_MASK 0xf0000000
#define SATA_PORT_CMD_ASP 0x08000000
#define SATA_PORT_CMD_ALPE 0x04000000
#define SATA_PORT_CMD_DLAE 0x02000000
#define SATA_PORT_CMD_ATAPI 0x01000000
#define SATA_PORT_CMD_APSTE 0x00800000
#define SATA_PORT_CMD_ESP 0x00200000
#define SATA_PORT_CMD_CPD 0x00100000
#define SATA_PORT_CMD_MPSP 0x00080000
#define SATA_PORT_CMD_HPCP 0x00040000
#define SATA_PORT_CMD_PMA 0x00020000
#define SATA_PORT_CMD_CPS 0x00010000
#define SATA_PORT_CMD_CR 0x00008000
#define SATA_PORT_CMD_FR 0x00004000
#define SATA_PORT_CMD_MPSS 0x00002000
#define SATA_PORT_CMD_CCS_MASK 0x00001f00
#define SATA_PORT_CMD_FRE 0x00000010
#define SATA_PORT_CMD_CLO 0x00000008
#define SATA_PORT_CMD_POD 0x00000004
#define SATA_PORT_CMD_SUD 0x00000002
#define SATA_PORT_CMD_ST 0x00000001
/* Port# Task File Data Register */
#define SATA_PORT_TFD_ERR_MASK 0x0000ff00
#define SATA_PORT_TFD_STS_MASK 0x000000ff
#define SATA_PORT_TFD_STS_ERR 0x00000001
#define SATA_PORT_TFD_STS_DRQ 0x00000008
#define SATA_PORT_TFD_STS_BSY 0x00000080
/* Port# Signature Register */
/* Port# Serial ATA Status {SStatus} Register */
#define SATA_PORT_SSTS_IPM_MASK 0x00000f00
#define SATA_PORT_SSTS_SPD_MASK 0x000000f0
#define SATA_PORT_SSTS_DET_MASK 0x0000000f
/* Port# Serial ATA Control {SControl} Register */
#define SATA_PORT_SCTL_IPM_MASK 0x00000f00
#define SATA_PORT_SCTL_SPD_MASK 0x000000f0
#define SATA_PORT_SCTL_DET_MASK 0x0000000f
/* Port# Serial ATA Error {SError} Register */
#define SATA_PORT_SERR_DIAG_X 0x04000000
#define SATA_PORT_SERR_DIAG_F 0x02000000
#define SATA_PORT_SERR_DIAG_T 0x01000000
#define SATA_PORT_SERR_DIAG_S 0x00800000
#define SATA_PORT_SERR_DIAG_H 0x00400000
#define SATA_PORT_SERR_DIAG_C 0x00200000
#define SATA_PORT_SERR_DIAG_D 0x00100000
#define SATA_PORT_SERR_DIAG_B 0x00080000
#define SATA_PORT_SERR_DIAG_W 0x00040000
#define SATA_PORT_SERR_DIAG_I 0x00020000
#define SATA_PORT_SERR_DIAG_N 0x00010000
#define SATA_PORT_SERR_ERR_E 0x00000800
#define SATA_PORT_SERR_ERR_P 0x00000400
#define SATA_PORT_SERR_ERR_C 0x00000200
#define SATA_PORT_SERR_ERR_T 0x00000100
#define SATA_PORT_SERR_ERR_M 0x00000002
#define SATA_PORT_SERR_ERR_I 0x00000001
/* Port# Serial ATA Active {SActive} Register */
/* Port# Command Issue Register */
/* Port# Serial ATA Notification Register */
/* Port# DMA Control Register */
#define SATA_PORT_DMACR_RXABL_MASK 0x0000f000
#define SATA_PORT_DMACR_TXABL_MASK 0x00000f00
#define SATA_PORT_DMACR_RXTS_MASK 0x000000f0
#define SATA_PORT_DMACR_TXTS_MASK 0x0000000f
/* Port# PHY Control Register */
/* Port# PHY Status Register */
#define SATA_HC_CMD_HDR_ENTRY_SIZE sizeof(struct cmd_hdr_entry)
/* DW0
*/
#define CMD_HDR_DI_CFL_MASK 0x0000001f
#define CMD_HDR_DI_CFL_OFFSET 0
#define CMD_HDR_DI_A 0x00000020
#define CMD_HDR_DI_W 0x00000040
#define CMD_HDR_DI_P 0x00000080
#define CMD_HDR_DI_R 0x00000100
#define CMD_HDR_DI_B 0x00000200
#define CMD_HDR_DI_C 0x00000400
#define CMD_HDR_DI_PMP_MASK 0x0000f000
#define CMD_HDR_DI_PMP_OFFSET 12
#define CMD_HDR_DI_PRDTL 0xffff0000
#define CMD_HDR_DI_PRDTL_OFFSET 16
/* prde_fis_len
*/
#define CMD_HDR_PRD_ENTRY_SHIFT 16
#define CMD_HDR_PRD_ENTRY_MASK 0x003f0000
#define CMD_HDR_FIS_LEN_SHIFT 2
/* attribute
*/
#define CMD_HDR_ATTR_RES 0x00000800 /* Reserved bit, should be 1 */
#define CMD_HDR_ATTR_VBIST 0x00000400 /* Vendor BIST */
/* Snoop enable for all descriptor */
#define CMD_HDR_ATTR_SNOOP 0x00000200
#define CMD_HDR_ATTR_FPDMA 0x00000100 /* FPDMA queued command */
#define CMD_HDR_ATTR_RESET 0x00000080 /* Reset - a SRST or device reset */
/* BIST - require the host to enter BIST mode */
#define CMD_HDR_ATTR_BIST 0x00000040
#define CMD_HDR_ATTR_ATAPI 0x00000020 /* ATAPI command */
#define CMD_HDR_ATTR_TAG 0x0000001f /* TAG mask */
#define FLAGS_DMA 0x00000000
#define FLAGS_FPDMA 0x00000001
#define SATA_FLAG_Q_DEP_MASK 0x0000000f
#define SATA_FLAG_WCACHE 0x00000100
#define SATA_FLAG_FLUSH 0x00000200
#define SATA_FLAG_FLUSH_EXT 0x00000400
#define READ_CMD 0
#define WRITE_CMD 1
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
#endif /* __FSL_SATA_H__ */

View file

@ -0,0 +1,919 @@
/*
* Copyright (C) 2008,2010 Freescale Semiconductor, Inc.
* Dave Liu <daveliu@freescale.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <command.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/fsl_serdes.h>
#include <malloc.h>
#include <libata.h>
#include <fis.h>
#include "fsl_sata.h"
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
#ifndef CONFIG_SYS_SATA1_FLAGS
#define CONFIG_SYS_SATA1_FLAGS FLAGS_DMA
#endif
#ifndef CONFIG_SYS_SATA2_FLAGS
#define CONFIG_SYS_SATA2_FLAGS FLAGS_DMA
#endif
static struct fsl_sata_info fsl_sata_info[] = {
#ifdef CONFIG_SATA1
{CONFIG_SYS_SATA1, CONFIG_SYS_SATA1_FLAGS},
#else
{0, 0},
#endif
#ifdef CONFIG_SATA2
{CONFIG_SYS_SATA2, CONFIG_SYS_SATA2_FLAGS},
#else
{0, 0},
#endif
};
static inline void sdelay(unsigned long sec)
{
unsigned long i;
for (i = 0; i < sec; i++)
mdelay(1000);
}
void dprint_buffer(unsigned char *buf, int len)
{
int i, j;
i = 0;
j = 0;
printf("\n\r");
for (i = 0; i < len; i++) {
printf("%02x ", *buf++);
j++;
if (j == 16) {
printf("\n\r");
j = 0;
}
}
printf("\n\r");
}
static void fsl_sata_dump_sfis(struct sata_fis_d2h *s)
{
printf("Status FIS dump:\n\r");
printf("fis_type: %02x\n\r", s->fis_type);
printf("pm_port_i: %02x\n\r", s->pm_port_i);
printf("status: %02x\n\r", s->status);
printf("error: %02x\n\r", s->error);
printf("lba_low: %02x\n\r", s->lba_low);
printf("lba_mid: %02x\n\r", s->lba_mid);
printf("lba_high: %02x\n\r", s->lba_high);
printf("device: %02x\n\r", s->device);
printf("lba_low_exp: %02x\n\r", s->lba_low_exp);
printf("lba_mid_exp: %02x\n\r", s->lba_mid_exp);
printf("lba_high_exp: %02x\n\r", s->lba_high_exp);
printf("res1: %02x\n\r", s->res1);
printf("sector_count: %02x\n\r", s->sector_count);
printf("sector_count_exp: %02x\n\r", s->sector_count_exp);
}
static int ata_wait_register(volatile unsigned *addr, u32 mask,
u32 val, u32 timeout_msec)
{
int i;
u32 temp;
for (i = 0; (((temp = in_le32(addr)) & mask) != val)
&& i < timeout_msec; i++)
mdelay(1);
return (i < timeout_msec) ? 0 : -1;
}
int init_sata(int dev)
{
u32 length, align;
cmd_hdr_tbl_t *cmd_hdr;
u32 cda;
u32 val32;
fsl_sata_reg_t *reg;
u32 sig;
int i;
fsl_sata_t *sata;
if (dev < 0 || dev > (CONFIG_SYS_SATA_MAX_DEVICE - 1)) {
printf("the sata index %d is out of ranges\n\r", dev);
return -1;
}
#ifdef CONFIG_MPC85xx
if ((dev == 0) && (!is_serdes_configured(SATA1))) {
printf("SATA%d [dev = %d] is not enabled\n", dev+1, dev);
return -1;
}
if ((dev == 1) && (!is_serdes_configured(SATA2))) {
printf("SATA%d [dev = %d] is not enabled\n", dev+1, dev);
return -1;
}
#endif
/* Allocate SATA device driver struct */
sata = (fsl_sata_t *)malloc(sizeof(fsl_sata_t));
if (!sata) {
printf("alloc the sata device struct failed\n\r");
return -1;
}
/* Zero all of the device driver struct */
memset((void *)sata, 0, sizeof(fsl_sata_t));
/* Save the private struct to block device struct */
sata_dev_desc[dev].priv = (void *)sata;
sprintf(sata->name, "SATA%d", dev);
/* Set the controller register base address to device struct */
reg = (fsl_sata_reg_t *)(fsl_sata_info[dev].sata_reg_base);
sata->reg_base = reg;
/* Allocate the command header table, 4 bytes aligned */
length = sizeof(struct cmd_hdr_tbl);
align = SATA_HC_CMD_HDR_TBL_ALIGN;
sata->cmd_hdr_tbl_offset = (void *)malloc(length + align);
if (!sata) {
printf("alloc the command header failed\n\r");
return -1;
}
cmd_hdr = (cmd_hdr_tbl_t *)(((u32)sata->cmd_hdr_tbl_offset + align)
& ~(align - 1));
sata->cmd_hdr = cmd_hdr;
/* Zero all of the command header table */
memset((void *)sata->cmd_hdr_tbl_offset, 0, length + align);
/* Allocate command descriptor for all command */
length = sizeof(struct cmd_desc) * SATA_HC_MAX_CMD;
align = SATA_HC_CMD_DESC_ALIGN;
sata->cmd_desc_offset = (void *)malloc(length + align);
if (!sata->cmd_desc_offset) {
printf("alloc the command descriptor failed\n\r");
return -1;
}
sata->cmd_desc = (cmd_desc_t *)(((u32)sata->cmd_desc_offset + align)
& ~(align - 1));
/* Zero all of command descriptor */
memset((void *)sata->cmd_desc_offset, 0, length + align);
/* Link the command descriptor to command header */
for (i = 0; i < SATA_HC_MAX_CMD; i++) {
cda = ((u32)sata->cmd_desc + SATA_HC_CMD_DESC_SIZE * i)
& ~(CMD_HDR_CDA_ALIGN - 1);
cmd_hdr->cmd_slot[i].cda = cpu_to_le32(cda);
}
/* To have safe state, force the controller offline */
val32 = in_le32(&reg->hcontrol);
val32 &= ~HCONTROL_ONOFF;
val32 |= HCONTROL_FORCE_OFFLINE;
out_le32(&reg->hcontrol, val32);
/* Wait the controller offline */
ata_wait_register(&reg->hstatus, HSTATUS_ONOFF, 0, 1000);
/* Set the command header base address to CHBA register to tell DMA */
out_le32(&reg->chba, (u32)cmd_hdr & ~0x3);
/* Snoop for the command header */
val32 = in_le32(&reg->hcontrol);
val32 |= HCONTROL_HDR_SNOOP;
out_le32(&reg->hcontrol, val32);
/* Disable all of interrupts */
val32 = in_le32(&reg->hcontrol);
val32 &= ~HCONTROL_INT_EN_ALL;
out_le32(&reg->hcontrol, val32);
/* Clear all of interrupts */
val32 = in_le32(&reg->hstatus);
out_le32(&reg->hstatus, val32);
/* Set the ICC, no interrupt coalescing */
out_le32(&reg->icc, 0x01000000);
/* No PM attatched, the SATA device direct connect */
out_le32(&reg->cqpmp, 0);
/* Clear SError register */
val32 = in_le32(&reg->serror);
out_le32(&reg->serror, val32);
/* Clear CER register */
val32 = in_le32(&reg->cer);
out_le32(&reg->cer, val32);
/* Clear DER register */
val32 = in_le32(&reg->der);
out_le32(&reg->der, val32);
/* No device detection or initialization action requested */
out_le32(&reg->scontrol, 0x00000300);
/* Configure the transport layer, default value */
out_le32(&reg->transcfg, 0x08000016);
/* Configure the link layer, default value */
out_le32(&reg->linkcfg, 0x0000ff34);
/* Bring the controller online */
val32 = in_le32(&reg->hcontrol);
val32 |= HCONTROL_ONOFF;
out_le32(&reg->hcontrol, val32);
mdelay(100);
/* print sata device name */
if (!dev)
printf("%s ", sata->name);
else
printf(" %s ", sata->name);
/* Wait PHY RDY signal changed for 500ms */
ata_wait_register(&reg->hstatus, HSTATUS_PHY_RDY,
HSTATUS_PHY_RDY, 500);
/* Check PHYRDY */
val32 = in_le32(&reg->hstatus);
if (val32 & HSTATUS_PHY_RDY) {
sata->link = 1;
} else {
sata->link = 0;
printf("(No RDY)\n\r");
return -1;
}
/* Wait for signature updated, which is 1st D2H */
ata_wait_register(&reg->hstatus, HSTATUS_SIGNATURE,
HSTATUS_SIGNATURE, 10000);
if (val32 & HSTATUS_SIGNATURE) {
sig = in_le32(&reg->sig);
debug("Signature updated, the sig =%08x\n\r", sig);
sata->ata_device_type = ata_dev_classify(sig);
}
/* Check the speed */
val32 = in_le32(&reg->sstatus);
if ((val32 & SSTATUS_SPD_MASK) == SSTATUS_SPD_GEN1)
printf("(1.5 Gbps)\n\r");
else if ((val32 & SSTATUS_SPD_MASK) == SSTATUS_SPD_GEN2)
printf("(3 Gbps)\n\r");
return 0;
}
/* Hardware reset, like Power-on and COMRESET */
void fsl_sata_hardware_reset(u32 reg_base)
{
fsl_sata_reg_t *reg = (fsl_sata_reg_t *)reg_base;
u32 scontrol;
/* Disable the SATA interface and put PHY offline */
scontrol = in_le32(&reg->scontrol);
scontrol = (scontrol & 0x0f0) | 0x304;
out_le32(&reg->scontrol, scontrol);
/* No speed strict */
scontrol = in_le32(&reg->scontrol);
scontrol = scontrol & ~0x0f0;
out_le32(&reg->scontrol, scontrol);
/* Issue PHY wake/reset, Hardware_reset_asserted */
scontrol = in_le32(&reg->scontrol);
scontrol = (scontrol & 0x0f0) | 0x301;
out_le32(&reg->scontrol, scontrol);
mdelay(100);
/* Resume PHY, COMRESET negated, the device initialize hardware
* and execute diagnostics, send good status-signature to host,
* which is D2H register FIS, and then the device enter idle state.
*/
scontrol = in_le32(&reg->scontrol);
scontrol = (scontrol & 0x0f0) | 0x300;
out_le32(&reg->scontrol, scontrol);
mdelay(100);
return;
}
static void fsl_sata_dump_regs(fsl_sata_reg_t *reg)
{
printf("\n\rSATA: %08x\n\r", (u32)reg);
printf("CQR: %08x\n\r", in_le32(&reg->cqr));
printf("CAR: %08x\n\r", in_le32(&reg->car));
printf("CCR: %08x\n\r", in_le32(&reg->ccr));
printf("CER: %08x\n\r", in_le32(&reg->cer));
printf("CQR: %08x\n\r", in_le32(&reg->cqr));
printf("DER: %08x\n\r", in_le32(&reg->der));
printf("CHBA: %08x\n\r", in_le32(&reg->chba));
printf("HStatus: %08x\n\r", in_le32(&reg->hstatus));
printf("HControl: %08x\n\r", in_le32(&reg->hcontrol));
printf("CQPMP: %08x\n\r", in_le32(&reg->cqpmp));
printf("SIG: %08x\n\r", in_le32(&reg->sig));
printf("ICC: %08x\n\r", in_le32(&reg->icc));
printf("SStatus: %08x\n\r", in_le32(&reg->sstatus));
printf("SError: %08x\n\r", in_le32(&reg->serror));
printf("SControl: %08x\n\r", in_le32(&reg->scontrol));
printf("SNotification: %08x\n\r", in_le32(&reg->snotification));
printf("TransCfg: %08x\n\r", in_le32(&reg->transcfg));
printf("TransStatus: %08x\n\r", in_le32(&reg->transstatus));
printf("LinkCfg: %08x\n\r", in_le32(&reg->linkcfg));
printf("LinkCfg1: %08x\n\r", in_le32(&reg->linkcfg1));
printf("LinkCfg2: %08x\n\r", in_le32(&reg->linkcfg2));
printf("LinkStatus: %08x\n\r", in_le32(&reg->linkstatus));
printf("LinkStatus1: %08x\n\r", in_le32(&reg->linkstatus1));
printf("PhyCtrlCfg: %08x\n\r", in_le32(&reg->phyctrlcfg));
printf("SYSPR: %08x\n\r", in_be32(&reg->syspr));
}
static int fsl_ata_exec_ata_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis,
int is_ncq, int tag, u8 *buffer, u32 len)
{
cmd_hdr_entry_t *cmd_hdr;
cmd_desc_t *cmd_desc;
sata_fis_h2d_t *h2d;
prd_entry_t *prde;
u32 ext_c_ddc;
u32 prde_count;
u32 val32;
u32 ttl;
fsl_sata_reg_t *reg = sata->reg_base;
int i;
/* Check xfer length */
if (len > SATA_HC_MAX_XFER_LEN) {
printf("max transfer length is 64MB\n\r");
return 0;
}
/* Setup the command descriptor */
cmd_desc = sata->cmd_desc + tag;
/* Get the pointer cfis of command descriptor */
h2d = (sata_fis_h2d_t *)cmd_desc->cfis;
/* Zero the cfis of command descriptor */
memset((void *)h2d, 0, SATA_HC_CMD_DESC_CFIS_SIZE);
/* Copy the cfis from user to command descriptor */
h2d->fis_type = cfis->fis_type;
h2d->pm_port_c = cfis->pm_port_c;
h2d->command = cfis->command;
h2d->features = cfis->features;
h2d->features_exp = cfis->features_exp;
h2d->lba_low = cfis->lba_low;
h2d->lba_mid = cfis->lba_mid;
h2d->lba_high = cfis->lba_high;
h2d->lba_low_exp = cfis->lba_low_exp;
h2d->lba_mid_exp = cfis->lba_mid_exp;
h2d->lba_high_exp = cfis->lba_high_exp;
if (!is_ncq) {
h2d->sector_count = cfis->sector_count;
h2d->sector_count_exp = cfis->sector_count_exp;
} else { /* NCQ */
h2d->sector_count = (u8)(tag << 3);
}
h2d->device = cfis->device;
h2d->control = cfis->control;
/* Setup the PRD table */
prde = (prd_entry_t *)cmd_desc->prdt;
memset((void *)prde, 0, sizeof(struct prdt));
prde_count = 0;
ttl = len;
for (i = 0; i < SATA_HC_MAX_PRD_DIRECT; i++) {
if (!len)
break;
prde->dba = cpu_to_le32((u32)buffer & ~0x3);
debug("dba = %08x\n\r", (u32)buffer);
if (len < PRD_ENTRY_MAX_XFER_SZ) {
ext_c_ddc = PRD_ENTRY_DATA_SNOOP | len;
debug("ext_c_ddc1 = %08x, len = %08x\n\r", ext_c_ddc, len);
prde->ext_c_ddc = cpu_to_le32(ext_c_ddc);
prde_count++;
prde++;
break;
} else {
ext_c_ddc = PRD_ENTRY_DATA_SNOOP; /* 4M bytes */
debug("ext_c_ddc2 = %08x, len = %08x\n\r", ext_c_ddc, len);
prde->ext_c_ddc = cpu_to_le32(ext_c_ddc);
buffer += PRD_ENTRY_MAX_XFER_SZ;
len -= PRD_ENTRY_MAX_XFER_SZ;
prde_count++;
prde++;
}
}
/* Setup the command slot of cmd hdr */
cmd_hdr = (cmd_hdr_entry_t *)&sata->cmd_hdr->cmd_slot[tag];
cmd_hdr->cda = cpu_to_le32((u32)cmd_desc & ~0x3);
val32 = prde_count << CMD_HDR_PRD_ENTRY_SHIFT;
val32 |= sizeof(sata_fis_h2d_t);
cmd_hdr->prde_fis_len = cpu_to_le32(val32);
cmd_hdr->ttl = cpu_to_le32(ttl);
if (!is_ncq) {
val32 = CMD_HDR_ATTR_RES | CMD_HDR_ATTR_SNOOP;
} else {
val32 = CMD_HDR_ATTR_RES | CMD_HDR_ATTR_SNOOP | CMD_HDR_ATTR_FPDMA;
}
tag &= CMD_HDR_ATTR_TAG;
val32 |= tag;
debug("attribute = %08x\n\r", val32);
cmd_hdr->attribute = cpu_to_le32(val32);
/* Make sure cmd desc and cmd slot valid before commmand issue */
sync();
/* PMP*/
val32 = (u32)(h2d->pm_port_c & 0x0f);
out_le32(&reg->cqpmp, val32);
/* Wait no active */
if (ata_wait_register(&reg->car, (1 << tag), 0, 10000))
printf("Wait no active time out\n\r");
/* Issue command */
if (!(in_le32(&reg->cqr) & (1 << tag))) {
val32 = 1 << tag;
out_le32(&reg->cqr, val32);
}
/* Wait command completed for 10s */
if (ata_wait_register(&reg->ccr, (1 << tag), (1 << tag), 10000)) {
if (!is_ncq)
printf("Non-NCQ command time out\n\r");
else
printf("NCQ command time out\n\r");
}
val32 = in_le32(&reg->cer);
if (val32) {
u32 der;
fsl_sata_dump_sfis((struct sata_fis_d2h *)cmd_desc->sfis);
printf("CE at device\n\r");
fsl_sata_dump_regs(reg);
der = in_le32(&reg->der);
out_le32(&reg->cer, val32);
out_le32(&reg->der, der);
}
/* Clear complete flags */
val32 = in_le32(&reg->ccr);
out_le32(&reg->ccr, val32);
return len;
}
static int fsl_ata_exec_reset_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis,
int tag, u8 *buffer, u32 len)
{
return 0;
}
static int fsl_sata_exec_cmd(struct fsl_sata *sata, struct sata_fis_h2d *cfis,
enum cmd_type command_type, int tag, u8 *buffer, u32 len)
{
int rc;
if (tag > SATA_HC_MAX_CMD || tag < 0) {
printf("tag is out of range, tag=%d\n\r", tag);
return -1;
}
switch (command_type) {
case CMD_ATA:
rc = fsl_ata_exec_ata_cmd(sata, cfis, 0, tag, buffer, len);
return rc;
case CMD_RESET:
rc = fsl_ata_exec_reset_cmd(sata, cfis, tag, buffer, len);
return rc;
case CMD_NCQ:
rc = fsl_ata_exec_ata_cmd(sata, cfis, 1, tag, buffer, len);
return rc;
case CMD_ATAPI:
case CMD_VENDOR_BIST:
case CMD_BIST:
printf("not support now\n\r");
return -1;
default:
break;
}
return -1;
}
static void fsl_sata_identify(int dev, u16 *id)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_ID_ATA;
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, (u8 *)id, ATA_ID_WORDS * 2);
ata_swap_buf_le16(id, ATA_ID_WORDS);
}
static void fsl_sata_xfer_mode(int dev, u16 *id)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
sata->pio = id[ATA_ID_PIO_MODES];
sata->mwdma = id[ATA_ID_MWDMA_MODES];
sata->udma = id[ATA_ID_UDMA_MODES];
debug("pio %04x, mwdma %04x, udma %04x\n\r", sata->pio, sata->mwdma, sata->udma);
}
static void fsl_sata_set_features(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u8 udma_cap;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_SET_FEATURES;
cfis->features = SETFEATURES_XFER;
/* First check the device capablity */
udma_cap = (u8)(sata->udma & 0xff);
debug("udma_cap %02x\n\r", udma_cap);
if (udma_cap == ATA_UDMA6)
cfis->sector_count = XFER_UDMA_6;
if (udma_cap == ATA_UDMA5)
cfis->sector_count = XFER_UDMA_5;
if (udma_cap == ATA_UDMA4)
cfis->sector_count = XFER_UDMA_4;
if (udma_cap == ATA_UDMA3)
cfis->sector_count = XFER_UDMA_3;
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0);
}
static u32 fsl_sata_rw_cmd(int dev, u32 start, u32 blkcnt, u8 *buffer, int is_write)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u32 block;
block = start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_WRITE : ATA_CMD_READ;
cfis->device = ATA_LBA;
cfis->device |= (block >> 24) & 0xf;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->sector_count = (u8)(blkcnt & 0xff);
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, buffer, ATA_SECT_SIZE * blkcnt);
return blkcnt;
}
void fsl_sata_flush_cache(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_FLUSH;
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0);
}
static u32 fsl_sata_rw_cmd_ext(int dev, u32 start, u32 blkcnt, u8 *buffer, int is_write)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
u64 block;
block = (u64)start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_WRITE_EXT
: ATA_CMD_READ_EXT;
cfis->lba_high_exp = (block >> 40) & 0xff;
cfis->lba_mid_exp = (block >> 32) & 0xff;
cfis->lba_low_exp = (block >> 24) & 0xff;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->device = ATA_LBA;
cfis->sector_count_exp = (blkcnt >> 8) & 0xff;
cfis->sector_count = blkcnt & 0xff;
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, buffer, ATA_SECT_SIZE * blkcnt);
return blkcnt;
}
u32 fsl_sata_rw_ncq_cmd(int dev, u32 start, u32 blkcnt, u8 *buffer, int is_write)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
int ncq_channel;
u64 block;
if (sata->lba48 != 1) {
printf("execute FPDMA command on non-LBA48 hard disk\n\r");
return -1;
}
block = (u64)start;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = (is_write) ? ATA_CMD_FPDMA_WRITE
: ATA_CMD_FPDMA_READ;
cfis->lba_high_exp = (block >> 40) & 0xff;
cfis->lba_mid_exp = (block >> 32) & 0xff;
cfis->lba_low_exp = (block >> 24) & 0xff;
cfis->lba_high = (block >> 16) & 0xff;
cfis->lba_mid = (block >> 8) & 0xff;
cfis->lba_low = block & 0xff;
cfis->device = ATA_LBA;
cfis->features_exp = (blkcnt >> 8) & 0xff;
cfis->features = blkcnt & 0xff;
if (sata->queue_depth >= SATA_HC_MAX_CMD)
ncq_channel = SATA_HC_MAX_CMD - 1;
else
ncq_channel = sata->queue_depth - 1;
/* Use the latest queue */
fsl_sata_exec_cmd(sata, cfis, CMD_NCQ, ncq_channel, buffer, ATA_SECT_SIZE * blkcnt);
return blkcnt;
}
void fsl_sata_flush_cache_ext(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
struct sata_fis_h2d h2d, *cfis = &h2d;
memset(cfis, 0, sizeof(struct sata_fis_h2d));
cfis->fis_type = SATA_FIS_TYPE_REGISTER_H2D;
cfis->pm_port_c = 0x80; /* is command */
cfis->command = ATA_CMD_FLUSH_EXT;
fsl_sata_exec_cmd(sata, cfis, CMD_ATA, 0, NULL, 0);
}
/* Software reset, set SRST of the Device Control register */
void fsl_sata_software_reset(int dev)
{
return;
}
static void fsl_sata_init_wcache(int dev, u16 *id)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id))
sata->wcache = 1;
if (ata_id_has_flush(id))
sata->flush = 1;
if (ata_id_has_flush_ext(id))
sata->flush_ext = 1;
}
static int fsl_sata_get_wcache(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
return sata->wcache;
}
static int fsl_sata_get_flush(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
return sata->flush;
}
static int fsl_sata_get_flush_ext(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
return sata->flush_ext;
}
u32 ata_low_level_rw_lba48(int dev, u32 blknr, u32 blkcnt, void *buffer, int is_write)
{
u32 start, blks;
u8 *addr;
int max_blks;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS_LBA48;
do {
if (blks > max_blks) {
if (fsl_sata_info[dev].flags != FLAGS_FPDMA)
fsl_sata_rw_cmd_ext(dev, start, max_blks, addr, is_write);
else
fsl_sata_rw_ncq_cmd(dev, start, max_blks, addr, is_write);
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
if (fsl_sata_info[dev].flags != FLAGS_FPDMA)
fsl_sata_rw_cmd_ext(dev, start, blks, addr, is_write);
else
fsl_sata_rw_ncq_cmd(dev, start, blks, addr, is_write);
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
u32 ata_low_level_rw_lba28(int dev, u32 blknr, u32 blkcnt, void *buffer, int is_write)
{
u32 start, blks;
u8 *addr;
int max_blks;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS;
do {
if (blks > max_blks) {
fsl_sata_rw_cmd(dev, start, max_blks, addr, is_write);
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
fsl_sata_rw_cmd(dev, start, blks, addr, is_write);
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
/*
* SATA interface between low level driver and command layer
*/
ulong sata_read(int dev, u32 blknr, u32 blkcnt, void *buffer)
{
u32 rc;
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
if (sata->lba48)
rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, buffer, READ_CMD);
else
rc = ata_low_level_rw_lba28(dev, blknr, blkcnt, buffer, READ_CMD);
return rc;
}
ulong sata_write(int dev, u32 blknr, u32 blkcnt, void *buffer)
{
u32 rc;
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
if (sata->lba48) {
rc = ata_low_level_rw_lba48(dev, blknr, blkcnt, buffer, WRITE_CMD);
if (fsl_sata_get_wcache(dev) && fsl_sata_get_flush_ext(dev))
fsl_sata_flush_cache_ext(dev);
} else {
rc = ata_low_level_rw_lba28(dev, blknr, blkcnt, buffer, WRITE_CMD);
if (fsl_sata_get_wcache(dev) && fsl_sata_get_flush(dev))
fsl_sata_flush_cache(dev);
}
return rc;
}
int scan_sata(int dev)
{
fsl_sata_t *sata = (fsl_sata_t *)sata_dev_desc[dev].priv;
unsigned char serial[ATA_ID_SERNO_LEN + 1];
unsigned char firmware[ATA_ID_FW_REV_LEN + 1];
unsigned char product[ATA_ID_PROD_LEN + 1];
u16 *id;
u64 n_sectors;
/* if no detected link */
if (!sata->link)
return -1;
id = (u16 *)malloc(ATA_ID_WORDS * 2);
if (!id) {
printf("id malloc failed\n\r");
return -1;
}
/* Identify device to get information */
fsl_sata_identify(dev, id);
/* Serial number */
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
memcpy(sata_dev_desc[dev].product, serial, sizeof(serial));
/* Firmware version */
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware));
/* Product model */
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
memcpy(sata_dev_desc[dev].vendor, product, sizeof(product));
/* Totoal sectors */
n_sectors = ata_id_n_sectors(id);
sata_dev_desc[dev].lba = (u32)n_sectors;
#ifdef CONFIG_LBA48
/* Check if support LBA48 */
if (ata_id_has_lba48(id)) {
sata->lba48 = 1;
debug("Device support LBA48\n\r");
} else
debug("Device supports LBA28\n\r");
#endif
/* Get the NCQ queue depth from device */
sata->queue_depth = ata_id_queue_depth(id);
/* Get the xfer mode from device */
fsl_sata_xfer_mode(dev, id);
/* Get the write cache status from device */
fsl_sata_init_wcache(dev, id);
/* Set the xfer mode to highest speed */
fsl_sata_set_features(dev);
#ifdef DEBUG
fsl_sata_identify(dev, id);
ata_dump_id(id);
#endif
free((void *)id);
return 0;
}

View file

@ -0,0 +1,333 @@
/*
* Copyright (C) 2007-2008 Freescale Semiconductor, Inc.
* Dave Liu <daveliu@freescale.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef __FSL_SATA_H__
#define __FSL_SATA_H__
#define SATA_HC_MAX_NUM 4 /* Max host controller numbers */
#define SATA_HC_MAX_CMD 16 /* Max command queue depth per host controller */
#define SATA_HC_MAX_PORT 16 /* Max port number per host controller */
/*
* SATA Host Controller Registers
*/
typedef struct fsl_sata_reg {
/* SATA command registers */
u32 cqr; /* Command queue register */
u8 res1[0x4];
u32 car; /* Command active register */
u8 res2[0x4];
u32 ccr; /* Command completed register */
u8 res3[0x4];
u32 cer; /* Command error register */
u8 res4[0x4];
u32 der; /* Device error register */
u32 chba; /* Command header base address */
u32 hstatus; /* Host status register */
u32 hcontrol; /* Host control register */
u32 cqpmp; /* Port number queue register */
u32 sig; /* Signature register */
u32 icc; /* Interrupt coalescing control register */
u8 res5[0xc4];
/* SATA supperset registers */
u32 sstatus; /* SATA interface status register */
u32 serror; /* SATA interface error register */
u32 scontrol; /* SATA interface control register */
u32 snotification; /* SATA interface notification register */
u8 res6[0x30];
/* SATA control status registers */
u32 transcfg; /* Transport layer configuration */
u32 transstatus; /* Transport layer status */
u32 linkcfg; /* Link layer configuration */
u32 linkcfg1; /* Link layer configuration1 */
u32 linkcfg2; /* Link layer configuration2 */
u32 linkstatus; /* Link layer status */
u32 linkstatus1; /* Link layer status1 */
u32 phyctrlcfg; /* PHY control configuration */
u8 res7[0x2b0];
/* SATA system control registers */
u32 syspr; /* System priority register - big endian */
u8 res8[0xbec];
} __attribute__ ((packed)) fsl_sata_reg_t;
/* HStatus register
*/
#define HSTATUS_ONOFF 0x80000000 /* Online/offline status */
#define HSTATUS_FORCE_OFFLINE 0x40000000 /* In process going offline */
#define HSTATUS_BIST_ERR 0x20000000
/* Fatal error */
#define HSTATUS_MASTER_ERR 0x00004000
#define HSTATUS_DATA_UNDERRUN 0x00002000
#define HSTATUS_DATA_OVERRUN 0x00001000
#define HSTATUS_CRC_ERR_TX 0x00000800
#define HSTATUS_CRC_ERR_RX 0x00000400
#define HSTATUS_FIFO_OVERFLOW_TX 0x00000200
#define HSTATUS_FIFO_OVERFLOW_RX 0x00000100
#define HSTATUS_FATAL_ERR_ALL (HSTATUS_MASTER_ERR | \
HSTATUS_DATA_UNDERRUN | \
HSTATUS_DATA_OVERRUN | \
HSTATUS_CRC_ERR_TX | \
HSTATUS_CRC_ERR_RX | \
HSTATUS_FIFO_OVERFLOW_TX | \
HSTATUS_FIFO_OVERFLOW_RX)
/* Interrupt status */
#define HSTATUS_FATAL_ERR 0x00000020
#define HSTATUS_PHY_RDY 0x00000010
#define HSTATUS_SIGNATURE 0x00000008
#define HSTATUS_SNOTIFY 0x00000004
#define HSTATUS_DEVICE_ERR 0x00000002
#define HSTATUS_CMD_COMPLETE 0x00000001
/* HControl register
*/
#define HCONTROL_ONOFF 0x80000000 /* Online or offline request */
#define HCONTROL_FORCE_OFFLINE 0x40000000 /* Force offline request */
#define HCONTROL_ENTERPRISE_EN 0x10000000 /* Enterprise mode enabled */
#define HCONTROL_HDR_SNOOP 0x00000400 /* Command header snoop */
#define HCONTROL_PMP_ATTACHED 0x00000200 /* Port multiplier attached */
/* Interrupt enable */
#define HCONTROL_FATAL_ERR 0x00000020
#define HCONTROL_PHY_RDY 0x00000010
#define HCONTROL_SIGNATURE 0x00000008
#define HCONTROL_SNOTIFY 0x00000004
#define HCONTROL_DEVICE_ERR 0x00000002
#define HCONTROL_CMD_COMPLETE 0x00000001
#define HCONTROL_INT_EN_ALL (HCONTROL_FATAL_ERR | \
HCONTROL_PHY_RDY | \
HCONTROL_SIGNATURE | \
HCONTROL_SNOTIFY | \
HCONTROL_DEVICE_ERR | \
HCONTROL_CMD_COMPLETE)
/* SStatus register
*/
#define SSTATUS_IPM_MASK 0x00000780
#define SSTATUS_IPM_NOPRESENT 0x00000000
#define SSTATUS_IPM_ACTIVE 0x00000080
#define SSTATUS_IPM_PATIAL 0x00000100
#define SSTATUS_IPM_SLUMBER 0x00000300
#define SSTATUS_SPD_MASK 0x000000f0
#define SSTATUS_SPD_GEN1 0x00000010
#define SSTATUS_SPD_GEN2 0x00000020
#define SSTATUS_DET_MASK 0x0000000f
#define SSTATUS_DET_NODEVICE 0x00000000
#define SSTATUS_DET_DISCONNECT 0x00000001
#define SSTATUS_DET_CONNECT 0x00000003
#define SSTATUS_DET_PHY_OFFLINE 0x00000004
/* SControl register
*/
#define SCONTROL_SPM_MASK 0x0000f000
#define SCONTROL_SPM_GO_PARTIAL 0x00001000
#define SCONTROL_SPM_GO_SLUMBER 0x00002000
#define SCONTROL_SPM_GO_ACTIVE 0x00004000
#define SCONTROL_IPM_MASK 0x00000f00
#define SCONTROL_IPM_NO_RESTRICT 0x00000000
#define SCONTROL_IPM_PARTIAL 0x00000100
#define SCONTROL_IPM_SLUMBER 0x00000200
#define SCONTROL_IPM_PART_SLUM 0x00000300
#define SCONTROL_SPD_MASK 0x000000f0
#define SCONTROL_SPD_NO_RESTRICT 0x00000000
#define SCONTROL_SPD_GEN1 0x00000010
#define SCONTROL_SPD_GEN2 0x00000020
#define SCONTROL_DET_MASK 0x0000000f
#define SCONTROL_DET_HRESET 0x00000001
#define SCONTROL_DET_DISABLE 0x00000004
/* TransCfg register
*/
#define TRANSCFG_DFIS_SIZE_SHIFT 16
#define TRANSCFG_RX_WATER_MARK_MASK 0x0000001f
/* PhyCtrlCfg register
*/
#define PHYCTRLCFG_FPRFTI_MASK 0x00000018
#define PHYCTRLCFG_LOOPBACK_MASK 0x0000000e
/*
* Command Header Entry
*/
typedef struct cmd_hdr_entry {
u32 cda; /* Command Descriptor Address, 4 bytes aligned */
u32 prde_fis_len; /* Number of PRD entries and FIS length */
u32 ttl; /* Total transfer length */
u32 attribute; /* the attribute of command */
} __attribute__ ((packed)) cmd_hdr_entry_t;
#define SATA_HC_CMD_HDR_ENTRY_SIZE sizeof(struct cmd_hdr_entry)
/* cda
*/
#define CMD_HDR_CDA_ALIGN 4
/* prde_fis_len
*/
#define CMD_HDR_PRD_ENTRY_SHIFT 16
#define CMD_HDR_PRD_ENTRY_MASK 0x003f0000
#define CMD_HDR_FIS_LEN_SHIFT 2
/* attribute
*/
#define CMD_HDR_ATTR_RES 0x00000800 /* Reserved bit, should be 1 */
#define CMD_HDR_ATTR_VBIST 0x00000400 /* Vendor BIST */
#define CMD_HDR_ATTR_SNOOP 0x00000200 /* Snoop enable for all descriptor */
#define CMD_HDR_ATTR_FPDMA 0x00000100 /* FPDMA queued command */
#define CMD_HDR_ATTR_RESET 0x00000080 /* Reset - a SRST or device reset */
#define CMD_HDR_ATTR_BIST 0x00000040 /* BIST - require the host to enter BIST mode */
#define CMD_HDR_ATTR_ATAPI 0x00000020 /* ATAPI command */
#define CMD_HDR_ATTR_TAG 0x0000001f /* TAG mask */
/* command type
*/
enum cmd_type {
CMD_VENDOR_BIST,
CMD_BIST,
CMD_RESET, /* SRST or device reset */
CMD_ATAPI,
CMD_NCQ,
CMD_ATA, /* None of all above */
};
/*
* Command Header Table
*/
typedef struct cmd_hdr_tbl {
cmd_hdr_entry_t cmd_slot[SATA_HC_MAX_CMD];
} __attribute__ ((packed)) cmd_hdr_tbl_t;
#define SATA_HC_CMD_HDR_TBL_SIZE sizeof(struct cmd_hdr_tbl)
#define SATA_HC_CMD_HDR_TBL_ALIGN 4
/*
* PRD entry - Physical Region Descriptor entry
*/
typedef struct prd_entry {
u32 dba; /* Data base address, 4 bytes aligned */
u32 res1;
u32 res2;
u32 ext_c_ddc; /* Indirect PRD flags, snoop and data word count */
} __attribute__ ((packed)) prd_entry_t;
#define SATA_HC_CMD_DESC_PRD_SIZE sizeof(struct prd_entry)
/* dba
*/
#define PRD_ENTRY_DBA_ALIGN 4
/* ext_c_ddc
*/
#define PRD_ENTRY_EXT 0x80000000 /* extension flag */
#ifdef CONFIG_FSL_SATA_V2
#define PRD_ENTRY_DATA_SNOOP 0x10000000 /* Data snoop enable */
#else
#define PRD_ENTRY_DATA_SNOOP 0x00400000 /* Data snoop enable */
#endif
#define PRD_ENTRY_LEN_MASK 0x003fffff /* Data word count */
#define PRD_ENTRY_MAX_XFER_SZ (PRD_ENTRY_LEN_MASK + 1)
/*
* This SATA host controller supports a max of 16 direct PRD entries, but if use
* chained indirect PRD entries, then the contollers supports upto a max of 63
* entries including direct and indirect PRD entries.
* The PRDT is an array of 63 PRD entries contigiously, but the PRD entries#15
* will be setup as an indirect descriptor, pointing to it's next (contigious)
* PRD entries#16.
*/
#define SATA_HC_MAX_PRD 63 /* Max PRD entry numbers per command */
#define SATA_HC_MAX_PRD_DIRECT 16 /* Direct PRDT entries */
#define SATA_HC_MAX_PRD_USABLE (SATA_HC_MAX_PRD - 1)
#define SATA_HC_MAX_XFER_LEN 0x4000000
/*
* PRDT - Physical Region Descriptor Table
*/
typedef struct prdt {
prd_entry_t prdt[SATA_HC_MAX_PRD];
} __attribute__ ((packed)) prdt_t;
/*
* Command Descriptor
*/
#define SATA_HC_CMD_DESC_CFIS_SIZE 32 /* bytes */
#define SATA_HC_CMD_DESC_SFIS_SIZE 32 /* bytes */
#define SATA_HC_CMD_DESC_ACMD_SIZE 16 /* bytes */
#define SATA_HC_CMD_DESC_RES 16 /* bytes */
typedef struct cmd_desc {
u8 cfis[SATA_HC_CMD_DESC_CFIS_SIZE];
u8 sfis[SATA_HC_CMD_DESC_SFIS_SIZE];
u8 acmd[SATA_HC_CMD_DESC_ACMD_SIZE];
u8 res[SATA_HC_CMD_DESC_RES];
prd_entry_t prdt[SATA_HC_MAX_PRD];
} __attribute__ ((packed)) cmd_desc_t;
#define SATA_HC_CMD_DESC_SIZE sizeof(struct cmd_desc)
#define SATA_HC_CMD_DESC_ALIGN 4
/*
* SATA device driver info
*/
typedef struct fsl_sata_info {
u32 sata_reg_base;
u32 flags;
} fsl_sata_info_t;
#define FLAGS_DMA 0x00000000
#define FLAGS_FPDMA 0x00000001
/*
* SATA device driver struct
*/
typedef struct fsl_sata {
char name[12];
fsl_sata_reg_t *reg_base; /* the base address of controller register */
void *cmd_hdr_tbl_offset; /* alloc address of command header table */
cmd_hdr_tbl_t *cmd_hdr; /* aligned address of command header table */
void *cmd_desc_offset; /* alloc address of command descriptor */
cmd_desc_t *cmd_desc; /* aligned address of command descriptor */
int link; /* PHY link status */
/* device attribute */
int ata_device_type; /* device type */
int lba48;
int queue_depth; /* Max NCQ queue depth */
u16 pio;
u16 mwdma;
u16 udma;
int wcache;
int flush;
int flush_ext;
} fsl_sata_t;
#define READ_CMD 0
#define WRITE_CMD 1
#endif /* __FSL_SATA_H__ */

View file

@ -0,0 +1,364 @@
/*
* Faraday FTIDE020 ATA Controller (AHB)
*
* (C) Copyright 2011 Andes Technology
* Greentime Hu <greentime@andestech.com>
* Macpaul Lin <macpaul@andestech.com>
* Kuo-Wei Chou <kwchou@andestech.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
*/
/* ftide020.c - ide support functions for the FTIDE020_S controller */
#include <config.h>
#include <common.h>
#include <ata.h>
#include <ide.h>
#include <asm/io.h>
#include <api_public.h>
#include "ftide020.h"
/* base address */
#define FTIDE_BASE CONFIG_SYS_ATA_BASE_ADDR
/*
* data address - The CMD and DATA use the same FIFO in FTIDE020_S
* FTIDE_DATA = CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_DATA_OFFSET
* = &ftide020->rw_fifo
*/
#define FTIDE_DATA (&ftide020->rw_fifo)
/* command and data I/O macros */
/* 0x0 - DATA FIFO */
#define WRITE_DATA(x) outl((x), &ftide020->rw_fifo) /* 0x00 */
#define READ_DATA() inl(&ftide020->rw_fifo) /* 0x00 */
/* 0x04 - R: Status Reg, W: CMD_FIFO */
#define WRITE_CMD(x) outl((x), &ftide020->cmd_fifo) /* 0x04 */
#define READ_STATUS() inl(&ftide020->cmd_fifo) /* 0x04 */
void ftide_set_device(int cx8, int dev)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
WRITE_CMD(SET_DEV_CMD | IDE_SET_CX8(cx8) | dev);
}
unsigned char ide_read_register(int dev, unsigned int port)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
ftide_set_device(0, dev);
WRITE_CMD(READ_REG_CMD | IDE_REG_CS_READ(CONFIG_IDE_REG_CS) |
IDE_REG_DA_WRITE(port));
return READ_DATA() & 0xff;
}
void ide_write_register(int dev, unsigned int port, unsigned char val)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
ftide_set_device(0, dev);
WRITE_CMD(WRITE_REG_CMD | IDE_REG_CS_WRITE(CONFIG_IDE_REG_CS) |
IDE_REG_DA_WRITE(port) | val);
}
void ide_write_data(int dev, ulong *sect_buf, int words)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
ftide_set_device(0, dev);
WRITE_CMD(WRITE_DATA_CMD | ((words << 2) - 1));
/* block write */
outsl(FTIDE_DATA, sect_buf, words);
}
void ide_read_data(int dev, ulong *sect_buf, int words)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
ftide_set_device(0, dev);
WRITE_CMD(READ_DATA_CMD | ((words << 2) - 1));
/* block read */
insl(FTIDE_DATA, sect_buf, words);
}
void ftide_dfifo_ready(ulong *time)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
while (!(READ_STATUS() & STATUS_RFE)) {
if (*time-- == 0)
break;
udelay(100);
}
}
extern ulong ide_bus_offset[CONFIG_SYS_IDE_MAXBUS];
/* Reset_IDE_controller */
static void reset_ide_controller(void)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
unsigned int val;
val = inl(&ftide020->cr);
val |= CONTROL_RST;
outl(val, &ftide020->cr);
/* wait until reset OK, this is poor HW design */
mdelay(50);
val &= ~(CONTROL_RST);
outl(val, &ftide020->cr);
mdelay(50);
val |= CONTROL_SRST;
outl(val, &ftide020->cr);
/* wait until reset OK, this is poor HW design */
mdelay(50);
val &= ~(CONTROL_SRST);
outl(val, &ftide020->cr);
/* IORDY enable for PIO, for 2 device */
val |= (CONTROL_IRE0 | CONTROL_IRE1);
outl(val, &ftide020->cr);
}
/* IDE clock frequence */
uint ftide_clock_freq(void)
{
/*
* todo: To aquire dynamic system frequency is dependend on the power
* management unit which the ftide020 is connected to. In current,
* there are only few PMU supports in u-boot.
* So this function is wait for future enhancement.
*/
return 100;
}
/* Calculate Timing Registers */
static unsigned int timing_cal(u16 t0, u16 t1, u16 t2, u16 t4)
{
unsigned int val, ahb_ns = 8;
u8 TEOC, T1, T2, T4;
T1 = (u8) (t1 / ahb_ns);
if ((T1 * ahb_ns) == t1)
T1--;
T2 = (u8) (t2 / ahb_ns);
if ((T2 * ahb_ns) == t2)
T2--;
T4 = (u8) (t4 / ahb_ns);
if ((T4 * ahb_ns) == t4)
T4--;
TEOC = (u8) (t0 / ahb_ns);
if ((TEOC * ahb_ns) == t0)
TEOC--;
TEOC = ((TEOC > (T1 + T2 + T4)) ? (TEOC - (T1 + T2 + T4)) : 0);
/*
* Here the fields in data timing registers in PIO mode
* is accessed the same way as command timing registers.
*/
val = DT_REG_PIO_T1(T1) |
DT_REG_PIO_T2(T2) |
DT_REG_PIO_T4(T4) |
DT_REG_PIO_TEOC(TEOC);
return val;
}
/* Set Timing Register */
static unsigned int set_mode_timing(u8 dev, u8 id, u8 mode)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
u16 t0, t1, t2, t4;
u8 tcyc, tcvs, tmli, tenv, tack, trp;
unsigned int val, sysclk = 8;
if (id >= TATOL_TIMING)
return 0;
sysclk = ftide_clock_freq();
switch (id) {
case CMD_TIMING:
if (mode < REG_MODE) {
t0 = REG_ACCESS_TIMING[REG_T0][mode];
t1 = REG_ACCESS_TIMING[REG_T1][mode];
t2 = REG_ACCESS_TIMING[REG_T2][mode];
t4 = REG_ACCESS_TIMING[REG_T4][mode];
val = timing_cal(t0, t1, t2, t4);
outl(val, (dev ? &ftide020->ctrd1 : &ftide020->ctrd0));
return 1;
} else
return 0;
case PIO_TIMING:
if (mode < PIO_MODE) {
t0 = PIO_ACCESS_TIMING[PIO_T0][mode];
t1 = PIO_ACCESS_TIMING[PIO_T1][mode];
t2 = PIO_ACCESS_TIMING[PIO_T2][mode];
t4 = PIO_ACCESS_TIMING[PIO_T4][mode];
val = timing_cal(t0, t1, t2, t4);
outl(val, (dev ? &ftide020->dtrd1 : &ftide020->dtrd0));
return 1;
} else
return 0;
case DMA_TIMING:
if (mode < UDMA_MODE) {
/*
* 0.999 is ceiling
* for tcyc, tcvs, tmli, tenv, trp, tack
*/
tcyc = (u8) (((UDMA_ACCESS_TIMING[UDMA_TCYC][mode] \
* sysclk) + 9990) / 10000);
tcvs = (u8) (((UDMA_ACCESS_TIMING[UDMA_TCVS][mode] \
* sysclk) + 9990) / 10000);
tmli = (u8) (((UDMA_ACCESS_TIMING[UDMA_TMLI][mode] \
* sysclk) + 9990) / 10000);
tenv = (u8) (((UDMA_ACCESS_TIMING[UDMA_TENV][mode] \
* sysclk) + 9990) / 10000);
trp = (u8) (((UDMA_ACCESS_TIMING[UDMA_TRP][mode] \
* sysclk) + 9990) / 10000);
tack = (u8) (((UDMA_ACCESS_TIMING[UDMA_TACK][mode] \
* sysclk) + 9990) / 10000);
val = DT_REG_UDMA_TENV((tenv > 0) ? (tenv - 1) : 0) |
DT_REG_UDMA_TMLI((tmli > 0) ? (tmli - 1) : 0) |
DT_REG_UDMA_TCYC((tcyc > 0) ? (tcyc - 1) : 0) |
DT_REG_UDMA_TACK((tack > 0) ? (tack - 1) : 0) |
DT_REG_UDMA_TCVS((tcvs > 0) ? (tcvs - 1) : 0) |
DT_REG_UDMA_TRP((trp > 0) ? (trp - 1) : 0);
outl(val, (dev ? &ftide020->dtrd1 : &ftide020->dtrd0));
return 1;
} else
return 0;
default:
return 0;
}
}
static void ftide_read_hwrev(void)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
unsigned int rev;
rev = inl(&ftide020->revision);
}
static int ftide_controller_probe(void)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
unsigned int bak;
bak = inl(&ftide020->ctrd1);
/* probing by using shorter setup time */
outl(CONFIG_CTRD1_PROBE_T1, &ftide020->ctrd1);
if ((inl(&ftide020->ctrd1) & 0xff) != CONFIG_CTRD1_PROBE_T1) {
outl(bak, &ftide020->ctrd1);
return 0;
}
/* probing by using longer setup time */
outl(CONFIG_CTRD1_PROBE_T2, &ftide020->ctrd1);
if ((inl(&ftide020->ctrd1) & 0xff) != CONFIG_CTRD1_PROBE_T2) {
outl(bak, &ftide020->ctrd1);
return 0;
}
outl(bak, &ftide020->ctrd1);
return 1;
}
/* ide_preinit() was migrated from linux driver ide_probe_for_ftide() */
int ide_preinit(void)
{
static struct ftide020_s *ftide020 = (struct ftide020_s *) FTIDE_BASE;
int status;
unsigned int val;
int i;
status = 1;
for (i = 0; i < CONFIG_SYS_IDE_MAXBUS; i++)
ide_bus_offset[i] = -ATA_STATUS;
/* auto-detect IDE controller */
if (ftide_controller_probe()) {
printf("FTIDE020_S\n");
} else {
printf("FTIDE020_S ATA controller not found.\n");
return API_ENODEV;
}
/* check HW IP revision */
ftide_read_hwrev();
/* set FIFO threshold */
outl(((WRITE_FIFO - RX_THRESH) << 16) | RX_THRESH, &ftide020->dmatirr);
/* set Device_0 PIO_4 timing */
set_mode_timing(0, CMD_TIMING, REG_MODE4);
set_mode_timing(0, PIO_TIMING, PIO_MODE4);
/* set Device_1 PIO_4 timing */
set_mode_timing(1, CMD_TIMING, REG_MODE4);
set_mode_timing(1, PIO_TIMING, PIO_MODE4);
/* from E-bios */
/* little endian */
outl(0x0, &ftide020->cr);
mdelay(10);
outl(0x0fff0fff, &ftide020->ahbtr);
mdelay(10);
/* Enable controller Interrupt */
val = inl(&ftide020->cr);
/* Enable: IDE IRQ, IDE Terminate ERROR IRQ, AHB Timeout error IRQ */
val |= (CONTROL_IIE | CONTROL_TERIE | CONTROL_AERIE);
outl(val, &ftide020->cr);
status = 0;
return status;
}
void ide_set_reset(int flag)
{
debug("ide_set_reset()\n");
reset_ide_controller();
return;
}

View file

@ -0,0 +1,283 @@
/*
* Faraday FTIDE020_s ATA Controller (AHB)
*
* (C) Copyright 2011 Andes Technology
* Greentime Hu <greentime@andestech.com>
* Macpaul Lin <macpaul@andestech.com>
* Kuo-Wei Chou <kwchou@andestech.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 __FTIDE020_H
#define __FTIDE020_H
/* ftide020.h - ide support functions for the FTIDE020_S controller */
/* ATA controller register offset */
struct ftide020_s {
unsigned int rw_fifo; /* 0x00 - READ/WRITE FIFO */
unsigned int cmd_fifo; /* 0x04 - R: Status Reg, W: CMD_FIFO */
unsigned int cr; /* 0x08 - Control Reg */
unsigned int dmatirr; /* 0x0c - DMA Threshold/Interrupt Reg */
unsigned int ctrd0; /* 0x10 - Command Timing Reg Device 0 */
unsigned int dtrd0; /* 0x14 - Data Timing Reg Device 0 */
unsigned int ctrd1; /* 0x18 - Command Timing Reg Device 1 */
unsigned int dtrd1; /* 0x1c - Data Timing Reg Device 1 */
unsigned int ahbtr; /* 0x20 - AHB Timeout Reg */
unsigned int RESVD0; /* 0x24 */
unsigned int RESVD1; /* 0x28 */
unsigned int RESVD2; /* 0x2c */
unsigned int f_cfifo; /* 0x30 - Feature Info of CMD_FIFO */
unsigned int f_wfifo; /* 0x34 - Feature Info of WRITE_FIFO */
unsigned int f_rfifo; /* 0x3c - Feature Info of READ_FIFO */
unsigned int revision; /* 0x38 - Revision No. of FTIDE020_S */
};
/* reference parameters */
#define CONFIG_IDE_REG_CS 0x2 /* ref: ATA spec chaper 10, table 42 */
#define CONFIG_CTRD1_PROBE_T1 0x2
#define CONFIG_CTRD1_PROBE_T2 0x5
/* status register - 0x04 */
#define STATUS_CSEL (1 << 0) /* CSEL */
#define STATUS_CS(x) (((x) >> 1) & 0x3) /* CS#[1:0] */
#define STATUS_DMACK (1 << 3) /* DMACK# */
#define STATUS_DMARQ (1 << 4) /* DMA req */
#define STATUS_INTRQ (1 << 5) /* INT req */
#define STATUS_DIOR (1 << 6) /* DIOR */
#define STATUS_IORDY (1 << 7) /* I/O ready */
#define STATUS_DIOW (1 << 8) /* DIOW# */
#define STATUS_PDIAG (1 << 9) /* PDIAG */
#define STATUS_DASP (1 << 10) /* DASP# */
#define STATUS_DEV (1 << 11) /* selected device */
#define STATUS_PIO (1 << 12) /* PIO in progress */
#define STATUS_DMA (1 << 13) /* DMA in progress */
#define STATUS_WFE (1 << 14) /* write fifo full */
#define STATUS_RFE (1 << 15) /* read fifo empty */
#define STATUS_COUNTER(x) (((x) >> 16) & 0x3fff) /* data tx counter */
#define STATUS_ERR (1 << 30) /* trasfer terminated */
#define STATUS_AER (1 << 31) /* AHB timeout indicate */
/* Control register - 0x08 */
#define CONTROL_TYPE_PIO 0x0
#define CONTROL_TYPE_UDMA 0x1
/* Device 0 */
#define CONTROL_TYP0(x) (((x) & 0x7) << 0)
#define CONTROL_IRE0 (1 << 3) /* enable IORDY for PIO */
#define CONTROL_RESVD_DW0 (1 << 4) /* Reserved - DW0 ? */
#define CONTROL_E0 (1 << 5) /* E0: 1: Big Endian */
#define CONTROL_RESVD_WP0 (1 << 6) /* Reserved - WP0 ? */
#define CONTROL_RESVD_SE0 (1 << 7) /* Reserved - SE0 ? */
#define CONTROL_RESVD_ECC0 (1 << 8) /* Reserved - ECC0 ? */
#define CONTROL_RAEIE (1 << 9) /* IRQ - read fifo almost full */
#define CONTROL_RNEIE (1 << 10) /* IRQ - read fifo not empty */
#define CONTROL_WAFIE (1 << 11) /* IRQ - write fifo almost empty */
#define CONTROL_WNFIE (1 << 12) /* IRQ - write fifo not full */
#define CONTROL_RESVD_FIRQ (1 << 13) /* RESERVED - FIRQ ? */
#define CONTROL_AERIE (1 << 14) /* IRQ - AHB timeout error */
#define CONTROL_IIE (1 << 15) /* IDE IRQ enable */
/* Device 1 */
#define CONTROL_TYP1(x) (((x) & 0x7) << 16)
#define CONTROL_IRE1 (1 << 19) /* enable IORDY for PIO */
#define CONTROL_RESVD_DW1 (1 << 20) /* Reserved - DW1 ? */
#define CONTROL_E1 (1 << 21) /* E1: 1: Big Endian */
#define CONTROL_RESVD_WP1 (1 << 22) /* Reserved - WP1 ? */
#define CONTROL_RESVD_SE1 (1 << 23) /* Reserved - SE1 ? */
#define CONTROL_RESVD_ECC1 (1 << 24) /* Reserved - ECC1 ? */
#define CONTROL_DRE (1 << 25) /* DMA receive enable */
#define CONTROL_DTE (1 << 26) /* DMA transmit enable */
#define CONTRIL_RESVD (1 << 27)
#define CONTROL_TERIE (1 << 28) /* transfer terminate error IRQ */
#define CONTROL_T (1 << 29) /* terminate current operation */
#define CONTROL_SRST (1 << 30) /* IDE soft reset */
#define CONTROL_RST (1 << 31) /* IDE hardware reset */
/* IRQ register - 0x0c */
#define IRQ_RXTHRESH(x) (((x) & 0x3ff) << 0) /* Read FIFO threshold */
#define IRQ_RFAEIRQ (1 << 10) /* Read FIFO almost full intr req */
#define IRQ_RFNEIRQ (1 << 11) /* Read FIFO not empty intr req */
#define IRQ_WFAFIRQ (1 << 12) /* Write FIFO almost empty int req */
#define IRQ_WFNFIRQ (1 << 13) /* Write FIFO not full intr req */
#define IRQ_RESVD_FIRQ (1 << 14) /* Reserved - FIRQ ? */
#define IRQ_IIRQ (1 << 15) /* IDE device interrupt request */
#define IRQ_TXTHRESH(x) (((x) & 0x3ff) << 16) /* Write FIFO thershold */
#define IRQ_TERMERR (1 << 28) /* Transfer termination indication */
#define IRQ_AHBERR (1 << 29) /* AHB Timeout indication */
/* Command Timing Register 0-1: ctrd (0x10, 0x18) */
#define CT_REG_T1(x) (((x) & 0xff) << 0) /* setup time of addressed */
#define CT_REG_T2(x) (((x) & 0xff) << 8) /* pluse width of DIOR/DIOW */
#define CT_REG_T4(x) (((x) & 0xff) << 16) /* data hold time */
#define CT_REG_TEOC(x) (((x) & 0xff) << 24) /* time to the end of a cycle */
/* Data Timing Register 0-1: dtrd (0x14, 0x1c) */
/*
* PIO mode:
* b(0:7) DT_REG_PIO_T1: the setup time of addressed
* b(8:15) DT_REG_PIO_T2: the pluse width of DIOR/DIOW
* b(16:23) DT_REG_PIO_T4: data hold time
* b(24:31) DT_REG_PIO_TEOC: the time to the end of a cycle
*/
#define DT_REG_PIO_T1(x) (((x) & 0xff) << 0)
#define DT_REG_PIO_T2(x) (((x) & 0xff) << 8)
#define DT_REG_PIO_T4(x) (((x) & 0xff) << 16)
#define DT_REG_PIO_TEOC(x) (((x) & 0xff) << 24)
/*
* UDMA mode:
* b(0:3) DT_REG_UDMA_TENV: the envelope time
* b(4:7) DT_REG_UDMA_TMLI: interlock time
* b(8:15) DT_REG_UDMA_TCYC: cycle time - data time
* b(16:19) DT_REG_UDMA_TACK: setup and hold time of DMACK
* b(23:30) DT_REG_UDMA_TCVS: setup time of CRC
* b(24:31) DT_REG_UDMA_TRP: time to ready to pause
*/
#define DT_REG_UDMA_TENV(x) (((x) & 0xf) << 0)
#define DT_REG_UDMA_TMLI(x) (((x) & 0xf) << 4)
#define DT_REG_UDMA_TCYC(x) (((x) & 0xff) << 8)
#define DT_REG_UDMA_TACK(x) (((x) & 0xf) << 16)
#define DT_REG_UDMA_TCVS(x) (((x) & 0xf) << 20)
#define DT_REG_UDMA_TRP(x) (((x) & 0xff) << 24)
/* ftide020_s command formats */
/* read: IDE Register (CF1) */
#define IDE_REG_OPCODE_READ (1 << 13) /* 0x2000 */
#define IDE_REG_CS_READ(x) (((x) & 0x3) << 11)
#define IDE_REG_DA_READ(x) (((x) & 0x7) << 8)
#define IDE_REG_CMD_READ(x) 0x0 /* fixed value */
/* write: IDE Register (CF2) */
#define IDE_REG_OPCODE_WRITE (0x5 << 13) /* 0xA000 */
#define IDE_REG_CS_WRITE(x) (((x) & 0x3) << 11)
#define IDE_REG_DA_WRITE(x) (((x) & 0x7) << 8)
/* b(0:7) IDE_REG_CMD_WRITE(x): Actual ATA command or data */
#define IDE_REG_CMD_WRITE(x) (((x) & 0xff) << 0)
/* read/write data: PIO/UDMA (CF3) */
#define IDE_DATA_WRITE (1 << 15) /* read: 0, write: 1 */
#define IDE_DATA_OPCODE (0x2 << 13) /* device data access opcode */
/* b(0:12) IDE_DATA_COUNTER(x): Number of transfers minus 1 */
#define IDE_DATA_COUNTER(x) (((x) & 0x1fff) << 0)
/* set device: (CF4) */
#define IDE_SET_OPCODE (0x2740 << 2) /* [15:2], 0x9d00 */
/* CF3 counter value: 0: Tx in bytes, 1: in blocks (each block is 8 bytes) */
#define IDE_SET_CX8(x) (((x) & 0x1) << 1)
#define IDE_SET_DEV(x) (((x) & 0x1) << 0) /* 0: Master, 1: Slave */
/*
* IDE command bit definition
* This section is designed for minor hardware revision compatibility.
*/
#define READ_REG_CMD IDE_REG_OPCODE_READ /* 0x2000 */
#define WRITE_REG_CMD IDE_REG_OPCODE_WRITE /* 0xA000 */
#define READ_DATA_CMD IDE_DATA_OPCODE /* 0x4000 */
#define WRITE_DATA_CMD (IDE_DATA_OPCODE | IDE_DATA_WRITE) /* 0xC000 */
#define SET_DEV_CMD IDE_SET_OPCODE /* 0x9D00 */
#define TATOL_TIMING 3
#define CMD_TIMING 0
#define PIO_TIMING 1
#define DMA_TIMING 2
/* Timing Parameters */
/* Register Access Timing Parameters */
#define REG_PARAMETER 4
#define REG_T0 0
#define REG_T1 1
#define REG_T2 2
#define REG_T4 3
#define REG_MODE 5
#define REG_MODE0 0
#define REG_MODE1 1
#define REG_MODE2 2
#define REG_MODE3 3
#define REG_MODE4 4
/* PIO Access Timing Parameters */
#define PIO_PARAMETER 4
#define PIO_T0 0
#define PIO_T1 1
#define PIO_T2 2
#define PIO_T4 3
#define PIO_MODE 5
#define PIO_MODE0 0
#define PIO_MODE1 1
#define PIO_MODE2 2
#define PIO_MODE3 3
#define PIO_MODE4 4
/* UDMA Access Timing Parameters */
#define UDMA_PARAMETER 6
#define UDMA_TCYC 0
#define UDMA_TCVS 1
#define UDMA_TMLI 2
#define UDMA_TENV 3
#define UDMA_TRP 4
#define UDMA_TACK 5
#define UDMA_MODE 7
#define UDMA_MODE0 0
#define UDMA_MODE1 1
#define UDMA_MODE2 2
#define UDMA_MODE3 3
#define UDMA_MODE4 4
#define UDMA_MODE5 5
#define UDMA_MODE6 6
/*
* RX_THRESH:
* hardware limitation: max = 8, should support 1,4,8,16,32,64,128,256
*/
#define RX_THRESH 8
#define WRITE_FIFO 32 /* Hardwired value */
/* Time Table */
unsigned int REG_ACCESS_TIMING[REG_PARAMETER][REG_MODE] = {
{600, 383, 330, 180, 120},
{70, 50, 30, 30, 25},
{290, 290, 290, 80, 70},
{30, 20, 15, 10, 10},
};
unsigned int PIO_ACCESS_TIMING[PIO_PARAMETER][PIO_MODE] = {
{600, 383, 240, 180, 120},
{70, 50, 30, 30, 25},
{165, 125, 100, 80, 70},
{30, 20, 15, 10, 10},
};
unsigned int UDMA_ACCESS_TIMING[UDMA_PARAMETER][UDMA_MODE] = {
{1120, 730, 540, 390, 250, 168, 130}, /* 10X */
{700, 480, 310, 200, 67, 100, 100}, /* 10X */
{200, 200, 200, 200, 200, 200, 200}, /* 10X */
{200, 200, 200, 200, 200, 200, 200}, /* 10X */
{1600, 1250, 1000, 1000, 1000, 850, 850}, /* 10X */
{200, 200, 200, 200, 200, 200, 200}, /* 10X */
};
#endif /* __FTIDE020_H */

View file

@ -0,0 +1,158 @@
/*
* Copyright (C) 2008 Freescale Semiconductor, Inc.
* Dave Liu <daveliu@freescale.com>
* port from the libata of linux kernel
*
* 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 <libata.h>
u64 ata_id_n_sectors(u16 *id)
{
if (ata_id_has_lba(id)) {
if (ata_id_has_lba48(id))
return ata_id_u64(id, ATA_ID_LBA48_SECTORS);
else
return ata_id_u32(id, ATA_ID_LBA_SECTORS);
} else {
return 0;
}
}
u32 ata_dev_classify(u32 sig)
{
u8 lbam, lbah;
lbam = (sig >> 16) & 0xff;
lbah = (sig >> 24) & 0xff;
if (((lbam == 0) && (lbah == 0)) ||
((lbam == 0x3c) && (lbah == 0xc3)))
return ATA_DEV_ATA;
if ((lbam == 0x14) && (lbah == 0xeb))
return ATA_DEV_ATAPI;
if ((lbam == 0x69) && (lbah == 0x96))
return ATA_DEV_PMP;
return ATA_DEV_UNKNOWN;
}
static void ata_id_string(const u16 *id, unsigned char *s,
unsigned int ofs, unsigned int len)
{
unsigned int c;
while (len > 0) {
c = id[ofs] >> 8;
*s = c;
s++;
c = id[ofs] & 0xff;
*s = c;
s++;
ofs++;
len -= 2;
}
}
void ata_id_c_string(const u16 *id, unsigned char *s,
unsigned int ofs, unsigned int len)
{
unsigned char *p;
ata_id_string(id, s, ofs, len - 1);
p = s + strnlen((char *)s, len - 1);
while (p > s && p[-1] == ' ')
p--;
*p = '\0';
}
void ata_dump_id(u16 *id)
{
unsigned char serial[ATA_ID_SERNO_LEN + 1];
unsigned char firmware[ATA_ID_FW_REV_LEN + 1];
unsigned char product[ATA_ID_PROD_LEN + 1];
u64 n_sectors;
/* Serial number */
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
printf("S/N: %s\n\r", serial);
/* Firmware version */
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
printf("Firmware version: %s\n\r", firmware);
/* Product model */
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
printf("Product model number: %s\n\r", product);
/* Total sectors of device */
n_sectors = ata_id_n_sectors(id);
printf("Capablity: %lld sectors\n\r", n_sectors);
printf ("id[49]: capabilities = 0x%04x\n"
"id[53]: field valid = 0x%04x\n"
"id[63]: mwdma = 0x%04x\n"
"id[64]: pio = 0x%04x\n"
"id[75]: queue depth = 0x%04x\n",
id[49],
id[53],
id[63],
id[64],
id[75]);
printf ("id[76]: sata capablity = 0x%04x\n"
"id[78]: sata features supported = 0x%04x\n"
"id[79]: sata features enable = 0x%04x\n",
id[76],
id[78],
id[79]);
printf ("id[80]: major version = 0x%04x\n"
"id[81]: minor version = 0x%04x\n"
"id[82]: command set supported 1 = 0x%04x\n"
"id[83]: command set supported 2 = 0x%04x\n"
"id[84]: command set extension = 0x%04x\n",
id[80],
id[81],
id[82],
id[83],
id[84]);
printf ("id[85]: command set enable 1 = 0x%04x\n"
"id[86]: command set enable 2 = 0x%04x\n"
"id[87]: command set default = 0x%04x\n"
"id[88]: udma = 0x%04x\n"
"id[93]: hardware reset result = 0x%04x\n",
id[85],
id[86],
id[87],
id[88],
id[93]);
}
void ata_swap_buf_le16(u16 *buf, unsigned int buf_words)
{
unsigned int i;
for (i = 0; i < buf_words; i++)
buf[i] = le16_to_cpu(buf[i]);
}

View file

@ -0,0 +1,174 @@
/*
* Copyright (C) 2010 Albert ARIBAUD <albert.u.boot@aribaud.net>
*
* Written-by: Albert ARIBAUD <albert.u.boot@aribaud.net>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <common.h>
#include <asm/io.h>
#if defined(CONFIG_ORION5X)
#include <asm/arch/orion5x.h>
#elif defined(CONFIG_KIRKWOOD)
#include <asm/arch/kirkwood.h>
#endif
/* SATA port registers */
struct mvsata_port_registers {
u32 reserved0[10];
u32 edma_cmd;
u32 reserved1[181];
/* offset 0x300 : ATA Interface registers */
u32 sstatus;
u32 serror;
u32 scontrol;
u32 ltmode;
u32 phymode3;
u32 phymode4;
u32 reserved2[5];
u32 phymode1;
u32 phymode2;
u32 bist_cr;
u32 bist_dw1;
u32 bist_dw2;
u32 serrorintrmask;
};
/*
* Sanity checks:
* - to compile at all, we need CONFIG_SYS_ATA_BASE_ADDR.
* - for ide_preinit to make sense, we need at least one of
* CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE0_OFFSET;
* - for inde_preinit to be called, we need CONFIG_IDE_PREINIT.
* Fail with an explanation message if these conditions are not met.
* This is particularly important for CONFIG_IDE_PREINIT, because
* its lack would not cause a build error.
*/
#if !defined(CONFIG_SYS_ATA_BASE_ADDR)
#error CONFIG_SYS_ATA_BASE_ADDR must be defined
#elif !defined(CONFIG_SYS_ATA_IDE0_OFFSET) \
&& !defined(CONFIG_SYS_ATA_IDE1_OFFSET)
#error CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE1_OFFSET \
must be defined
#elif !defined(CONFIG_IDE_PREINIT)
#error CONFIG_IDE_PREINIT must be defined
#endif
/*
* Masks and values for SControl DETection and Interface Power Management,
* and for SStatus DETection.
*/
#define MVSATA_EDMA_CMD_ATA_RST 0x00000004
#define MVSATA_SCONTROL_DET_MASK 0x0000000F
#define MVSATA_SCONTROL_DET_NONE 0x00000000
#define MVSATA_SCONTROL_DET_INIT 0x00000001
#define MVSATA_SCONTROL_IPM_MASK 0x00000F00
#define MVSATA_SCONTROL_IPM_NO_LP_ALLOWED 0x00000300
#define MVSATA_SCONTROL_MASK \
(MVSATA_SCONTROL_DET_MASK|MVSATA_SCONTROL_IPM_MASK)
#define MVSATA_PORT_INIT \
(MVSATA_SCONTROL_DET_INIT|MVSATA_SCONTROL_IPM_NO_LP_ALLOWED)
#define MVSATA_PORT_USE \
(MVSATA_SCONTROL_DET_NONE|MVSATA_SCONTROL_IPM_NO_LP_ALLOWED)
#define MVSATA_SSTATUS_DET_MASK 0x0000000F
#define MVSATA_SSTATUS_DET_DEVCOMM 0x00000003
/*
* Status codes to return to client callers. Currently, callers ignore
* exact value and only care for zero or nonzero, so no need to make this
* public, it is only #define'd for clarity.
* If/when standard negative codes are implemented in U-boot, then these
* #defines should be moved to, or replaced by ones from, the common list
* of status codes.
*/
#define MVSATA_STATUS_OK 0
#define MVSATA_STATUS_TIMEOUT -1
/*
* Initialize one MVSATAHC port: set SControl's IPM to "always active"
* and DET to "reset", then wait for SStatus's DET to become "device and
* comm ok" (or time out after 50 us if no device), then set SControl's
* DET back to "no action".
*/
static int mvsata_ide_initialize_port(struct mvsata_port_registers *port)
{
u32 control;
u32 status;
u32 timeleft = 10000; /* wait at most 10 ms for SATA reset to complete */
/* Hard reset */
writel(MVSATA_EDMA_CMD_ATA_RST, &port->edma_cmd);
udelay(25); /* taken from original marvell port */
writel(0, &port->edma_cmd);
/* Set control IPM to 3 (no low power) and DET to 1 (initialize) */
control = readl(&port->scontrol);
control = (control & ~MVSATA_SCONTROL_MASK) | MVSATA_PORT_INIT;
writel(control, &port->scontrol);
/* Toggle control DET back to 0 (normal operation) */
control = (control & ~MVSATA_SCONTROL_MASK) | MVSATA_PORT_USE;
writel(control, &port->scontrol);
/* wait for status DET to become 3 (device and communication OK) */
while (--timeleft) {
status = readl(&port->sstatus) & MVSATA_SSTATUS_DET_MASK;
if (status == MVSATA_SSTATUS_DET_DEVCOMM)
break;
udelay(1);
}
/* return success or time-out error depending on time left */
if (!timeleft)
return MVSATA_STATUS_TIMEOUT;
return MVSATA_STATUS_OK;
}
/*
* ide_preinit() will be called by ide_init in cmd_ide.c and will
* reset the MVSTATHC ports needed by the board.
*/
int ide_preinit(void)
{
int ret = MVSATA_STATUS_TIMEOUT;
int status;
/* Enable ATA port 0 (could be SATA port 0 or 1) if declared */
#if defined(CONFIG_SYS_ATA_IDE0_OFFSET)
status = mvsata_ide_initialize_port(
(struct mvsata_port_registers *)
(CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_IDE0_OFFSET));
if (status == MVSATA_STATUS_OK)
ret = MVSATA_STATUS_OK;
#endif
/* Enable ATA port 1 (could be SATA port 0 or 1) if declared */
#if defined(CONFIG_SYS_ATA_IDE1_OFFSET)
status = mvsata_ide_initialize_port(
(struct mvsata_port_registers *)
(CONFIG_SYS_ATA_BASE_ADDR + CONFIG_SYS_ATA_IDE1_OFFSET));
if (status == MVSATA_STATUS_OK)
ret = MVSATA_STATUS_OK;
#endif
/* Return success if at least one port initialization succeeded */
return ret;
}

View file

@ -0,0 +1,142 @@
/*
* Freescale iMX51 ATA driver
*
* Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
*
* Based on code by:
* Mahesh Mahadevan <mahesh.mahadevan@freescale.com>
*
* Based on code from original FSL ATA driver, which is
* part of eCos, the Embedded Configurable Operating System.
* Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
*
* 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 <common.h>
#include <command.h>
#include <config.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <ide.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/clock.h>
/* MXC ATA register offsets */
struct mxc_ata_config_regs {
u8 time_off; /* 0x00 */
u8 time_on;
u8 time_1;
u8 time_2w;
u8 time_2r;
u8 time_ax;
u8 time_pio_rdx;
u8 time_4;
u8 time_9;
u8 time_m;
u8 time_jn;
u8 time_d;
u8 time_k;
u8 time_ack;
u8 time_env;
u8 time_udma_rdx;
u8 time_zah; /* 0x10 */
u8 time_mlix;
u8 time_dvh;
u8 time_dzfs;
u8 time_dvs;
u8 time_cvh;
u8 time_ss;
u8 time_cyc;
u32 fifo_data_32; /* 0x18 */
u32 fifo_data_16;
u32 fifo_fill;
u32 ata_control;
u32 interrupt_pending;
u32 interrupt_enable;
u32 interrupt_clear;
u32 fifo_alarm;
};
struct mxc_data_hdd_regs {
u32 drive_data; /* 0xa0 */
u32 drive_features;
u32 drive_sector_count;
u32 drive_sector_num;
u32 drive_cyl_low;
u32 drive_cyl_high;
u32 drive_dev_head;
u32 command;
u32 status;
u32 alt_status;
};
/* PIO timing table */
#define NR_PIO_SPECS 5
static uint16_t pio_t1[NR_PIO_SPECS] = { 70, 50, 30, 30, 25 };
static uint16_t pio_t2_8[NR_PIO_SPECS] = { 290, 290, 290, 80, 70 };
static uint16_t pio_t4[NR_PIO_SPECS] = { 30, 20, 15, 10, 10 };
static uint16_t pio_t9[NR_PIO_SPECS] = { 20, 15, 10, 10, 10 };
static uint16_t pio_tA[NR_PIO_SPECS] = { 50, 50, 50, 50, 50 };
#define REG2OFF(reg) ((((uint32_t)reg) & 0x3) * 8)
static void set_ata_bus_timing(unsigned char mode)
{
uint32_t T = 1000000000 / mxc_get_clock(MXC_IPG_CLK);
struct mxc_ata_config_regs *ata_regs;
ata_regs = (struct mxc_ata_config_regs *)CONFIG_SYS_ATA_BASE_ADDR;
if (mode >= NR_PIO_SPECS)
return;
/* Write TIME_OFF/ON/1/2W */
writeb(3, &ata_regs->time_off);
writeb(3, &ata_regs->time_on);
writeb((pio_t1[mode] + T) / T, &ata_regs->time_1);
writeb((pio_t2_8[mode] + T) / T, &ata_regs->time_2w);
/* Write TIME_2R/AX/RDX/4 */
writeb((pio_t2_8[mode] + T) / T, &ata_regs->time_2r);
writeb((pio_tA[mode] + T) / T + 2, &ata_regs->time_ax);
writeb(1, &ata_regs->time_pio_rdx);
writeb((pio_t4[mode] + T) / T, &ata_regs->time_4);
/* Write TIME_9 ; the rest of timing registers is irrelevant for PIO */
writeb((pio_t9[mode] + T) / T, &ata_regs->time_9);
}
int ide_preinit(void)
{
struct mxc_ata_config_regs *ata_regs;
ata_regs = (struct mxc_ata_config_regs *)CONFIG_SYS_ATA_BASE_ADDR;
/* 46.3.3.4 @ FSL iMX51 manual */
/* FIFO normal op., drive reset */
writel(0x80, &ata_regs->ata_control);
/* FIFO normal op., drive not reset */
writel(0xc0, &ata_regs->ata_control);
/* Configure the PIO timing */
set_ata_bus_timing(CONFIG_MXC_ATA_PIO_MODE);
/* 46.3.3.4 @ FSL iMX51 manual */
/* Drive not reset, IORDY handshake */
writel(0x41, &ata_regs->ata_control);
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,173 @@
/*
* Driver for Blackfin on-chip ATAPI controller.
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Copyright (c) 2008 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef PATA_BFIN_H
#define PATA_BFIN_H
#include <asm/blackfin_local.h>
struct ata_ioports {
unsigned long cmd_addr;
unsigned long data_addr;
unsigned long error_addr;
unsigned long feature_addr;
unsigned long nsect_addr;
unsigned long lbal_addr;
unsigned long lbam_addr;
unsigned long lbah_addr;
unsigned long device_addr;
unsigned long status_addr;
unsigned long command_addr;
unsigned long altstatus_addr;
unsigned long ctl_addr;
unsigned long bmdma_addr;
unsigned long scr_addr;
};
struct ata_port {
unsigned int port_no; /* primary=0, secondary=1 */
struct ata_ioports ioaddr; /* ATA cmd/ctl/dma reg blks */
unsigned long flag;
unsigned int ata_mode;
unsigned char ctl_reg;
unsigned char last_ctl;
unsigned char dev_mask;
};
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
#define DRV_NAME "pata-bfin"
#define DRV_VERSION "0.9"
#define __iomem
#define ATA_REG_CTRL 0x0E
#define ATA_REG_ALTSTATUS ATA_REG_CTRL
#define ATA_TMOUT_BOOT 30000
#define ATA_TMOUT_BOOT_QUICK 7000
#define PATA_BFIN_WAIT_TIMEOUT 10000
#define PATA_DEV_NUM_PER_PORT 2
/* These are the offset of the controller's registers */
#define ATAPI_OFFSET_CONTROL 0x00
#define ATAPI_OFFSET_STATUS 0x04
#define ATAPI_OFFSET_DEV_ADDR 0x08
#define ATAPI_OFFSET_DEV_TXBUF 0x0c
#define ATAPI_OFFSET_DEV_RXBUF 0x10
#define ATAPI_OFFSET_INT_MASK 0x14
#define ATAPI_OFFSET_INT_STATUS 0x18
#define ATAPI_OFFSET_XFER_LEN 0x1c
#define ATAPI_OFFSET_LINE_STATUS 0x20
#define ATAPI_OFFSET_SM_STATE 0x24
#define ATAPI_OFFSET_TERMINATE 0x28
#define ATAPI_OFFSET_PIO_TFRCNT 0x2c
#define ATAPI_OFFSET_DMA_TFRCNT 0x30
#define ATAPI_OFFSET_UMAIN_TFRCNT 0x34
#define ATAPI_OFFSET_UDMAOUT_TFRCNT 0x38
#define ATAPI_OFFSET_REG_TIM_0 0x40
#define ATAPI_OFFSET_PIO_TIM_0 0x44
#define ATAPI_OFFSET_PIO_TIM_1 0x48
#define ATAPI_OFFSET_MULTI_TIM_0 0x50
#define ATAPI_OFFSET_MULTI_TIM_1 0x54
#define ATAPI_OFFSET_MULTI_TIM_2 0x58
#define ATAPI_OFFSET_ULTRA_TIM_0 0x60
#define ATAPI_OFFSET_ULTRA_TIM_1 0x64
#define ATAPI_OFFSET_ULTRA_TIM_2 0x68
#define ATAPI_OFFSET_ULTRA_TIM_3 0x6c
#define ATAPI_GET_CONTROL(base)\
bfin_read16(base + ATAPI_OFFSET_CONTROL)
#define ATAPI_SET_CONTROL(base, val)\
bfin_write16(base + ATAPI_OFFSET_CONTROL, val)
#define ATAPI_GET_STATUS(base)\
bfin_read16(base + ATAPI_OFFSET_STATUS)
#define ATAPI_GET_DEV_ADDR(base)\
bfin_read16(base + ATAPI_OFFSET_DEV_ADDR)
#define ATAPI_SET_DEV_ADDR(base, val)\
bfin_write16(base + ATAPI_OFFSET_DEV_ADDR, val)
#define ATAPI_GET_DEV_TXBUF(base)\
bfin_read16(base + ATAPI_OFFSET_DEV_TXBUF)
#define ATAPI_SET_DEV_TXBUF(base, val)\
bfin_write16(base + ATAPI_OFFSET_DEV_TXBUF, val)
#define ATAPI_GET_DEV_RXBUF(base)\
bfin_read16(base + ATAPI_OFFSET_DEV_RXBUF)
#define ATAPI_SET_DEV_RXBUF(base, val)\
bfin_write16(base + ATAPI_OFFSET_DEV_RXBUF, val)
#define ATAPI_GET_INT_MASK(base)\
bfin_read16(base + ATAPI_OFFSET_INT_MASK)
#define ATAPI_SET_INT_MASK(base, val)\
bfin_write16(base + ATAPI_OFFSET_INT_MASK, val)
#define ATAPI_GET_INT_STATUS(base)\
bfin_read16(base + ATAPI_OFFSET_INT_STATUS)
#define ATAPI_SET_INT_STATUS(base, val)\
bfin_write16(base + ATAPI_OFFSET_INT_STATUS, val)
#define ATAPI_GET_XFER_LEN(base)\
bfin_read16(base + ATAPI_OFFSET_XFER_LEN)
#define ATAPI_SET_XFER_LEN(base, val)\
bfin_write16(base + ATAPI_OFFSET_XFER_LEN, val)
#define ATAPI_GET_LINE_STATUS(base)\
bfin_read16(base + ATAPI_OFFSET_LINE_STATUS)
#define ATAPI_GET_SM_STATE(base)\
bfin_read16(base + ATAPI_OFFSET_SM_STATE)
#define ATAPI_GET_TERMINATE(base)\
bfin_read16(base + ATAPI_OFFSET_TERMINATE)
#define ATAPI_SET_TERMINATE(base, val)\
bfin_write16(base + ATAPI_OFFSET_TERMINATE, val)
#define ATAPI_GET_PIO_TFRCNT(base)\
bfin_read16(base + ATAPI_OFFSET_PIO_TFRCNT)
#define ATAPI_GET_DMA_TFRCNT(base)\
bfin_read16(base + ATAPI_OFFSET_DMA_TFRCNT)
#define ATAPI_GET_UMAIN_TFRCNT(base)\
bfin_read16(base + ATAPI_OFFSET_UMAIN_TFRCNT)
#define ATAPI_GET_UDMAOUT_TFRCNT(base)\
bfin_read16(base + ATAPI_OFFSET_UDMAOUT_TFRCNT)
#define ATAPI_GET_REG_TIM_0(base)\
bfin_read16(base + ATAPI_OFFSET_REG_TIM_0)
#define ATAPI_SET_REG_TIM_0(base, val)\
bfin_write16(base + ATAPI_OFFSET_REG_TIM_0, val)
#define ATAPI_GET_PIO_TIM_0(base)\
bfin_read16(base + ATAPI_OFFSET_PIO_TIM_0)
#define ATAPI_SET_PIO_TIM_0(base, val)\
bfin_write16(base + ATAPI_OFFSET_PIO_TIM_0, val)
#define ATAPI_GET_PIO_TIM_1(base)\
bfin_read16(base + ATAPI_OFFSET_PIO_TIM_1)
#define ATAPI_SET_PIO_TIM_1(base, val)\
bfin_write16(base + ATAPI_OFFSET_PIO_TIM_1, val)
#define ATAPI_GET_MULTI_TIM_0(base)\
bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_0)
#define ATAPI_SET_MULTI_TIM_0(base, val)\
bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_0, val)
#define ATAPI_GET_MULTI_TIM_1(base)\
bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_1)
#define ATAPI_SET_MULTI_TIM_1(base, val)\
bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_1, val)
#define ATAPI_GET_MULTI_TIM_2(base)\
bfin_read16(base + ATAPI_OFFSET_MULTI_TIM_2)
#define ATAPI_SET_MULTI_TIM_2(base, val)\
bfin_write16(base + ATAPI_OFFSET_MULTI_TIM_2, val)
#define ATAPI_GET_ULTRA_TIM_0(base)\
bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_0)
#define ATAPI_SET_ULTRA_TIM_0(base, val)\
bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_0, val)
#define ATAPI_GET_ULTRA_TIM_1(base)\
bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_1)
#define ATAPI_SET_ULTRA_TIM_1(base, val)\
bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_1, val)
#define ATAPI_GET_ULTRA_TIM_2(base)\
bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_2)
#define ATAPI_SET_ULTRA_TIM_2(base, val)\
bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_2, val)
#define ATAPI_GET_ULTRA_TIM_3(base)\
bfin_read16(base + ATAPI_OFFSET_ULTRA_TIM_3)
#define ATAPI_SET_ULTRA_TIM_3(base, val)\
bfin_write16(base + ATAPI_OFFSET_ULTRA_TIM_3, val)
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,477 @@
/*
* sata_dwc.h
*
* Synopsys DesignWare Cores (DWC) SATA host driver
*
* Author: Mark Miesfeld <mmiesfeld@amcc.com>
*
* Ported from 2.6.19.2 to 2.6.25/26 by Stefan Roese <sr@denx.de>
* Copyright 2008 DENX Software Engineering
*
* Based on versions provided by AMCC and Synopsys which are:
* Copyright 2006 Applied Micro Circuits Corporation
* COPYRIGHT (C) 2005 SYNOPSYS, INC. ALL RIGHTS RESERVED
*
* 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.
*
*/
/*
* SATA support based on the chip canyonlands.
*
* 04-17-2009
* The local version of this driver for the canyonlands board
* does not use interrupts but polls the chip instead.
*/
#ifndef _SATA_DWC_H_
#define _SATA_DWC_H_
#define __U_BOOT__
#define HZ 100
#define READ 0
#define WRITE 1
enum {
ATA_READID_POSTRESET = (1 << 0),
ATA_DNXFER_PIO = 0,
ATA_DNXFER_DMA = 1,
ATA_DNXFER_40C = 2,
ATA_DNXFER_FORCE_PIO = 3,
ATA_DNXFER_FORCE_PIO0 = 4,
ATA_DNXFER_QUIET = (1 << 31),
};
enum hsm_task_states {
HSM_ST_IDLE,
HSM_ST_FIRST,
HSM_ST,
HSM_ST_LAST,
HSM_ST_ERR,
};
#define ATA_SHORT_PAUSE ((HZ >> 6) + 1)
struct ata_queued_cmd {
struct ata_port *ap;
struct ata_device *dev;
struct ata_taskfile tf;
u8 cdb[ATAPI_CDB_LEN];
unsigned long flags;
unsigned int tag;
unsigned int n_elem;
int dma_dir;
unsigned int sect_size;
unsigned int nbytes;
unsigned int extrabytes;
unsigned int curbytes;
unsigned int err_mask;
struct ata_taskfile result_tf;
void *private_data;
#ifndef __U_BOOT__
void *lldd_task;
#endif
unsigned char *pdata;
};
typedef void (*ata_qc_cb_t) (struct ata_queued_cmd *qc);
#define ATA_TAG_POISON 0xfafbfcfdU
enum {
LIBATA_MAX_PRD = ATA_MAX_PRD / 2,
LIBATA_DUMB_MAX_PRD = ATA_MAX_PRD / 4,
ATA_MAX_PORTS = 8,
ATA_DEF_QUEUE = 1,
ATA_MAX_QUEUE = 32,
ATA_TAG_INTERNAL = ATA_MAX_QUEUE - 1,
ATA_MAX_BUS = 2,
ATA_DEF_BUSY_WAIT = 10000,
ATAPI_MAX_DRAIN = 16 << 10,
ATA_SHT_EMULATED = 1,
ATA_SHT_CMD_PER_LUN = 1,
ATA_SHT_THIS_ID = -1,
ATA_SHT_USE_CLUSTERING = 1,
ATA_DFLAG_LBA = (1 << 0),
ATA_DFLAG_LBA48 = (1 << 1),
ATA_DFLAG_CDB_INTR = (1 << 2),
ATA_DFLAG_NCQ = (1 << 3),
ATA_DFLAG_FLUSH_EXT = (1 << 4),
ATA_DFLAG_ACPI_PENDING = (1 << 5),
ATA_DFLAG_ACPI_FAILED = (1 << 6),
ATA_DFLAG_AN = (1 << 7),
ATA_DFLAG_HIPM = (1 << 8),
ATA_DFLAG_DIPM = (1 << 9),
ATA_DFLAG_DMADIR = (1 << 10),
ATA_DFLAG_CFG_MASK = (1 << 12) - 1,
ATA_DFLAG_PIO = (1 << 12),
ATA_DFLAG_NCQ_OFF = (1 << 13),
ATA_DFLAG_SPUNDOWN = (1 << 14),
ATA_DFLAG_SLEEPING = (1 << 15),
ATA_DFLAG_DUBIOUS_XFER = (1 << 16),
ATA_DFLAG_INIT_MASK = (1 << 24) - 1,
ATA_DFLAG_DETACH = (1 << 24),
ATA_DFLAG_DETACHED = (1 << 25),
ATA_LFLAG_HRST_TO_RESUME = (1 << 0),
ATA_LFLAG_SKIP_D2H_BSY = (1 << 1),
ATA_LFLAG_NO_SRST = (1 << 2),
ATA_LFLAG_ASSUME_ATA = (1 << 3),
ATA_LFLAG_ASSUME_SEMB = (1 << 4),
ATA_LFLAG_ASSUME_CLASS = ATA_LFLAG_ASSUME_ATA | ATA_LFLAG_ASSUME_SEMB,
ATA_LFLAG_NO_RETRY = (1 << 5),
ATA_LFLAG_DISABLED = (1 << 6),
ATA_FLAG_SLAVE_POSS = (1 << 0),
ATA_FLAG_SATA = (1 << 1),
ATA_FLAG_NO_LEGACY = (1 << 2),
ATA_FLAG_MMIO = (1 << 3),
ATA_FLAG_SRST = (1 << 4),
ATA_FLAG_SATA_RESET = (1 << 5),
ATA_FLAG_NO_ATAPI = (1 << 6),
ATA_FLAG_PIO_DMA = (1 << 7),
ATA_FLAG_PIO_LBA48 = (1 << 8),
ATA_FLAG_PIO_POLLING = (1 << 9),
ATA_FLAG_NCQ = (1 << 10),
ATA_FLAG_DEBUGMSG = (1 << 13),
ATA_FLAG_IGN_SIMPLEX = (1 << 15),
ATA_FLAG_NO_IORDY = (1 << 16),
ATA_FLAG_ACPI_SATA = (1 << 17),
ATA_FLAG_AN = (1 << 18),
ATA_FLAG_PMP = (1 << 19),
ATA_FLAG_IPM = (1 << 20),
ATA_FLAG_DISABLED = (1 << 23),
ATA_PFLAG_EH_PENDING = (1 << 0),
ATA_PFLAG_EH_IN_PROGRESS = (1 << 1),
ATA_PFLAG_FROZEN = (1 << 2),
ATA_PFLAG_RECOVERED = (1 << 3),
ATA_PFLAG_LOADING = (1 << 4),
ATA_PFLAG_UNLOADING = (1 << 5),
ATA_PFLAG_SCSI_HOTPLUG = (1 << 6),
ATA_PFLAG_INITIALIZING = (1 << 7),
ATA_PFLAG_RESETTING = (1 << 8),
ATA_PFLAG_SUSPENDED = (1 << 17),
ATA_PFLAG_PM_PENDING = (1 << 18),
ATA_QCFLAG_ACTIVE = (1 << 0),
ATA_QCFLAG_DMAMAP = (1 << 1),
ATA_QCFLAG_IO = (1 << 3),
ATA_QCFLAG_RESULT_TF = (1 << 4),
ATA_QCFLAG_CLEAR_EXCL = (1 << 5),
ATA_QCFLAG_QUIET = (1 << 6),
ATA_QCFLAG_FAILED = (1 << 16),
ATA_QCFLAG_SENSE_VALID = (1 << 17),
ATA_QCFLAG_EH_SCHEDULED = (1 << 18),
ATA_HOST_SIMPLEX = (1 << 0),
ATA_HOST_STARTED = (1 << 1),
ATA_TMOUT_BOOT = 30 * 100,
ATA_TMOUT_BOOT_QUICK = 7 * 100,
ATA_TMOUT_INTERNAL = 30 * 100,
ATA_TMOUT_INTERNAL_QUICK = 5 * 100,
/* FIXME: GoVault needs 2s but we can't afford that without
* parallel probing. 800ms is enough for iVDR disk
* HHD424020F7SV00. Increase to 2secs when parallel probing
* is in place.
*/
ATA_TMOUT_FF_WAIT = 4 * 100 / 5,
BUS_UNKNOWN = 0,
BUS_DMA = 1,
BUS_IDLE = 2,
BUS_NOINTR = 3,
BUS_NODATA = 4,
BUS_TIMER = 5,
BUS_PIO = 6,
BUS_EDD = 7,
BUS_IDENTIFY = 8,
BUS_PACKET = 9,
PORT_UNKNOWN = 0,
PORT_ENABLED = 1,
PORT_DISABLED = 2,
/* encoding various smaller bitmaps into a single
* unsigned long bitmap
*/
ATA_NR_PIO_MODES = 7,
ATA_NR_MWDMA_MODES = 5,
ATA_NR_UDMA_MODES = 8,
ATA_SHIFT_PIO = 0,
ATA_SHIFT_MWDMA = ATA_SHIFT_PIO + ATA_NR_PIO_MODES,
ATA_SHIFT_UDMA = ATA_SHIFT_MWDMA + ATA_NR_MWDMA_MODES,
ATA_DMA_PAD_SZ = 4,
ATA_ERING_SIZE = 32,
ATA_DEFER_LINK = 1,
ATA_DEFER_PORT = 2,
ATA_EH_DESC_LEN = 80,
ATA_EH_REVALIDATE = (1 << 0),
ATA_EH_SOFTRESET = (1 << 1),
ATA_EH_HARDRESET = (1 << 2),
ATA_EH_ENABLE_LINK = (1 << 3),
ATA_EH_LPM = (1 << 4),
ATA_EH_RESET_MASK = ATA_EH_SOFTRESET | ATA_EH_HARDRESET,
ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE,
ATA_EHI_HOTPLUGGED = (1 << 0),
ATA_EHI_RESUME_LINK = (1 << 1),
ATA_EHI_NO_AUTOPSY = (1 << 2),
ATA_EHI_QUIET = (1 << 3),
ATA_EHI_DID_SOFTRESET = (1 << 16),
ATA_EHI_DID_HARDRESET = (1 << 17),
ATA_EHI_PRINTINFO = (1 << 18),
ATA_EHI_SETMODE = (1 << 19),
ATA_EHI_POST_SETMODE = (1 << 20),
ATA_EHI_DID_RESET = ATA_EHI_DID_SOFTRESET | ATA_EHI_DID_HARDRESET,
ATA_EHI_RESET_MODIFIER_MASK = ATA_EHI_RESUME_LINK,
ATA_EH_MAX_TRIES = 5,
ATA_PROBE_MAX_TRIES = 3,
ATA_EH_DEV_TRIES = 3,
ATA_EH_PMP_TRIES = 5,
ATA_EH_PMP_LINK_TRIES = 3,
SATA_PMP_SCR_TIMEOUT = 250,
/* Horkage types. May be set by libata or controller on drives
(some horkage may be drive/controller pair dependant */
ATA_HORKAGE_DIAGNOSTIC = (1 << 0),
ATA_HORKAGE_NODMA = (1 << 1),
ATA_HORKAGE_NONCQ = (1 << 2),
ATA_HORKAGE_MAX_SEC_128 = (1 << 3),
ATA_HORKAGE_BROKEN_HPA = (1 << 4),
ATA_HORKAGE_SKIP_PM = (1 << 5),
ATA_HORKAGE_HPA_SIZE = (1 << 6),
ATA_HORKAGE_IPM = (1 << 7),
ATA_HORKAGE_IVB = (1 << 8),
ATA_HORKAGE_STUCK_ERR = (1 << 9),
ATA_DMA_MASK_ATA = (1 << 0),
ATA_DMA_MASK_ATAPI = (1 << 1),
ATA_DMA_MASK_CFA = (1 << 2),
ATAPI_READ = 0,
ATAPI_WRITE = 1,
ATAPI_READ_CD = 2,
ATAPI_PASS_THRU = 3,
ATAPI_MISC = 4,
};
enum ata_completion_errors {
AC_ERR_DEV = (1 << 0),
AC_ERR_HSM = (1 << 1),
AC_ERR_TIMEOUT = (1 << 2),
AC_ERR_MEDIA = (1 << 3),
AC_ERR_ATA_BUS = (1 << 4),
AC_ERR_HOST_BUS = (1 << 5),
AC_ERR_SYSTEM = (1 << 6),
AC_ERR_INVALID = (1 << 7),
AC_ERR_OTHER = (1 << 8),
AC_ERR_NODEV_HINT = (1 << 9),
AC_ERR_NCQ = (1 << 10),
};
enum ata_xfer_mask {
ATA_MASK_PIO = ((1LU << ATA_NR_PIO_MODES) - 1) << ATA_SHIFT_PIO,
ATA_MASK_MWDMA = ((1LU << ATA_NR_MWDMA_MODES) - 1) << ATA_SHIFT_MWDMA,
ATA_MASK_UDMA = ((1LU << ATA_NR_UDMA_MODES) - 1) << ATA_SHIFT_UDMA,
};
struct ata_port_info {
#ifndef __U_BOOT__
struct scsi_host_template *sht;
#endif
unsigned long flags;
unsigned long link_flags;
unsigned long pio_mask;
unsigned long mwdma_mask;
unsigned long udma_mask;
#ifndef __U_BOOT__
const struct ata_port_operations *port_ops;
void *private_data;
#endif
};
struct ata_ioports {
void __iomem *cmd_addr;
void __iomem *data_addr;
void __iomem *error_addr;
void __iomem *feature_addr;
void __iomem *nsect_addr;
void __iomem *lbal_addr;
void __iomem *lbam_addr;
void __iomem *lbah_addr;
void __iomem *device_addr;
void __iomem *status_addr;
void __iomem *command_addr;
void __iomem *altstatus_addr;
void __iomem *ctl_addr;
#ifndef __U_BOOT__
void __iomem *bmdma_addr;
#endif
void __iomem *scr_addr;
};
struct ata_host {
#ifndef __U_BOOT__
void __iomem * const *iomap;
void *private_data;
const struct ata_port_operations *ops;
unsigned long flags;
struct ata_port *simplex_claimed;
#endif
unsigned int n_ports;
struct ata_port *ports[0];
};
#ifndef __U_BOOT__
struct ata_port_stats {
unsigned long unhandled_irq;
unsigned long idle_irq;
unsigned long rw_reqbuf;
};
#endif
struct ata_device {
struct ata_link *link;
unsigned int devno;
unsigned long flags;
unsigned int horkage;
#ifndef __U_BOOT__
struct scsi_device *sdev;
#ifdef CONFIG_ATA_ACPI
acpi_handle acpi_handle;
union acpi_object *gtf_cache;
#endif
#endif
u64 n_sectors;
unsigned int class;
union {
u16 id[ATA_ID_WORDS];
u32 gscr[SATA_PMP_GSCR_DWORDS];
};
#ifndef __U_BOOT__
u8 pio_mode;
u8 dma_mode;
u8 xfer_mode;
unsigned int xfer_shift;
#endif
unsigned int multi_count;
unsigned int max_sectors;
unsigned int cdb_len;
#ifndef __U_BOOT__
unsigned long pio_mask;
unsigned long mwdma_mask;
#endif
unsigned long udma_mask;
u16 cylinders;
u16 heads;
u16 sectors;
#ifndef __U_BOOT__
int spdn_cnt;
#endif
};
enum dma_data_direction {
DMA_BIDIRECTIONAL = 0,
DMA_TO_DEVICE = 1,
DMA_FROM_DEVICE = 2,
DMA_NONE = 3,
};
struct ata_link {
struct ata_port *ap;
int pmp;
unsigned int active_tag;
u32 sactive;
unsigned int flags;
unsigned int hw_sata_spd_limit;
#ifndef __U_BOOT__
unsigned int sata_spd_limit;
unsigned int sata_spd;
struct ata_device device[2];
#endif
};
struct ata_port {
unsigned long flags;
unsigned int pflags;
unsigned int print_id;
unsigned int port_no;
struct ata_ioports ioaddr;
u8 ctl;
u8 last_ctl;
unsigned int pio_mask;
unsigned int mwdma_mask;
unsigned int udma_mask;
unsigned int cbl;
struct ata_queued_cmd qcmd[ATA_MAX_QUEUE];
unsigned long qc_allocated;
unsigned int qc_active;
int nr_active_links;
struct ata_link link;
#ifndef __U_BOOT__
int nr_pmp_links;
struct ata_link *pmp_link;
#endif
struct ata_link *excl_link;
int nr_pmp_links;
#ifndef __U_BOOT__
struct ata_port_stats stats;
struct device *dev;
u32 msg_enable;
#endif
struct ata_host *host;
void *port_task_data;
unsigned int hsm_task_state;
void *private_data;
unsigned char *pdata;
};
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#endif

View file

@ -0,0 +1,721 @@
/*
* Copyright (C) 2011 Freescale Semiconductor, Inc.
* Author: Tang Yuantian <b29983@freescale.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <pci.h>
#include <command.h>
#include <asm/byteorder.h>
#include <malloc.h>
#include <asm/io.h>
#include <fis.h>
#include <libata.h>
#include "sata_sil.h"
/* Convert sectorsize to wordsize */
#define ATA_SECTOR_WORDS (ATA_SECT_SIZE/2)
#define virt_to_bus(devno, v) pci_virt_to_mem(devno, (void *) (v))
static struct sata_info sata_info;
static struct pci_device_id supported[] = {
{PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3131},
{PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3132},
{PCI_VENDOR_ID_SILICONIMAGE, PCI_DEVICE_ID_SIL3124},
{}
};
static void sil_sata_dump_fis(struct sata_fis_d2h *s)
{
printf("Status FIS dump:\n");
printf("fis_type: %02x\n", s->fis_type);
printf("pm_port_i: %02x\n", s->pm_port_i);
printf("status: %02x\n", s->status);
printf("error: %02x\n", s->error);
printf("lba_low: %02x\n", s->lba_low);
printf("lba_mid: %02x\n", s->lba_mid);
printf("lba_high: %02x\n", s->lba_high);
printf("device: %02x\n", s->device);
printf("lba_low_exp: %02x\n", s->lba_low_exp);
printf("lba_mid_exp: %02x\n", s->lba_mid_exp);
printf("lba_high_exp: %02x\n", s->lba_high_exp);
printf("res1: %02x\n", s->res1);
printf("sector_count: %02x\n", s->sector_count);
printf("sector_count_exp: %02x\n", s->sector_count_exp);
}
static const char *sata_spd_string(unsigned int speed)
{
static const char * const spd_str[] = {
"1.5 Gbps",
"3.0 Gbps",
"6.0 Gbps",
};
if ((speed - 1) > 2)
return "<unknown>";
return spd_str[speed - 1];
}
static u32 ata_wait_register(void *reg, u32 mask,
u32 val, int timeout_msec)
{
u32 tmp;
tmp = readl(reg);
while ((tmp & mask) == val && timeout_msec > 0) {
mdelay(1);
timeout_msec--;
tmp = readl(reg);
}
return tmp;
}
static void sil_config_port(void *port)
{
/* configure IRQ WoC */
writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
/* zero error counters. */
writew(0x8000, port + PORT_DECODE_ERR_THRESH);
writew(0x8000, port + PORT_CRC_ERR_THRESH);
writew(0x8000, port + PORT_HSHK_ERR_THRESH);
writew(0x0000, port + PORT_DECODE_ERR_CNT);
writew(0x0000, port + PORT_CRC_ERR_CNT);
writew(0x0000, port + PORT_HSHK_ERR_CNT);
/* always use 64bit activation */
writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR);
/* clear port multiplier enable and resume bits */
writel(PORT_CS_PMP_EN | PORT_CS_PMP_RESUME, port + PORT_CTRL_CLR);
}
static int sil_init_port(void *port)
{
u32 tmp;
writel(PORT_CS_INIT, port + PORT_CTRL_STAT);
ata_wait_register(port + PORT_CTRL_STAT,
PORT_CS_INIT, PORT_CS_INIT, 100);
tmp = ata_wait_register(port + PORT_CTRL_STAT,
PORT_CS_RDY, 0, 100);
if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY)
return 1;
return 0;
}
static void sil_read_fis(int dev, int tag, struct sata_fis_d2h *fis)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
void *port = sata->port;
struct sil_prb *prb;
int i;
u32 *src, *dst;
prb = port + PORT_LRAM + tag * PORT_LRAM_SLOT_SZ;
src = (u32 *)&prb->fis;
dst = (u32 *)fis;
for (i = 0; i < sizeof(struct sata_fis_h2d); i += 4)
*dst++ = readl(src++);
}
static int sil_exec_cmd(int dev, struct sil_cmd_block *pcmd, int tag)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
void *port = sata->port;
u64 paddr = virt_to_bus(sata->devno, pcmd);
u32 irq_mask, irq_stat;
int rc;
writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR, port + PORT_IRQ_ENABLE_CLR);
/* better to add momery barrior here */
writel((u32)paddr, port + PORT_CMD_ACTIVATE + tag * 8);
writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + tag * 8 + 4);
irq_mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT;
irq_stat = ata_wait_register(port + PORT_IRQ_STAT, irq_mask,
0, 10000);
/* clear IRQs */
writel(irq_mask, port + PORT_IRQ_STAT);
irq_stat >>= PORT_IRQ_RAW_SHIFT;
if (irq_stat & PORT_IRQ_COMPLETE)
rc = 0;
else {
/* force port into known state */
sil_init_port(port);
if (irq_stat & PORT_IRQ_ERROR)
rc = 1; /* error */
else
rc = 2; /* busy */
}
return rc;
}
static int sil_cmd_set_feature(int dev)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
struct sil_cmd_block cmdb, *pcmd = &cmdb;
struct sata_fis_d2h fis;
u8 udma_cap;
int ret;
memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block));
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
pcmd->prb.fis.command = ATA_CMD_SET_FEATURES;
pcmd->prb.fis.features = SETFEATURES_XFER;
/* First check the device capablity */
udma_cap = (u8)(sata->udma & 0xff);
debug("udma_cap %02x\n", udma_cap);
if (udma_cap == ATA_UDMA6)
pcmd->prb.fis.sector_count = XFER_UDMA_6;
if (udma_cap == ATA_UDMA5)
pcmd->prb.fis.sector_count = XFER_UDMA_5;
if (udma_cap == ATA_UDMA4)
pcmd->prb.fis.sector_count = XFER_UDMA_4;
if (udma_cap == ATA_UDMA3)
pcmd->prb.fis.sector_count = XFER_UDMA_3;
ret = sil_exec_cmd(dev, pcmd, 0);
if (ret) {
sil_read_fis(dev, 0, &fis);
printf("Err: exe cmd(0x%x).\n",
readl(sata->port + PORT_SERROR));
sil_sata_dump_fis(&fis);
return 1;
}
return 0;
}
static int sil_cmd_identify_device(int dev, u16 *id)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
struct sil_cmd_block cmdb, *pcmd = &cmdb;
struct sata_fis_d2h fis;
int ret;
memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block));
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL);
pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ);
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
pcmd->prb.fis.command = ATA_CMD_ID_ATA;
pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, id));
pcmd->sge.cnt = cpu_to_le32(sizeof(id[0]) * ATA_ID_WORDS);
pcmd->sge.flags = cpu_to_le32(SGE_TRM);
ret = sil_exec_cmd(dev, pcmd, 0);
if (ret) {
sil_read_fis(dev, 0, &fis);
printf("Err: id cmd(0x%x).\n", readl(sata->port + PORT_SERROR));
sil_sata_dump_fis(&fis);
return 1;
}
ata_swap_buf_le16(id, ATA_ID_WORDS);
return 0;
}
static int sil_cmd_soft_reset(int dev)
{
struct sil_cmd_block cmdb, *pcmd = &cmdb;
struct sil_sata *sata = sata_dev_desc[dev].priv;
struct sata_fis_d2h fis;
void *port = sata->port;
int ret;
/* put the port into known state */
if (sil_init_port(port)) {
printf("SRST: port %d not ready\n", dev);
return 1;
}
memset((void *)&cmdb, 0, sizeof(struct sil_cmd_block));
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_SRST);
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = 0xf;
ret = sil_exec_cmd(dev, &cmdb, 0);
if (ret) {
sil_read_fis(dev, 0, &fis);
printf("SRST cmd error.\n");
sil_sata_dump_fis(&fis);
return 1;
}
return 0;
}
static ulong sil_sata_rw_cmd(int dev, ulong start, ulong blkcnt,
u8 *buffer, int is_write)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
struct sil_cmd_block cmdb, *pcmd = &cmdb;
struct sata_fis_d2h fis;
u64 block;
int ret;
block = (u64)start;
memset(pcmd, 0, sizeof(struct sil_cmd_block));
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL);
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
if (is_write) {
pcmd->prb.fis.command = ATA_CMD_WRITE;
pcmd->prb.prot = cpu_to_le16(PRB_PROT_WRITE);
} else {
pcmd->prb.fis.command = ATA_CMD_READ;
pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ);
}
pcmd->prb.fis.device = ATA_LBA;
pcmd->prb.fis.device |= (block >> 24) & 0xf;
pcmd->prb.fis.lba_high = (block >> 16) & 0xff;
pcmd->prb.fis.lba_mid = (block >> 8) & 0xff;
pcmd->prb.fis.lba_low = block & 0xff;
pcmd->prb.fis.sector_count = (u8)blkcnt & 0xff;
pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, buffer));
pcmd->sge.cnt = cpu_to_le32(blkcnt * ATA_SECT_SIZE);
pcmd->sge.flags = cpu_to_le32(SGE_TRM);
ret = sil_exec_cmd(dev, pcmd, 0);
if (ret) {
sil_read_fis(dev, 0, &fis);
printf("Err: rw cmd(0x%08x).\n",
readl(sata->port + PORT_SERROR));
sil_sata_dump_fis(&fis);
return 1;
}
return blkcnt;
}
static ulong sil_sata_rw_cmd_ext(int dev, ulong start, ulong blkcnt,
u8 *buffer, int is_write)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
struct sil_cmd_block cmdb, *pcmd = &cmdb;
struct sata_fis_d2h fis;
u64 block;
int ret;
block = (u64)start;
memset(pcmd, 0, sizeof(struct sil_cmd_block));
pcmd->prb.ctrl = cpu_to_le16(PRB_CTRL_PROTOCOL);
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
if (is_write) {
pcmd->prb.fis.command = ATA_CMD_WRITE_EXT;
pcmd->prb.prot = cpu_to_le16(PRB_PROT_WRITE);
} else {
pcmd->prb.fis.command = ATA_CMD_READ_EXT;
pcmd->prb.prot = cpu_to_le16(PRB_PROT_READ);
}
pcmd->prb.fis.lba_high_exp = (block >> 40) & 0xff;
pcmd->prb.fis.lba_mid_exp = (block >> 32) & 0xff;
pcmd->prb.fis.lba_low_exp = (block >> 24) & 0xff;
pcmd->prb.fis.lba_high = (block >> 16) & 0xff;
pcmd->prb.fis.lba_mid = (block >> 8) & 0xff;
pcmd->prb.fis.lba_low = block & 0xff;
pcmd->prb.fis.device = ATA_LBA;
pcmd->prb.fis.sector_count_exp = (blkcnt >> 8) & 0xff;
pcmd->prb.fis.sector_count = blkcnt & 0xff;
pcmd->sge.addr = cpu_to_le64(virt_to_bus(sata->devno, buffer));
pcmd->sge.cnt = cpu_to_le32(blkcnt * ATA_SECT_SIZE);
pcmd->sge.flags = cpu_to_le32(SGE_TRM);
ret = sil_exec_cmd(dev, pcmd, 0);
if (ret) {
sil_read_fis(dev, 0, &fis);
printf("Err: rw ext cmd(0x%08x).\n",
readl(sata->port + PORT_SERROR));
sil_sata_dump_fis(&fis);
return 1;
}
return blkcnt;
}
ulong sil_sata_rw_lba28(int dev, ulong blknr, lbaint_t blkcnt,
void *buffer, int is_write)
{
ulong start, blks, max_blks;
u8 *addr;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS;
do {
if (blks > max_blks) {
sil_sata_rw_cmd(dev, start, max_blks, addr, is_write);
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
sil_sata_rw_cmd(dev, start, blks, addr, is_write);
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
ulong sil_sata_rw_lba48(int dev, ulong blknr, lbaint_t blkcnt,
void *buffer, int is_write)
{
ulong start, blks, max_blks;
u8 *addr;
start = blknr;
blks = blkcnt;
addr = (u8 *)buffer;
max_blks = ATA_MAX_SECTORS_LBA48;
do {
if (blks > max_blks) {
sil_sata_rw_cmd_ext(dev, start, max_blks,
addr, is_write);
start += max_blks;
blks -= max_blks;
addr += ATA_SECT_SIZE * max_blks;
} else {
sil_sata_rw_cmd_ext(dev, start, blks,
addr, is_write);
start += blks;
blks = 0;
addr += ATA_SECT_SIZE * blks;
}
} while (blks != 0);
return blkcnt;
}
void sil_sata_cmd_flush_cache(int dev)
{
struct sil_cmd_block cmdb, *pcmd = &cmdb;
memset((void *)pcmd, 0, sizeof(struct sil_cmd_block));
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
pcmd->prb.fis.command = ATA_CMD_FLUSH;
sil_exec_cmd(dev, pcmd, 0);
}
void sil_sata_cmd_flush_cache_ext(int dev)
{
struct sil_cmd_block cmdb, *pcmd = &cmdb;
memset((void *)pcmd, 0, sizeof(struct sil_cmd_block));
pcmd->prb.fis.fis_type = SATA_FIS_TYPE_REGISTER_H2D;
pcmd->prb.fis.pm_port_c = (1 << 7);
pcmd->prb.fis.command = ATA_CMD_FLUSH_EXT;
sil_exec_cmd(dev, pcmd, 0);
}
static void sil_sata_init_wcache(int dev, u16 *id)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
if (ata_id_has_wcache(id) && ata_id_wcache_enabled(id))
sata->wcache = 1;
if (ata_id_has_flush(id))
sata->flush = 1;
if (ata_id_has_flush_ext(id))
sata->flush_ext = 1;
}
static int sil_sata_get_wcache(int dev)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
return sata->wcache;
}
static int sil_sata_get_flush(int dev)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
return sata->flush;
}
static int sil_sata_get_flush_ext(int dev)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
return sata->flush_ext;
}
/*
* SATA interface between low level driver and command layer
*/
ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
ulong rc;
if (sata->lba48)
rc = sil_sata_rw_lba48(dev, blknr, blkcnt, buffer, READ_CMD);
else
rc = sil_sata_rw_lba28(dev, blknr, blkcnt, buffer, READ_CMD);
return rc;
}
/*
* SATA interface between low level driver and command layer
*/
ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, void *buffer)
{
struct sil_sata *sata = sata_dev_desc[dev].priv;
ulong rc;
if (sata->lba48) {
rc = sil_sata_rw_lba48(dev, blknr, blkcnt, buffer, WRITE_CMD);
if (sil_sata_get_wcache(dev) && sil_sata_get_flush_ext(dev))
sil_sata_cmd_flush_cache_ext(dev);
} else {
rc = sil_sata_rw_lba28(dev, blknr, blkcnt, buffer, WRITE_CMD);
if (sil_sata_get_wcache(dev) && sil_sata_get_flush(dev))
sil_sata_cmd_flush_cache(dev);
}
return rc;
}
/*
* SATA interface between low level driver and command layer
*/
int init_sata(int dev)
{
static int init_done, idx;
pci_dev_t devno;
u16 word;
if (init_done == 1 && dev < sata_info.maxport)
return 1;
init_done = 1;
/* Find PCI device(s) */
devno = pci_find_devices(supported, idx++);
if (devno == -1)
return 1;
pci_read_config_word(devno, PCI_DEVICE_ID, &word);
/* get the port count */
word &= 0xf;
sata_info.portbase = sata_info.maxport;
sata_info.maxport = sata_info.portbase + word;
sata_info.devno = devno;
/* Read out all BARs */
sata_info.iobase[0] = (ulong)pci_map_bar(devno,
PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
sata_info.iobase[1] = (ulong)pci_map_bar(devno,
PCI_BASE_ADDRESS_2, PCI_REGION_MEM);
sata_info.iobase[2] = (ulong)pci_map_bar(devno,
PCI_BASE_ADDRESS_4, PCI_REGION_MEM);
/* mask out the unused bits */
sata_info.iobase[0] &= 0xffffff80;
sata_info.iobase[1] &= 0xfffffc00;
sata_info.iobase[2] &= 0xffffff80;
/* Enable Bus Mastering and memory region */
pci_write_config_word(devno, PCI_COMMAND,
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
/* Check if mem accesses and Bus Mastering are enabled. */
pci_read_config_word(devno, PCI_COMMAND, &word);
if (!(word & PCI_COMMAND_MEMORY) ||
(!(word & PCI_COMMAND_MASTER))) {
printf("Error: Can not enable MEM access or Bus Mastering.\n");
debug("PCI command: %04x\n", word);
return 1;
}
/* GPIO off */
writel(0, (void *)(sata_info.iobase[0] + HOST_FLASH_CMD));
/* clear global reset & mask interrupts during initialization */
writel(0, (void *)(sata_info.iobase[0] + HOST_CTRL));
return 0;
}
/*
* SATA interface between low level driver and command layer
*/
int scan_sata(int dev)
{
unsigned char serial[ATA_ID_SERNO_LEN + 1];
unsigned char firmware[ATA_ID_FW_REV_LEN + 1];
unsigned char product[ATA_ID_PROD_LEN + 1];
struct sil_sata *sata;
void *port;
int cnt;
u16 *id;
u32 tmp;
if (dev >= sata_info.maxport) {
printf("SATA#%d is not present\n", dev);
return 1;
}
printf("SATA#%d\n", dev);
port = (void *)sata_info.iobase[1] +
PORT_REGS_SIZE * (dev - sata_info.portbase);
/* Initial PHY setting */
writel(0x20c, port + PORT_PHY_CFG);
/* clear port RST */
tmp = readl(port + PORT_CTRL_STAT);
if (tmp & PORT_CS_PORT_RST) {
writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR);
tmp = ata_wait_register(port + PORT_CTRL_STAT,
PORT_CS_PORT_RST, PORT_CS_PORT_RST, 100);
if (tmp & PORT_CS_PORT_RST)
printf("Err: Failed to clear port RST\n");
}
/* Check if device is present */
for (cnt = 0; cnt < 100; cnt++) {
tmp = readl(port + PORT_SSTATUS);
if ((tmp & 0xF) == 0x3)
break;
mdelay(1);
}
tmp = readl(port + PORT_SSTATUS);
if ((tmp & 0xf) != 0x3) {
printf(" (No RDY)\n");
return 1;
}
/* Wait for port ready */
tmp = ata_wait_register(port + PORT_CTRL_STAT,
PORT_CS_RDY, PORT_CS_RDY, 100);
if ((tmp & PORT_CS_RDY) != PORT_CS_RDY) {
printf("%d port not ready.\n", dev);
return 1;
}
/* configure port */
sil_config_port(port);
/* Reset port */
writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT);
readl(port + PORT_CTRL_STAT);
tmp = ata_wait_register(port + PORT_CTRL_STAT, PORT_CS_DEV_RST,
PORT_CS_DEV_RST, 100);
if (tmp & PORT_CS_DEV_RST) {
printf("%d port reset failed.\n", dev);
return 1;
}
sata = (struct sil_sata *)malloc(sizeof(struct sil_sata));
if (!sata) {
printf("%d no memory.\n", dev);
return 1;
}
memset((void *)sata, 0, sizeof(struct sil_sata));
/* turn on port interrupt */
tmp = readl((void *)(sata_info.iobase[0] + HOST_CTRL));
tmp |= (1 << (dev - sata_info.portbase));
writel(tmp, (void *)(sata_info.iobase[0] + HOST_CTRL));
/* Save the private struct to block device struct */
sata_dev_desc[dev].priv = (void *)sata;
sata->port = port;
sata->devno = sata_info.devno;
sprintf(sata->name, "SATA#%d", dev);
sil_cmd_soft_reset(dev);
tmp = readl(port + PORT_SSTATUS);
tmp = (tmp >> 4) & 0xf;
printf(" (%s)\n", sata_spd_string(tmp));
id = (u16 *)malloc(ATA_ID_WORDS * 2);
if (!id) {
printf("Id malloc failed\n");
free((void *)sata);
return 1;
}
sil_cmd_identify_device(dev, id);
#ifdef CONFIG_LBA48
/* Check if support LBA48 */
if (ata_id_has_lba48(id)) {
sata_dev_desc[dev].lba48 = 1;
sata->lba48 = 1;
debug("Device supports LBA48\n");
} else
debug("Device supports LBA28\n");
#endif
/* Serial number */
ata_id_c_string(id, serial, ATA_ID_SERNO, sizeof(serial));
memcpy(sata_dev_desc[dev].product, serial, sizeof(serial));
/* Firmware version */
ata_id_c_string(id, firmware, ATA_ID_FW_REV, sizeof(firmware));
memcpy(sata_dev_desc[dev].revision, firmware, sizeof(firmware));
/* Product model */
ata_id_c_string(id, product, ATA_ID_PROD, sizeof(product));
memcpy(sata_dev_desc[dev].vendor, product, sizeof(product));
/* Totoal sectors */
sata_dev_desc[dev].lba = ata_id_n_sectors(id);
sil_sata_init_wcache(dev, id);
sil_cmd_set_feature(dev);
#ifdef DEBUG
sil_cmd_identify_device(dev, id);
ata_dump_id(id);
#endif
free((void *)id);
return 0;
}

View file

@ -0,0 +1,229 @@
/*
* Copyright (C) 2011 Freescale Semiconductor, Inc.
* Author: Tang Yuantian <b29983@freescale.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef SATA_SIL3132_H
#define SATA_SIL3132_H
#define READ_CMD 0
#define WRITE_CMD 1
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
/*
* SATA device driver struct for each dev
*/
struct sil_sata {
char name[12];
void *port; /* the port base address */
int lba48;
u16 pio;
u16 mwdma;
u16 udma;
pci_dev_t devno;
int wcache;
int flush;
int flush_ext;
};
/* sata info for each controller */
struct sata_info {
ulong iobase[3];
pci_dev_t devno;
int portbase;
int maxport;
};
/*
* Scatter gather entry (SGE),MUST 8 bytes aligned
*/
struct sil_sge {
__le64 addr;
__le32 cnt;
__le32 flags;
} __attribute__ ((aligned(8), packed));
/*
* Port request block, MUST 8 bytes aligned
*/
struct sil_prb {
__le16 ctrl;
__le16 prot;
__le32 rx_cnt;
struct sata_fis_h2d fis;
} __attribute__ ((aligned(8), packed));
struct sil_cmd_block {
struct sil_prb prb;
struct sil_sge sge;
};
enum {
HOST_SLOT_STAT = 0x00, /* 32 bit slot stat * 4 */
HOST_CTRL = 0x40,
HOST_IRQ_STAT = 0x44,
HOST_PHY_CFG = 0x48,
HOST_BIST_CTRL = 0x50,
HOST_BIST_PTRN = 0x54,
HOST_BIST_STAT = 0x58,
HOST_MEM_BIST_STAT = 0x5c,
HOST_FLASH_CMD = 0x70,
/* 8 bit regs */
HOST_FLASH_DATA = 0x74,
HOST_TRANSITION_DETECT = 0x75,
HOST_GPIO_CTRL = 0x76,
HOST_I2C_ADDR = 0x78, /* 32 bit */
HOST_I2C_DATA = 0x7c,
HOST_I2C_XFER_CNT = 0x7e,
HOST_I2C_CTRL = 0x7f,
/* HOST_SLOT_STAT bits */
HOST_SSTAT_ATTN = (1 << 31),
/* HOST_CTRL bits */
HOST_CTRL_M66EN = (1 << 16), /* M66EN PCI bus signal */
HOST_CTRL_TRDY = (1 << 17), /* latched PCI TRDY */
HOST_CTRL_STOP = (1 << 18), /* latched PCI STOP */
HOST_CTRL_DEVSEL = (1 << 19), /* latched PCI DEVSEL */
HOST_CTRL_REQ64 = (1 << 20), /* latched PCI REQ64 */
HOST_CTRL_GLOBAL_RST = (1 << 31), /* global reset */
/*
* Port registers
* (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2)
*/
PORT_REGS_SIZE = 0x2000,
PORT_LRAM = 0x0000, /* 31 LRAM slots and PMP regs */
PORT_LRAM_SLOT_SZ = 0x0080, /* 32 bytes PRB + 2 SGE, ACT... */
PORT_PMP = 0x0f80, /* 8 bytes PMP * 16 (128 bytes) */
PORT_PMP_STATUS = 0x0000, /* port device status offset */
PORT_PMP_QACTIVE = 0x0004, /* port device QActive offset */
PORT_PMP_SIZE = 0x0008, /* 8 bytes per PMP */
/* 32 bit regs */
PORT_CTRL_STAT = 0x1000, /* write: ctrl-set, read: stat */
PORT_CTRL_CLR = 0x1004, /* write: ctrl-clear */
PORT_IRQ_STAT = 0x1008, /* high: status, low: interrupt */
PORT_IRQ_ENABLE_SET = 0x1010, /* write: enable-set */
PORT_IRQ_ENABLE_CLR = 0x1014, /* write: enable-clear */
PORT_ACTIVATE_UPPER_ADDR = 0x101c,
PORT_EXEC_FIFO = 0x1020, /* command execution fifo */
PORT_CMD_ERR = 0x1024, /* command error number */
PORT_FIS_CFG = 0x1028,
PORT_FIFO_THRES = 0x102c,
/* 16 bit regs */
PORT_DECODE_ERR_CNT = 0x1040,
PORT_DECODE_ERR_THRESH = 0x1042,
PORT_CRC_ERR_CNT = 0x1044,
PORT_CRC_ERR_THRESH = 0x1046,
PORT_HSHK_ERR_CNT = 0x1048,
PORT_HSHK_ERR_THRESH = 0x104a,
/* 32 bit regs */
PORT_PHY_CFG = 0x1050,
PORT_SLOT_STAT = 0x1800,
PORT_CMD_ACTIVATE = 0x1c00, /* 64 bit cmd activate * 31 */
PORT_CONTEXT = 0x1e04,
PORT_EXEC_DIAG = 0x1e00, /* 32bit exec diag * 16 */
PORT_PSD_DIAG = 0x1e40, /* 32bit psd diag * 16 */
PORT_SCONTROL = 0x1f00,
PORT_SSTATUS = 0x1f04,
PORT_SERROR = 0x1f08,
PORT_SACTIVE = 0x1f0c,
/* PORT_CTRL_STAT bits */
PORT_CS_PORT_RST = (1 << 0), /* port reset */
PORT_CS_DEV_RST = (1 << 1), /* device reset */
PORT_CS_INIT = (1 << 2), /* port initialize */
PORT_CS_IRQ_WOC = (1 << 3), /* interrupt write one to clear */
PORT_CS_CDB16 = (1 << 5), /* 0=12b cdb, 1=16b cdb */
PORT_CS_PMP_RESUME = (1 << 6), /* PMP resume */
PORT_CS_32BIT_ACTV = (1 << 10), /* 32-bit activation */
PORT_CS_PMP_EN = (1 << 13), /* port multiplier enable */
PORT_CS_RDY = (1 << 31), /* port ready to accept commands */
/* PORT_IRQ_STAT/ENABLE_SET/CLR */
/* bits[11:0] are masked */
PORT_IRQ_COMPLETE = (1 << 0), /* command(s) completed */
PORT_IRQ_ERROR = (1 << 1), /* command execution error */
PORT_IRQ_PORTRDY_CHG = (1 << 2), /* port ready change */
PORT_IRQ_PWR_CHG = (1 << 3), /* power management change */
PORT_IRQ_PHYRDY_CHG = (1 << 4), /* PHY ready change */
PORT_IRQ_COMWAKE = (1 << 5), /* COMWAKE received */
PORT_IRQ_UNK_FIS = (1 << 6), /* unknown FIS received */
PORT_IRQ_DEV_XCHG = (1 << 7), /* device exchanged */
PORT_IRQ_8B10B = (1 << 8), /* 8b/10b decode error threshold */
PORT_IRQ_CRC = (1 << 9), /* CRC error threshold */
PORT_IRQ_HANDSHAKE = (1 << 10), /* handshake error threshold */
PORT_IRQ_SDB_NOTIFY = (1 << 11), /* SDB notify received */
DEF_PORT_IRQ = PORT_IRQ_COMPLETE | PORT_IRQ_ERROR |
PORT_IRQ_PHYRDY_CHG | PORT_IRQ_DEV_XCHG |
PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_NOTIFY,
/* bits[27:16] are unmasked (raw) */
PORT_IRQ_RAW_SHIFT = 16,
PORT_IRQ_MASKED_MASK = 0x7ff,
PORT_IRQ_RAW_MASK = (0x7ff << PORT_IRQ_RAW_SHIFT),
/* ENABLE_SET/CLR specific, intr steering - 2 bit field */
PORT_IRQ_STEER_SHIFT = 30,
PORT_IRQ_STEER_MASK = (3 << PORT_IRQ_STEER_SHIFT),
/* PORT_CMD_ERR constants */
PORT_CERR_DEV = 1, /* Error bit in D2H Register FIS */
PORT_CERR_SDB = 2, /* Error bit in SDB FIS */
PORT_CERR_DATA = 3, /* Error in data FIS not detected by dev */
PORT_CERR_SEND = 4, /* Initial cmd FIS transmission failure */
PORT_CERR_INCONSISTENT = 5, /* Protocol mismatch */
PORT_CERR_DIRECTION = 6, /* Data direction mismatch */
PORT_CERR_UNDERRUN = 7, /* Ran out of SGEs while writing */
PORT_CERR_OVERRUN = 8, /* Ran out of SGEs while reading */
/* bits of PRB control field */
PRB_CTRL_PROTOCOL = (1 << 0), /* override def. ATA protocol */
PRB_CTRL_PACKET_READ = (1 << 4), /* PACKET cmd read */
PRB_CTRL_PACKET_WRITE = (1 << 5), /* PACKET cmd write */
PRB_CTRL_NIEN = (1 << 6), /* Mask completion irq */
PRB_CTRL_SRST = (1 << 7), /* Soft reset request (ign BSY?) */
/* PRB protocol field */
PRB_PROT_PACKET = (1 << 0),
PRB_PROT_TCQ = (1 << 1),
PRB_PROT_NCQ = (1 << 2),
PRB_PROT_READ = (1 << 3),
PRB_PROT_WRITE = (1 << 4),
PRB_PROT_TRANSPARENT = (1 << 5),
/*
* Other constants
*/
SGE_TRM = (1 << 31), /* Last SGE in chain */
SGE_LNK = (1 << 30), /* linked list
Points to SGT, not SGE */
SGE_DRD = (1 << 29), /* discard data read (/dev/null)
data address ignored */
CMD_ERR = 0x21,
};
#endif

View file

@ -0,0 +1,843 @@
/*
* Copyright (C) Excito Elektronik i Skåne AB, All rights reserved.
* Author: Tor Krill <tor@excito.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* This is a driver for Silicon Image sil3114 sata chip modelled on
* the ata_piix driver
*/
#include <common.h>
#include <pci.h>
#include <command.h>
#include <config.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#include <ide.h>
#include <libata.h>
#include "sata_sil3114.h"
/* Convert sectorsize to wordsize */
#define ATA_SECTOR_WORDS (ATA_SECT_SIZE/2)
/* Forwards */
u8 sil3114_spin_up (int num);
u8 sil3114_spin_down (int num);
static int sata_bus_softreset (int num);
static void sata_identify (int num, int dev);
static u8 check_power_mode (int num);
static void sata_port (struct sata_ioports *ioport);
static void set_Feature_cmd (int num, int dev);
static u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits,
unsigned int max, u8 usealtstatus);
static u8 sata_chk_status (struct sata_ioports *ioaddr, u8 usealtstatus);
static void msleep (int count);
static u32 iobase[6] = { 0, 0, 0, 0, 0, 0}; /* PCI BAR registers for device */
extern block_dev_desc_t sata_dev_desc[CONFIG_SYS_SATA_MAX_DEVICE];
static struct sata_port port[CONFIG_SYS_SATA_MAX_DEVICE];
static void output_data (struct sata_ioports *ioaddr, u16 * sect_buf, int words)
{
while (words--) {
__raw_writew (*sect_buf++, (void *)ioaddr->data_addr);
}
}
static int input_data (struct sata_ioports *ioaddr, u16 * sect_buf, int words)
{
while (words--) {
*sect_buf++ = __raw_readw ((void *)ioaddr->data_addr);
}
return 0;
}
static int sata_bus_softreset (int num)
{
u8 status = 0;
port[num].dev_mask = 1;
port[num].ctl_reg = 0x08; /*Default value of control reg */
writeb (port[num].ctl_reg, port[num].ioaddr.ctl_addr);
udelay (10);
writeb (port[num].ctl_reg | ATA_SRST, port[num].ioaddr.ctl_addr);
udelay (10);
writeb (port[num].ctl_reg, port[num].ioaddr.ctl_addr);
/* spec mandates ">= 2ms" before checking status.
* We wait 150ms, because that was the magic delay used for
* ATAPI devices in Hale Landis's ATADRVR, for the period of time
* between when the ATA command register is written, and then
* status is checked. Because waiting for "a while" before
* checking status is fine, post SRST, we perform this magic
* delay here as well.
*/
msleep (150);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 300, 0);
while ((status & ATA_BUSY)) {
msleep (100);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 3, 0);
}
if (status & ATA_BUSY) {
printf ("ata%u is slow to respond,plz be patient\n", num);
}
while ((status & ATA_BUSY)) {
msleep (100);
status = sata_chk_status (&port[num].ioaddr, 0);
}
if (status & ATA_BUSY) {
printf ("ata%u failed to respond : ", num);
printf ("bus reset failed\n");
port[num].dev_mask = 0;
return 1;
}
return 0;
}
static void sata_identify (int num, int dev)
{
u8 cmd = 0, status = 0, devno = num;
u16 iobuf[ATA_SECTOR_WORDS];
u64 n_sectors = 0;
memset (iobuf, 0, sizeof (iobuf));
if (!(port[num].dev_mask & 0x01)) {
printf ("dev%d is not present on port#%d\n", dev, num);
return;
}
debug ("port=%d dev=%d\n", num, dev);
status = 0;
cmd = ATA_CMD_ID_ATA; /*Device Identify Command */
writeb (cmd, port[num].ioaddr.command_addr);
readb (port[num].ioaddr.altstatus_addr);
udelay (10);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 1000, 0);
if (status & ATA_ERR) {
printf ("\ndevice not responding\n");
port[num].dev_mask &= ~0x01;
return;
}
input_data (&port[num].ioaddr, iobuf, ATA_SECTOR_WORDS);
ata_swap_buf_le16 (iobuf, ATA_SECTOR_WORDS);
debug ("Specific config: %x\n", iobuf[2]);
/* we require LBA and DMA support (bits 8 & 9 of word 49) */
if (!ata_id_has_dma (iobuf) || !ata_id_has_lba (iobuf)) {
debug ("ata%u: no dma/lba\n", num);
}
#ifdef DEBUG
ata_dump_id (iobuf);
#endif
n_sectors = ata_id_n_sectors (iobuf);
if (n_sectors == 0) {
port[num].dev_mask &= ~0x01;
return;
}
ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].revision,
ATA_ID_FW_REV, sizeof (sata_dev_desc[devno].revision));
ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].vendor,
ATA_ID_PROD, sizeof (sata_dev_desc[devno].vendor));
ata_id_c_string (iobuf, (unsigned char *)sata_dev_desc[devno].product,
ATA_ID_SERNO, sizeof (sata_dev_desc[devno].product));
/* TODO - atm we asume harddisk ie not removable */
sata_dev_desc[devno].removable = 0;
sata_dev_desc[devno].lba = (u32) n_sectors;
debug("lba=0x%lx\n", sata_dev_desc[devno].lba);
#ifdef CONFIG_LBA48
if (iobuf[83] & (1 << 10)) {
sata_dev_desc[devno].lba48 = 1;
} else {
sata_dev_desc[devno].lba48 = 0;
}
#endif
/* assuming HD */
sata_dev_desc[devno].type = DEV_TYPE_HARDDISK;
sata_dev_desc[devno].blksz = ATA_SECT_SIZE;
sata_dev_desc[devno].lun = 0; /* just to fill something in... */
}
static void set_Feature_cmd (int num, int dev)
{
u8 status = 0;
if (!(port[num].dev_mask & 0x01)) {
debug ("dev%d is not present on port#%d\n", dev, num);
return;
}
writeb (SETFEATURES_XFER, port[num].ioaddr.feature_addr);
writeb (XFER_PIO_4, port[num].ioaddr.nsect_addr);
writeb (0, port[num].ioaddr.lbal_addr);
writeb (0, port[num].ioaddr.lbam_addr);
writeb (0, port[num].ioaddr.lbah_addr);
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
writeb (ATA_CMD_SET_FEATURES, port[num].ioaddr.command_addr);
udelay (50);
msleep (150);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 5000, 0);
if ((status & (ATA_BUSY | ATA_ERR))) {
printf ("Error : status 0x%02x\n", status);
port[num].dev_mask &= ~0x01;
}
}
u8 sil3114_spin_down (int num)
{
u8 status = 0;
debug ("Spin down disk\n");
if (!(port[num].dev_mask & 0x01)) {
debug ("Device ata%d is not present\n", num);
return 1;
}
if ((status = check_power_mode (num)) == 0x00) {
debug ("Already in standby\n");
return 0;
}
if (status == 0x01) {
printf ("Failed to check power mode on ata%d\n", num);
return 1;
}
if (!((status = sata_chk_status (&port[num].ioaddr, 0)) & ATA_DRDY)) {
printf ("Device ata%d not ready\n", num);
return 1;
}
writeb (0x00, port[num].ioaddr.feature_addr);
writeb (0x00, port[num].ioaddr.nsect_addr);
writeb (0x00, port[num].ioaddr.lbal_addr);
writeb (0x00, port[num].ioaddr.lbam_addr);
writeb (0x00, port[num].ioaddr.lbah_addr);
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
writeb (ATA_CMD_STANDBY, port[num].ioaddr.command_addr);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 30000, 0);
if ((status & (ATA_BUSY | ATA_ERR))) {
printf ("Error waiting for disk spin down: status 0x%02x\n",
status);
port[num].dev_mask &= ~0x01;
return 1;
}
return 0;
}
u8 sil3114_spin_up (int num)
{
u8 status = 0;
debug ("Spin up disk\n");
if (!(port[num].dev_mask & 0x01)) {
debug ("Device ata%d is not present\n", num);
return 1;
}
if ((status = check_power_mode (num)) != 0x00) {
if (status == 0x01) {
printf ("Failed to check power mode on ata%d\n", num);
return 1;
} else {
/* should be up and running already */
return 0;
}
}
if (!((status = sata_chk_status (&port[num].ioaddr, 0)) & ATA_DRDY)) {
printf ("Device ata%d not ready\n", num);
return 1;
}
debug ("Stautus of device check: %d\n", status);
writeb (0x00, port[num].ioaddr.feature_addr);
writeb (0x00, port[num].ioaddr.nsect_addr);
writeb (0x00, port[num].ioaddr.lbal_addr);
writeb (0x00, port[num].ioaddr.lbam_addr);
writeb (0x00, port[num].ioaddr.lbah_addr);
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
writeb (ATA_CMD_IDLE, port[num].ioaddr.command_addr);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 30000, 0);
if ((status & (ATA_BUSY | ATA_ERR))) {
printf ("Error waiting for disk spin up: status 0x%02x\n",
status);
port[num].dev_mask &= ~0x01;
return 1;
}
/* Wait for disk to enter Active state */
do {
msleep (10);
status = check_power_mode (num);
} while ((status == 0x00) || (status == 0x80));
if (status == 0x01) {
printf ("Falied waiting for disk to spin up\n");
return 1;
}
return 0;
}
/* Return value is not the usual here
* 0x00 - Device stand by
* 0x01 - Operation failed
* 0x80 - Device idle
* 0xff - Device active
*/
static u8 check_power_mode (int num)
{
u8 status = 0;
u8 res = 0;
if (!(port[num].dev_mask & 0x01)) {
debug ("Device ata%d is not present\n", num);
return 1;
}
if (!(sata_chk_status (&port[num].ioaddr, 0) & ATA_DRDY)) {
printf ("Device ata%d not ready\n", num);
return 1;
}
writeb (0, port[num].ioaddr.feature_addr);
writeb (0, port[num].ioaddr.nsect_addr);
writeb (0, port[num].ioaddr.lbal_addr);
writeb (0, port[num].ioaddr.lbam_addr);
writeb (0, port[num].ioaddr.lbah_addr);
writeb (ATA_DEVICE_OBS, port[num].ioaddr.device_addr);
writeb (ATA_CMD_CHK_POWER, port[num].ioaddr.command_addr);
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 5000, 0);
if ((status & (ATA_BUSY | ATA_ERR))) {
printf
("Error waiting for check power mode complete : status 0x%02x\n",
status);
port[num].dev_mask &= ~0x01;
return 1;
}
res = readb (port[num].ioaddr.nsect_addr);
debug ("Check powermode: %d\n", res);
return res;
}
static void sata_port (struct sata_ioports *ioport)
{
ioport->data_addr = ioport->cmd_addr + ATA_REG_DATA;
ioport->error_addr = ioport->cmd_addr + ATA_REG_ERR;
ioport->feature_addr = ioport->cmd_addr + ATA_REG_FEATURE;
ioport->nsect_addr = ioport->cmd_addr + ATA_REG_NSECT;
ioport->lbal_addr = ioport->cmd_addr + ATA_REG_LBAL;
ioport->lbam_addr = ioport->cmd_addr + ATA_REG_LBAM;
ioport->lbah_addr = ioport->cmd_addr + ATA_REG_LBAH;
ioport->device_addr = ioport->cmd_addr + ATA_REG_DEVICE;
ioport->status_addr = ioport->cmd_addr + ATA_REG_STATUS;
ioport->command_addr = ioport->cmd_addr + ATA_REG_CMD;
}
static u8 wait_for_irq (int num, unsigned int max)
{
u32 port = iobase[5];
switch (num) {
case 0:
port += VND_TF_CNST_CH0;
break;
case 1:
port += VND_TF_CNST_CH1;
break;
case 2:
port += VND_TF_CNST_CH2;
break;
case 3:
port += VND_TF_CNST_CH3;
break;
default:
return 1;
}
do {
if (readl (port) & VND_TF_CNST_INTST) {
break;
}
udelay (1000);
max--;
} while ((max > 0));
return (max == 0);
}
static u8 sata_busy_wait (struct sata_ioports *ioaddr, int bits,
unsigned int max, u8 usealtstatus)
{
u8 status;
do {
if (!((status = sata_chk_status (ioaddr, usealtstatus)) & bits)) {
break;
}
udelay (1000);
max--;
} while ((status & bits) && (max > 0));
return status;
}
static u8 sata_chk_status (struct sata_ioports *ioaddr, u8 usealtstatus)
{
if (!usealtstatus) {
return readb (ioaddr->status_addr);
} else {
return readb (ioaddr->altstatus_addr);
}
}
static void msleep (int count)
{
int i;
for (i = 0; i < count; i++)
udelay (1000);
}
/* Read up to 255 sectors
*
* Returns sectors read
*/
static u8 do_one_read (int device, ulong block, u8 blkcnt, u16 * buff,
uchar lba48)
{
u8 sr = 0;
u8 status;
u64 blknr = (u64) block;
if (!(sata_chk_status (&port[device].ioaddr, 0) & ATA_DRDY)) {
printf ("Device ata%d not ready\n", device);
return 0;
}
/* Set up transfer */
#ifdef CONFIG_LBA48
if (lba48) {
/* write high bits */
writeb (0, port[device].ioaddr.nsect_addr);
writeb ((blknr >> 24) & 0xFF, port[device].ioaddr.lbal_addr);
writeb ((blknr >> 32) & 0xFF, port[device].ioaddr.lbam_addr);
writeb ((blknr >> 40) & 0xFF, port[device].ioaddr.lbah_addr);
}
#endif
writeb (blkcnt, port[device].ioaddr.nsect_addr);
writeb (((blknr) >> 0) & 0xFF, port[device].ioaddr.lbal_addr);
writeb ((blknr >> 8) & 0xFF, port[device].ioaddr.lbam_addr);
writeb ((blknr >> 16) & 0xFF, port[device].ioaddr.lbah_addr);
#ifdef CONFIG_LBA48
if (lba48) {
writeb (ATA_LBA, port[device].ioaddr.device_addr);
writeb (ATA_CMD_PIO_READ_EXT, port[device].ioaddr.command_addr);
} else
#endif
{
writeb (ATA_LBA | ((blknr >> 24) & 0xF),
port[device].ioaddr.device_addr);
writeb (ATA_CMD_PIO_READ, port[device].ioaddr.command_addr);
}
status = sata_busy_wait (&port[device].ioaddr, ATA_BUSY, 10000, 1);
if (status & ATA_BUSY) {
u8 err = 0;
printf ("Device %d not responding status %d\n", device, status);
err = readb (port[device].ioaddr.error_addr);
printf ("Error reg = 0x%x\n", err);
return (sr);
}
while (blkcnt--) {
if (wait_for_irq (device, 500)) {
printf ("ata%u irq failed\n", device);
return sr;
}
status = sata_chk_status (&port[device].ioaddr, 0);
if (status & ATA_ERR) {
printf ("ata%u error %d\n", device,
readb (port[device].ioaddr.error_addr));
return sr;
}
/* Read one sector */
input_data (&port[device].ioaddr, buff, ATA_SECTOR_WORDS);
buff += ATA_SECTOR_WORDS;
sr++;
}
return sr;
}
ulong sata_read (int device, ulong block, lbaint_t blkcnt, void *buff)
{
ulong n = 0, sread;
u16 *buffer = (u16 *) buff;
u8 status = 0;
u64 blknr = (u64) block;
unsigned char lba48 = 0;
#ifdef CONFIG_LBA48
if (blknr > 0xfffffff) {
if (!sata_dev_desc[device].lba48) {
printf ("Drive doesn't support 48-bit addressing\n");
return 0;
}
/* more than 28 bits used, use 48bit mode */
lba48 = 1;
}
#endif
while (blkcnt > 0) {
if (blkcnt > 255) {
sread = 255;
} else {
sread = blkcnt;
}
status = do_one_read (device, blknr, sread, buffer, lba48);
if (status != sread) {
printf ("Read failed\n");
return n;
}
blkcnt -= sread;
blknr += sread;
n += sread;
buffer += sread * ATA_SECTOR_WORDS;
}
return n;
}
ulong sata_write (int device, ulong block, lbaint_t blkcnt, const void *buff)
{
ulong n = 0;
u16 *buffer = (u16 *) buff;
unsigned char status = 0, num = 0;
u64 blknr = (u64) block;
#ifdef CONFIG_LBA48
unsigned char lba48 = 0;
if (blknr > 0xfffffff) {
if (!sata_dev_desc[device].lba48) {
printf ("Drive doesn't support 48-bit addressing\n");
return 0;
}
/* more than 28 bits used, use 48bit mode */
lba48 = 1;
}
#endif
/*Port Number */
num = device;
while (blkcnt-- > 0) {
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 500, 0);
if (status & ATA_BUSY) {
printf ("ata%u failed to respond\n", port[num].port_no);
return n;
}
#ifdef CONFIG_LBA48
if (lba48) {
/* write high bits */
writeb (0, port[num].ioaddr.nsect_addr);
writeb ((blknr >> 24) & 0xFF,
port[num].ioaddr.lbal_addr);
writeb ((blknr >> 32) & 0xFF,
port[num].ioaddr.lbam_addr);
writeb ((blknr >> 40) & 0xFF,
port[num].ioaddr.lbah_addr);
}
#endif
writeb (1, port[num].ioaddr.nsect_addr);
writeb ((blknr >> 0) & 0xFF, port[num].ioaddr.lbal_addr);
writeb ((blknr >> 8) & 0xFF, port[num].ioaddr.lbam_addr);
writeb ((blknr >> 16) & 0xFF, port[num].ioaddr.lbah_addr);
#ifdef CONFIG_LBA48
if (lba48) {
writeb (ATA_LBA, port[num].ioaddr.device_addr);
writeb (ATA_CMD_PIO_WRITE_EXT, port[num].ioaddr.command_addr);
} else
#endif
{
writeb (ATA_LBA | ((blknr >> 24) & 0xF),
port[num].ioaddr.device_addr);
writeb (ATA_CMD_PIO_WRITE, port[num].ioaddr.command_addr);
}
msleep (50);
/*may take up to 4 sec */
status = sata_busy_wait (&port[num].ioaddr, ATA_BUSY, 4000, 0);
if ((status & (ATA_DRQ | ATA_BUSY | ATA_ERR)) != ATA_DRQ) {
printf ("Error no DRQ dev %d blk %ld: sts 0x%02x\n",
device, (ulong) blknr, status);
return (n);
}
output_data (&port[num].ioaddr, buffer, ATA_SECTOR_WORDS);
readb (port[num].ioaddr.altstatus_addr);
udelay (50);
++n;
++blknr;
buffer += ATA_SECTOR_WORDS;
}
return n;
}
/* Driver implementation */
static u8 sil_get_device_cache_line (pci_dev_t pdev)
{
u8 cache_line = 0;
pci_read_config_byte (pdev, PCI_CACHE_LINE_SIZE, &cache_line);
return cache_line;
}
int init_sata (int dev)
{
static u8 init_done = 0;
static int res = 1;
pci_dev_t devno;
u8 cls = 0;
u16 cmd = 0;
u32 sconf = 0;
if (init_done) {
return res;
}
init_done = 1;
if ((devno = pci_find_device (SIL_VEND_ID, SIL3114_DEVICE_ID, 0)) == -1) {
res = 1;
return res;
}
/* Read out all BARs, even though we only use MMIO from BAR5 */
pci_read_config_dword (devno, PCI_BASE_ADDRESS_0, &iobase[0]);
pci_read_config_dword (devno, PCI_BASE_ADDRESS_1, &iobase[1]);
pci_read_config_dword (devno, PCI_BASE_ADDRESS_2, &iobase[2]);
pci_read_config_dword (devno, PCI_BASE_ADDRESS_3, &iobase[3]);
pci_read_config_dword (devno, PCI_BASE_ADDRESS_4, &iobase[4]);
pci_read_config_dword (devno, PCI_BASE_ADDRESS_5, &iobase[5]);
if ((iobase[0] == 0xFFFFFFFF) || (iobase[1] == 0xFFFFFFFF) ||
(iobase[2] == 0xFFFFFFFF) || (iobase[3] == 0xFFFFFFFF) ||
(iobase[4] == 0xFFFFFFFF) || (iobase[5] == 0xFFFFFFFF)) {
printf ("Error no base addr for SATA controller\n");
res = 1;
return res;
}
/* mask off unused bits */
iobase[0] &= 0xfffffffc;
iobase[1] &= 0xfffffff8;
iobase[2] &= 0xfffffffc;
iobase[3] &= 0xfffffff8;
iobase[4] &= 0xfffffff0;
iobase[5] &= 0xfffffc00;
/* from sata_sil in Linux kernel */
cls = sil_get_device_cache_line (devno);
if (cls) {
cls >>= 3;
cls++; /* cls = (line_size/8)+1 */
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH0);
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH1);
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH2);
writel (cls << 8 | cls, iobase[5] + VND_FIFOCFG_CH3);
} else {
printf ("Cache line not set. Driver may not function\n");
}
/* Enable operation */
pci_read_config_word (devno, PCI_COMMAND, &cmd);
cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
pci_write_config_word (devno, PCI_COMMAND, cmd);
/* Disable interrupt usage */
pci_read_config_dword (devno, VND_SYSCONFSTAT, &sconf);
sconf |= (VND_SYSCONFSTAT_CHN_0_INTBLOCK | VND_SYSCONFSTAT_CHN_1_INTBLOCK);
pci_write_config_dword (devno, VND_SYSCONFSTAT, sconf);
res = 0;
return res;
}
/* Check if device is connected to port */
int sata_bus_probe (int portno)
{
u32 port = iobase[5];
u32 val;
switch (portno) {
case 0:
port += VND_SSTATUS_CH0;
break;
case 1:
port += VND_SSTATUS_CH1;
break;
case 2:
port += VND_SSTATUS_CH2;
break;
case 3:
port += VND_SSTATUS_CH3;
break;
default:
return 0;
}
val = readl (port);
if ((val & SATA_DET_PRES) == SATA_DET_PRES) {
return 1;
} else {
return 0;
}
}
int sata_phy_reset (int portno)
{
u32 port = iobase[5];
u32 val;
switch (portno) {
case 0:
port += VND_SCONTROL_CH0;
break;
case 1:
port += VND_SCONTROL_CH1;
break;
case 2:
port += VND_SCONTROL_CH2;
break;
case 3:
port += VND_SCONTROL_CH3;
break;
default:
return 0;
}
val = readl (port);
writel (val | SATA_SC_DET_RST, port);
msleep (150);
writel (val & ~SATA_SC_DET_RST, port);
return 0;
}
int scan_sata (int dev)
{
/* A bit brain dead, but the code has a legacy */
switch (dev) {
case 0:
port[0].port_no = 0;
port[0].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH0;
port[0].ioaddr.altstatus_addr = port[0].ioaddr.ctl_addr =
(iobase[5] + VND_TF2_CH0) | ATA_PCI_CTL_OFS;
port[0].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH0;
break;
#if (CONFIG_SYS_SATA_MAX_DEVICE >= 1)
case 1:
port[1].port_no = 0;
port[1].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH1;
port[1].ioaddr.altstatus_addr = port[1].ioaddr.ctl_addr =
(iobase[5] + VND_TF2_CH1) | ATA_PCI_CTL_OFS;
port[1].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH1;
break;
#elif (CONFIG_SYS_SATA_MAX_DEVICE >= 2)
case 2:
port[2].port_no = 0;
port[2].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH2;
port[2].ioaddr.altstatus_addr = port[2].ioaddr.ctl_addr =
(iobase[5] + VND_TF2_CH2) | ATA_PCI_CTL_OFS;
port[2].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH2;
break;
#elif (CONFIG_SYS_SATA_MAX_DEVICE >= 3)
case 3:
port[3].port_no = 0;
port[3].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH3;
port[3].ioaddr.altstatus_addr = port[3].ioaddr.ctl_addr =
(iobase[5] + VND_TF2_CH3) | ATA_PCI_CTL_OFS;
port[3].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH3;
break;
#endif
default:
printf ("Tried to scan unknown port: ata%d\n", dev);
return 1;
}
/* Initialize other registers */
sata_port (&port[dev].ioaddr);
/* Check for attached device */
if (!sata_bus_probe (dev)) {
port[dev].port_state = 0;
debug ("SATA#%d port is not present\n", dev);
} else {
debug ("SATA#%d port is present\n", dev);
if (sata_bus_softreset (dev)) {
/* soft reset failed, try a hard one */
sata_phy_reset (dev);
if (sata_bus_softreset (dev)) {
port[dev].port_state = 0;
} else {
port[dev].port_state = 1;
}
} else {
port[dev].port_state = 1;
}
}
if (port[dev].port_state == 1) {
/* Probe device and set xfer mode */
sata_identify (dev, 0);
set_Feature_cmd (dev, 0);
}
return 0;
}

View file

@ -0,0 +1,147 @@
/*
* Copyright (C) Excito Elektronik i Skåne AB, All rights reserved.
* Author: Tor Krill <tor@excito.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#ifndef SATA_SIL3114_H
#define SATA_SIL3114_H
struct sata_ioports {
unsigned long cmd_addr;
unsigned long data_addr;
unsigned long error_addr;
unsigned long feature_addr;
unsigned long nsect_addr;
unsigned long lbal_addr;
unsigned long lbam_addr;
unsigned long lbah_addr;
unsigned long device_addr;
unsigned long status_addr;
unsigned long command_addr;
unsigned long altstatus_addr;
unsigned long ctl_addr;
unsigned long bmdma_addr;
unsigned long scr_addr;
};
struct sata_port {
unsigned char port_no; /* primary=0, secondary=1 */
struct sata_ioports ioaddr; /* ATA cmd/ctl/dma reg blks */
unsigned char ctl_reg;
unsigned char last_ctl;
unsigned char port_state; /* 1-port is available and */
/* 0-port is not available */
unsigned char dev_mask;
};
/* Missing ata defines */
#define ATA_CMD_STANDBY 0xE2
#define ATA_CMD_STANDBYNOW1 0xE0
#define ATA_CMD_IDLE 0xE3
#define ATA_CMD_IDLEIMMEDIATE 0xE1
/* Defines for SIL3114 chip */
/* PCI defines */
#define SIL_VEND_ID 0x1095
#define SIL3114_DEVICE_ID 0x3114
/* some vendor specific registers */
#define VND_SYSCONFSTAT 0x88 /* System Configuration Status and Command */
#define VND_SYSCONFSTAT_CHN_0_INTBLOCK (1<<22)
#define VND_SYSCONFSTAT_CHN_1_INTBLOCK (1<<23)
#define VND_SYSCONFSTAT_CHN_2_INTBLOCK (1<<24)
#define VND_SYSCONFSTAT_CHN_3_INTBLOCK (1<<25)
/* internal registers mapped by BAR5 */
/* SATA Control*/
#define VND_SCONTROL_CH0 0x100
#define VND_SCONTROL_CH1 0x180
#define VND_SCONTROL_CH2 0x300
#define VND_SCONTROL_CH3 0x380
#define SATA_SC_IPM_T2P (1<<16)
#define SATA_SC_IPM_T2S (2<<16)
#define SATA_SC_SPD_1_5 (1<<4)
#define SATA_SC_SPD_3_0 (2<<4)
#define SATA_SC_DET_RST (1) /* ATA Reset sequence */
#define SATA_SC_DET_PDIS (4) /* PHY Disable */
/* SATA Status */
#define VND_SSTATUS_CH0 0x104
#define VND_SSTATUS_CH1 0x184
#define VND_SSTATUS_CH2 0x304
#define VND_SSTATUS_CH3 0x384
#define SATA_SS_IPM_ACTIVE (1<<8)
#define SATA_SS_IPM_PARTIAL (2<<8)
#define SATA_SS_IPM_SLUMBER (6<<8)
#define SATA_SS_SPD_1_5 (1<<4)
#define SATA_SS_SPD_3_0 (2<<4)
#define SATA_DET_P_NOPHY (1) /* Device presence but no PHY connection established */
#define SATA_DET_PRES (3) /* Device presence and active PHY */
#define SATA_DET_OFFLINE (4) /* Device offline or in loopback mode */
/* Task file registers in BAR5 mapping */
#define VND_TF0_CH0 0x80
#define VND_TF0_CH1 0xc0
#define VND_TF0_CH2 0x280
#define VND_TF0_CH3 0x2c0
#define VND_TF1_CH0 0x88
#define VND_TF1_CH1 0xc8
#define VND_TF1_CH2 0x288
#define VND_TF1_CH3 0x2c8
#define VND_TF2_CH0 0x88
#define VND_TF2_CH1 0xc8
#define VND_TF2_CH2 0x288
#define VND_TF2_CH3 0x2c8
#define VND_BMDMA_CH0 0x00
#define VND_BMDMA_CH1 0x08
#define VND_BMDMA_CH2 0x200
#define VND_BMDMA_CH3 0x208
#define VND_BMDMA2_CH0 0x10
#define VND_BMDMA2_CH1 0x18
#define VND_BMDMA2_CH2 0x210
#define VND_BMDMA2_CH3 0x218
/* FIFO control */
#define VND_FIFOCFG_CH0 0x40
#define VND_FIFOCFG_CH1 0x44
#define VND_FIFOCFG_CH2 0x240
#define VND_FIFOCFG_CH3 0x244
/* Task File configuration and status */
#define VND_TF_CNST_CH0 0xa0
#define VND_TF_CNST_CH1 0xe0
#define VND_TF_CNST_CH2 0x2a0
#define VND_TF_CNST_CH3 0x2e0
#define VND_TF_CNST_BFCMD (1<<1)
#define VND_TF_CNST_CHNRST (1<<2)
#define VND_TF_CNST_VDMA (1<<10)
#define VND_TF_CNST_INTST (1<<11)
#define VND_TF_CNST_WDTO (1<<12)
#define VND_TF_CNST_WDEN (1<<13)
#define VND_TF_CNST_WDIEN (1<<14)
/* for testing */
#define VND_SSDR 0x04c /* System Software Data Register */
#define VND_FMACS 0x050 /* Flash Memory Address control and status */
#endif

View file

@ -0,0 +1,107 @@
/*
* (C) Copyright 2007
* Gary Jennejohn, DENX Software Engineering, garyj@denx.de.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
*/
/* sil680.c - ide support functions for the Sil0680A controller */
/*
* The following parameters must be defined in the configuration file
* of the target board:
*
* #define CONFIG_IDE_SIL680
*
* #define CONFIG_PCI_PNP
* NOTE it may also be necessary to define this if the default of 8 is
* incorrect for the target board (e.g. the sequoia board requires 0).
* #define CONFIG_SYS_PCI_CACHE_LINE_SIZE 0
*
* #define CONFIG_CMD_IDE
* #undef CONFIG_IDE_8xx_DIRECT
* #undef CONFIG_IDE_LED
* #undef CONFIG_IDE_RESET
* #define CONFIG_IDE_PREINIT
* #define CONFIG_SYS_IDE_MAXBUS 2 - modify to suit
* #define CONFIG_SYS_IDE_MAXDEVICE (CONFIG_SYS_IDE_MAXBUS*2) - modify to suit
* #define CONFIG_SYS_ATA_BASE_ADDR 0
* #define CONFIG_SYS_ATA_IDE0_OFFSET 0
* #define CONFIG_SYS_ATA_IDE1_OFFSET 0
* #define CONFIG_SYS_ATA_DATA_OFFSET 0
* #define CONFIG_SYS_ATA_REG_OFFSET 0
* #define CONFIG_SYS_ATA_ALT_OFFSET 0x0004
*
* The mapping for PCI IO-space.
* NOTE this is the value for the sequoia board. Modify to suit.
* #define CONFIG_SYS_PCI0_IO_SPACE 0xE8000000
*/
#include <common.h>
#include <ata.h>
#include <ide.h>
#include <pci.h>
extern ulong ide_bus_offset[CONFIG_SYS_IDE_MAXBUS];
int ide_preinit (void)
{
int status;
pci_dev_t devbusfn;
int l;
status = 1;
for (l = 0; l < CONFIG_SYS_IDE_MAXBUS; l++) {
ide_bus_offset[l] = -ATA_STATUS;
}
devbusfn = pci_find_device (0x1095, 0x0680, 0);
if (devbusfn != -1) {
status = 0;
pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_0,
(u32 *) &ide_bus_offset[0]);
ide_bus_offset[0] &= 0xfffffff8;
ide_bus_offset[0] += CONFIG_SYS_PCI0_IO_SPACE;
pci_read_config_dword (devbusfn, PCI_BASE_ADDRESS_2,
(u32 *) &ide_bus_offset[1]);
ide_bus_offset[1] &= 0xfffffff8;
ide_bus_offset[1] += CONFIG_SYS_PCI0_IO_SPACE;
/* init various things - taken from the Linux driver */
/* set PIO mode */
pci_write_config_byte(devbusfn, 0x80, 0x00);
pci_write_config_byte(devbusfn, 0x84, 0x00);
/* IDE0 */
pci_write_config_byte(devbusfn, 0xA1, 0x02);
pci_write_config_word(devbusfn, 0xA2, 0x328A);
pci_write_config_dword(devbusfn, 0xA4, 0x62DD62DD);
pci_write_config_dword(devbusfn, 0xA8, 0x43924392);
pci_write_config_dword(devbusfn, 0xAC, 0x40094009);
/* IDE1 */
pci_write_config_byte(devbusfn, 0xB1, 0x02);
pci_write_config_word(devbusfn, 0xB2, 0x328A);
pci_write_config_dword(devbusfn, 0xB4, 0x62DD62DD);
pci_write_config_dword(devbusfn, 0xB8, 0x43924392);
pci_write_config_dword(devbusfn, 0xBC, 0x40094009);
}
return (status);
}
void ide_set_reset (int flag) {
return;
}

View file

@ -0,0 +1,868 @@
/*
* (C) Copyright 2001
* Denis Peter, MPL AG Switzerland, d.peter@mpl.ch.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
* partly derived from
* linux/drivers/scsi/sym53c8xx.c
*
*/
/*
* SCSI support based on the chip sym53C810.
*
* 09-19-2001 Andreas Heppel, Sysgo RTS GmbH <aheppel@sysgo.de>
* The local version of this driver for the BAB750 board does not
* use interrupts but polls the chip instead (see the call of
* 'handle_scsi_int()' in 'scsi_issue()'.
*/
#include <common.h>
#include <command.h>
#include <pci.h>
#include <asm/processor.h>
#include <sym53c8xx.h>
#include <scsi.h>
#undef SYM53C8XX_DEBUG
#ifdef SYM53C8XX_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
#if defined(CONFIG_CMD_SCSI) && defined(CONFIG_SCSI_SYM53C8XX)
#undef SCSI_SINGLE_STEP
/*
* Single Step is only used for debug purposes
*/
#ifdef SCSI_SINGLE_STEP
static unsigned long start_script_select;
static unsigned long start_script_msgout;
static unsigned long start_script_msgin;
static unsigned long start_script_msg_ext;
static unsigned long start_script_cmd;
static unsigned long start_script_data_in;
static unsigned long start_script_data_out;
static unsigned long start_script_status;
static unsigned long start_script_complete;
static unsigned long start_script_error;
static unsigned long start_script_reselection;
static unsigned int len_script_select;
static unsigned int len_script_msgout;
static unsigned int len_script_msgin;
static unsigned int len_script_msg_ext;
static unsigned int len_script_cmd;
static unsigned int len_script_data_in;
static unsigned int len_script_data_out;
static unsigned int len_script_status;
static unsigned int len_script_complete;
static unsigned int len_script_error;
static unsigned int len_script_reselection;
#endif
static unsigned short scsi_int_mask; /* shadow register for SCSI related interrupts */
static unsigned char script_int_mask; /* shadow register for SCRIPT related interrupts */
static unsigned long script_select[8]; /* script for selection */
static unsigned long script_msgout[8]; /* script for message out phase (NOT USED) */
static unsigned long script_msgin[14]; /* script for message in phase */
static unsigned long script_msg_ext[32]; /* script for message in phase when more than 1 byte message */
static unsigned long script_cmd[18]; /* script for command phase */
static unsigned long script_data_in[8]; /* script for data in phase */
static unsigned long script_data_out[8]; /* script for data out phase */
static unsigned long script_status[6]; /* script for status phase */
static unsigned long script_complete[10]; /* script for complete */
static unsigned long script_reselection[4]; /* script for reselection (NOT USED) */
static unsigned long script_error[2]; /* script for error handling */
static unsigned long int_stat[3]; /* interrupt status */
static unsigned long scsi_mem_addr; /* base memory address =SCSI_MEM_ADDRESS; */
#define bus_to_phys(a) pci_mem_to_phys(busdevfunc, (unsigned long) (a))
#define phys_to_bus(a) pci_phys_to_mem(busdevfunc, (unsigned long) (a))
#define SCSI_MAX_RETRY 3 /* number of retries in scsi_issue() */
#define SCSI_MAX_RETRY_NOT_READY 10 /* number of retries when device is not ready */
#define SCSI_NOT_READY_TIME_OUT 500 /* timeout per retry when not ready */
/*********************************************************************************
* forward declerations
*/
void scsi_chip_init(void);
void handle_scsi_int(void);
/********************************************************************************
* reports SCSI errors to the user
*/
void scsi_print_error (ccb * pccb)
{
int i;
printf ("SCSI Error: Target %d LUN %d Command %02X\n", pccb->target,
pccb->lun, pccb->cmd[0]);
printf (" CCB: ");
for (i = 0; i < pccb->cmdlen; i++)
printf ("%02X ", pccb->cmd[i]);
printf ("(len=%d)\n", pccb->cmdlen);
printf (" Cntrl: ");
switch (pccb->contr_stat) {
case SIR_COMPLETE:
printf ("Complete (no Error)\n");
break;
case SIR_SEL_ATN_NO_MSG_OUT:
printf ("Selected with ATN no MSG out phase\n");
break;
case SIR_CMD_OUT_ILL_PH:
printf ("Command out illegal phase\n");
break;
case SIR_MSG_RECEIVED:
printf ("MSG received Error\n");
break;
case SIR_DATA_IN_ERR:
printf ("Data in Error\n");
break;
case SIR_DATA_OUT_ERR:
printf ("Data out Error\n");
break;
case SIR_SCRIPT_ERROR:
printf ("Script Error\n");
break;
case SIR_MSG_OUT_NO_CMD:
printf ("MSG out no Command phase\n");
break;
case SIR_MSG_OVER7:
printf ("MSG in over 7 bytes\n");
break;
case INT_ON_FY:
printf ("Interrupt on fly\n");
break;
case SCSI_SEL_TIME_OUT:
printf ("SCSI Selection Timeout\n");
break;
case SCSI_HNS_TIME_OUT:
printf ("SCSI Handshake Timeout\n");
break;
case SCSI_MA_TIME_OUT:
printf ("SCSI Phase Error\n");
break;
case SCSI_UNEXP_DIS:
printf ("SCSI unexpected disconnect\n");
break;
default:
printf ("unknown status %lx\n", pccb->contr_stat);
break;
}
printf (" Sense: SK %x (", pccb->sense_buf[2] & 0x0f);
switch (pccb->sense_buf[2] & 0xf) {
case SENSE_NO_SENSE:
printf ("No Sense)");
break;
case SENSE_RECOVERED_ERROR:
printf ("Recovered Error)");
break;
case SENSE_NOT_READY:
printf ("Not Ready)");
break;
case SENSE_MEDIUM_ERROR:
printf ("Medium Error)");
break;
case SENSE_HARDWARE_ERROR:
printf ("Hardware Error)");
break;
case SENSE_ILLEGAL_REQUEST:
printf ("Illegal request)");
break;
case SENSE_UNIT_ATTENTION:
printf ("Unit Attention)");
break;
case SENSE_DATA_PROTECT:
printf ("Data Protect)");
break;
case SENSE_BLANK_CHECK:
printf ("Blank check)");
break;
case SENSE_VENDOR_SPECIFIC:
printf ("Vendor specific)");
break;
case SENSE_COPY_ABORTED:
printf ("Copy aborted)");
break;
case SENSE_ABORTED_COMMAND:
printf ("Aborted Command)");
break;
case SENSE_VOLUME_OVERFLOW:
printf ("Volume overflow)");
break;
case SENSE_MISCOMPARE:
printf ("Misscompare\n");
break;
default:
printf ("Illegal Sensecode\n");
break;
}
printf (" ASC %x ASCQ %x\n", pccb->sense_buf[12],
pccb->sense_buf[13]);
printf (" Status: ");
switch (pccb->status) {
case S_GOOD:
printf ("Good\n");
break;
case S_CHECK_COND:
printf ("Check condition\n");
break;
case S_COND_MET:
printf ("Condition Met\n");
break;
case S_BUSY:
printf ("Busy\n");
break;
case S_INT:
printf ("Intermediate\n");
break;
case S_INT_COND_MET:
printf ("Intermediate condition met\n");
break;
case S_CONFLICT:
printf ("Reservation conflict\n");
break;
case S_TERMINATED:
printf ("Command terminated\n");
break;
case S_QUEUE_FULL:
printf ("Task set full\n");
break;
default:
printf ("unknown: %02X\n", pccb->status);
break;
}
}
/******************************************************************************
* sets-up the SCSI controller
* the base memory address is retrived via the pci_read_config_dword
*/
void scsi_low_level_init(int busdevfunc)
{
unsigned int cmd;
unsigned int addr;
unsigned char vec;
pci_read_config_byte(busdevfunc, PCI_INTERRUPT_LINE, &vec);
pci_read_config_dword(busdevfunc, PCI_BASE_ADDRESS_1, &addr);
addr = bus_to_phys(addr & ~0xf);
/*
* Enable bus mastering in case this has not been done, yet.
*/
pci_read_config_dword(busdevfunc, PCI_COMMAND, &cmd);
cmd |= PCI_COMMAND_MASTER;
pci_write_config_dword(busdevfunc, PCI_COMMAND, cmd);
scsi_mem_addr = addr;
scsi_chip_init();
scsi_bus_reset();
}
/************************************************************************************
* Low level Part of SCSI Driver
*/
/*
* big-endian -> little endian conversion for the script
*/
unsigned long swap_script(unsigned long val)
{
unsigned long tmp;
tmp = ((val>>24)&0xff) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | ((val<<24)&0xff000000);
return tmp;
}
void scsi_write_byte(ulong offset,unsigned char val)
{
out8(scsi_mem_addr+offset,val);
}
unsigned char scsi_read_byte(ulong offset)
{
return(in8(scsi_mem_addr+offset));
}
/********************************************************************************
* interrupt handler
*/
void handle_scsi_int(void)
{
unsigned char stat,stat1,stat2;
unsigned short sstat;
int i;
#ifdef SCSI_SINGLE_STEP
unsigned long tt;
#endif
stat=scsi_read_byte(ISTAT);
if((stat & DIP)==DIP) { /* DMA Interrupt pending */
stat1=scsi_read_byte(DSTAT);
#ifdef SCSI_SINGLE_STEP
if((stat1 & SSI)==SSI) {
tt=in32r(scsi_mem_addr+DSP);
if(((tt)>=start_script_select) && ((tt)<start_script_select+len_script_select)) {
printf("select %d\n",(tt-start_script_select)>>2);
goto end_single;
}
if(((tt)>=start_script_msgout) && ((tt)<start_script_msgout+len_script_msgout)) {
printf("msgout %d\n",(tt-start_script_msgout)>>2);
goto end_single;
}
if(((tt)>=start_script_msgin) && ((tt)<start_script_msgin+len_script_msgin)) {
printf("msgin %d\n",(tt-start_script_msgin)>>2);
goto end_single;
}
if(((tt)>=start_script_msg_ext) && ((tt)<start_script_msg_ext+len_script_msg_ext)) {
printf("msgin_ext %d\n",(tt-start_script_msg_ext)>>2);
goto end_single;
}
if(((tt)>=start_script_cmd) && ((tt)<start_script_cmd+len_script_cmd)) {
printf("cmd %d\n",(tt-start_script_cmd)>>2);
goto end_single;
}
if(((tt)>=start_script_data_in) && ((tt)<start_script_data_in+len_script_data_in)) {
printf("data_in %d\n",(tt-start_script_data_in)>>2);
goto end_single;
}
if(((tt)>=start_script_data_out) && ((tt)<start_script_data_out+len_script_data_out)) {
printf("data_out %d\n",(tt-start_script_data_out)>>2);
goto end_single;
}
if(((tt)>=start_script_status) && ((tt)<start_script_status+len_script_status)) {
printf("status %d\n",(tt-start_script_status)>>2);
goto end_single;
}
if(((tt)>=start_script_complete) && ((tt)<start_script_complete+len_script_complete)) {
printf("complete %d\n",(tt-start_script_complete)>>2);
goto end_single;
}
if(((tt)>=start_script_error) && ((tt)<start_script_error+len_script_error)) {
printf("error %d\n",(tt-start_script_error)>>2);
goto end_single;
}
if(((tt)>=start_script_reselection) && ((tt)<start_script_reselection+len_script_reselection)) {
printf("reselection %d\n",(tt-start_script_reselection)>>2);
goto end_single;
}
printf("sc: %lx\n",tt);
end_single:
stat2=scsi_read_byte(DCNTL);
stat2|=STD;
scsi_write_byte(DCNTL,stat2);
}
#endif
if((stat1 & SIR)==SIR) /* script interrupt */
{
int_stat[0]=in32(scsi_mem_addr+DSPS);
}
if((stat1 & DFE)==0) { /* fifo not epmty */
scsi_write_byte(CTEST3,CLF); /* Clear DMA FIFO */
stat2=scsi_read_byte(STEST3);
scsi_write_byte(STEST3,(stat2 | CSF)); /* Clear SCSI FIFO */
}
}
if((stat & SIP)==SIP) { /* scsi interrupt */
sstat = (unsigned short)scsi_read_byte(SIST+1);
sstat <<=8;
sstat |= (unsigned short)scsi_read_byte(SIST);
for(i=0;i<3;i++) {
if(int_stat[i]==0)
break; /* found an empty int status */
}
int_stat[i]=SCSI_INT_STATE | sstat;
stat1=scsi_read_byte(DSTAT);
if((stat1 & DFE)==0) { /* fifo not epmty */
scsi_write_byte(CTEST3,CLF); /* Clear DMA FIFO */
stat2=scsi_read_byte(STEST3);
scsi_write_byte(STEST3,(stat2 | CSF)); /* Clear SCSI FIFO */
}
}
if((stat & INTF)==INTF) { /* interrupt on Fly */
scsi_write_byte(ISTAT,stat); /* clear it */
for(i=0;i<3;i++) {
if(int_stat[i]==0)
break; /* found an empty int status */
}
int_stat[i]=INT_ON_FY;
}
}
void scsi_bus_reset(void)
{
unsigned char t;
int i;
int end = CONFIG_SYS_SCSI_SPIN_UP_TIME*1000;
t=scsi_read_byte(SCNTL1);
scsi_write_byte(SCNTL1,(t | CRST));
udelay(50);
scsi_write_byte(SCNTL1,t);
puts("waiting for devices to spin up");
for(i=0;i<end;i++) {
udelay(1000); /* give the devices time to spin up */
if (i % 1000 == 0)
putc('.');
}
putc('\n');
scsi_chip_init(); /* reinit the chip ...*/
}
void scsi_int_enable(void)
{
scsi_write_byte(SIEN,(unsigned char)scsi_int_mask);
scsi_write_byte(SIEN+1,(unsigned char)(scsi_int_mask>>8));
scsi_write_byte(DIEN,script_int_mask);
}
void scsi_write_dsp(unsigned long start)
{
#ifdef SCSI_SINGLE_STEP
unsigned char t;
#endif
out32r(scsi_mem_addr + DSP,start);
#ifdef SCSI_SINGLE_STEP
t=scsi_read_byte(DCNTL);
t|=STD;
scsi_write_byte(DCNTL,t);
#endif
}
/* only used for debug purposes */
void scsi_print_script(void)
{
printf("script_select @ 0x%08lX\n",(unsigned long)&script_select[0]);
printf("script_msgout @ 0x%08lX\n",(unsigned long)&script_msgout[0]);
printf("script_msgin @ 0x%08lX\n",(unsigned long)&script_msgin[0]);
printf("script_msgext @ 0x%08lX\n",(unsigned long)&script_msg_ext[0]);
printf("script_cmd @ 0x%08lX\n",(unsigned long)&script_cmd[0]);
printf("script_data_in @ 0x%08lX\n",(unsigned long)&script_data_in[0]);
printf("script_data_out @ 0x%08lX\n",(unsigned long)&script_data_out[0]);
printf("script_status @ 0x%08lX\n",(unsigned long)&script_status[0]);
printf("script_complete @ 0x%08lX\n",(unsigned long)&script_complete[0]);
printf("script_error @ 0x%08lX\n",(unsigned long)&script_error[0]);
}
void scsi_set_script(ccb *pccb)
{
int busdevfunc = pccb->priv;
int i;
i=0;
script_select[i++]=swap_script(SCR_REG_REG(GPREG, SCR_AND, 0xfe));
script_select[i++]=0; /* LED ON */
script_select[i++]=swap_script(SCR_CLR(SCR_TRG)); /* select initiator mode */
script_select[i++]=0;
/* script_select[i++]=swap_script(SCR_SEL_ABS_ATN | pccb->target << 16); */
script_select[i++]=swap_script(SCR_SEL_ABS | pccb->target << 16);
script_select[i++]=swap_script(phys_to_bus(&script_cmd[4])); /* error handling */
script_select[i++]=swap_script(SCR_JUMP); /* next section */
/* script_select[i++]=swap_script((unsigned long)&script_msgout[0]); */ /* message out */
script_select[i++]=swap_script(phys_to_bus(&script_cmd[0])); /* command out */
#ifdef SCSI_SINGLE_STEP
start_script_select=(unsigned long)&script_select[0];
len_script_select=i*4;
#endif
i=0;
script_msgout[i++]=swap_script(SCR_INT ^ IFFALSE (WHEN (SCR_MSG_OUT)));
script_msgout[i++]=SIR_SEL_ATN_NO_MSG_OUT;
script_msgout[i++]=swap_script( SCR_MOVE_ABS(1) ^ SCR_MSG_OUT);
script_msgout[i++]=swap_script(phys_to_bus(&pccb->msgout[0]));
script_msgout[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_COMMAND))); /* if Command phase */
script_msgout[i++]=swap_script(phys_to_bus(&script_cmd[0])); /* switch to command */
script_msgout[i++]=swap_script(SCR_INT); /* interrupt if not */
script_msgout[i++]=SIR_MSG_OUT_NO_CMD;
#ifdef SCSI_SINGLE_STEP
start_script_msgout=(unsigned long)&script_msgout[0];
len_script_msgout=i*4;
#endif
i=0;
script_cmd[i++]=swap_script(SCR_MOVE_ABS(pccb->cmdlen) ^ SCR_COMMAND);
script_cmd[i++]=swap_script(phys_to_bus(&pccb->cmd[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN))); /* message in ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_msgin[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_DATA_OUT))); /* data out ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_data_out[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_DATA_IN))); /* data in ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_data_in[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_STATUS))); /* status ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_status[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_COMMAND))); /* command ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_cmd[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT))); /* message out ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_msgout[0]));
script_cmd[i++]=swap_script(SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN))); /* just for error handling message in ? */
script_cmd[i++]=swap_script(phys_to_bus(&script_msgin[0]));
script_cmd[i++]=swap_script(SCR_INT); /* interrupt if not */
script_cmd[i++]=SIR_CMD_OUT_ILL_PH;
#ifdef SCSI_SINGLE_STEP
start_script_cmd=(unsigned long)&script_cmd[0];
len_script_cmd=i*4;
#endif
i=0;
script_data_out[i++]=swap_script(SCR_MOVE_ABS(pccb->datalen)^ SCR_DATA_OUT); /* move */
script_data_out[i++]=swap_script(phys_to_bus(pccb->pdata)); /* pointer to buffer */
script_data_out[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)));
script_data_out[i++]=swap_script(phys_to_bus(&script_status[0]));
script_data_out[i++]=swap_script(SCR_INT);
script_data_out[i++]=SIR_DATA_OUT_ERR;
#ifdef SCSI_SINGLE_STEP
start_script_data_out=(unsigned long)&script_data_out[0];
len_script_data_out=i*4;
#endif
i=0;
script_data_in[i++]=swap_script(SCR_MOVE_ABS(pccb->datalen)^ SCR_DATA_IN); /* move */
script_data_in[i++]=swap_script(phys_to_bus(pccb->pdata)); /* pointer to buffer */
script_data_in[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_STATUS)));
script_data_in[i++]=swap_script(phys_to_bus(&script_status[0]));
script_data_in[i++]=swap_script(SCR_INT);
script_data_in[i++]=SIR_DATA_IN_ERR;
#ifdef SCSI_SINGLE_STEP
start_script_data_in=(unsigned long)&script_data_in[0];
len_script_data_in=i*4;
#endif
i=0;
script_msgin[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN);
script_msgin[i++]=swap_script(phys_to_bus(&pccb->msgin[0]));
script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_COMPLETE)));
script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0]));
script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_DISCONNECT)));
script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0]));
script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_SAVE_DP)));
script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0]));
script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_RESTORE_DP)));
script_msgin[i++]=swap_script(phys_to_bus(&script_complete[0]));
script_msgin[i++]=swap_script(SCR_JUMP ^ IFTRUE (DATA (M_EXTENDED)));
script_msgin[i++]=swap_script(phys_to_bus(&script_msg_ext[0]));
script_msgin[i++]=swap_script(SCR_INT);
script_msgin[i++]=SIR_MSG_RECEIVED;
#ifdef SCSI_SINGLE_STEP
start_script_msgin=(unsigned long)&script_msgin[0];
len_script_msgin=i*4;
#endif
i=0;
script_msg_ext[i++]=swap_script(SCR_CLR (SCR_ACK)); /* clear ACK */
script_msg_ext[i++]=0;
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* assuming this is the msg length */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[1]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[2]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[3]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[4]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[5]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[6]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_MSG_IN); /* next */
script_msg_ext[i++]=swap_script(phys_to_bus(&pccb->msgin[7]));
script_msg_ext[i++]=swap_script(SCR_JUMP ^ IFFALSE (IF (SCR_MSG_IN)));
script_msg_ext[i++]=swap_script(phys_to_bus(&script_complete[0])); /* no more bytes */
script_msg_ext[i++]=swap_script(SCR_INT);
script_msg_ext[i++]=SIR_MSG_OVER7;
#ifdef SCSI_SINGLE_STEP
start_script_msg_ext=(unsigned long)&script_msg_ext[0];
len_script_msg_ext=i*4;
#endif
i=0;
script_status[i++]=swap_script(SCR_MOVE_ABS (1) ^ SCR_STATUS);
script_status[i++]=swap_script(phys_to_bus(&pccb->status));
script_status[i++]=swap_script(SCR_JUMP ^ IFTRUE (WHEN (SCR_MSG_IN)));
script_status[i++]=swap_script(phys_to_bus(&script_msgin[0]));
script_status[i++]=swap_script(SCR_INT);
script_status[i++]=SIR_STATUS_ILL_PH;
#ifdef SCSI_SINGLE_STEP
start_script_status=(unsigned long)&script_status[0];
len_script_status=i*4;
#endif
i=0;
script_complete[i++]=swap_script(SCR_REG_REG (SCNTL2, SCR_AND, 0x7f));
script_complete[i++]=0;
script_complete[i++]=swap_script(SCR_CLR (SCR_ACK|SCR_ATN));
script_complete[i++]=0;
script_complete[i++]=swap_script(SCR_WAIT_DISC);
script_complete[i++]=0;
script_complete[i++]=swap_script(SCR_REG_REG(GPREG, SCR_OR, 0x01));
script_complete[i++]=0; /* LED OFF */
script_complete[i++]=swap_script(SCR_INT);
script_complete[i++]=SIR_COMPLETE;
#ifdef SCSI_SINGLE_STEP
start_script_complete=(unsigned long)&script_complete[0];
len_script_complete=i*4;
#endif
i=0;
script_error[i++]=swap_script(SCR_INT); /* interrupt if error */
script_error[i++]=SIR_SCRIPT_ERROR;
#ifdef SCSI_SINGLE_STEP
start_script_error=(unsigned long)&script_error[0];
len_script_error=i*4;
#endif
i=0;
script_reselection[i++]=swap_script(SCR_CLR (SCR_TRG)); /* target status */
script_reselection[i++]=0;
script_reselection[i++]=swap_script(SCR_WAIT_RESEL);
script_reselection[i++]=swap_script(phys_to_bus(&script_select[0])); /* len = 4 */
#ifdef SCSI_SINGLE_STEP
start_script_reselection=(unsigned long)&script_reselection[0];
len_script_reselection=i*4;
#endif
}
void scsi_issue(ccb *pccb)
{
int busdevfunc = pccb->priv;
int i;
unsigned short sstat;
int retrycnt; /* retry counter */
for(i=0;i<3;i++)
int_stat[i]=0; /* delete all int status */
/* struct pccb must be set-up correctly */
retrycnt=0;
PRINTF("ID %d issue cmd %02X\n",pccb->target,pccb->cmd[0]);
pccb->trans_bytes=0; /* no bytes transfered yet */
scsi_set_script(pccb); /* fill in SCRIPT */
scsi_int_mask=STO | UDC | MA; /* | CMP; / * Interrupts which are enabled */
script_int_mask=0xff; /* enable all Ints */
scsi_int_enable();
scsi_write_dsp(phys_to_bus(&script_select[0])); /* start script */
/* now we have to wait for IRQs */
retry:
/*
* This version of the driver is _not_ interrupt driven,
* but polls the chip's interrupt registers (ISTAT, DSTAT).
*/
while(int_stat[0]==0)
handle_scsi_int();
if(int_stat[0]==SIR_COMPLETE) {
if(pccb->msgin[0]==M_DISCONNECT) {
PRINTF("Wait for reselection\n");
for(i=0;i<3;i++)
int_stat[i]=0; /* delete all int status */
scsi_write_dsp(phys_to_bus(&script_reselection[0])); /* start reselection script */
goto retry;
}
pccb->contr_stat=SIR_COMPLETE;
return;
}
if((int_stat[0] & SCSI_INT_STATE)==SCSI_INT_STATE) { /* scsi interrupt */
sstat=(unsigned short)int_stat[0];
if((sstat & STO)==STO) { /* selection timeout */
pccb->contr_stat=SCSI_SEL_TIME_OUT;
scsi_write_byte(GPREG,0x01);
PRINTF("ID: %X Selection Timeout\n",pccb->target);
return;
}
if((sstat & UDC)==UDC) { /* unexpected disconnect */
pccb->contr_stat=SCSI_UNEXP_DIS;
scsi_write_byte(GPREG,0x01);
PRINTF("ID: %X Unexpected Disconnect\n",pccb->target);
return;
}
if((sstat & RSL)==RSL) { /* reselection */
pccb->contr_stat=SCSI_UNEXP_DIS;
scsi_write_byte(GPREG,0x01);
PRINTF("ID: %X Unexpected Disconnect\n",pccb->target);
return;
}
if(((sstat & MA)==MA)||((sstat & HTH)==HTH)) { /* phase missmatch */
if(retrycnt<SCSI_MAX_RETRY) {
pccb->trans_bytes=pccb->datalen -
((unsigned long)scsi_read_byte(DBC) |
((unsigned long)scsi_read_byte(DBC+1)<<8) |
((unsigned long)scsi_read_byte(DBC+2)<<16));
for(i=0;i<3;i++)
int_stat[i]=0; /* delete all int status */
retrycnt++;
PRINTF("ID: %X Phase Missmatch Retry %d Phase %02X transfered %lx\n",
pccb->target,retrycnt,scsi_read_byte(SBCL),pccb->trans_bytes);
scsi_write_dsp(phys_to_bus(&script_cmd[4])); /* start retry script */
goto retry;
}
if((sstat & MA)==MA)
pccb->contr_stat=SCSI_MA_TIME_OUT;
else
pccb->contr_stat=SCSI_HNS_TIME_OUT;
PRINTF("Phase Missmatch stat %lx\n",pccb->contr_stat);
return;
} /* no phase int */
/* if((sstat & CMP)==CMP) {
pccb->contr_stat=SIR_COMPLETE;
return;
}
*/
PRINTF("SCSI INT %lX\n",int_stat[0]);
pccb->contr_stat=int_stat[0];
return;
} /* end scsi int */
PRINTF("SCRIPT INT %lX phase %02X\n",int_stat[0],scsi_read_byte(SBCL));
pccb->contr_stat=int_stat[0];
return;
}
int scsi_exec(ccb *pccb)
{
unsigned char tmpcmd[16],tmpstat;
int i,retrycnt,t;
unsigned long transbytes,datalen;
unsigned char *tmpptr;
retrycnt=0;
retry:
scsi_issue(pccb);
if(pccb->contr_stat!=SIR_COMPLETE)
return FALSE;
if(pccb->status==S_GOOD)
return TRUE;
if(pccb->status==S_CHECK_COND) { /* check condition */
for(i=0;i<16;i++)
tmpcmd[i]=pccb->cmd[i];
pccb->cmd[0]=SCSI_REQ_SENSE;
pccb->cmd[1]=pccb->lun<<5;
pccb->cmd[2]=0;
pccb->cmd[3]=0;
pccb->cmd[4]=14;
pccb->cmd[5]=0;
pccb->cmdlen=6;
pccb->msgout[0]=SCSI_IDENTIFY;
transbytes=pccb->trans_bytes;
tmpptr=pccb->pdata;
pccb->pdata = &pccb->sense_buf[0];
datalen=pccb->datalen;
pccb->datalen=14;
tmpstat=pccb->status;
scsi_issue(pccb);
for(i=0;i<16;i++)
pccb->cmd[i]=tmpcmd[i];
pccb->trans_bytes=transbytes;
pccb->pdata=tmpptr;
pccb->datalen=datalen;
pccb->status=tmpstat;
PRINTF("Request_sense sense key %x ASC %x ASCQ %x\n",pccb->sense_buf[2]&0x0f,
pccb->sense_buf[12],pccb->sense_buf[13]);
switch(pccb->sense_buf[2]&0xf) {
case SENSE_NO_SENSE:
case SENSE_RECOVERED_ERROR:
/* seems to be ok */
return TRUE;
break;
case SENSE_NOT_READY:
if((pccb->sense_buf[12]!=0x04)||(pccb->sense_buf[13]!=0x01)) {
/* if device is not in process of becoming ready */
return FALSE;
break;
} /* else fall through */
case SENSE_UNIT_ATTENTION:
if(retrycnt<SCSI_MAX_RETRY_NOT_READY) {
PRINTF("Target %d not ready, retry %d\n",pccb->target,retrycnt);
for(t=0;t<SCSI_NOT_READY_TIME_OUT;t++)
udelay(1000); /* 1sec wait */
retrycnt++;
goto retry;
}
PRINTF("Target %d not ready, %d retried\n",pccb->target,retrycnt);
return FALSE;
default:
return FALSE;
}
}
PRINTF("Status = %X\n",pccb->status);
return FALSE;
}
void scsi_chip_init(void)
{
/* first we issue a soft reset */
scsi_write_byte(ISTAT,SRST);
udelay(1000);
scsi_write_byte(ISTAT,0);
/* setup chip */
scsi_write_byte(SCNTL0,0xC0); /* full arbitration no start, no message, parity disabled, master */
scsi_write_byte(SCNTL1,0x00);
scsi_write_byte(SCNTL2,0x00);
#ifndef CONFIG_SYS_SCSI_SYM53C8XX_CCF /* config value for none 40 MHz clocks */
scsi_write_byte(SCNTL3,0x13); /* synchronous clock 40/4=10MHz, asynchronous 40MHz */
#else
scsi_write_byte(SCNTL3,CONFIG_SYS_SCSI_SYM53C8XX_CCF); /* config value for none 40 MHz clocks */
#endif
scsi_write_byte(SCID,0x47); /* ID=7, enable reselection */
scsi_write_byte(SXFER,0x00); /* synchronous transfer period 10MHz, asynchronous */
scsi_write_byte(SDID,0x00); /* targed SCSI ID = 0 */
scsi_int_mask=0x0000; /* no Interrupt is enabled */
script_int_mask=0x00;
scsi_int_enable();
scsi_write_byte(GPREG,0x01); /* GPIO0 is LED (off) */
scsi_write_byte(GPCNTL,0x0E); /* GPIO0 is Output */
scsi_write_byte(STIME0,0x08); /* handshake timer disabled, selection timeout 512msec */
scsi_write_byte(RESPID,0x80); /* repond only to the own ID (reselection) */
scsi_write_byte(STEST1,0x00); /* not isolated, SCLK is used */
scsi_write_byte(STEST2,0x00); /* no Lowlevel Mode? */
scsi_write_byte(STEST3,0x80); /* enable tolerANT */
scsi_write_byte(CTEST3,0x04); /* clear FIFO */
scsi_write_byte(CTEST4,0x00);
scsi_write_byte(CTEST5,0x00);
#ifdef SCSI_SINGLE_STEP
/* scsi_write_byte(DCNTL,IRQM | SSM); */
scsi_write_byte(DCNTL,IRQD | SSM);
scsi_write_byte(DMODE,MAN);
#else
/* scsi_write_byte(DCNTL,IRQM); */
scsi_write_byte(DCNTL,IRQD);
scsi_write_byte(DMODE,0x00);
#endif
}
#endif

View file

@ -0,0 +1,257 @@
/*
* Copyright (c) 2004 Picture Elements, Inc.
* Stephen Williams (XXXXXXXXXXXXXXXX)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form 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
*/
/*
* The Xilinx SystemACE chip support is activated by defining
* CONFIG_SYSTEMACE to turn on support, and CONFIG_SYS_SYSTEMACE_BASE
* to set the base address of the device. This code currently
* assumes that the chip is connected via a byte-wide bus.
*
* The CONFIG_SYSTEMACE also adds to fat support the device class
* "ace" that allows the user to execute "fatls ace 0" and the
* like. This works by making the systemace_get_dev function
* available to cmd_fat.c:get_dev and filling in a block device
* description that has all the bits needed for FAT support to
* read sectors.
*
* According to Xilinx technical support, before accessing the
* SystemACE CF you need to set the following control bits:
* FORCECFGMODE : 1
* CFGMODE : 0
* CFGSTART : 0
*/
#include <common.h>
#include <command.h>
#include <systemace.h>
#include <part.h>
#include <asm/io.h>
/*
* The ace_readw and writew functions read/write 16bit words, but the
* offset value is the BYTE offset as most used in the Xilinx
* datasheet for the SystemACE chip. The CONFIG_SYS_SYSTEMACE_BASE is defined
* to be the base address for the chip, usually in the local
* peripheral bus.
*/
#if (CONFIG_SYS_SYSTEMACE_WIDTH == 8)
#if !defined(__BIG_ENDIAN)
#define ace_readw(off) ((readb(CONFIG_SYS_SYSTEMACE_BASE+off)<<8) | \
(readb(CONFIG_SYS_SYSTEMACE_BASE+off+1)))
#define ace_writew(val, off) {writeb(val>>8, CONFIG_SYS_SYSTEMACE_BASE+off); \
writeb(val, CONFIG_SYS_SYSTEMACE_BASE+off+1);}
#else
#define ace_readw(off) ((readb(CONFIG_SYS_SYSTEMACE_BASE+off)) | \
(readb(CONFIG_SYS_SYSTEMACE_BASE+off+1)<<8))
#define ace_writew(val, off) {writeb(val, CONFIG_SYS_SYSTEMACE_BASE+off); \
writeb(val>>8, CONFIG_SYS_SYSTEMACE_BASE+off+1);}
#endif
#else
#define ace_readw(off) (in16(CONFIG_SYS_SYSTEMACE_BASE+off))
#define ace_writew(val, off) (out16(CONFIG_SYS_SYSTEMACE_BASE+off,val))
#endif
/* */
static unsigned long systemace_read(int dev, unsigned long start,
unsigned long blkcnt, void *buffer);
static block_dev_desc_t systemace_dev = { 0 };
static int get_cf_lock(void)
{
int retry = 10;
/* CONTROLREG = LOCKREG */
unsigned val = ace_readw(0x18);
val |= 0x0002;
ace_writew((val & 0xffff), 0x18);
/* Wait for MPULOCK in STATUSREG[15:0] */
while (!(ace_readw(0x04) & 0x0002)) {
if (retry < 0)
return -1;
udelay(100000);
retry -= 1;
}
return 0;
}
static void release_cf_lock(void)
{
unsigned val = ace_readw(0x18);
val &= ~(0x0002);
ace_writew((val & 0xffff), 0x18);
}
#ifdef CONFIG_PARTITIONS
block_dev_desc_t *systemace_get_dev(int dev)
{
/* The first time through this, the systemace_dev object is
not yet initialized. In that case, fill it in. */
if (systemace_dev.blksz == 0) {
systemace_dev.if_type = IF_TYPE_UNKNOWN;
systemace_dev.dev = 0;
systemace_dev.part_type = PART_TYPE_UNKNOWN;
systemace_dev.type = DEV_TYPE_HARDDISK;
systemace_dev.blksz = 512;
systemace_dev.removable = 1;
systemace_dev.block_read = systemace_read;
/*
* Ensure the correct bus mode (8/16 bits) gets enabled
*/
ace_writew(CONFIG_SYS_SYSTEMACE_WIDTH == 8 ? 0 : 0x0001, 0);
init_part(&systemace_dev);
}
return &systemace_dev;
}
#endif
/*
* This function is called (by dereferencing the block_read pointer in
* the dev_desc) to read blocks of data. The return value is the
* number of blocks read. A zero return indicates an error.
*/
static unsigned long systemace_read(int dev, unsigned long start,
unsigned long blkcnt, void *buffer)
{
int retry;
unsigned blk_countdown;
unsigned char *dp = buffer;
unsigned val;
if (get_cf_lock() < 0) {
unsigned status = ace_readw(0x04);
/* If CFDETECT is false, card is missing. */
if (!(status & 0x0010)) {
printf("** CompactFlash card not present. **\n");
return 0;
}
printf("**** ACE locked away from me (STATUSREG=%04x)\n",
status);
return 0;
}
#ifdef DEBUG_SYSTEMACE
printf("... systemace read %lu sectors at %lu\n", blkcnt, start);
#endif
retry = 2000;
for (;;) {
val = ace_readw(0x04);
/* If CFDETECT is false, card is missing. */
if (!(val & 0x0010)) {
printf("**** ACE CompactFlash not found.\n");
release_cf_lock();
return 0;
}
/* If RDYFORCMD, then we are ready to go. */
if (val & 0x0100)
break;
if (retry < 0) {
printf("**** SystemACE not ready.\n");
release_cf_lock();
return 0;
}
udelay(1000);
retry -= 1;
}
/* The SystemACE can only transfer 256 sectors at a time, so
limit the current chunk of sectors. The blk_countdown
variable is the number of sectors left to transfer. */
blk_countdown = blkcnt;
while (blk_countdown > 0) {
unsigned trans = blk_countdown;
if (trans > 256)
trans = 256;
#ifdef DEBUG_SYSTEMACE
printf("... transfer %lu sector in a chunk\n", trans);
#endif
/* Write LBA block address */
ace_writew((start >> 0) & 0xffff, 0x10);
ace_writew((start >> 16) & 0x0fff, 0x12);
/* NOTE: in the Write Sector count below, a count of 0
causes a transfer of 256, so &0xff gives the right
value for whatever transfer count we want. */
/* Write sector count | ReadMemCardData. */
ace_writew((trans & 0xff) | 0x0300, 0x14);
/*
* For FPGA configuration via SystemACE is reset unacceptable
* CFGDONE bit in STATUSREG is not set to 1.
*/
#ifndef SYSTEMACE_CONFIG_FPGA
/* Reset the configruation controller */
val = ace_readw(0x18);
val |= 0x0080;
ace_writew(val, 0x18);
#endif
retry = trans * 16;
while (retry > 0) {
int idx;
/* Wait for buffer to become ready. */
while (!(ace_readw(0x04) & 0x0020)) {
udelay(100);
}
/* Read 16 words of 2bytes from the sector buffer. */
for (idx = 0; idx < 16; idx += 1) {
unsigned short val = ace_readw(0x40);
*dp++ = val & 0xff;
*dp++ = (val >> 8) & 0xff;
}
retry -= 1;
}
/* Clear the configruation controller reset */
val = ace_readw(0x18);
val &= ~0x0080;
ace_writew(val, 0x18);
/* Count the blocks we transfer this time. */
start += trans;
blk_countdown -= trans;
}
release_cf_lock();
return blkcnt;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,242 @@
/*
* Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h>
/* Functions for initializing variable tables of different types of tasks. */
/*
* Do not edit!
*/
#include <MCD_dma.h>
extern dmaRegs *MCD_dmaBar;
/* Task 0 */
void MCD_startDmaChainNoEu(int *currBD, short srcIncr, short destIncr,
int xferSize, short xferSizeIncr, int *cSave,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 2, (u32) currBD); /* var[2] */
MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */
MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */
MCD_SET_VAR(taskChan, 11, (u32) xferSize); /* var[11] */
MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */
MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */
MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */
MCD_SET_VAR(taskChan, 3, (u32) 0x00000000); /* var[3] */
MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */
MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */
MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */
MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */
MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */
MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */
MCD_SET_VAR(taskChan, 12, (u32) 0x00000000); /* var[12] */
MCD_SET_VAR(taskChan, 13, (u32) 0x80000000); /* var[13] */
MCD_SET_VAR(taskChan, 14, (u32) 0x00000010); /* var[14] */
MCD_SET_VAR(taskChan, 15, (u32) 0x00000004); /* var[15] */
MCD_SET_VAR(taskChan, 16, (u32) 0x08000000); /* var[16] */
MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */
MCD_SET_VAR(taskChan, 28, (u32) 0x80000000); /* inc[4] */
MCD_SET_VAR(taskChan, 29, (u32) 0x80000001); /* inc[5] */
MCD_SET_VAR(taskChan, 30, (u32) 0x40000000); /* inc[6] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}
/* Task 1 */
void MCD_startDmaSingleNoEu(char *srcAddr, short srcIncr, char *destAddr,
short destIncr, int dmaSize, short xferSizeIncr,
int flags, int *currBD, int *cSave,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 7, (u32) srcAddr); /* var[7] */
MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */
MCD_SET_VAR(taskChan, 2, (u32) destAddr); /* var[2] */
MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */
MCD_SET_VAR(taskChan, 3, (u32) dmaSize); /* var[3] */
MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */
MCD_SET_VAR(taskChan, 5, (u32) flags); /* var[5] */
MCD_SET_VAR(taskChan, 1, (u32) currBD); /* var[1] */
MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */
MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */
MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */
MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */
MCD_SET_VAR(taskChan, 9, (u32) 0x00000004); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x08000000); /* var[10] */
MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */
MCD_SET_VAR(taskChan, 28, (u32) 0x80000001); /* inc[4] */
MCD_SET_VAR(taskChan, 29, (u32) 0x40000000); /* inc[5] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}
/* Task 2 */
void MCD_startDmaChainEu(int *currBD, short srcIncr, short destIncr,
int xferSize, short xferSizeIncr, int *cSave,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */
MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */
MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */
MCD_SET_VAR(taskChan, 12, (u32) xferSize); /* var[12] */
MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */
MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */
MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */
MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */
MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */
MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */
MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */
MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */
MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */
MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */
MCD_SET_VAR(taskChan, 11, (u32) 0x00000000); /* var[11] */
MCD_SET_VAR(taskChan, 13, (u32) 0x00000000); /* var[13] */
MCD_SET_VAR(taskChan, 14, (u32) 0x80000000); /* var[14] */
MCD_SET_VAR(taskChan, 15, (u32) 0x00000010); /* var[15] */
MCD_SET_VAR(taskChan, 16, (u32) 0x00000001); /* var[16] */
MCD_SET_VAR(taskChan, 17, (u32) 0x00000004); /* var[17] */
MCD_SET_VAR(taskChan, 18, (u32) 0x08000000); /* var[18] */
MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */
MCD_SET_VAR(taskChan, 28, (u32) 0x80000000); /* inc[4] */
MCD_SET_VAR(taskChan, 29, (u32) 0xc0000000); /* inc[5] */
MCD_SET_VAR(taskChan, 30, (u32) 0x80000001); /* inc[6] */
MCD_SET_VAR(taskChan, 31, (u32) 0x40000000); /* inc[7] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}
/* Task 3 */
void MCD_startDmaSingleEu(char *srcAddr, short srcIncr, char *destAddr,
short destIncr, int dmaSize, short xferSizeIncr,
int flags, int *currBD, int *cSave,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 8, (u32) srcAddr); /* var[8] */
MCD_SET_VAR(taskChan, 25, (u32) (0xe000 << 16) | (0xffff & srcIncr)); /* inc[1] */
MCD_SET_VAR(taskChan, 3, (u32) destAddr); /* var[3] */
MCD_SET_VAR(taskChan, 24, (u32) (0xe000 << 16) | (0xffff & destIncr)); /* inc[0] */
MCD_SET_VAR(taskChan, 4, (u32) dmaSize); /* var[4] */
MCD_SET_VAR(taskChan, 26, (u32) (0x2000 << 16) | (0xffff & xferSizeIncr)); /* inc[2] */
MCD_SET_VAR(taskChan, 6, (u32) flags); /* var[6] */
MCD_SET_VAR(taskChan, 2, (u32) currBD); /* var[2] */
MCD_SET_VAR(taskChan, 0, (u32) cSave); /* var[0] */
MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */
MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */
MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */
MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x00000001); /* var[10] */
MCD_SET_VAR(taskChan, 11, (u32) 0x00000004); /* var[11] */
MCD_SET_VAR(taskChan, 12, (u32) 0x08000000); /* var[12] */
MCD_SET_VAR(taskChan, 27, (u32) 0x00000000); /* inc[3] */
MCD_SET_VAR(taskChan, 28, (u32) 0xc0000000); /* inc[4] */
MCD_SET_VAR(taskChan, 29, (u32) 0x80000000); /* inc[5] */
MCD_SET_VAR(taskChan, 30, (u32) 0x80000001); /* inc[6] */
MCD_SET_VAR(taskChan, 31, (u32) 0x40000000); /* inc[7] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}
/* Task 4 */
void MCD_startDmaENetRcv(char *bDBase, char *currBD, char *rcvFifoPtr,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 0, (u32) bDBase); /* var[0] */
MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */
MCD_SET_VAR(taskChan, 6, (u32) rcvFifoPtr); /* var[6] */
MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */
MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */
MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */
MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */
MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */
MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */
MCD_SET_VAR(taskChan, 9, (u32) 0x0000ffff); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x30000000); /* var[10] */
MCD_SET_VAR(taskChan, 11, (u32) 0x0fffffff); /* var[11] */
MCD_SET_VAR(taskChan, 12, (u32) 0x00000008); /* var[12] */
MCD_SET_VAR(taskChan, 24, (u32) 0x00000000); /* inc[0] */
MCD_SET_VAR(taskChan, 25, (u32) 0x60000000); /* inc[1] */
MCD_SET_VAR(taskChan, 26, (u32) 0x20000004); /* inc[2] */
MCD_SET_VAR(taskChan, 27, (u32) 0x40000000); /* inc[3] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}
/* Task 5 */
void MCD_startDmaENetXmit(char *bDBase, char *currBD, char *xmitFifoPtr,
volatile TaskTableEntry * taskTable, int channel)
{
volatile TaskTableEntry *taskChan = taskTable + channel;
MCD_SET_VAR(taskChan, 0, (u32) bDBase); /* var[0] */
MCD_SET_VAR(taskChan, 3, (u32) currBD); /* var[3] */
MCD_SET_VAR(taskChan, 11, (u32) xmitFifoPtr); /* var[11] */
MCD_SET_VAR(taskChan, 1, (u32) 0x00000000); /* var[1] */
MCD_SET_VAR(taskChan, 2, (u32) 0x00000000); /* var[2] */
MCD_SET_VAR(taskChan, 4, (u32) 0x00000000); /* var[4] */
MCD_SET_VAR(taskChan, 5, (u32) 0x00000000); /* var[5] */
MCD_SET_VAR(taskChan, 6, (u32) 0x00000000); /* var[6] */
MCD_SET_VAR(taskChan, 7, (u32) 0x00000000); /* var[7] */
MCD_SET_VAR(taskChan, 8, (u32) 0x00000000); /* var[8] */
MCD_SET_VAR(taskChan, 9, (u32) 0x00000000); /* var[9] */
MCD_SET_VAR(taskChan, 10, (u32) 0x00000000); /* var[10] */
MCD_SET_VAR(taskChan, 12, (u32) 0x00000000); /* var[12] */
MCD_SET_VAR(taskChan, 13, (u32) 0x0000ffff); /* var[13] */
MCD_SET_VAR(taskChan, 14, (u32) 0xffffffff); /* var[14] */
MCD_SET_VAR(taskChan, 15, (u32) 0x00000004); /* var[15] */
MCD_SET_VAR(taskChan, 16, (u32) 0x00000008); /* var[16] */
MCD_SET_VAR(taskChan, 24, (u32) 0x00000000); /* inc[0] */
MCD_SET_VAR(taskChan, 25, (u32) 0x60000000); /* inc[1] */
MCD_SET_VAR(taskChan, 26, (u32) 0x40000000); /* inc[2] */
MCD_SET_VAR(taskChan, 27, (u32) 0xc000fffc); /* inc[3] */
MCD_SET_VAR(taskChan, 28, (u32) 0xe0000004); /* inc[4] */
MCD_SET_VAR(taskChan, 29, (u32) 0x80000000); /* inc[5] */
MCD_SET_VAR(taskChan, 30, (u32) 0x4000ffff); /* inc[6] */
MCD_SET_VAR(taskChan, 31, (u32) 0xe0000001); /* inc[7] */
/* Set the task's Enable bit in its Task Control Register */
MCD_dmaBar->taskControl[channel] |= (u16) 0x8000;
}

View file

@ -0,0 +1,49 @@
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# 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 $(TOPDIR)/config.mk
LIB := $(obj)libdma.o
COBJS-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o
COBJS-$(CONFIG_APBH_DMA) += apbh_dma.o
COBJS-$(CONFIG_FSL_DMA) += fsl_dma.o
COBJS-$(CONFIG_OMAP3_DMA) += omap3_dma.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,599 @@
/*
* Freescale i.MX28 APBH DMA driver
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* Based on code from LTIB:
* Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/list.h>
#include <common.h>
#include <malloc.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
#include <asm/arch/dma.h>
static struct mxs_dma_chan mxs_dma_channels[MXS_MAX_DMA_CHANNELS];
/*
* Test is the DMA channel is valid channel
*/
int mxs_dma_validate_chan(int channel)
{
struct mxs_dma_chan *pchan;
if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS))
return -EINVAL;
pchan = mxs_dma_channels + channel;
if (!(pchan->flags & MXS_DMA_FLAGS_ALLOCATED))
return -EINVAL;
return 0;
}
/*
* Return the address of the command within a descriptor.
*/
static unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc)
{
return desc->address + offsetof(struct mxs_dma_desc, cmd);
}
/*
* Read a DMA channel's hardware semaphore.
*
* As used by the MXS platform's DMA software, the DMA channel's hardware
* semaphore reflects the number of DMA commands the hardware will process, but
* has not yet finished. This is a volatile value read directly from hardware,
* so it must be be viewed as immediately stale.
*
* If the channel is not marked busy, or has finished processing all its
* commands, this value should be zero.
*
* See mxs_dma_append() for details on how DMA command blocks must be configured
* to maintain the expected behavior of the semaphore's value.
*/
static int mxs_dma_read_semaphore(int channel)
{
struct mx28_apbh_regs *apbh_regs =
(struct mx28_apbh_regs *)MXS_APBH_BASE;
uint32_t tmp;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
tmp = readl(&apbh_regs->ch[channel].hw_apbh_ch_sema);
tmp &= APBH_CHn_SEMA_PHORE_MASK;
tmp >>= APBH_CHn_SEMA_PHORE_OFFSET;
return tmp;
}
#ifndef CONFIG_SYS_DCACHE_OFF
void mxs_dma_flush_desc(struct mxs_dma_desc *desc)
{
uint32_t addr;
uint32_t size;
addr = (uint32_t)desc;
size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT);
flush_dcache_range(addr, addr + size);
}
#else
inline void mxs_dma_flush_desc(struct mxs_dma_desc *desc) {}
#endif
/*
* Enable a DMA channel.
*
* If the given channel has any DMA descriptors on its active list, this
* function causes the DMA hardware to begin processing them.
*
* This function marks the DMA channel as "busy," whether or not there are any
* descriptors to process.
*/
static int mxs_dma_enable(int channel)
{
struct mx28_apbh_regs *apbh_regs =
(struct mx28_apbh_regs *)MXS_APBH_BASE;
unsigned int sem;
struct mxs_dma_chan *pchan;
struct mxs_dma_desc *pdesc;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
pchan = mxs_dma_channels + channel;
if (pchan->pending_num == 0) {
pchan->flags |= MXS_DMA_FLAGS_BUSY;
return 0;
}
pdesc = list_first_entry(&pchan->active, struct mxs_dma_desc, node);
if (pdesc == NULL)
return -EFAULT;
if (pchan->flags & MXS_DMA_FLAGS_BUSY) {
if (!(pdesc->cmd.data & MXS_DMA_DESC_CHAIN))
return 0;
sem = mxs_dma_read_semaphore(channel);
if (sem == 0)
return 0;
if (sem == 1) {
pdesc = list_entry(pdesc->node.next,
struct mxs_dma_desc, node);
writel(mxs_dma_cmd_address(pdesc),
&apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar);
}
writel(pchan->pending_num,
&apbh_regs->ch[channel].hw_apbh_ch_sema);
pchan->active_num += pchan->pending_num;
pchan->pending_num = 0;
} else {
pchan->active_num += pchan->pending_num;
pchan->pending_num = 0;
writel(mxs_dma_cmd_address(pdesc),
&apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar);
writel(pchan->active_num,
&apbh_regs->ch[channel].hw_apbh_ch_sema);
writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET),
&apbh_regs->hw_apbh_ctrl0_clr);
}
pchan->flags |= MXS_DMA_FLAGS_BUSY;
return 0;
}
/*
* Disable a DMA channel.
*
* This function shuts down a DMA channel and marks it as "not busy." Any
* descriptors on the active list are immediately moved to the head of the
* "done" list, whether or not they have actually been processed by the
* hardware. The "ready" flags of these descriptors are NOT cleared, so they
* still appear to be active.
*
* This function immediately shuts down a DMA channel's hardware, aborting any
* I/O that may be in progress, potentially leaving I/O hardware in an undefined
* state. It is unwise to call this function if there is ANY chance the hardware
* is still processing a command.
*/
static int mxs_dma_disable(int channel)
{
struct mxs_dma_chan *pchan;
struct mx28_apbh_regs *apbh_regs =
(struct mx28_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
pchan = mxs_dma_channels + channel;
if (!(pchan->flags & MXS_DMA_FLAGS_BUSY))
return -EINVAL;
writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET),
&apbh_regs->hw_apbh_ctrl0_set);
pchan->flags &= ~MXS_DMA_FLAGS_BUSY;
pchan->active_num = 0;
pchan->pending_num = 0;
list_splice_init(&pchan->active, &pchan->done);
return 0;
}
/*
* Resets the DMA channel hardware.
*/
static int mxs_dma_reset(int channel)
{
struct mx28_apbh_regs *apbh_regs =
(struct mx28_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
writel(1 << (channel + APBH_CHANNEL_CTRL_RESET_CHANNEL_OFFSET),
&apbh_regs->hw_apbh_channel_ctrl_set);
return 0;
}
/*
* Enable or disable DMA interrupt.
*
* This function enables the given DMA channel to interrupt the CPU.
*/
static int mxs_dma_enable_irq(int channel, int enable)
{
struct mx28_apbh_regs *apbh_regs =
(struct mx28_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
if (enable)
writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET),
&apbh_regs->hw_apbh_ctrl1_set);
else
writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET),
&apbh_regs->hw_apbh_ctrl1_clr);
return 0;
}
/*
* Clear DMA interrupt.
*
* The software that is using the DMA channel must register to receive its
* interrupts and, when they arrive, must call this function to clear them.
*/
static int mxs_dma_ack_irq(int channel)
{
struct mx28_apbh_regs *apbh_regs =
(struct mx28_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
writel(1 << channel, &apbh_regs->hw_apbh_ctrl1_clr);
writel(1 << channel, &apbh_regs->hw_apbh_ctrl2_clr);
return 0;
}
/*
* Request to reserve a DMA channel
*/
static int mxs_dma_request(int channel)
{
struct mxs_dma_chan *pchan;
if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS))
return -EINVAL;
pchan = mxs_dma_channels + channel;
if ((pchan->flags & MXS_DMA_FLAGS_VALID) != MXS_DMA_FLAGS_VALID)
return -ENODEV;
if (pchan->flags & MXS_DMA_FLAGS_ALLOCATED)
return -EBUSY;
pchan->flags |= MXS_DMA_FLAGS_ALLOCATED;
pchan->active_num = 0;
pchan->pending_num = 0;
INIT_LIST_HEAD(&pchan->active);
INIT_LIST_HEAD(&pchan->done);
return 0;
}
/*
* Release a DMA channel.
*
* This function releases a DMA channel from its current owner.
*
* The channel will NOT be released if it's marked "busy" (see
* mxs_dma_enable()).
*/
int mxs_dma_release(int channel)
{
struct mxs_dma_chan *pchan;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
pchan = mxs_dma_channels + channel;
if (pchan->flags & MXS_DMA_FLAGS_BUSY)
return -EBUSY;
pchan->dev = 0;
pchan->active_num = 0;
pchan->pending_num = 0;
pchan->flags &= ~MXS_DMA_FLAGS_ALLOCATED;
return 0;
}
/*
* Allocate DMA descriptor
*/
struct mxs_dma_desc *mxs_dma_desc_alloc(void)
{
struct mxs_dma_desc *pdesc;
uint32_t size;
size = roundup(sizeof(struct mxs_dma_desc), MXS_DMA_ALIGNMENT);
pdesc = memalign(MXS_DMA_ALIGNMENT, size);
if (pdesc == NULL)
return NULL;
memset(pdesc, 0, sizeof(*pdesc));
pdesc->address = (dma_addr_t)pdesc;
return pdesc;
};
/*
* Free DMA descriptor
*/
void mxs_dma_desc_free(struct mxs_dma_desc *pdesc)
{
if (pdesc == NULL)
return;
free(pdesc);
}
/*
* Add a DMA descriptor to a channel.
*
* If the descriptor list for this channel is not empty, this function sets the
* CHAIN bit and the NEXTCMD_ADDR fields in the last descriptor's DMA command so
* it will chain to the new descriptor's command.
*
* Then, this function marks the new descriptor as "ready," adds it to the end
* of the active descriptor list, and increments the count of pending
* descriptors.
*
* The MXS platform DMA software imposes some rules on DMA commands to maintain
* important invariants. These rules are NOT checked, but they must be carefully
* applied by software that uses MXS DMA channels.
*
* Invariant:
* The DMA channel's hardware semaphore must reflect the number of DMA
* commands the hardware will process, but has not yet finished.
*
* Explanation:
* A DMA channel begins processing commands when its hardware semaphore is
* written with a value greater than zero, and it stops processing commands
* when the semaphore returns to zero.
*
* When a channel finishes a DMA command, it will decrement its semaphore if
* the DECREMENT_SEMAPHORE bit is set in that command's flags bits.
*
* In principle, it's not necessary for the DECREMENT_SEMAPHORE to be set,
* unless it suits the purposes of the software. For example, one could
* construct a series of five DMA commands, with the DECREMENT_SEMAPHORE
* bit set only in the last one. Then, setting the DMA channel's hardware
* semaphore to one would cause the entire series of five commands to be
* processed. However, this example would violate the invariant given above.
*
* Rule:
* ALL DMA commands MUST have the DECREMENT_SEMAPHORE bit set so that the DMA
* channel's hardware semaphore will be decremented EVERY time a command is
* processed.
*/
int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc)
{
struct mxs_dma_chan *pchan;
struct mxs_dma_desc *last;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
pchan = mxs_dma_channels + channel;
pdesc->cmd.next = mxs_dma_cmd_address(pdesc);
pdesc->flags |= MXS_DMA_DESC_FIRST | MXS_DMA_DESC_LAST;
if (!list_empty(&pchan->active)) {
last = list_entry(pchan->active.prev, struct mxs_dma_desc,
node);
pdesc->flags &= ~MXS_DMA_DESC_FIRST;
last->flags &= ~MXS_DMA_DESC_LAST;
last->cmd.next = mxs_dma_cmd_address(pdesc);
last->cmd.data |= MXS_DMA_DESC_CHAIN;
mxs_dma_flush_desc(last);
}
pdesc->flags |= MXS_DMA_DESC_READY;
if (pdesc->flags & MXS_DMA_DESC_FIRST)
pchan->pending_num++;
list_add_tail(&pdesc->node, &pchan->active);
mxs_dma_flush_desc(pdesc);
return ret;
}
/*
* Clean up processed DMA descriptors.
*
* This function removes processed DMA descriptors from the "active" list. Pass
* in a non-NULL list head to get the descriptors moved to your list. Pass NULL
* to get the descriptors moved to the channel's "done" list. Descriptors on
* the "done" list can be retrieved with mxs_dma_get_finished().
*
* This function marks the DMA channel as "not busy" if no unprocessed
* descriptors remain on the "active" list.
*/
static int mxs_dma_finish(int channel, struct list_head *head)
{
int sem;
struct mxs_dma_chan *pchan;
struct list_head *p, *q;
struct mxs_dma_desc *pdesc;
int ret;
ret = mxs_dma_validate_chan(channel);
if (ret)
return ret;
pchan = mxs_dma_channels + channel;
sem = mxs_dma_read_semaphore(channel);
if (sem < 0)
return sem;
if (sem == pchan->active_num)
return 0;
list_for_each_safe(p, q, &pchan->active) {
if ((pchan->active_num) <= sem)
break;
pdesc = list_entry(p, struct mxs_dma_desc, node);
pdesc->flags &= ~MXS_DMA_DESC_READY;
if (head)
list_move_tail(p, head);
else
list_move_tail(p, &pchan->done);
if (pdesc->flags & MXS_DMA_DESC_LAST)
pchan->active_num--;
}
if (sem == 0)
pchan->flags &= ~MXS_DMA_FLAGS_BUSY;
return 0;
}
/*
* Wait for DMA channel to complete
*/
static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)
{
struct mx28_apbh_regs *apbh_regs =
(struct mx28_apbh_regs *)MXS_APBH_BASE;
int ret;
ret = mxs_dma_validate_chan(chan);
if (ret)
return ret;
if (mx28_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg,
1 << chan, timeout)) {
ret = -ETIMEDOUT;
mxs_dma_reset(chan);
}
return ret;
}
/*
* Execute the DMA channel
*/
int mxs_dma_go(int chan)
{
uint32_t timeout = 10000;
int ret;
LIST_HEAD(tmp_desc_list);
mxs_dma_enable_irq(chan, 1);
mxs_dma_enable(chan);
/* Wait for DMA to finish. */
ret = mxs_dma_wait_complete(timeout, chan);
/* Clear out the descriptors we just ran. */
mxs_dma_finish(chan, &tmp_desc_list);
/* Shut the DMA channel down. */
mxs_dma_ack_irq(chan);
mxs_dma_reset(chan);
mxs_dma_enable_irq(chan, 0);
mxs_dma_disable(chan);
return ret;
}
/*
* Initialize the DMA hardware
*/
void mxs_dma_init(void)
{
struct mx28_apbh_regs *apbh_regs =
(struct mx28_apbh_regs *)MXS_APBH_BASE;
mx28_reset_block(&apbh_regs->hw_apbh_ctrl0_reg);
#ifdef CONFIG_APBH_DMA_BURST8
writel(APBH_CTRL0_AHB_BURST8_EN,
&apbh_regs->hw_apbh_ctrl0_set);
#else
writel(APBH_CTRL0_AHB_BURST8_EN,
&apbh_regs->hw_apbh_ctrl0_clr);
#endif
#ifdef CONFIG_APBH_DMA_BURST
writel(APBH_CTRL0_APB_BURST_EN,
&apbh_regs->hw_apbh_ctrl0_set);
#else
writel(APBH_CTRL0_APB_BURST_EN,
&apbh_regs->hw_apbh_ctrl0_clr);
#endif
}
int mxs_dma_init_channel(int channel)
{
struct mxs_dma_chan *pchan;
int ret;
pchan = mxs_dma_channels + channel;
pchan->flags = MXS_DMA_FLAGS_VALID;
ret = mxs_dma_request(channel);
if (ret) {
printf("MXS DMA: Can't acquire DMA channel %i\n",
channel);
return ret;
}
mxs_dma_reset(channel);
mxs_dma_ack_irq(channel);
return 0;
}

View file

@ -0,0 +1,184 @@
/*
* Copyright 2004,2007,2008 Freescale Semiconductor, Inc.
* (C) Copyright 2002, 2003 Motorola Inc.
* Xianghua Xiao (X.Xiao@motorola.com)
*
* (C) Copyright 2000
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <config.h>
#include <common.h>
#include <asm/io.h>
#include <asm/fsl_dma.h>
/* Controller can only transfer 2^26 - 1 bytes at a time */
#define FSL_DMA_MAX_SIZE (0x3ffffff)
#if defined(CONFIG_MPC83xx)
#define FSL_DMA_MR_DEFAULT (FSL_DMA_MR_CTM_DIRECT | FSL_DMA_MR_DMSEN)
#else
#define FSL_DMA_MR_DEFAULT (FSL_DMA_MR_BWC_DIS | FSL_DMA_MR_CTM_DIRECT)
#endif
#if defined(CONFIG_MPC83xx)
dma83xx_t *dma_base = (void *)(CONFIG_SYS_MPC83xx_DMA_ADDR);
#elif defined(CONFIG_MPC85xx)
ccsr_dma_t *dma_base = (void *)(CONFIG_SYS_MPC85xx_DMA_ADDR);
#elif defined(CONFIG_MPC86xx)
ccsr_dma_t *dma_base = (void *)(CONFIG_SYS_MPC86xx_DMA_ADDR);
#else
#error "Freescale DMA engine not supported on your processor"
#endif
static void dma_sync(void)
{
#if defined(CONFIG_MPC85xx)
asm("sync; isync; msync");
#elif defined(CONFIG_MPC86xx)
asm("sync; isync");
#endif
}
static void out_dma32(volatile unsigned *addr, int val)
{
#if defined(CONFIG_MPC83xx)
out_le32(addr, val);
#else
out_be32(addr, val);
#endif
}
static uint in_dma32(volatile unsigned *addr)
{
#if defined(CONFIG_MPC83xx)
return in_le32(addr);
#else
return in_be32(addr);
#endif
}
static uint dma_check(void) {
volatile fsl_dma_t *dma = &dma_base->dma[0];
uint status;
/* While the channel is busy, spin */
do {
status = in_dma32(&dma->sr);
} while (status & FSL_DMA_SR_CB);
/* clear MR[CS] channel start bit */
out_dma32(&dma->mr, in_dma32(&dma->mr) & ~FSL_DMA_MR_CS);
dma_sync();
if (status != 0)
printf ("DMA Error: status = %x\n", status);
return status;
}
#if !defined(CONFIG_MPC83xx)
void dma_init(void) {
volatile fsl_dma_t *dma = &dma_base->dma[0];
out_dma32(&dma->satr, FSL_DMA_SATR_SREAD_SNOOP);
out_dma32(&dma->datr, FSL_DMA_DATR_DWRITE_SNOOP);
out_dma32(&dma->sr, 0xffffffff); /* clear any errors */
dma_sync();
}
#endif
int dmacpy(phys_addr_t dest, phys_addr_t src, phys_size_t count) {
volatile fsl_dma_t *dma = &dma_base->dma[0];
uint xfer_size;
while (count) {
xfer_size = MIN(FSL_DMA_MAX_SIZE, count);
out_dma32(&dma->dar, (u32) (dest & 0xFFFFFFFF));
out_dma32(&dma->sar, (u32) (src & 0xFFFFFFFF));
#if !defined(CONFIG_MPC83xx)
out_dma32(&dma->satr,
in_dma32(&dma->satr) | (u32)((u64)src >> 32));
out_dma32(&dma->datr,
in_dma32(&dma->datr) | (u32)((u64)dest >> 32));
#endif
out_dma32(&dma->bcr, xfer_size);
dma_sync();
/* Prepare mode register */
out_dma32(&dma->mr, FSL_DMA_MR_DEFAULT);
dma_sync();
/* Start the transfer */
out_dma32(&dma->mr, FSL_DMA_MR_DEFAULT | FSL_DMA_MR_CS);
count -= xfer_size;
src += xfer_size;
dest += xfer_size;
dma_sync();
if (dma_check())
return -1;
}
return 0;
}
/*
* 85xx/86xx use dma to initialize SDRAM when !CONFIG_ECC_INIT_VIA_DDRCONTROLLER
* while 83xx uses dma to initialize SDRAM when CONFIG_DDR_ECC_INIT_VIA_DMA
*/
#if ((!defined CONFIG_MPC83xx && defined(CONFIG_DDR_ECC) && \
!defined(CONFIG_ECC_INIT_VIA_DDRCONTROLLER)) || \
(defined(CONFIG_MPC83xx) && defined(CONFIG_DDR_ECC_INIT_VIA_DMA)))
void dma_meminit(uint val, uint size)
{
uint *p = 0;
uint i = 0;
for (*p = 0; p < (uint *)(8 * 1024); p++) {
if (((uint)p & 0x1f) == 0)
ppcDcbz((ulong)p);
*p = (uint)CONFIG_MEM_INIT_VALUE;
if (((uint)p & 0x1c) == 0x1c)
ppcDcbf((ulong)p);
}
dmacpy(0x002000, 0, 0x002000); /* 8K */
dmacpy(0x004000, 0, 0x004000); /* 16K */
dmacpy(0x008000, 0, 0x008000); /* 32K */
dmacpy(0x010000, 0, 0x010000); /* 64K */
dmacpy(0x020000, 0, 0x020000); /* 128K */
dmacpy(0x040000, 0, 0x040000); /* 256K */
dmacpy(0x080000, 0, 0x080000); /* 512K */
dmacpy(0x100000, 0, 0x100000); /* 1M */
dmacpy(0x200000, 0, 0x200000); /* 2M */
dmacpy(0x400000, 0, 0x400000); /* 4M */
for (i = 1; i < size / 0x800000; i++)
dmacpy((0x800000 * i), 0, 0x800000);
}
#endif

View file

@ -0,0 +1,180 @@
/* Copyright (C) 2011
* Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de>
*
* 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
*/
/* This is a basic implementation of the SDMA/DMA4 controller of OMAP3
* Tested on Silicon Revision major:0x4 minor:0x0
*/
#include <common.h>
#include <asm/arch/cpu.h>
#include <asm/arch/omap3.h>
#include <asm/arch/dma.h>
#include <asm/io.h>
#include <asm/errno.h>
static struct dma4 *dma4_cfg = (struct dma4 *)OMAP34XX_DMA4_BASE;
uint32_t dma_active; /* if a transfer is started the respective
bit is set for the logical channel */
/* Check if we have the given channel
* PARAMETERS:
* chan: Channel number
*
* RETURN of non-zero means error */
static inline int check_channel(uint32_t chan)
{
if (chan < CHAN_NR_MIN || chan > CHAN_NR_MAX)
return -EINVAL;
return 0;
}
static inline void reset_irq(uint32_t chan)
{
/* reset IRQ reason */
writel(0x1DFE, &dma4_cfg->chan[chan].csr);
/* reset IRQ */
writel((1 << chan), &dma4_cfg->irqstatus_l[0]);
dma_active &= ~(1 << chan);
}
/* Set Source, Destination and Size of DMA transfer for the
* specified channel.
* PARAMETERS:
* chan: channel to use
* src: source of the transfer
* dst: destination of the transfer
* sze: Size of the transfer
*
* RETURN of non-zero means error */
int omap3_dma_conf_transfer(uint32_t chan, uint32_t *src, uint32_t *dst,
uint32_t sze)
{
if (check_channel(chan))
return -EINVAL;
/* CDSA0 */
writel((uint32_t)src, &dma4_cfg->chan[chan].cssa);
writel((uint32_t)dst, &dma4_cfg->chan[chan].cdsa);
writel(sze, &dma4_cfg->chan[chan].cen);
return 0;
}
/* Start the DMA transfer */
int omap3_dma_start_transfer(uint32_t chan)
{
uint32_t val;
if (check_channel(chan))
return -EINVAL;
val = readl(&dma4_cfg->chan[chan].ccr);
/* Test for channel already in use */
if (val & CCR_ENABLE_ENABLE)
return -EBUSY;
writel((val | CCR_ENABLE_ENABLE), &dma4_cfg->chan[chan].ccr);
dma_active |= (1 << chan);
debug("started transfer...\n");
return 0;
}
/* Busy-waiting for a DMA transfer
* This has to be called before another transfer is started
* PARAMETER
* chan: Channel to wait for
*
* RETURN of non-zero means error*/
int omap3_dma_wait_for_transfer(uint32_t chan)
{
uint32_t val;
if (!(dma_active & (1 << chan))) {
val = readl(&dma4_cfg->irqstatus_l[0]);
if (!(val & chan)) {
debug("dma: The channel you are trying to wait for "
"was never activated - ERROR\n");
return -1; /* channel was never active */
}
}
/* all irqs on line 0 */
while (!(readl(&dma4_cfg->irqstatus_l[0]) & (1 << chan)))
asm("nop");
val = readl(&dma4_cfg->chan[chan].csr);
if ((val & CSR_TRANS_ERR) | (val & CSR_SUPERVISOR_ERR) |
(val & CSR_MISALIGNED_ADRS_ERR)) {
debug("err code: %X\n", val);
debug("dma: transfer error detected\n");
reset_irq(chan);
return -1;
}
reset_irq(chan);
return 0;
}
/* Get the revision of the DMA module
* PARAMETER
* minor: Address of minor revision to write
* major: Address of major revision to write
*
* RETURN of non-zero means error
*/
int omap3_dma_get_revision(uint32_t *minor, uint32_t *major)
{
uint32_t val;
/* debug information */
val = readl(&dma4_cfg->revision);
*major = (val & 0x000000F0) >> 4;
*minor = (val & 0x0000000F);
debug("DMA Silicon revision (maj/min): 0x%X/0x%X\n", *major, *minor);
return 0;
}
/* Initial config of omap dma
*/
void omap3_dma_init(void)
{
dma_active = 0;
/* All interrupts on channel 0 */
writel(0xFFFFFFFF, &dma4_cfg->irqenable_l[0]);
}
/* set channel config to config
*
* RETURN of non-zero means error */
int omap3_dma_conf_chan(uint32_t chan, struct dma4_chan *config)
{
if (check_channel(chan))
return -EINVAL;
dma4_cfg->chan[chan] = *config;
return 0;
}
/* get channel config to config
*
* RETURN of non-zero means error */
int omap3_dma_get_conf_chan(uint32_t chan, struct dma4_chan *config)
{
if (check_channel(chan))
return -EINVAL;
*config = dma4_cfg->chan[chan];
return 0;
}

View file

@ -0,0 +1,266 @@
/*
* (C) Copyright 2003
* Steven Scholz, imc Measurement & Control, steven.scholz@imc-berlin.de
*
* (C) Copyright 2002
* Rich Ireland, Enterasys Networks, rireland@enterasys.com.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h> /* core U-Boot definitions */
#include <ACEX1K.h> /* ACEX device family */
/* Define FPGA_DEBUG to get debug printf's */
#ifdef FPGA_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
/* Note: The assumption is that we cannot possibly run fast enough to
* overrun the device (the Slave Parallel mode can free run at 50MHz).
* If there is a need to operate slower, define CONFIG_FPGA_DELAY in
* the board config file to slow things down.
*/
#ifndef CONFIG_FPGA_DELAY
#define CONFIG_FPGA_DELAY()
#endif
#ifndef CONFIG_SYS_FPGA_WAIT
#define CONFIG_SYS_FPGA_WAIT CONFIG_SYS_HZ/10 /* 100 ms */
#endif
static int ACEX1K_ps_load(Altera_desc *desc, const void *buf, size_t bsize);
static int ACEX1K_ps_dump(Altera_desc *desc, const void *buf, size_t bsize);
/* static int ACEX1K_ps_info(Altera_desc *desc); */
/* ------------------------------------------------------------------------- */
/* ACEX1K Generic Implementation */
int ACEX1K_load(Altera_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case passive_serial:
PRINTF ("%s: Launching Passive Serial Loader\n", __FUNCTION__);
ret_val = ACEX1K_ps_load (desc, buf, bsize);
break;
/* Add new interface types here */
default:
printf ("%s: Unsupported interface type, %d\n",
__FUNCTION__, desc->iface);
}
return ret_val;
}
int ACEX1K_dump(Altera_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case passive_serial:
PRINTF ("%s: Launching Passive Serial Dump\n", __FUNCTION__);
ret_val = ACEX1K_ps_dump (desc, buf, bsize);
break;
/* Add new interface types here */
default:
printf ("%s: Unsupported interface type, %d\n",
__FUNCTION__, desc->iface);
}
return ret_val;
}
int ACEX1K_info( Altera_desc *desc )
{
return FPGA_SUCCESS;
}
/* ------------------------------------------------------------------------- */
/* ACEX1K Passive Serial Generic Implementation */
static int ACEX1K_ps_load(Altera_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume the worst */
Altera_ACEX1K_Passive_Serial_fns *fn = desc->iface_fns;
int i;
PRINTF ("%s: start with interface functions @ 0x%p\n",
__FUNCTION__, fn);
if (fn) {
size_t bytecount = 0;
unsigned char *data = (unsigned char *) buf;
int cookie = desc->cookie; /* make a local copy */
unsigned long ts; /* timestamp */
PRINTF ("%s: Function Table:\n"
"ptr:\t0x%p\n"
"struct: 0x%p\n"
"config:\t0x%p\n"
"status:\t0x%p\n"
"clk:\t0x%p\n"
"data:\t0x%p\n"
"done:\t0x%p\n\n",
__FUNCTION__, &fn, fn, fn->config, fn->status,
fn->clk, fn->data, fn->done);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
printf ("Loading FPGA Device %d...", cookie);
#endif
/*
* Run the pre configuration function if there is one.
*/
if (*fn->pre) {
(*fn->pre) (cookie);
}
/* Establish the initial state */
(*fn->config) (TRUE, TRUE, cookie); /* Assert nCONFIG */
udelay(2); /* T_cfg > 2us */
/* nSTATUS should be asserted now */
(*fn->done) (cookie);
if ( !(*fn->status) (cookie) ) {
puts ("** nSTATUS is not asserted.\n");
(*fn->abort) (cookie);
return FPGA_FAIL;
}
(*fn->config) (FALSE, TRUE, cookie); /* Deassert nCONFIG */
udelay(2); /* T_cf2st1 < 4us */
/* Wait for nSTATUS to be released (i.e. deasserted) */
ts = get_timer (0); /* get current time */
do {
CONFIG_FPGA_DELAY ();
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for STATUS to go high.\n");
(*fn->abort) (cookie);
return FPGA_FAIL;
}
(*fn->done) (cookie);
} while ((*fn->status) (cookie));
/* Get ready for the burn */
CONFIG_FPGA_DELAY ();
/* Load the data */
while (bytecount < bsize) {
unsigned char val=0;
#ifdef CONFIG_SYS_FPGA_CHECK_CTRLC
if (ctrlc ()) {
(*fn->abort) (cookie);
return FPGA_FAIL;
}
#endif
/* Altera detects an error if INIT goes low (active)
while DONE is low (inactive) */
#if 0 /* not yet implemented */
if ((*fn->done) (cookie) == 0 && (*fn->init) (cookie)) {
puts ("** CRC error during FPGA load.\n");
(*fn->abort) (cookie);
return (FPGA_FAIL);
}
#endif
val = data [bytecount ++ ];
i = 8;
do {
/* Deassert the clock */
(*fn->clk) (FALSE, TRUE, cookie);
CONFIG_FPGA_DELAY ();
/* Write data */
(*fn->data) ( (val & 0x01), TRUE, cookie);
CONFIG_FPGA_DELAY ();
/* Assert the clock */
(*fn->clk) (TRUE, TRUE, cookie);
CONFIG_FPGA_DELAY ();
val >>= 1;
i --;
} while (i > 0);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (bytecount % (bsize / 40) == 0)
putc ('.'); /* let them know we are alive */
#endif
}
CONFIG_FPGA_DELAY ();
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
putc (' '); /* terminate the dotted line */
#endif
/*
* Checking FPGA's CONF_DONE signal - correctly booted ?
*/
if ( ! (*fn->done) (cookie) ) {
puts ("** Booting failed! CONF_DONE is still deasserted.\n");
(*fn->abort) (cookie);
return (FPGA_FAIL);
}
/*
* "DCLK must be clocked an additional 10 times fpr ACEX 1K..."
*/
for (i = 0; i < 12; i++) {
CONFIG_FPGA_DELAY ();
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
CONFIG_FPGA_DELAY ();
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
}
ret_val = FPGA_SUCCESS;
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (ret_val == FPGA_SUCCESS) {
puts ("Done.\n");
}
else {
puts ("Fail.\n");
}
#endif
(*fn->post) (cookie);
} else {
printf ("%s: NULL Interface function table!\n", __FUNCTION__);
}
return ret_val;
}
static int ACEX1K_ps_dump(Altera_desc *desc, const void *buf, size_t bsize)
{
/* Readback is only available through the Slave Parallel and */
/* boundary-scan interfaces. */
printf ("%s: Passive Serial Dumping is unavailable\n",
__FUNCTION__);
return FPGA_FAIL;
}

View file

@ -0,0 +1,59 @@
#
# (C) Copyright 2008
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# 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 $(TOPDIR)/config.mk
LIB := $(obj)libfpga.o
ifdef CONFIG_FPGA
COBJS-y += fpga.o
COBJS-$(CONFIG_FPGA_SPARTAN2) += spartan2.o
COBJS-$(CONFIG_FPGA_SPARTAN3) += spartan3.o
COBJS-$(CONFIG_FPGA_VIRTEX2) += virtex2.o
COBJS-$(CONFIG_FPGA_XILINX) += xilinx.o
COBJS-$(CONFIG_FPGA_LATTICE) += ivm_core.o lattice.o
ifdef CONFIG_FPGA_ALTERA
COBJS-y += altera.o
COBJS-$(CONFIG_FPGA_ACEX1K) += ACEX1K.o
COBJS-$(CONFIG_FPGA_CYCLON2) += cyclon2.o
COBJS-$(CONFIG_FPGA_STRATIX_II) += stratixII.o
endif
endif
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,244 @@
/*
* (C) Copyright 2003
* Steven Scholz, imc Measurement & Control, steven.scholz@imc-berlin.de
*
* (C) Copyright 2002
* Rich Ireland, Enterasys Networks, rireland@enterasys.com.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
*/
/*
* Altera FPGA support
*/
#include <common.h>
#include <ACEX1K.h>
#include <stratixII.h>
/* Define FPGA_DEBUG to get debug printf's */
/* #define FPGA_DEBUG */
#ifdef FPGA_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
/* Local Static Functions */
static int altera_validate (Altera_desc * desc, const char *fn);
/* ------------------------------------------------------------------------- */
int altera_load(Altera_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume a failure */
if (!altera_validate (desc, (char *)__FUNCTION__)) {
printf ("%s: Invalid device descriptor\n", __FUNCTION__);
} else {
switch (desc->family) {
case Altera_ACEX1K:
case Altera_CYC2:
#if defined(CONFIG_FPGA_ACEX1K)
PRINTF ("%s: Launching the ACEX1K Loader...\n",
__FUNCTION__);
ret_val = ACEX1K_load (desc, buf, bsize);
#elif defined(CONFIG_FPGA_CYCLON2)
PRINTF ("%s: Launching the CYCLONE II Loader...\n",
__FUNCTION__);
ret_val = CYC2_load (desc, buf, bsize);
#else
printf ("%s: No support for ACEX1K devices.\n",
__FUNCTION__);
#endif
break;
#if defined(CONFIG_FPGA_STRATIX_II)
case Altera_StratixII:
PRINTF ("%s: Launching the Stratix II Loader...\n",
__FUNCTION__);
ret_val = StratixII_load (desc, buf, bsize);
break;
#endif
default:
printf ("%s: Unsupported family type, %d\n",
__FUNCTION__, desc->family);
}
}
return ret_val;
}
int altera_dump(Altera_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume a failure */
if (!altera_validate (desc, (char *)__FUNCTION__)) {
printf ("%s: Invalid device descriptor\n", __FUNCTION__);
} else {
switch (desc->family) {
case Altera_ACEX1K:
#if defined(CONFIG_FPGA_ACEX)
PRINTF ("%s: Launching the ACEX1K Reader...\n",
__FUNCTION__);
ret_val = ACEX1K_dump (desc, buf, bsize);
#else
printf ("%s: No support for ACEX1K devices.\n",
__FUNCTION__);
#endif
break;
#if defined(CONFIG_FPGA_STRATIX_II)
case Altera_StratixII:
PRINTF ("%s: Launching the Stratix II Reader...\n",
__FUNCTION__);
ret_val = StratixII_dump (desc, buf, bsize);
break;
#endif
default:
printf ("%s: Unsupported family type, %d\n",
__FUNCTION__, desc->family);
}
}
return ret_val;
}
int altera_info( Altera_desc *desc )
{
int ret_val = FPGA_FAIL;
if (altera_validate (desc, (char *)__FUNCTION__)) {
printf ("Family: \t");
switch (desc->family) {
case Altera_ACEX1K:
printf ("ACEX1K\n");
break;
case Altera_CYC2:
printf ("CYCLON II\n");
break;
case Altera_StratixII:
printf ("Stratix II\n");
break;
/* Add new family types here */
default:
printf ("Unknown family type, %d\n", desc->family);
}
printf ("Interface type:\t");
switch (desc->iface) {
case passive_serial:
printf ("Passive Serial (PS)\n");
break;
case passive_parallel_synchronous:
printf ("Passive Parallel Synchronous (PPS)\n");
break;
case passive_parallel_asynchronous:
printf ("Passive Parallel Asynchronous (PPA)\n");
break;
case passive_serial_asynchronous:
printf ("Passive Serial Asynchronous (PSA)\n");
break;
case altera_jtag_mode: /* Not used */
printf ("JTAG Mode\n");
break;
case fast_passive_parallel:
printf ("Fast Passive Parallel (FPP)\n");
break;
case fast_passive_parallel_security:
printf
("Fast Passive Parallel with Security (FPPS) \n");
break;
/* Add new interface types here */
default:
printf ("Unsupported interface type, %d\n", desc->iface);
}
printf ("Device Size: \t%d bytes\n"
"Cookie: \t0x%x (%d)\n",
desc->size, desc->cookie, desc->cookie);
if (desc->iface_fns) {
printf ("Device Function Table @ 0x%p\n", desc->iface_fns);
switch (desc->family) {
case Altera_ACEX1K:
case Altera_CYC2:
#if defined(CONFIG_FPGA_ACEX1K)
ACEX1K_info (desc);
#elif defined(CONFIG_FPGA_CYCLON2)
CYC2_info (desc);
#else
/* just in case */
printf ("%s: No support for ACEX1K devices.\n",
__FUNCTION__);
#endif
break;
#if defined(CONFIG_FPGA_STRATIX_II)
case Altera_StratixII:
StratixII_info (desc);
break;
#endif
/* Add new family types here */
default:
/* we don't need a message here - we give one up above */
break;
}
} else {
printf ("No Device Function Table.\n");
}
ret_val = FPGA_SUCCESS;
} else {
printf ("%s: Invalid device descriptor\n", __FUNCTION__);
}
return ret_val;
}
/* ------------------------------------------------------------------------- */
static int altera_validate (Altera_desc * desc, const char *fn)
{
int ret_val = FALSE;
if (desc) {
if ((desc->family > min_altera_type) &&
(desc->family < max_altera_type)) {
if ((desc->iface > min_altera_iface_type) &&
(desc->iface < max_altera_iface_type)) {
if (desc->size) {
ret_val = TRUE;
} else {
printf ("%s: NULL part size\n", fn);
}
} else {
printf ("%s: Invalid Interface type, %d\n",
fn, desc->iface);
}
} else {
printf ("%s: Invalid family type, %d\n", fn, desc->family);
}
} else {
printf ("%s: NULL descriptor!\n", fn);
}
return ret_val;
}
/* ------------------------------------------------------------------------- */

View file

@ -0,0 +1,220 @@
/*
* (C) Copyright 2006
* Heiko Schocher, hs@denx.de
* Based on ACE1XK.c
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h> /* core U-Boot definitions */
#include <altera.h>
#include <ACEX1K.h> /* ACEX device family */
/* Define FPGA_DEBUG to get debug printf's */
#ifdef FPGA_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
/* Note: The assumption is that we cannot possibly run fast enough to
* overrun the device (the Slave Parallel mode can free run at 50MHz).
* If there is a need to operate slower, define CONFIG_FPGA_DELAY in
* the board config file to slow things down.
*/
#ifndef CONFIG_FPGA_DELAY
#define CONFIG_FPGA_DELAY()
#endif
#ifndef CONFIG_SYS_FPGA_WAIT
#define CONFIG_SYS_FPGA_WAIT CONFIG_SYS_HZ/10 /* 100 ms */
#endif
static int CYC2_ps_load(Altera_desc *desc, const void *buf, size_t bsize);
static int CYC2_ps_dump(Altera_desc *desc, const void *buf, size_t bsize);
/* static int CYC2_ps_info( Altera_desc *desc ); */
/* ------------------------------------------------------------------------- */
/* CYCLON2 Generic Implementation */
int CYC2_load(Altera_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case passive_serial:
PRINTF ("%s: Launching Passive Serial Loader\n", __FUNCTION__);
ret_val = CYC2_ps_load (desc, buf, bsize);
break;
case fast_passive_parallel:
/* Fast Passive Parallel (FPP) and PS only differ in what is
* done in the write() callback. Use the existing PS load
* function for FPP, too.
*/
PRINTF ("%s: Launching Fast Passive Parallel Loader\n",
__FUNCTION__);
ret_val = CYC2_ps_load(desc, buf, bsize);
break;
/* Add new interface types here */
default:
printf ("%s: Unsupported interface type, %d\n",
__FUNCTION__, desc->iface);
}
return ret_val;
}
int CYC2_dump(Altera_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case passive_serial:
PRINTF ("%s: Launching Passive Serial Dump\n", __FUNCTION__);
ret_val = CYC2_ps_dump (desc, buf, bsize);
break;
/* Add new interface types here */
default:
printf ("%s: Unsupported interface type, %d\n",
__FUNCTION__, desc->iface);
}
return ret_val;
}
int CYC2_info( Altera_desc *desc )
{
return FPGA_SUCCESS;
}
/* ------------------------------------------------------------------------- */
/* CYCLON2 Passive Serial Generic Implementation */
static int CYC2_ps_load(Altera_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume the worst */
Altera_CYC2_Passive_Serial_fns *fn = desc->iface_fns;
int ret = 0;
PRINTF ("%s: start with interface functions @ 0x%p\n",
__FUNCTION__, fn);
if (fn) {
int cookie = desc->cookie; /* make a local copy */
unsigned long ts; /* timestamp */
PRINTF ("%s: Function Table:\n"
"ptr:\t0x%p\n"
"struct: 0x%p\n"
"config:\t0x%p\n"
"status:\t0x%p\n"
"write:\t0x%p\n"
"done:\t0x%p\n\n",
__FUNCTION__, &fn, fn, fn->config, fn->status,
fn->write, fn->done);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
printf ("Loading FPGA Device %d...", cookie);
#endif
/*
* Run the pre configuration function if there is one.
*/
if (*fn->pre) {
(*fn->pre) (cookie);
}
/* Establish the initial state */
(*fn->config) (TRUE, TRUE, cookie); /* Assert nCONFIG */
udelay(2); /* T_cfg > 2us */
/* Wait for nSTATUS to be asserted */
ts = get_timer (0); /* get current time */
do {
CONFIG_FPGA_DELAY ();
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for STATUS to go high.\n");
(*fn->abort) (cookie);
return FPGA_FAIL;
}
} while (!(*fn->status) (cookie));
/* Get ready for the burn */
CONFIG_FPGA_DELAY ();
ret = (*fn->write) (buf, bsize, TRUE, cookie);
if (ret) {
puts ("** Write failed.\n");
(*fn->abort) (cookie);
return FPGA_FAIL;
}
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
puts(" OK? ...");
#endif
CONFIG_FPGA_DELAY ();
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
putc (' '); /* terminate the dotted line */
#endif
/*
* Checking FPGA's CONF_DONE signal - correctly booted ?
*/
if ( ! (*fn->done) (cookie) ) {
puts ("** Booting failed! CONF_DONE is still deasserted.\n");
(*fn->abort) (cookie);
return (FPGA_FAIL);
}
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
puts(" OK\n");
#endif
ret_val = FPGA_SUCCESS;
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (ret_val == FPGA_SUCCESS) {
puts ("Done.\n");
}
else {
puts ("Fail.\n");
}
#endif
(*fn->post) (cookie);
} else {
printf ("%s: NULL Interface function table!\n", __FUNCTION__);
}
return ret_val;
}
static int CYC2_ps_dump(Altera_desc *desc, const void *buf, size_t bsize)
{
/* Readback is only available through the Slave Parallel and */
/* boundary-scan interfaces. */
printf ("%s: Passive Serial Dumping is unavailable\n",
__FUNCTION__);
return FPGA_FAIL;
}

View file

@ -0,0 +1,315 @@
/*
* (C) Copyright 2002
* Rich Ireland, Enterasys Networks, rireland@enterasys.com.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
*/
/*
* Generic FPGA support
*/
#include <common.h> /* core U-Boot definitions */
#include <xilinx.h> /* xilinx specific definitions */
#include <altera.h> /* altera specific definitions */
#include <lattice.h>
#if 0
#define FPGA_DEBUG /* define FPGA_DEBUG to get debug messages */
#endif
/* Local definitions */
#ifndef CONFIG_MAX_FPGA_DEVICES
#define CONFIG_MAX_FPGA_DEVICES 5
#endif
/* Enable/Disable debug console messages */
#ifdef FPGA_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
/* Local static data */
static int next_desc = FPGA_INVALID_DEVICE;
static fpga_desc desc_table[CONFIG_MAX_FPGA_DEVICES];
/* Local static functions */
static __attribute__((__const__)) fpga_desc * __attribute__((__const__)) fpga_get_desc( int devnum );
static __attribute__((__const__)) fpga_desc * __attribute__((__const__)) fpga_validate(int devnum, const void *buf,
size_t bsize, char *fn );
static int fpga_dev_info( int devnum );
/* ------------------------------------------------------------------------- */
/* fpga_no_sup
* 'no support' message function
*/
static void fpga_no_sup( char *fn, char *msg )
{
if ( fn && msg ) {
printf( "%s: No support for %s.\n", fn, msg);
} else if ( msg ) {
printf( "No support for %s.\n", msg);
} else {
printf( "No FPGA suport!\n");
}
}
/* fpga_get_desc
* map a device number to a descriptor
*/
static __attribute__((__const__)) fpga_desc * __attribute__((__const__)) fpga_get_desc( int devnum )
{
fpga_desc *desc = (fpga_desc * )NULL;
if (( devnum >= 0 ) && (devnum < next_desc )) {
desc = &desc_table[devnum];
PRINTF( "%s: found fpga descriptor #%d @ 0x%p\n",
__FUNCTION__, devnum, desc );
}
return desc;
}
/* fpga_validate
* generic parameter checking code
*/
static __attribute__((__const__)) fpga_desc * __attribute__((__const__)) fpga_validate(int devnum, const void *buf,
size_t bsize, char *fn )
{
fpga_desc * desc = fpga_get_desc( devnum );
if ( !desc ) {
printf( "%s: Invalid device number %d\n", fn, devnum );
}
if ( !buf ) {
printf( "%s: Null buffer.\n", fn );
return (fpga_desc * const)NULL;
}
return desc;
}
/* fpga_dev_info
* generic multiplexing code
*/
static int fpga_dev_info( int devnum )
{
int ret_val = FPGA_FAIL; /* assume failure */
const fpga_desc * const desc = fpga_get_desc( devnum );
if ( desc ) {
PRINTF( "%s: Device Descriptor @ 0x%p\n",
__FUNCTION__, desc->devdesc );
switch ( desc->devtype ) {
case fpga_xilinx:
#if defined(CONFIG_FPGA_XILINX)
printf( "Xilinx Device\nDescriptor @ 0x%p\n", desc );
ret_val = xilinx_info( desc->devdesc );
#else
fpga_no_sup( (char *)__FUNCTION__, "Xilinx devices" );
#endif
break;
case fpga_altera:
#if defined(CONFIG_FPGA_ALTERA)
printf( "Altera Device\nDescriptor @ 0x%p\n", desc );
ret_val = altera_info( desc->devdesc );
#else
fpga_no_sup( (char *)__FUNCTION__, "Altera devices" );
#endif
break;
case fpga_lattice:
#if defined(CONFIG_FPGA_LATTICE)
printf("Lattice Device\nDescriptor @ 0x%p\n", desc);
ret_val = lattice_info(desc->devdesc);
#else
fpga_no_sup( (char *)__FUNCTION__, "Lattice devices" );
#endif
break;
default:
printf( "%s: Invalid or unsupported device type %d\n",
__FUNCTION__, desc->devtype );
}
} else {
printf( "%s: Invalid device number %d\n",
__FUNCTION__, devnum );
}
return ret_val;
}
/* ------------------------------------------------------------------------- */
/* fgpa_init is usually called from misc_init_r() and MUST be called
* before any of the other fpga functions are used.
*/
void fpga_init(void)
{
next_desc = 0;
memset( desc_table, 0, sizeof(desc_table));
PRINTF( "%s: CONFIG_FPGA = 0x%x\n", __FUNCTION__, CONFIG_FPGA );
}
/* fpga_count
* Basic interface function to get the current number of devices available.
*/
int fpga_count( void )
{
return next_desc;
}
/* fpga_add
* Add the device descriptor to the device table.
*/
int fpga_add( fpga_type devtype, void *desc )
{
int devnum = FPGA_INVALID_DEVICE;
if ( next_desc < 0 ) {
printf( "%s: FPGA support not initialized!\n", __FUNCTION__ );
} else if (( devtype > fpga_min_type ) && ( devtype < fpga_undefined )) {
if ( desc ) {
if ( next_desc < CONFIG_MAX_FPGA_DEVICES ) {
devnum = next_desc;
desc_table[next_desc].devtype = devtype;
desc_table[next_desc++].devdesc = desc;
} else {
printf( "%s: Exceeded Max FPGA device count\n", __FUNCTION__ );
}
} else {
printf( "%s: NULL device descriptor\n", __FUNCTION__ );
}
} else {
printf( "%s: Unsupported FPGA type %d\n", __FUNCTION__, devtype );
}
return devnum;
}
/*
* Generic multiplexing code
*/
int fpga_load(int devnum, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume failure */
fpga_desc * desc = fpga_validate( devnum, buf, bsize, (char *)__FUNCTION__ );
if ( desc ) {
switch ( desc->devtype ) {
case fpga_xilinx:
#if defined(CONFIG_FPGA_XILINX)
ret_val = xilinx_load( desc->devdesc, buf, bsize );
#else
fpga_no_sup( (char *)__FUNCTION__, "Xilinx devices" );
#endif
break;
case fpga_altera:
#if defined(CONFIG_FPGA_ALTERA)
ret_val = altera_load( desc->devdesc, buf, bsize );
#else
fpga_no_sup( (char *)__FUNCTION__, "Altera devices" );
#endif
break;
case fpga_lattice:
#if defined(CONFIG_FPGA_LATTICE)
ret_val = lattice_load(desc->devdesc, buf, bsize);
#else
fpga_no_sup( (char *)__FUNCTION__, "Lattice devices" );
#endif
break;
default:
printf( "%s: Invalid or unsupported device type %d\n",
__FUNCTION__, desc->devtype );
}
}
return ret_val;
}
/* fpga_dump
* generic multiplexing code
*/
int fpga_dump(int devnum, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume failure */
fpga_desc * desc = fpga_validate( devnum, buf, bsize, (char *)__FUNCTION__ );
if ( desc ) {
switch ( desc->devtype ) {
case fpga_xilinx:
#if defined(CONFIG_FPGA_XILINX)
ret_val = xilinx_dump( desc->devdesc, buf, bsize );
#else
fpga_no_sup( (char *)__FUNCTION__, "Xilinx devices" );
#endif
break;
case fpga_altera:
#if defined(CONFIG_FPGA_ALTERA)
ret_val = altera_dump( desc->devdesc, buf, bsize );
#else
fpga_no_sup( (char *)__FUNCTION__, "Altera devices" );
#endif
break;
case fpga_lattice:
#if defined(CONFIG_FPGA_LATTICE)
ret_val = lattice_dump(desc->devdesc, buf, bsize);
#else
fpga_no_sup( (char *)__FUNCTION__, "Lattice devices" );
#endif
break;
default:
printf( "%s: Invalid or unsupported device type %d\n",
__FUNCTION__, desc->devtype );
}
}
return ret_val;
}
/* fpga_info
* front end to fpga_dev_info. If devnum is invalid, report on all
* available devices.
*/
int fpga_info( int devnum )
{
if ( devnum == FPGA_INVALID_DEVICE ) {
if ( next_desc > 0 ) {
int dev;
for ( dev = 0; dev < next_desc; dev++ ) {
fpga_dev_info( dev );
}
return FPGA_SUCCESS;
} else {
printf( "%s: No FPGA devices available.\n", __FUNCTION__ );
return FPGA_FAIL;
}
}
else return fpga_dev_info( devnum );
}
/* ------------------------------------------------------------------------- */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,397 @@
/*
* (C) Copyright 2010
* Stefano Babic, DENX Software Engineering, sbabic@denx.de.
*
* (C) Copyright 2002
* Rich Ireland, Enterasys Networks, rireland@enterasys.com.
*
* ispVM functions adapted from Lattice's ispmVMEmbedded code:
* Copyright 2009 Lattice Semiconductor Corp.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h>
#include <malloc.h>
#include <fpga.h>
#include <lattice.h>
static lattice_board_specific_func *pfns;
static const char *fpga_image;
static unsigned long read_bytes;
static unsigned long bufsize;
static unsigned short expectedCRC;
/*
* External variables and functions declared in ivm_core.c module.
*/
extern unsigned short g_usCalculatedCRC;
extern unsigned short g_usDataType;
extern unsigned char *g_pucIntelBuffer;
extern unsigned char *g_pucHeapMemory;
extern unsigned short g_iHeapCounter;
extern unsigned short g_iHEAPSize;
extern unsigned short g_usIntelDataIndex;
extern unsigned short g_usIntelBufferSize;
extern char *const g_szSupportedVersions[];
/*
* ispVMDelay
*
* Users must implement a delay to observe a_usTimeDelay, where
* bit 15 of the a_usTimeDelay defines the unit.
* 1 = milliseconds
* 0 = microseconds
* Example:
* a_usTimeDelay = 0x0001 = 1 microsecond delay.
* a_usTimeDelay = 0x8001 = 1 millisecond delay.
*
* This subroutine is called upon to provide a delay from 1 millisecond to a few
* hundreds milliseconds each time.
* It is understood that due to a_usTimeDelay is defined as unsigned short, a 16
* bits integer, this function is restricted to produce a delay to 64000
* micro-seconds or 32000 milli-second maximum. The VME file will never pass on
* to this function a delay time > those maximum number. If it needs more than
* those maximum, the VME file will launch the delay function several times to
* realize a larger delay time cummulatively.
* It is perfectly alright to provide a longer delay than required. It is not
* acceptable if the delay is shorter.
*/
void ispVMDelay(unsigned short delay)
{
if (delay & 0x8000)
delay = (delay & ~0x8000) * 1000;
udelay(delay);
}
void writePort(unsigned char a_ucPins, unsigned char a_ucValue)
{
a_ucValue = a_ucValue ? 1 : 0;
switch (a_ucPins) {
case g_ucPinTDI:
pfns->jtag_set_tdi(a_ucValue);
break;
case g_ucPinTCK:
pfns->jtag_set_tck(a_ucValue);
break;
case g_ucPinTMS:
pfns->jtag_set_tms(a_ucValue);
break;
default:
printf("%s: requested unknown pin\n", __func__);
}
}
unsigned char readPort(void)
{
return pfns->jtag_get_tdo();
}
void sclock(void)
{
writePort(g_ucPinTCK, 0x01);
writePort(g_ucPinTCK, 0x00);
}
void calibration(void)
{
/* Apply 2 pulses to TCK. */
writePort(g_ucPinTCK, 0x00);
writePort(g_ucPinTCK, 0x01);
writePort(g_ucPinTCK, 0x00);
writePort(g_ucPinTCK, 0x01);
writePort(g_ucPinTCK, 0x00);
ispVMDelay(0x8001);
/* Apply 2 pulses to TCK. */
writePort(g_ucPinTCK, 0x01);
writePort(g_ucPinTCK, 0x00);
writePort(g_ucPinTCK, 0x01);
writePort(g_ucPinTCK, 0x00);
}
/*
* GetByte
*
* Returns a byte to the caller. The returned byte depends on the
* g_usDataType register. If the HEAP_IN bit is set, then the byte
* is returned from the HEAP. If the LHEAP_IN bit is set, then
* the byte is returned from the intelligent buffer. Otherwise,
* the byte is returned directly from the VME file.
*/
unsigned char GetByte(void)
{
unsigned char ucData;
unsigned int block_size = 4 * 1024;
if (g_usDataType & HEAP_IN) {
/*
* Get data from repeat buffer.
*/
if (g_iHeapCounter > g_iHEAPSize) {
/*
* Data over-run.
*/
return 0xFF;
}
ucData = g_pucHeapMemory[g_iHeapCounter++];
} else if (g_usDataType & LHEAP_IN) {
/*
* Get data from intel buffer.
*/
if (g_usIntelDataIndex >= g_usIntelBufferSize) {
return 0xFF;
}
ucData = g_pucIntelBuffer[g_usIntelDataIndex++];
} else {
if (read_bytes == bufsize) {
return 0xFF;
}
ucData = *fpga_image++;
read_bytes++;
if (!(read_bytes % block_size)) {
printf("Downloading FPGA %ld/%ld completed\r",
read_bytes,
bufsize);
}
if (expectedCRC != 0) {
ispVMCalculateCRC32(ucData);
}
}
return ucData;
}
signed char ispVM(void)
{
char szFileVersion[9] = { 0 };
signed char cRetCode = 0;
signed char cIndex = 0;
signed char cVersionIndex = 0;
unsigned char ucReadByte = 0;
unsigned short crc;
g_pucHeapMemory = NULL;
g_iHeapCounter = 0;
g_iHEAPSize = 0;
g_usIntelDataIndex = 0;
g_usIntelBufferSize = 0;
g_usCalculatedCRC = 0;
expectedCRC = 0;
ucReadByte = GetByte();
switch (ucReadByte) {
case FILE_CRC:
crc = (unsigned char)GetByte();
crc <<= 8;
crc |= GetByte();
expectedCRC = crc;
for (cIndex = 0; cIndex < 8; cIndex++)
szFileVersion[cIndex] = GetByte();
break;
default:
szFileVersion[0] = (signed char) ucReadByte;
for (cIndex = 1; cIndex < 8; cIndex++)
szFileVersion[cIndex] = GetByte();
break;
}
/*
*
* Compare the VME file version against the supported version.
*
*/
for (cVersionIndex = 0; g_szSupportedVersions[cVersionIndex] != 0;
cVersionIndex++) {
for (cIndex = 0; cIndex < 8; cIndex++) {
if (szFileVersion[cIndex] !=
g_szSupportedVersions[cVersionIndex][cIndex]) {
cRetCode = VME_VERSION_FAILURE;
break;
}
cRetCode = 0;
}
if (cRetCode == 0) {
break;
}
}
if (cRetCode < 0) {
return VME_VERSION_FAILURE;
}
printf("VME file checked: starting downloading to FPGA\n");
ispVMStart();
cRetCode = ispVMCode();
ispVMEnd();
ispVMFreeMem();
puts("\n");
if (cRetCode == 0 && expectedCRC != 0 &&
(expectedCRC != g_usCalculatedCRC)) {
printf("Expected CRC: 0x%.4X\n", expectedCRC);
printf("Calculated CRC: 0x%.4X\n", g_usCalculatedCRC);
return VME_CRC_FAILURE;
}
return cRetCode;
}
static int lattice_validate(Lattice_desc *desc, const char *fn)
{
int ret_val = FALSE;
if (desc) {
if ((desc->family > min_lattice_type) &&
(desc->family < max_lattice_type)) {
if ((desc->iface > min_lattice_iface_type) &&
(desc->iface < max_lattice_iface_type)) {
if (desc->size) {
ret_val = TRUE;
} else {
printf("%s: NULL part size\n", fn);
}
} else {
printf("%s: Invalid Interface type, %d\n",
fn, desc->iface);
}
} else {
printf("%s: Invalid family type, %d\n",
fn, desc->family);
}
} else {
printf("%s: NULL descriptor!\n", fn);
}
return ret_val;
}
int lattice_load(Lattice_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
if (!lattice_validate(desc, (char *)__func__)) {
printf("%s: Invalid device descriptor\n", __func__);
} else {
pfns = desc->iface_fns;
switch (desc->family) {
case Lattice_XP2:
fpga_image = buf;
read_bytes = 0;
bufsize = bsize;
debug("%s: Launching the Lattice ISPVME Loader:"
" addr %p size 0x%lx...\n",
__func__, fpga_image, bufsize);
ret_val = ispVM();
if (ret_val)
printf("%s: error %d downloading FPGA image\n",
__func__, ret_val);
else
puts("FPGA downloaded successfully\n");
break;
default:
printf("%s: Unsupported family type, %d\n",
__func__, desc->family);
}
}
return ret_val;
}
int lattice_dump(Lattice_desc *desc, const void *buf, size_t bsize)
{
puts("Dump not supported for Lattice FPGA\n");
return FPGA_FAIL;
}
int lattice_info(Lattice_desc *desc)
{
int ret_val = FPGA_FAIL;
if (lattice_validate(desc, (char *)__func__)) {
printf("Family: \t");
switch (desc->family) {
case Lattice_XP2:
puts("XP2\n");
break;
/* Add new family types here */
default:
printf("Unknown family type, %d\n", desc->family);
}
puts("Interface type:\t");
switch (desc->iface) {
case lattice_jtag_mode:
puts("JTAG Mode\n");
break;
/* Add new interface types here */
default:
printf("Unsupported interface type, %d\n", desc->iface);
}
printf("Device Size: \t%d bytes\n",
desc->size);
if (desc->iface_fns) {
printf("Device Function Table @ 0x%p\n",
desc->iface_fns);
switch (desc->family) {
case Lattice_XP2:
break;
/* Add new family types here */
default:
break;
}
} else {
puts("No Device Function Table.\n");
}
if (desc->desc)
printf("Model: \t%s\n", desc->desc);
ret_val = FPGA_SUCCESS;
} else {
printf("%s: Invalid device descriptor\n", __func__);
}
return ret_val;
}

View file

@ -0,0 +1,466 @@
/*
* (C) Copyright 2002
* Rich Ireland, Enterasys Networks, rireland@enterasys.com.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h> /* core U-Boot definitions */
#include <spartan2.h> /* Spartan-II device family */
/* Define FPGA_DEBUG to get debug printf's */
#ifdef FPGA_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
#undef CONFIG_SYS_FPGA_CHECK_BUSY
#undef CONFIG_SYS_FPGA_PROG_FEEDBACK
/* Note: The assumption is that we cannot possibly run fast enough to
* overrun the device (the Slave Parallel mode can free run at 50MHz).
* If there is a need to operate slower, define CONFIG_FPGA_DELAY in
* the board config file to slow things down.
*/
#ifndef CONFIG_FPGA_DELAY
#define CONFIG_FPGA_DELAY()
#endif
#ifndef CONFIG_SYS_FPGA_WAIT
#define CONFIG_SYS_FPGA_WAIT CONFIG_SYS_HZ/100 /* 10 ms */
#endif
static int Spartan2_sp_load(Xilinx_desc *desc, const void *buf, size_t bsize);
static int Spartan2_sp_dump(Xilinx_desc *desc, const void *buf, size_t bsize);
/* static int Spartan2_sp_info(Xilinx_desc *desc ); */
static int Spartan2_ss_load(Xilinx_desc *desc, const void *buf, size_t bsize);
static int Spartan2_ss_dump(Xilinx_desc *desc, const void *buf, size_t bsize);
/* static int Spartan2_ss_info(Xilinx_desc *desc ); */
/* ------------------------------------------------------------------------- */
/* Spartan-II Generic Implementation */
int Spartan2_load(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case slave_serial:
PRINTF ("%s: Launching Slave Serial Load\n", __FUNCTION__);
ret_val = Spartan2_ss_load (desc, buf, bsize);
break;
case slave_parallel:
PRINTF ("%s: Launching Slave Parallel Load\n", __FUNCTION__);
ret_val = Spartan2_sp_load (desc, buf, bsize);
break;
default:
printf ("%s: Unsupported interface type, %d\n",
__FUNCTION__, desc->iface);
}
return ret_val;
}
int Spartan2_dump(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case slave_serial:
PRINTF ("%s: Launching Slave Serial Dump\n", __FUNCTION__);
ret_val = Spartan2_ss_dump (desc, buf, bsize);
break;
case slave_parallel:
PRINTF ("%s: Launching Slave Parallel Dump\n", __FUNCTION__);
ret_val = Spartan2_sp_dump (desc, buf, bsize);
break;
default:
printf ("%s: Unsupported interface type, %d\n",
__FUNCTION__, desc->iface);
}
return ret_val;
}
int Spartan2_info( Xilinx_desc *desc )
{
return FPGA_SUCCESS;
}
/* ------------------------------------------------------------------------- */
/* Spartan-II Slave Parallel Generic Implementation */
static int Spartan2_sp_load(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume the worst */
Xilinx_Spartan2_Slave_Parallel_fns *fn = desc->iface_fns;
PRINTF ("%s: start with interface functions @ 0x%p\n",
__FUNCTION__, fn);
if (fn) {
size_t bytecount = 0;
unsigned char *data = (unsigned char *) buf;
int cookie = desc->cookie; /* make a local copy */
unsigned long ts; /* timestamp */
PRINTF ("%s: Function Table:\n"
"ptr:\t0x%p\n"
"struct: 0x%p\n"
"pre: 0x%p\n"
"pgm:\t0x%p\n"
"init:\t0x%p\n"
"err:\t0x%p\n"
"clk:\t0x%p\n"
"cs:\t0x%p\n"
"wr:\t0x%p\n"
"read data:\t0x%p\n"
"write data:\t0x%p\n"
"busy:\t0x%p\n"
"abort:\t0x%p\n",
"post:\t0x%p\n\n",
__FUNCTION__, &fn, fn, fn->pre, fn->pgm, fn->init, fn->err,
fn->clk, fn->cs, fn->wr, fn->rdata, fn->wdata, fn->busy,
fn->abort, fn->post);
/*
* This code is designed to emulate the "Express Style"
* Continuous Data Loading in Slave Parallel Mode for
* the Spartan-II Family.
*/
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
printf ("Loading FPGA Device %d...\n", cookie);
#endif
/*
* Run the pre configuration function if there is one.
*/
if (*fn->pre) {
(*fn->pre) (cookie);
}
/* Establish the initial state */
(*fn->pgm) (TRUE, TRUE, cookie); /* Assert the program, commit */
/* Get ready for the burn */
CONFIG_FPGA_DELAY ();
(*fn->pgm) (FALSE, TRUE, cookie); /* Deassert the program, commit */
ts = get_timer (0); /* get current time */
/* Now wait for INIT and BUSY to go high */
do {
CONFIG_FPGA_DELAY ();
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for INIT to clear.\n");
(*fn->abort) (cookie); /* abort the burn */
return FPGA_FAIL;
}
} while ((*fn->init) (cookie) && (*fn->busy) (cookie));
(*fn->wr) (TRUE, TRUE, cookie); /* Assert write, commit */
(*fn->cs) (TRUE, TRUE, cookie); /* Assert chip select, commit */
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
/* Load the data */
while (bytecount < bsize) {
/* XXX - do we check for an Ctrl-C press in here ??? */
/* XXX - Check the error bit? */
(*fn->wdata) (data[bytecount++], TRUE, cookie); /* write the data */
CONFIG_FPGA_DELAY ();
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
CONFIG_FPGA_DELAY ();
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
#ifdef CONFIG_SYS_FPGA_CHECK_BUSY
ts = get_timer (0); /* get current time */
while ((*fn->busy) (cookie)) {
/* XXX - we should have a check in here somewhere to
* make sure we aren't busy forever... */
CONFIG_FPGA_DELAY ();
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
CONFIG_FPGA_DELAY ();
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for BUSY to clear.\n");
(*fn->abort) (cookie); /* abort the burn */
return FPGA_FAIL;
}
}
#endif
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (bytecount % (bsize / 40) == 0)
putc ('.'); /* let them know we are alive */
#endif
}
CONFIG_FPGA_DELAY ();
(*fn->cs) (FALSE, TRUE, cookie); /* Deassert the chip select */
(*fn->wr) (FALSE, TRUE, cookie); /* Deassert the write pin */
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
putc ('\n'); /* terminate the dotted line */
#endif
/* now check for done signal */
ts = get_timer (0); /* get current time */
ret_val = FPGA_SUCCESS;
while ((*fn->done) (cookie) == FPGA_FAIL) {
CONFIG_FPGA_DELAY ();
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
CONFIG_FPGA_DELAY ();
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for DONE to clear.\n");
(*fn->abort) (cookie); /* abort the burn */
ret_val = FPGA_FAIL;
break;
}
}
/*
* Run the post configuration function if there is one.
*/
if (*fn->post)
(*fn->post) (cookie);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (ret_val == FPGA_SUCCESS)
puts ("Done.\n");
else
puts ("Fail.\n");
#endif
} else {
printf ("%s: NULL Interface function table!\n", __FUNCTION__);
}
return ret_val;
}
static int Spartan2_sp_dump(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume the worst */
Xilinx_Spartan2_Slave_Parallel_fns *fn = desc->iface_fns;
if (fn) {
unsigned char *data = (unsigned char *) buf;
size_t bytecount = 0;
int cookie = desc->cookie; /* make a local copy */
printf ("Starting Dump of FPGA Device %d...\n", cookie);
(*fn->cs) (TRUE, TRUE, cookie); /* Assert chip select, commit */
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
/* dump the data */
while (bytecount < bsize) {
/* XXX - do we check for an Ctrl-C press in here ??? */
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
(*fn->rdata) (&(data[bytecount++]), cookie); /* read the data */
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (bytecount % (bsize / 40) == 0)
putc ('.'); /* let them know we are alive */
#endif
}
(*fn->cs) (FALSE, FALSE, cookie); /* Deassert the chip select */
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
putc ('\n'); /* terminate the dotted line */
#endif
puts ("Done.\n");
/* XXX - checksum the data? */
} else {
printf ("%s: NULL Interface function table!\n", __FUNCTION__);
}
return ret_val;
}
/* ------------------------------------------------------------------------- */
static int Spartan2_ss_load(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume the worst */
Xilinx_Spartan2_Slave_Serial_fns *fn = desc->iface_fns;
int i;
unsigned char val;
PRINTF ("%s: start with interface functions @ 0x%p\n",
__FUNCTION__, fn);
if (fn) {
size_t bytecount = 0;
unsigned char *data = (unsigned char *) buf;
int cookie = desc->cookie; /* make a local copy */
unsigned long ts; /* timestamp */
PRINTF ("%s: Function Table:\n"
"ptr:\t0x%p\n"
"struct: 0x%p\n"
"pgm:\t0x%p\n"
"init:\t0x%p\n"
"clk:\t0x%p\n"
"wr:\t0x%p\n"
"done:\t0x%p\n\n",
__FUNCTION__, &fn, fn, fn->pgm, fn->init,
fn->clk, fn->wr, fn->done);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
printf ("Loading FPGA Device %d...\n", cookie);
#endif
/*
* Run the pre configuration function if there is one.
*/
if (*fn->pre) {
(*fn->pre) (cookie);
}
/* Establish the initial state */
(*fn->pgm) (TRUE, TRUE, cookie); /* Assert the program, commit */
/* Wait for INIT state (init low) */
ts = get_timer (0); /* get current time */
do {
CONFIG_FPGA_DELAY ();
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for INIT to start.\n");
return FPGA_FAIL;
}
} while (!(*fn->init) (cookie));
/* Get ready for the burn */
CONFIG_FPGA_DELAY ();
(*fn->pgm) (FALSE, TRUE, cookie); /* Deassert the program, commit */
ts = get_timer (0); /* get current time */
/* Now wait for INIT to go high */
do {
CONFIG_FPGA_DELAY ();
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for INIT to clear.\n");
return FPGA_FAIL;
}
} while ((*fn->init) (cookie));
/* Load the data */
while (bytecount < bsize) {
/* Xilinx detects an error if INIT goes low (active)
while DONE is low (inactive) */
if ((*fn->done) (cookie) == 0 && (*fn->init) (cookie)) {
puts ("** CRC error during FPGA load.\n");
return (FPGA_FAIL);
}
val = data [bytecount ++];
i = 8;
do {
/* Deassert the clock */
(*fn->clk) (FALSE, TRUE, cookie);
CONFIG_FPGA_DELAY ();
/* Write data */
(*fn->wr) ((val & 0x80), TRUE, cookie);
CONFIG_FPGA_DELAY ();
/* Assert the clock */
(*fn->clk) (TRUE, TRUE, cookie);
CONFIG_FPGA_DELAY ();
val <<= 1;
i --;
} while (i > 0);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (bytecount % (bsize / 40) == 0)
putc ('.'); /* let them know we are alive */
#endif
}
CONFIG_FPGA_DELAY ();
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
putc ('\n'); /* terminate the dotted line */
#endif
/* now check for done signal */
ts = get_timer (0); /* get current time */
ret_val = FPGA_SUCCESS;
(*fn->wr) (TRUE, TRUE, cookie);
while (! (*fn->done) (cookie)) {
CONFIG_FPGA_DELAY ();
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
CONFIG_FPGA_DELAY ();
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
putc ('*');
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for DONE to clear.\n");
ret_val = FPGA_FAIL;
break;
}
}
putc ('\n'); /* terminate the dotted line */
/*
* Run the post configuration function if there is one.
*/
if (*fn->post)
(*fn->post) (cookie);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (ret_val == FPGA_SUCCESS)
puts ("Done.\n");
else
puts ("Fail.\n");
#endif
} else {
printf ("%s: NULL Interface function table!\n", __FUNCTION__);
}
return ret_val;
}
static int Spartan2_ss_dump(Xilinx_desc *desc, const void *buf, size_t bsize)
{
/* Readback is only available through the Slave Parallel and */
/* boundary-scan interfaces. */
printf ("%s: Slave Serial Dumping is unavailable\n",
__FUNCTION__);
return FPGA_FAIL;
}

View file

@ -0,0 +1,484 @@
/*
* (C) Copyright 2002
* Rich Ireland, Enterasys Networks, rireland@enterasys.com.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
*/
/*
* Configuration support for Xilinx Spartan3 devices. Based
* on spartan2.c (Rich Ireland, rireland@enterasys.com).
*/
#include <common.h> /* core U-Boot definitions */
#include <spartan3.h> /* Spartan-II device family */
/* Define FPGA_DEBUG to get debug printf's */
#ifdef FPGA_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
#undef CONFIG_SYS_FPGA_CHECK_BUSY
/* Note: The assumption is that we cannot possibly run fast enough to
* overrun the device (the Slave Parallel mode can free run at 50MHz).
* If there is a need to operate slower, define CONFIG_FPGA_DELAY in
* the board config file to slow things down.
*/
#ifndef CONFIG_FPGA_DELAY
#define CONFIG_FPGA_DELAY()
#endif
#ifndef CONFIG_SYS_FPGA_WAIT
#define CONFIG_SYS_FPGA_WAIT CONFIG_SYS_HZ/100 /* 10 ms */
#endif
static int Spartan3_sp_load(Xilinx_desc *desc, const void *buf, size_t bsize);
static int Spartan3_sp_dump(Xilinx_desc *desc, const void *buf, size_t bsize);
/* static int Spartan3_sp_info(Xilinx_desc *desc ); */
static int Spartan3_ss_load(Xilinx_desc *desc, const void *buf, size_t bsize);
static int Spartan3_ss_dump(Xilinx_desc *desc, const void *buf, size_t bsize);
/* static int Spartan3_ss_info(Xilinx_desc *desc); */
/* ------------------------------------------------------------------------- */
/* Spartan-II Generic Implementation */
int Spartan3_load(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case slave_serial:
PRINTF ("%s: Launching Slave Serial Load\n", __FUNCTION__);
ret_val = Spartan3_ss_load (desc, buf, bsize);
break;
case slave_parallel:
PRINTF ("%s: Launching Slave Parallel Load\n", __FUNCTION__);
ret_val = Spartan3_sp_load (desc, buf, bsize);
break;
default:
printf ("%s: Unsupported interface type, %d\n",
__FUNCTION__, desc->iface);
}
return ret_val;
}
int Spartan3_dump(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case slave_serial:
PRINTF ("%s: Launching Slave Serial Dump\n", __FUNCTION__);
ret_val = Spartan3_ss_dump (desc, buf, bsize);
break;
case slave_parallel:
PRINTF ("%s: Launching Slave Parallel Dump\n", __FUNCTION__);
ret_val = Spartan3_sp_dump (desc, buf, bsize);
break;
default:
printf ("%s: Unsupported interface type, %d\n",
__FUNCTION__, desc->iface);
}
return ret_val;
}
int Spartan3_info( Xilinx_desc *desc )
{
return FPGA_SUCCESS;
}
/* ------------------------------------------------------------------------- */
/* Spartan-II Slave Parallel Generic Implementation */
static int Spartan3_sp_load(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume the worst */
Xilinx_Spartan3_Slave_Parallel_fns *fn = desc->iface_fns;
PRINTF ("%s: start with interface functions @ 0x%p\n",
__FUNCTION__, fn);
if (fn) {
size_t bytecount = 0;
unsigned char *data = (unsigned char *) buf;
int cookie = desc->cookie; /* make a local copy */
unsigned long ts; /* timestamp */
PRINTF ("%s: Function Table:\n"
"ptr:\t0x%p\n"
"struct: 0x%p\n"
"pre: 0x%p\n"
"pgm:\t0x%p\n"
"init:\t0x%p\n"
"err:\t0x%p\n"
"clk:\t0x%p\n"
"cs:\t0x%p\n"
"wr:\t0x%p\n"
"read data:\t0x%p\n"
"write data:\t0x%p\n"
"busy:\t0x%p\n"
"abort:\t0x%p\n",
"post:\t0x%p\n\n",
__FUNCTION__, &fn, fn, fn->pre, fn->pgm, fn->init, fn->err,
fn->clk, fn->cs, fn->wr, fn->rdata, fn->wdata, fn->busy,
fn->abort, fn->post);
/*
* This code is designed to emulate the "Express Style"
* Continuous Data Loading in Slave Parallel Mode for
* the Spartan-II Family.
*/
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
printf ("Loading FPGA Device %d...\n", cookie);
#endif
/*
* Run the pre configuration function if there is one.
*/
if (*fn->pre) {
(*fn->pre) (cookie);
}
/* Establish the initial state */
(*fn->pgm) (TRUE, TRUE, cookie); /* Assert the program, commit */
/* Get ready for the burn */
CONFIG_FPGA_DELAY ();
(*fn->pgm) (FALSE, TRUE, cookie); /* Deassert the program, commit */
ts = get_timer (0); /* get current time */
/* Now wait for INIT and BUSY to go high */
do {
CONFIG_FPGA_DELAY ();
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for INIT to clear.\n");
(*fn->abort) (cookie); /* abort the burn */
return FPGA_FAIL;
}
} while ((*fn->init) (cookie) && (*fn->busy) (cookie));
(*fn->wr) (TRUE, TRUE, cookie); /* Assert write, commit */
(*fn->cs) (TRUE, TRUE, cookie); /* Assert chip select, commit */
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
/* Load the data */
while (bytecount < bsize) {
/* XXX - do we check for an Ctrl-C press in here ??? */
/* XXX - Check the error bit? */
(*fn->wdata) (data[bytecount++], TRUE, cookie); /* write the data */
CONFIG_FPGA_DELAY ();
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
CONFIG_FPGA_DELAY ();
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
#ifdef CONFIG_SYS_FPGA_CHECK_BUSY
ts = get_timer (0); /* get current time */
while ((*fn->busy) (cookie)) {
/* XXX - we should have a check in here somewhere to
* make sure we aren't busy forever... */
CONFIG_FPGA_DELAY ();
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
CONFIG_FPGA_DELAY ();
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for BUSY to clear.\n");
(*fn->abort) (cookie); /* abort the burn */
return FPGA_FAIL;
}
}
#endif
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (bytecount % (bsize / 40) == 0)
putc ('.'); /* let them know we are alive */
#endif
}
CONFIG_FPGA_DELAY ();
(*fn->cs) (FALSE, TRUE, cookie); /* Deassert the chip select */
(*fn->wr) (FALSE, TRUE, cookie); /* Deassert the write pin */
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
putc ('\n'); /* terminate the dotted line */
#endif
/* now check for done signal */
ts = get_timer (0); /* get current time */
ret_val = FPGA_SUCCESS;
while ((*fn->done) (cookie) == FPGA_FAIL) {
/* XXX - we should have a check in here somewhere to
* make sure we aren't busy forever... */
CONFIG_FPGA_DELAY ();
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
CONFIG_FPGA_DELAY ();
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for DONE to clear.\n");
(*fn->abort) (cookie); /* abort the burn */
ret_val = FPGA_FAIL;
break;
}
}
/*
* Run the post configuration function if there is one.
*/
if (*fn->post)
(*fn->post) (cookie);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (ret_val == FPGA_SUCCESS)
puts ("Done.\n");
else
puts ("Fail.\n");
#endif
} else {
printf ("%s: NULL Interface function table!\n", __FUNCTION__);
}
return ret_val;
}
static int Spartan3_sp_dump(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume the worst */
Xilinx_Spartan3_Slave_Parallel_fns *fn = desc->iface_fns;
if (fn) {
unsigned char *data = (unsigned char *) buf;
size_t bytecount = 0;
int cookie = desc->cookie; /* make a local copy */
printf ("Starting Dump of FPGA Device %d...\n", cookie);
(*fn->cs) (TRUE, TRUE, cookie); /* Assert chip select, commit */
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
/* dump the data */
while (bytecount < bsize) {
/* XXX - do we check for an Ctrl-C press in here ??? */
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
(*fn->rdata) (&(data[bytecount++]), cookie); /* read the data */
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (bytecount % (bsize / 40) == 0)
putc ('.'); /* let them know we are alive */
#endif
}
(*fn->cs) (FALSE, FALSE, cookie); /* Deassert the chip select */
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
putc ('\n'); /* terminate the dotted line */
#endif
puts ("Done.\n");
/* XXX - checksum the data? */
} else {
printf ("%s: NULL Interface function table!\n", __FUNCTION__);
}
return ret_val;
}
/* ------------------------------------------------------------------------- */
static int Spartan3_ss_load(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume the worst */
Xilinx_Spartan3_Slave_Serial_fns *fn = desc->iface_fns;
int i;
unsigned char val;
PRINTF ("%s: start with interface functions @ 0x%p\n",
__FUNCTION__, fn);
if (fn) {
size_t bytecount = 0;
unsigned char *data = (unsigned char *) buf;
int cookie = desc->cookie; /* make a local copy */
unsigned long ts; /* timestamp */
PRINTF ("%s: Function Table:\n"
"ptr:\t0x%p\n"
"struct: 0x%p\n"
"pgm:\t0x%p\n"
"init:\t0x%p\n"
"clk:\t0x%p\n"
"wr:\t0x%p\n"
"done:\t0x%p\n\n",
__FUNCTION__, &fn, fn, fn->pgm, fn->init,
fn->clk, fn->wr, fn->done);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
printf ("Loading FPGA Device %d...\n", cookie);
#endif
/*
* Run the pre configuration function if there is one.
*/
if (*fn->pre) {
(*fn->pre) (cookie);
}
/* Establish the initial state */
(*fn->pgm) (TRUE, TRUE, cookie); /* Assert the program, commit */
/* Wait for INIT state (init low) */
ts = get_timer (0); /* get current time */
do {
CONFIG_FPGA_DELAY ();
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for INIT to start.\n");
if (*fn->abort)
(*fn->abort) (cookie);
return FPGA_FAIL;
}
} while (!(*fn->init) (cookie));
/* Get ready for the burn */
CONFIG_FPGA_DELAY ();
(*fn->pgm) (FALSE, TRUE, cookie); /* Deassert the program, commit */
ts = get_timer (0); /* get current time */
/* Now wait for INIT to go high */
do {
CONFIG_FPGA_DELAY ();
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for INIT to clear.\n");
if (*fn->abort)
(*fn->abort) (cookie);
return FPGA_FAIL;
}
} while ((*fn->init) (cookie));
/* Load the data */
if(*fn->bwr)
(*fn->bwr) (data, bsize, TRUE, cookie);
else {
while (bytecount < bsize) {
/* Xilinx detects an error if INIT goes low (active)
while DONE is low (inactive) */
if ((*fn->done) (cookie) == 0 && (*fn->init) (cookie)) {
puts ("** CRC error during FPGA load.\n");
if (*fn->abort)
(*fn->abort) (cookie);
return (FPGA_FAIL);
}
val = data [bytecount ++];
i = 8;
do {
/* Deassert the clock */
(*fn->clk) (FALSE, TRUE, cookie);
CONFIG_FPGA_DELAY ();
/* Write data */
(*fn->wr) ((val & 0x80), TRUE, cookie);
CONFIG_FPGA_DELAY ();
/* Assert the clock */
(*fn->clk) (TRUE, TRUE, cookie);
CONFIG_FPGA_DELAY ();
val <<= 1;
i --;
} while (i > 0);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (bytecount % (bsize / 40) == 0)
putc ('.'); /* let them know we are alive */
#endif
}
}
CONFIG_FPGA_DELAY ();
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
putc ('\n'); /* terminate the dotted line */
#endif
/* now check for done signal */
ts = get_timer (0); /* get current time */
ret_val = FPGA_SUCCESS;
(*fn->wr) (TRUE, TRUE, cookie);
while (! (*fn->done) (cookie)) {
/* XXX - we should have a check in here somewhere to
* make sure we aren't busy forever... */
CONFIG_FPGA_DELAY ();
(*fn->clk) (FALSE, TRUE, cookie); /* Deassert the clock pin */
CONFIG_FPGA_DELAY ();
(*fn->clk) (TRUE, TRUE, cookie); /* Assert the clock pin */
putc ('*');
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT) { /* check the time */
puts ("** Timeout waiting for DONE to clear.\n");
ret_val = FPGA_FAIL;
break;
}
}
putc ('\n'); /* terminate the dotted line */
/*
* Run the post configuration function if there is one.
*/
if (*fn->post)
(*fn->post) (cookie);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (ret_val == FPGA_SUCCESS)
puts ("Done.\n");
else
puts ("Fail.\n");
#endif
} else {
printf ("%s: NULL Interface function table!\n", __FUNCTION__);
}
return ret_val;
}
static int Spartan3_ss_dump(Xilinx_desc *desc, const void *buf, size_t bsize)
{
/* Readback is only available through the Slave Parallel and */
/* boundary-scan interfaces. */
printf ("%s: Slave Serial Dumping is unavailable\n",
__FUNCTION__);
return FPGA_FAIL;
}

View file

@ -0,0 +1,207 @@
/*
* (C) Copyright 2007
* Eran Liberty, Extricom , eran.liberty@gmail.com
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h> /* core U-Boot definitions */
#include <altera.h>
int StratixII_ps_fpp_load (Altera_desc * desc, void *buf, size_t bsize,
int isSerial, int isSecure);
int StratixII_ps_fpp_dump (Altera_desc * desc, void *buf, size_t bsize);
/****************************************************************/
/* Stratix II Generic Implementation */
int StratixII_load (Altera_desc * desc, void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case passive_serial:
ret_val = StratixII_ps_fpp_load (desc, buf, bsize, 1, 0);
break;
case fast_passive_parallel:
ret_val = StratixII_ps_fpp_load (desc, buf, bsize, 0, 0);
break;
case fast_passive_parallel_security:
ret_val = StratixII_ps_fpp_load (desc, buf, bsize, 0, 1);
break;
/* Add new interface types here */
default:
printf ("%s: Unsupported interface type, %d\n", __FUNCTION__,
desc->iface);
}
return ret_val;
}
int StratixII_dump (Altera_desc * desc, void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case passive_serial:
case fast_passive_parallel:
case fast_passive_parallel_security:
ret_val = StratixII_ps_fpp_dump (desc, buf, bsize);
break;
/* Add new interface types here */
default:
printf ("%s: Unsupported interface type, %d\n", __FUNCTION__,
desc->iface);
}
return ret_val;
}
int StratixII_info (Altera_desc * desc)
{
return FPGA_SUCCESS;
}
int StratixII_ps_fpp_dump (Altera_desc * desc, void *buf, size_t bsize)
{
printf ("Stratix II Fast Passive Parallel dump is not implemented\n");
return FPGA_FAIL;
}
int StratixII_ps_fpp_load (Altera_desc * desc, void *buf, size_t bsize,
int isSerial, int isSecure)
{
altera_board_specific_func *fns;
int cookie;
int ret_val = FPGA_FAIL;
int bytecount;
char *buff = buf;
int i;
if (!desc) {
printf ("%s(%d) Altera_desc missing\n", __FUNCTION__, __LINE__);
return FPGA_FAIL;
}
if (!buff) {
printf ("%s(%d) buffer is missing\n", __FUNCTION__, __LINE__);
return FPGA_FAIL;
}
if (!bsize) {
printf ("%s(%d) size is zero\n", __FUNCTION__, __LINE__);
return FPGA_FAIL;
}
if (!desc->iface_fns) {
printf
("%s(%d) Altera_desc function interface table is missing\n",
__FUNCTION__, __LINE__);
return FPGA_FAIL;
}
fns = (altera_board_specific_func *) (desc->iface_fns);
cookie = desc->cookie;
if (!
(fns->config && fns->status && fns->done && fns->data
&& fns->abort)) {
printf
("%s(%d) Missing some function in the function interface table\n",
__FUNCTION__, __LINE__);
return FPGA_FAIL;
}
/* 1. give board specific a chance to do anything before we start */
if (fns->pre) {
if ((ret_val = fns->pre (cookie)) < 0) {
return ret_val;
}
}
/* from this point on we must fail gracfully by calling lower layer abort */
/* 2. Strat burn cycle by deasserting config for t_CFG and waiting t_CF2CK after reaserted */
fns->config (0, 1, cookie);
udelay (5); /* nCONFIG low pulse width 2usec */
fns->config (1, 1, cookie);
udelay (100); /* nCONFIG high to first rising edge on DCLK */
/* 3. Start the Data cycle with clk deasserted */
bytecount = 0;
fns->clk (0, 1, cookie);
printf ("loading to fpga ");
while (bytecount < bsize) {
/* 3.1 check stratix has not signaled us an error */
if (fns->status (cookie) != 1) {
printf
("\n%s(%d) Stratix failed (byte transfered till failure 0x%x)\n",
__FUNCTION__, __LINE__, bytecount);
fns->abort (cookie);
return FPGA_FAIL;
}
if (isSerial) {
int i;
uint8_t data = buff[bytecount++];
for (i = 0; i < 8; i++) {
/* 3.2(ps) put data on the bus */
fns->data ((data >> i) & 1, 1, cookie);
/* 3.3(ps) clock once */
fns->clk (1, 1, cookie);
fns->clk (0, 1, cookie);
}
} else {
/* 3.2(fpp) put data on the bus */
fns->data (buff[bytecount++], 1, cookie);
/* 3.3(fpp) clock once */
fns->clk (1, 1, cookie);
fns->clk (0, 1, cookie);
/* 3.4(fpp) for secure cycle push 3 more clocks */
for (i = 0; isSecure && i < 3; i++) {
fns->clk (1, 1, cookie);
fns->clk (0, 1, cookie);
}
}
/* 3.5 while clk is deasserted it is safe to print some progress indication */
if ((bytecount % (bsize / 100)) == 0) {
printf ("\b\b\b%02d\%", bytecount * 100 / bsize);
}
}
/* 4. Set one last clock and check conf done signal */
fns->clk (1, 1, cookie);
udelay (100);
if (!fns->done (cookie)) {
printf (" error!.\n");
fns->abort (cookie);
return FPGA_FAIL;
} else {
printf ("\b\b\b done.\n");
}
/* 5. call lower layer post configuration */
if (fns->post) {
if ((ret_val = fns->post (cookie)) < 0) {
fns->abort (cookie);
return ret_val;
}
}
return FPGA_SUCCESS;
}

View file

@ -0,0 +1,436 @@
/*
* (C) Copyright 2002
* Rich Ireland, Enterasys Networks, rireland@enterasys.com.
* Keith Outwater, keith_outwater@mvis.com
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
*/
/*
* Configuration support for Xilinx Virtex2 devices. Based
* on spartan2.c (Rich Ireland, rireland@enterasys.com).
*/
#include <common.h>
#include <virtex2.h>
#if 0
#define FPGA_DEBUG
#endif
#ifdef FPGA_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
/*
* If the SelectMap interface can be overrun by the processor, define
* CONFIG_SYS_FPGA_CHECK_BUSY and/or CONFIG_FPGA_DELAY in the board configuration
* file and add board-specific support for checking BUSY status. By default,
* assume that the SelectMap interface cannot be overrun.
*/
#ifndef CONFIG_SYS_FPGA_CHECK_BUSY
#undef CONFIG_SYS_FPGA_CHECK_BUSY
#endif
#ifndef CONFIG_FPGA_DELAY
#define CONFIG_FPGA_DELAY()
#endif
#ifndef CONFIG_SYS_FPGA_PROG_FEEDBACK
#define CONFIG_SYS_FPGA_PROG_FEEDBACK
#endif
/*
* Don't allow config cycle to be interrupted
*/
#ifndef CONFIG_SYS_FPGA_CHECK_CTRLC
#undef CONFIG_SYS_FPGA_CHECK_CTRLC
#endif
/*
* Check for errors during configuration by default
*/
#ifndef CONFIG_SYS_FPGA_CHECK_ERROR
#define CONFIG_SYS_FPGA_CHECK_ERROR
#endif
/*
* The default timeout in mS for INIT_B to deassert after PROG_B has
* been deasserted. Per the latest Virtex II Handbook (page 347), the
* max time from PORG_B deassertion to INIT_B deassertion is 4uS per
* data frame for the XC2V8000. The XC2V8000 has 2860 data frames
* which yields 11.44 mS. So let's make it bigger in order to handle
* an XC2V1000, if anyone can ever get ahold of one.
*/
#ifndef CONFIG_SYS_FPGA_WAIT_INIT
#define CONFIG_SYS_FPGA_WAIT_INIT CONFIG_SYS_HZ/2 /* 500 ms */
#endif
/*
* The default timeout for waiting for BUSY to deassert during configuration.
* This is normally not necessary since for most reasonable configuration
* clock frequencies (i.e. 66 MHz or less), BUSY monitoring is unnecessary.
*/
#ifndef CONFIG_SYS_FPGA_WAIT_BUSY
#define CONFIG_SYS_FPGA_WAIT_BUSY CONFIG_SYS_HZ/200 /* 5 ms*/
#endif
/* Default timeout for waiting for FPGA to enter operational mode after
* configuration data has been written.
*/
#ifndef CONFIG_SYS_FPGA_WAIT_CONFIG
#define CONFIG_SYS_FPGA_WAIT_CONFIG CONFIG_SYS_HZ/5 /* 200 ms */
#endif
static int Virtex2_ssm_load(Xilinx_desc *desc, const void *buf, size_t bsize);
static int Virtex2_ssm_dump(Xilinx_desc *desc, const void *buf, size_t bsize);
static int Virtex2_ss_load(Xilinx_desc *desc, const void *buf, size_t bsize);
static int Virtex2_ss_dump(Xilinx_desc *desc, const void *buf, size_t bsize);
int Virtex2_load(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case slave_serial:
PRINTF ("%s: Launching Slave Serial Load\n", __FUNCTION__);
ret_val = Virtex2_ss_load (desc, buf, bsize);
break;
case slave_selectmap:
PRINTF ("%s: Launching Slave Parallel Load\n", __FUNCTION__);
ret_val = Virtex2_ssm_load (desc, buf, bsize);
break;
default:
printf ("%s: Unsupported interface type, %d\n",
__FUNCTION__, desc->iface);
}
return ret_val;
}
int Virtex2_dump(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
switch (desc->iface) {
case slave_serial:
PRINTF ("%s: Launching Slave Serial Dump\n", __FUNCTION__);
ret_val = Virtex2_ss_dump (desc, buf, bsize);
break;
case slave_parallel:
PRINTF ("%s: Launching Slave Parallel Dump\n", __FUNCTION__);
ret_val = Virtex2_ssm_dump (desc, buf, bsize);
break;
default:
printf ("%s: Unsupported interface type, %d\n",
__FUNCTION__, desc->iface);
}
return ret_val;
}
int Virtex2_info (Xilinx_desc * desc)
{
return FPGA_SUCCESS;
}
/*
* Virtex-II Slave SelectMap configuration loader. Configuration via
* SelectMap is as follows:
* 1. Set the FPGA's PROG_B line low.
* 2. Set the FPGA's PROG_B line high. Wait for INIT_B to go high.
* 3. Write data to the SelectMap port. If INIT_B goes low at any time
* this process, a configuration error (most likely CRC failure) has
* ocurred. At this point a status word may be read from the
* SelectMap interface to determine the source of the problem (You
* could, for instance, put this in your 'abort' function handler).
* 4. After all data has been written, test the state of the FPGA
* INIT_B and DONE lines. If both are high, configuration has
* succeeded. Congratulations!
*/
static int Virtex2_ssm_load(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
Xilinx_Virtex2_Slave_SelectMap_fns *fn = desc->iface_fns;
PRINTF ("%s:%d: Start with interface functions @ 0x%p\n",
__FUNCTION__, __LINE__, fn);
if (fn) {
size_t bytecount = 0;
unsigned char *data = (unsigned char *) buf;
int cookie = desc->cookie;
unsigned long ts;
/* Gotta split this one up (so the stack won't blow??) */
PRINTF ("%s:%d: Function Table:\n"
" base 0x%p\n"
" struct 0x%p\n"
" pre 0x%p\n"
" prog 0x%p\n"
" init 0x%p\n"
" error 0x%p\n",
__FUNCTION__, __LINE__,
&fn, fn, fn->pre, fn->pgm, fn->init, fn->err);
PRINTF (" clock 0x%p\n"
" cs 0x%p\n"
" write 0x%p\n"
" rdata 0x%p\n"
" wdata 0x%p\n"
" busy 0x%p\n"
" abort 0x%p\n"
" post 0x%p\n\n",
fn->clk, fn->cs, fn->wr, fn->rdata, fn->wdata,
fn->busy, fn->abort, fn->post);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
printf ("Initializing FPGA Device %d...\n", cookie);
#endif
/*
* Run the pre configuration function if there is one.
*/
if (*fn->pre) {
(*fn->pre) (cookie);
}
/*
* Assert the program line. The minimum pulse width for
* Virtex II devices is 300 nS (Tprogram parameter in datasheet).
* There is no maximum value for the pulse width. Check to make
* sure that INIT_B goes low after assertion of PROG_B
*/
(*fn->pgm) (TRUE, TRUE, cookie);
udelay (10);
ts = get_timer (0);
do {
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT_INIT) {
printf ("%s:%d: ** Timeout after %d ticks waiting for INIT"
" to assert.\n", __FUNCTION__, __LINE__,
CONFIG_SYS_FPGA_WAIT_INIT);
(*fn->abort) (cookie);
return FPGA_FAIL;
}
} while (!(*fn->init) (cookie));
(*fn->pgm) (FALSE, TRUE, cookie);
CONFIG_FPGA_DELAY ();
(*fn->clk) (TRUE, TRUE, cookie);
/*
* Start a timer and wait for INIT_B to go high
*/
ts = get_timer (0);
do {
CONFIG_FPGA_DELAY ();
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT_INIT) {
printf ("%s:%d: ** Timeout after %d ticks waiting for INIT"
" to deassert.\n", __FUNCTION__, __LINE__,
CONFIG_SYS_FPGA_WAIT_INIT);
(*fn->abort) (cookie);
return FPGA_FAIL;
}
} while ((*fn->init) (cookie) && (*fn->busy) (cookie));
(*fn->wr) (TRUE, TRUE, cookie);
(*fn->cs) (TRUE, TRUE, cookie);
udelay (10000);
/*
* Load the data byte by byte
*/
while (bytecount < bsize) {
#ifdef CONFIG_SYS_FPGA_CHECK_CTRLC
if (ctrlc ()) {
(*fn->abort) (cookie);
return FPGA_FAIL;
}
#endif
if ((*fn->done) (cookie) == FPGA_SUCCESS) {
PRINTF ("%s:%d:done went active early, bytecount = %d\n",
__FUNCTION__, __LINE__, bytecount);
break;
}
#ifdef CONFIG_SYS_FPGA_CHECK_ERROR
if ((*fn->init) (cookie)) {
printf ("\n%s:%d: ** Error: INIT asserted during"
" configuration\n", __FUNCTION__, __LINE__);
printf ("%d = buffer offset, %d = buffer size\n",
bytecount, bsize);
(*fn->abort) (cookie);
return FPGA_FAIL;
}
#endif
(*fn->wdata) (data[bytecount++], TRUE, cookie);
CONFIG_FPGA_DELAY ();
/*
* Cycle the clock pin
*/
(*fn->clk) (FALSE, TRUE, cookie);
CONFIG_FPGA_DELAY ();
(*fn->clk) (TRUE, TRUE, cookie);
#ifdef CONFIG_SYS_FPGA_CHECK_BUSY
ts = get_timer (0);
while ((*fn->busy) (cookie)) {
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT_BUSY) {
printf ("%s:%d: ** Timeout after %d ticks waiting for"
" BUSY to deassert\n",
__FUNCTION__, __LINE__, CONFIG_SYS_FPGA_WAIT_BUSY);
(*fn->abort) (cookie);
return FPGA_FAIL;
}
}
#endif
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (bytecount % (bsize / 40) == 0)
putc ('.');
#endif
}
/*
* Finished writing the data; deassert FPGA CS_B and WRITE_B signals.
*/
CONFIG_FPGA_DELAY ();
(*fn->cs) (FALSE, TRUE, cookie);
(*fn->wr) (FALSE, TRUE, cookie);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
putc ('\n');
#endif
/*
* Check for successful configuration. FPGA INIT_B and DONE should
* both be high upon successful configuration.
*/
ts = get_timer (0);
ret_val = FPGA_SUCCESS;
while (((*fn->done) (cookie) == FPGA_FAIL) || (*fn->init) (cookie)) {
if (get_timer (ts) > CONFIG_SYS_FPGA_WAIT_CONFIG) {
printf ("%s:%d: ** Timeout after %d ticks waiting for DONE to"
"assert and INIT to deassert\n",
__FUNCTION__, __LINE__, CONFIG_SYS_FPGA_WAIT_CONFIG);
(*fn->abort) (cookie);
ret_val = FPGA_FAIL;
break;
}
}
if (ret_val == FPGA_SUCCESS) {
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
printf ("Initialization of FPGA device %d complete\n", cookie);
#endif
/*
* Run the post configuration function if there is one.
*/
if (*fn->post) {
(*fn->post) (cookie);
}
} else {
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
printf ("** Initialization of FPGA device %d FAILED\n",
cookie);
#endif
}
} else {
printf ("%s:%d: NULL Interface function table!\n",
__FUNCTION__, __LINE__);
}
return ret_val;
}
/*
* Read the FPGA configuration data
*/
static int Virtex2_ssm_dump(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL;
Xilinx_Virtex2_Slave_SelectMap_fns *fn = desc->iface_fns;
if (fn) {
unsigned char *data = (unsigned char *) buf;
size_t bytecount = 0;
int cookie = desc->cookie;
printf ("Starting Dump of FPGA Device %d...\n", cookie);
(*fn->cs) (TRUE, TRUE, cookie);
(*fn->clk) (TRUE, TRUE, cookie);
while (bytecount < bsize) {
#ifdef CONFIG_SYS_FPGA_CHECK_CTRLC
if (ctrlc ()) {
(*fn->abort) (cookie);
return FPGA_FAIL;
}
#endif
/*
* Cycle the clock and read the data
*/
(*fn->clk) (FALSE, TRUE, cookie);
(*fn->clk) (TRUE, TRUE, cookie);
(*fn->rdata) (&(data[bytecount++]), cookie);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
if (bytecount % (bsize / 40) == 0)
putc ('.');
#endif
}
/*
* Deassert CS_B and cycle the clock to deselect the device.
*/
(*fn->cs) (FALSE, FALSE, cookie);
(*fn->clk) (FALSE, TRUE, cookie);
(*fn->clk) (TRUE, TRUE, cookie);
#ifdef CONFIG_SYS_FPGA_PROG_FEEDBACK
putc ('\n');
#endif
puts ("Done.\n");
} else {
printf ("%s:%d: NULL Interface function table!\n",
__FUNCTION__, __LINE__);
}
return ret_val;
}
static int Virtex2_ss_load(Xilinx_desc *desc, const void *buf, size_t bsize)
{
printf ("%s: Slave Serial Loading is unsupported\n", __FUNCTION__);
return FPGA_FAIL;
}
static int Virtex2_ss_dump(Xilinx_desc *desc, const void *buf, size_t bsize)
{
printf ("%s: Slave Serial Dumping is unsupported\n", __FUNCTION__);
return FPGA_FAIL;
}
/* vim: set ts=4 tw=78: */

View file

@ -0,0 +1,265 @@
/*
* (C) Copyright 2002
* Rich Ireland, Enterasys Networks, rireland@enterasys.com.
* Keith Outwater, keith_outwater@mvis.com
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
*/
/*
* Xilinx FPGA support
*/
#include <common.h>
#include <virtex2.h>
#include <spartan2.h>
#include <spartan3.h>
#if 0
#define FPGA_DEBUG
#endif
/* Define FPGA_DEBUG to get debug printf's */
#ifdef FPGA_DEBUG
#define PRINTF(fmt,args...) printf (fmt ,##args)
#else
#define PRINTF(fmt,args...)
#endif
/* Local Static Functions */
static int xilinx_validate (Xilinx_desc * desc, char *fn);
/* ------------------------------------------------------------------------- */
int xilinx_load(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume a failure */
if (!xilinx_validate (desc, (char *)__FUNCTION__)) {
printf ("%s: Invalid device descriptor\n", __FUNCTION__);
} else
switch (desc->family) {
case Xilinx_Spartan2:
#if defined(CONFIG_FPGA_SPARTAN2)
PRINTF ("%s: Launching the Spartan-II Loader...\n",
__FUNCTION__);
ret_val = Spartan2_load (desc, buf, bsize);
#else
printf ("%s: No support for Spartan-II devices.\n",
__FUNCTION__);
#endif
break;
case Xilinx_Spartan3:
#if defined(CONFIG_FPGA_SPARTAN3)
PRINTF ("%s: Launching the Spartan-III Loader...\n",
__FUNCTION__);
ret_val = Spartan3_load (desc, buf, bsize);
#else
printf ("%s: No support for Spartan-III devices.\n",
__FUNCTION__);
#endif
break;
case Xilinx_Virtex2:
#if defined(CONFIG_FPGA_VIRTEX2)
PRINTF ("%s: Launching the Virtex-II Loader...\n",
__FUNCTION__);
ret_val = Virtex2_load (desc, buf, bsize);
#else
printf ("%s: No support for Virtex-II devices.\n",
__FUNCTION__);
#endif
break;
default:
printf ("%s: Unsupported family type, %d\n",
__FUNCTION__, desc->family);
}
return ret_val;
}
int xilinx_dump(Xilinx_desc *desc, const void *buf, size_t bsize)
{
int ret_val = FPGA_FAIL; /* assume a failure */
if (!xilinx_validate (desc, (char *)__FUNCTION__)) {
printf ("%s: Invalid device descriptor\n", __FUNCTION__);
} else
switch (desc->family) {
case Xilinx_Spartan2:
#if defined(CONFIG_FPGA_SPARTAN2)
PRINTF ("%s: Launching the Spartan-II Reader...\n",
__FUNCTION__);
ret_val = Spartan2_dump (desc, buf, bsize);
#else
printf ("%s: No support for Spartan-II devices.\n",
__FUNCTION__);
#endif
break;
case Xilinx_Spartan3:
#if defined(CONFIG_FPGA_SPARTAN3)
PRINTF ("%s: Launching the Spartan-III Reader...\n",
__FUNCTION__);
ret_val = Spartan3_dump (desc, buf, bsize);
#else
printf ("%s: No support for Spartan-III devices.\n",
__FUNCTION__);
#endif
break;
case Xilinx_Virtex2:
#if defined( CONFIG_FPGA_VIRTEX2)
PRINTF ("%s: Launching the Virtex-II Reader...\n",
__FUNCTION__);
ret_val = Virtex2_dump (desc, buf, bsize);
#else
printf ("%s: No support for Virtex-II devices.\n",
__FUNCTION__);
#endif
break;
default:
printf ("%s: Unsupported family type, %d\n",
__FUNCTION__, desc->family);
}
return ret_val;
}
int xilinx_info (Xilinx_desc * desc)
{
int ret_val = FPGA_FAIL;
if (xilinx_validate (desc, (char *)__FUNCTION__)) {
printf ("Family: \t");
switch (desc->family) {
case Xilinx_Spartan2:
printf ("Spartan-II\n");
break;
case Xilinx_Spartan3:
printf ("Spartan-III\n");
break;
case Xilinx_Virtex2:
printf ("Virtex-II\n");
break;
/* Add new family types here */
default:
printf ("Unknown family type, %d\n", desc->family);
}
printf ("Interface type:\t");
switch (desc->iface) {
case slave_serial:
printf ("Slave Serial\n");
break;
case master_serial: /* Not used */
printf ("Master Serial\n");
break;
case slave_parallel:
printf ("Slave Parallel\n");
break;
case jtag_mode: /* Not used */
printf ("JTAG Mode\n");
break;
case slave_selectmap:
printf ("Slave SelectMap Mode\n");
break;
case master_selectmap:
printf ("Master SelectMap Mode\n");
break;
/* Add new interface types here */
default:
printf ("Unsupported interface type, %d\n", desc->iface);
}
printf ("Device Size: \t%d bytes\n"
"Cookie: \t0x%x (%d)\n",
desc->size, desc->cookie, desc->cookie);
if (desc->iface_fns) {
printf ("Device Function Table @ 0x%p\n", desc->iface_fns);
switch (desc->family) {
case Xilinx_Spartan2:
#if defined(CONFIG_FPGA_SPARTAN2)
Spartan2_info (desc);
#else
/* just in case */
printf ("%s: No support for Spartan-II devices.\n",
__FUNCTION__);
#endif
break;
case Xilinx_Spartan3:
#if defined(CONFIG_FPGA_SPARTAN3)
Spartan3_info (desc);
#else
/* just in case */
printf ("%s: No support for Spartan-III devices.\n",
__FUNCTION__);
#endif
break;
case Xilinx_Virtex2:
#if defined(CONFIG_FPGA_VIRTEX2)
Virtex2_info (desc);
#else
/* just in case */
printf ("%s: No support for Virtex-II devices.\n",
__FUNCTION__);
#endif
break;
/* Add new family types here */
default:
/* we don't need a message here - we give one up above */
;
}
} else
printf ("No Device Function Table.\n");
ret_val = FPGA_SUCCESS;
} else {
printf ("%s: Invalid device descriptor\n", __FUNCTION__);
}
return ret_val;
}
/* ------------------------------------------------------------------------- */
static int xilinx_validate (Xilinx_desc * desc, char *fn)
{
int ret_val = FALSE;
if (desc) {
if ((desc->family > min_xilinx_type) &&
(desc->family < max_xilinx_type)) {
if ((desc->iface > min_xilinx_iface_type) &&
(desc->iface < max_xilinx_iface_type)) {
if (desc->size) {
ret_val = TRUE;
} else
printf ("%s: NULL part size\n", fn);
} else
printf ("%s: Invalid Interface type, %d\n",
fn, desc->iface);
} else
printf ("%s: Invalid family type, %d\n", fn, desc->family);
} else
printf ("%s: NULL descriptor!\n", fn);
return ret_val;
}

View file

@ -0,0 +1,61 @@
#
# Copyright 2000-2008
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# 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 $(TOPDIR)/config.mk
LIB := $(obj)libgpio.o
COBJS-$(CONFIG_AT91_GPIO) += at91_gpio.o
COBJS-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o
COBJS-$(CONFIG_MARVELL_GPIO) += mvgpio.o
COBJS-$(CONFIG_MARVELL_MFP) += mvmfp.o
COBJS-$(CONFIG_MXC_GPIO) += mxc_gpio.o
COBJS-$(CONFIG_MXS_GPIO) += mxs_gpio.o
COBJS-$(CONFIG_PCA953X) += pca953x.o
COBJS-$(CONFIG_PCA9698) += pca9698.o
COBJS-$(CONFIG_S5P) += s5p_gpio.o
COBJS-$(CONFIG_SANDBOX_GPIO) += sandbox.o
COBJS-$(CONFIG_SPEAR_GPIO) += spear_gpio.o
COBJS-$(CONFIG_TEGRA_GPIO) += tegra_gpio.o
COBJS-$(CONFIG_DA8XX_GPIO) += da8xx_gpio.o
COBJS-$(CONFIG_ALTERA_PIO) += altera_pio.o
COBJS-$(CONFIG_MPC83XX_GPIO) += mpc83xx_gpio.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
########################################################################

View file

@ -0,0 +1,299 @@
/*
* Driver for Altera's PIO ip core
*
* Copyright (C) 2011 Missing Link Electronics
* Joachim Foerster <joachim@missinglinkelectronics.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* To use this driver, in your board's config. header:
* #define CONFIG_ALTERA_PIO
* #define CONFIG_SYS_ALTERA_PIO_NUM <number-of-pio-cores>
* #define CONFIG_SYS_ALTERA_PIO_GPIO_NUM <total-number-of-gpios>
* And in your board's early setup routine:
* altera_pio_init(<baseaddr>, <width>, 'i'|'o'|'t',
* <reset-value>, <neg-mask>, "label");
* - 'i'|'o'|'t': PIO is input-only/output-only/tri-state
* - <reset-value>: for correct initial status display, output-only
* - <neg-mask> is meant to be used to in cases of active-low
* GPIOs, such as LEDs and buttons (on/pressed == 0). Each bit
* which is 1 in <neg-mask> inverts the corresponding GPIO's value
* before set/after get. So: gpio_set_value(gpio, 1) => LED on .
*
* Do NOT define CONFIG_SYS_GPIO_BASE !
*
* Optionally, in your board's config. header:
* - To force a GPIO numbering scheme like in Linux ...
* #define CONFIG_GPIO_DOWNTO_NUMBERING
* ... starting with 255 (default)
* #define CONFIG_GPIO_DOWNTO_MAX 255
*/
#include <common.h>
#include <asm/io.h>
#include <asm/gpio.h>
#ifdef CONFIG_GPIO_DOWNTO_NUMBERING
#ifndef CONFIG_GPIO_DOWNTO_MAX
#define CONFIG_GPIO_DOWNTO_MAX 255
#endif
#endif
#define ALTERA_PIO_DATA 0x0
#define ALTERA_PIO_DIR 0x4
#define GPIO_LABEL_SIZE 9
static struct altera_pio {
u32 base;
u8 width;
char iot;
u32 negmask;
u32 sh_data;
u32 sh_dir;
int gidx;
char label[GPIO_LABEL_SIZE];
} pios[CONFIG_SYS_ALTERA_PIO_NUM];
static int pio_num;
static struct altera_pio_gpio {
unsigned num;
struct altera_pio *pio;
char reqlabel[GPIO_LABEL_SIZE];
} gpios[CONFIG_SYS_ALTERA_PIO_GPIO_NUM];
static int pio_gpio_num;
static int altera_pio_gidx(unsigned gpio)
{
int i;
for (i = 0; i < pio_gpio_num; ++i) {
if (gpio == gpios[i].num)
break;
}
if (i >= pio_gpio_num)
return -1;
return i;
}
static struct altera_pio *altera_pio_get_and_mask(unsigned gpio, u32 *mask)
{
int gidx = altera_pio_gidx(gpio);
if (gidx < 0)
return NULL;
if (mask)
*mask = 1 << (gidx - gpios[gidx].pio->gidx);
return gpios[gidx].pio;
}
#define altera_pio_use_gidx(_gidx, _reqlabel) \
{ strncpy(gpios[_gidx].reqlabel, _reqlabel, GPIO_LABEL_SIZE); }
#define altera_pio_unuse_gidx(_gidx) { gpios[_gidx].reqlabel[0] = '\0'; }
#define altera_pio_is_gidx_used(_gidx) (gpios[_gidx].reqlabel[0] != '\0')
static int altera_pio_gpio_init(struct altera_pio *pio, u8 width)
{
u8 gidx = pio_gpio_num;
int i;
if (!width)
return -1;
if ((pio_gpio_num + width) > CONFIG_SYS_ALTERA_PIO_GPIO_NUM)
return -1;
for (i = 0; i < width; ++i) {
#ifdef CONFIG_GPIO_DOWNTO_NUMBERING
gpios[pio_gpio_num + i].num = \
CONFIG_GPIO_DOWNTO_MAX + 1 - gidx - width + i;
#else
gpios[pio_gpio_num + i].num = pio_gpio_num + i;
#endif
gpios[pio_gpio_num + i].pio = pio;
altera_pio_unuse_gidx(pio_gpio_num + i);
}
pio_gpio_num += width;
return gidx;
}
int altera_pio_init(u32 base, u8 width, char iot, u32 rstval, u32 negmask,
const char *label)
{
if (pio_num >= CONFIG_SYS_ALTERA_PIO_NUM)
return -1;
pios[pio_num].base = base;
pios[pio_num].width = width;
pios[pio_num].iot = iot;
switch (iot) {
case 'i':
/* input only */
pios[pio_num].sh_dir = 0;
pios[pio_num].sh_data = readl(base + ALTERA_PIO_DATA);
break;
case 'o':
/* output only */
pios[pio_num].sh_dir = 0xffffffff & ((1 << width) - 1);
pios[pio_num].sh_data = rstval;
break;
case 't':
/* bidir, tri-state */
pios[pio_num].sh_dir = readl(base + ALTERA_PIO_DIR);
pios[pio_num].sh_data = readl(base + ALTERA_PIO_DATA);
break;
default:
return -1;
}
pios[pio_num].negmask = negmask & ((1 << width) - 1);
pios[pio_num].gidx = altera_pio_gpio_init(&pios[pio_num], width);
if (pios[pio_num].gidx < 0)
return -1;
strncpy(pios[pio_num].label, label, GPIO_LABEL_SIZE);
return pio_num++;
}
void altera_pio_info(void)
{
int i;
int j;
int gidx;
u32 mask;
for (i = 0; i < pio_num; ++i) {
printf("Altera PIO % 2d, @0x%08x, "
"width: %u, label: %s\n",
i, pios[i].base, pios[i].width, pios[i].label);
gidx = pios[i].gidx;
for (j = gidx; j < (gidx + pios[i].width); ++j) {
mask = 1 << (j - gidx);
printf("\tGPIO % 4d: %s %s [%c] %s\n",
gpios[j].num,
gpios[j].pio->sh_dir & mask ? "out" : " in",
gpio_get_value(gpios[j].num) ? "set" : "clr",
altera_pio_is_gidx_used(j) ? 'x' : ' ',
gpios[j].reqlabel);
}
}
}
int gpio_request(unsigned gpio, const char *label)
{
int gidx = altera_pio_gidx(gpio);
if (gidx < 0)
return gidx;
if (altera_pio_is_gidx_used(gidx))
return -1;
altera_pio_use_gidx(gidx, label);
return 0;
}
int gpio_free(unsigned gpio)
{
int gidx = altera_pio_gidx(gpio);
if (gidx < 0)
return gidx;
if (!altera_pio_is_gidx_used(gidx))
return -1;
altera_pio_unuse_gidx(gidx);
return 0;
}
int gpio_direction_input(unsigned gpio)
{
u32 mask;
struct altera_pio *pio;
pio = altera_pio_get_and_mask(gpio, &mask);
if (!pio)
return -1;
if (pio->iot == 'o')
return -1;
writel(pio->sh_dir &= ~mask, pio->base + ALTERA_PIO_DIR);
return 0;
}
int gpio_direction_output(unsigned gpio, int value)
{
u32 mask;
struct altera_pio *pio;
pio = altera_pio_get_and_mask(gpio, &mask);
if (!pio)
return -1;
if (pio->iot == 'i')
return -1;
value = (pio->negmask & mask) ? !value : value;
if (value)
pio->sh_data |= mask;
else
pio->sh_data &= ~mask;
writel(pio->sh_data, pio->base + ALTERA_PIO_DATA);
writel(pio->sh_dir |= mask, pio->base + ALTERA_PIO_DIR);
return 0;
}
int gpio_get_value(unsigned gpio)
{
u32 mask;
struct altera_pio *pio;
u32 val;
pio = altera_pio_get_and_mask(gpio, &mask);
if (!pio)
return -1;
if ((pio->sh_dir & mask) || (pio->iot == 'o'))
val = pio->sh_data & mask;
else
val = readl(pio->base + ALTERA_PIO_DATA) & mask;
return (pio->negmask & mask) ? !val : val;
}
void gpio_set_value(unsigned gpio, int value)
{
u32 mask;
struct altera_pio *pio;
pio = altera_pio_get_and_mask(gpio, &mask);
if (!pio)
return;
if (pio->iot == 'i')
return;
value = (pio->negmask & mask) ? !value : value;
if (value)
pio->sh_data |= mask;
else
pio->sh_data &= ~mask;
writel(pio->sh_data, pio->base + ALTERA_PIO_DATA);
return;
}
int gpio_is_valid(int number)
{
int gidx = altera_pio_gidx(number);
if (gidx < 0)
return 1;
return 0;
}

View file

@ -0,0 +1,345 @@
/*
* Memory Setup stuff - taken from blob memsetup.S
*
* Copyright (C) 2009 Jens Scharsig (js_at_ng@scharsoft.de)
*
* Copyright (C) 2005 HP Labs
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*/
/*
* WARNING:
*
* As the code is right now, it expects all PIO ports A,B,C,...
* to be evenly spaced in the memory map:
* ATMEL_BASE_PIOA + port * sizeof at91pio_t
* This might not necessaryly be true in future Atmel SoCs.
* This code should be fixed to use a pointer array to the ports.
*/
#include <config.h>
#include <common.h>
#include <asm/io.h>
#include <asm/sizes.h>
#include <asm/arch/hardware.h>
#include <asm/arch/at91_pio.h>
int at91_set_pio_pullup(unsigned port, unsigned pin, int use_pullup)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
if (use_pullup)
writel(1 << pin, &pio->port[port].puer);
else
writel(1 << pin, &pio->port[port].pudr);
writel(mask, &pio->port[port].per);
}
return 0;
}
/*
* mux the pin to the "GPIO" peripheral role.
*/
int at91_set_pio_periph(unsigned port, unsigned pin, int use_pullup)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
writel(mask, &pio->port[port].idr);
at91_set_pio_pullup(port, pin, use_pullup);
writel(mask, &pio->port[port].per);
}
return 0;
}
/*
* mux the pin to the "A" internal peripheral role.
*/
int at91_set_a_periph(unsigned port, unsigned pin, int use_pullup)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
writel(mask, &pio->port[port].idr);
at91_set_pio_pullup(port, pin, use_pullup);
#if defined(CPU_HAS_PIO3)
writel(readl(&pio->port[port].abcdsr1) & ~mask,
&pio->port[port].abcdsr1);
writel(readl(&pio->port[port].abcdsr2) & ~mask,
&pio->port[port].abcdsr2);
#else
writel(mask, &pio->port[port].asr);
#endif
writel(mask, &pio->port[port].pdr);
}
return 0;
}
/*
* mux the pin to the "B" internal peripheral role.
*/
int at91_set_b_periph(unsigned port, unsigned pin, int use_pullup)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
writel(mask, &pio->port[port].idr);
at91_set_pio_pullup(port, pin, use_pullup);
#if defined(CPU_HAS_PIO3)
writel(readl(&pio->port[port].abcdsr1) | mask,
&pio->port[port].abcdsr1);
writel(readl(&pio->port[port].abcdsr2) & ~mask,
&pio->port[port].abcdsr2);
#else
writel(mask, &pio->port[port].bsr);
#endif
writel(mask, &pio->port[port].pdr);
}
return 0;
}
#if defined(CPU_HAS_PIO3)
/*
* mux the pin to the "C" internal peripheral role.
*/
int at91_set_c_periph(unsigned port, unsigned pin, int use_pullup)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
writel(mask, &pio->port[port].idr);
at91_set_pio_pullup(port, pin, use_pullup);
writel(readl(&pio->port[port].abcdsr1) & ~mask,
&pio->port[port].abcdsr1);
writel(readl(&pio->port[port].abcdsr2) | mask,
&pio->port[port].abcdsr2);
writel(mask, &pio->port[port].pdr);
}
return 0;
}
/*
* mux the pin to the "D" internal peripheral role.
*/
int at91_set_d_periph(unsigned port, unsigned pin, int use_pullup)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
writel(mask, &pio->port[port].idr);
at91_set_pio_pullup(port, pin, use_pullup);
writel(readl(&pio->port[port].abcdsr1) | mask,
&pio->port[port].abcdsr1);
writel(readl(&pio->port[port].abcdsr2) | mask,
&pio->port[port].abcdsr2);
writel(mask, &pio->port[port].pdr);
}
return 0;
}
#endif
/*
* mux the pin to the gpio controller (instead of "A" or "B" peripheral), and
* configure it for an input.
*/
int at91_set_pio_input(unsigned port, u32 pin, int use_pullup)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
writel(mask, &pio->port[port].idr);
at91_set_pio_pullup(port, pin, use_pullup);
writel(mask, &pio->port[port].odr);
writel(mask, &pio->port[port].per);
}
return 0;
}
/*
* mux the pin to the gpio controller (instead of "A" or "B" peripheral),
* and configure it for an output.
*/
int at91_set_pio_output(unsigned port, u32 pin, int value)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
writel(mask, &pio->port[port].idr);
writel(mask, &pio->port[port].pudr);
if (value)
writel(mask, &pio->port[port].sodr);
else
writel(mask, &pio->port[port].codr);
writel(mask, &pio->port[port].oer);
writel(mask, &pio->port[port].per);
}
return 0;
}
/*
* enable/disable the glitch filter. mostly used with IRQ handling.
*/
int at91_set_pio_deglitch(unsigned port, unsigned pin, int is_on)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
if (is_on) {
#if defined(CPU_HAS_PIO3)
writel(mask, &pio->port[port].ifscdr);
#endif
writel(mask, &pio->port[port].ifer);
} else {
writel(mask, &pio->port[port].ifdr);
}
}
return 0;
}
#if defined(CPU_HAS_PIO3)
/*
* enable/disable the debounce filter.
*/
int at91_set_pio_debounce(unsigned port, unsigned pin, int is_on, int div)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
if (is_on) {
writel(mask, &pio->port[port].ifscer);
writel(div & PIO_SCDR_DIV, &pio->port[port].scdr);
writel(mask, &pio->port[port].ifer);
} else {
writel(mask, &pio->port[port].ifdr);
}
}
return 0;
}
/*
* enable/disable the pull-down.
* If pull-up already enabled while calling the function, we disable it.
*/
int at91_set_pio_pulldown(unsigned port, unsigned pin, int is_on)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
writel(mask, &pio->port[port].pudr);
if (is_on)
writel(mask, &pio->port[port].ppder);
else
writel(mask, &pio->port[port].ppddr);
}
return 0;
}
/*
* disable Schmitt trigger
*/
int at91_set_pio_disable_schmitt_trig(unsigned port, unsigned pin)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
writel(readl(&pio->port[port].schmitt) | mask,
&pio->port[port].schmitt);
}
return 0;
}
#endif
/*
* enable/disable the multi-driver. This is only valid for output and
* allows the output pin to run as an open collector output.
*/
int at91_set_pio_multi_drive(unsigned port, unsigned pin, int is_on)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
if (is_on)
writel(mask, &pio->port[port].mder);
else
writel(mask, &pio->port[port].mddr);
}
return 0;
}
/*
* assuming the pin is muxed as a gpio output, set its value.
*/
int at91_set_pio_value(unsigned port, unsigned pin, int value)
{
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
if (value)
writel(mask, &pio->port[port].sodr);
else
writel(mask, &pio->port[port].codr);
}
return 0;
}
/*
* read the pin's value (works even if it's not muxed as a gpio).
*/
int at91_get_pio_value(unsigned port, unsigned pin)
{
u32 pdsr = 0;
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA;
u32 mask;
if ((port < ATMEL_PIO_PORTS) && (pin < 32)) {
mask = 1 << pin;
pdsr = readl(&pio->port[port].pdsr) & mask;
}
return pdsr != 0;
}

View file

@ -0,0 +1,272 @@
/*
* GPIO driver for TI DaVinci DA8xx SOCs.
*
* (C) Copyright 2011 Guralp Systems Ltd.
* Laurence Withers <lwithers@guralp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <asm/arch/hardware.h>
#include <asm/arch/davinci_misc.h>
static struct gpio_registry {
int is_registered;
char name[GPIO_NAME_SIZE];
} gpio_registry[MAX_NUM_GPIOS];
#define pinmux(x) (&davinci_syscfg_regs->pinmux[x])
static const struct pinmux_config gpio_pinmux[] = {
{ pinmux(1), 8, 7 }, /* GP0[0] */
{ pinmux(1), 8, 6 },
{ pinmux(1), 8, 5 },
{ pinmux(1), 8, 4 },
{ pinmux(1), 8, 3 },
{ pinmux(1), 8, 2 },
{ pinmux(1), 8, 1 },
{ pinmux(1), 8, 0 },
{ pinmux(0), 8, 7 },
{ pinmux(0), 8, 6 },
{ pinmux(0), 8, 5 },
{ pinmux(0), 8, 4 },
{ pinmux(0), 8, 3 },
{ pinmux(0), 8, 2 },
{ pinmux(0), 8, 1 },
{ pinmux(0), 8, 0 },
{ pinmux(4), 8, 7 }, /* GP1[0] */
{ pinmux(4), 8, 6 },
{ pinmux(4), 8, 5 },
{ pinmux(4), 8, 4 },
{ pinmux(4), 8, 3 },
{ pinmux(4), 8, 2 },
{ pinmux(4), 4, 1 },
{ pinmux(4), 4, 0 },
{ pinmux(3), 4, 0 },
{ pinmux(2), 4, 6 },
{ pinmux(2), 4, 5 },
{ pinmux(2), 4, 4 },
{ pinmux(2), 4, 3 },
{ pinmux(2), 4, 2 },
{ pinmux(2), 4, 1 },
{ pinmux(2), 8, 0 },
{ pinmux(6), 8, 7 }, /* GP2[0] */
{ pinmux(6), 8, 6 },
{ pinmux(6), 8, 5 },
{ pinmux(6), 8, 4 },
{ pinmux(6), 8, 3 },
{ pinmux(6), 8, 2 },
{ pinmux(6), 8, 1 },
{ pinmux(6), 8, 0 },
{ pinmux(5), 8, 7 },
{ pinmux(5), 8, 6 },
{ pinmux(5), 8, 5 },
{ pinmux(5), 8, 4 },
{ pinmux(5), 8, 3 },
{ pinmux(5), 8, 2 },
{ pinmux(5), 8, 1 },
{ pinmux(5), 8, 0 },
{ pinmux(8), 8, 7 }, /* GP3[0] */
{ pinmux(8), 8, 6 },
{ pinmux(8), 8, 5 },
{ pinmux(8), 8, 4 },
{ pinmux(8), 8, 3 },
{ pinmux(8), 8, 2 },
{ pinmux(8), 8, 1 },
{ pinmux(8), 8, 0 },
{ pinmux(7), 8, 7 },
{ pinmux(7), 8, 6 },
{ pinmux(7), 8, 5 },
{ pinmux(7), 8, 4 },
{ pinmux(7), 8, 3 },
{ pinmux(7), 8, 2 },
{ pinmux(7), 8, 1 },
{ pinmux(7), 8, 0 },
{ pinmux(10), 8, 7 }, /* GP4[0] */
{ pinmux(10), 8, 6 },
{ pinmux(10), 8, 5 },
{ pinmux(10), 8, 4 },
{ pinmux(10), 8, 3 },
{ pinmux(10), 8, 2 },
{ pinmux(10), 8, 1 },
{ pinmux(10), 8, 0 },
{ pinmux(9), 8, 7 },
{ pinmux(9), 8, 6 },
{ pinmux(9), 8, 5 },
{ pinmux(9), 8, 4 },
{ pinmux(9), 8, 3 },
{ pinmux(9), 8, 2 },
{ pinmux(9), 8, 1 },
{ pinmux(9), 8, 0 },
{ pinmux(12), 8, 7 }, /* GP5[0] */
{ pinmux(12), 8, 6 },
{ pinmux(12), 8, 5 },
{ pinmux(12), 8, 4 },
{ pinmux(12), 8, 3 },
{ pinmux(12), 8, 2 },
{ pinmux(12), 8, 1 },
{ pinmux(12), 8, 0 },
{ pinmux(11), 8, 7 },
{ pinmux(11), 8, 6 },
{ pinmux(11), 8, 5 },
{ pinmux(11), 8, 4 },
{ pinmux(11), 8, 3 },
{ pinmux(11), 8, 2 },
{ pinmux(11), 8, 1 },
{ pinmux(11), 8, 0 },
{ pinmux(19), 8, 6 }, /* GP6[0] */
{ pinmux(19), 8, 5 },
{ pinmux(19), 8, 4 },
{ pinmux(19), 8, 3 },
{ pinmux(19), 8, 2 },
{ pinmux(16), 8, 1 },
{ pinmux(14), 8, 1 },
{ pinmux(14), 8, 0 },
{ pinmux(13), 8, 7 },
{ pinmux(13), 8, 6 },
{ pinmux(13), 8, 5 },
{ pinmux(13), 8, 4 },
{ pinmux(13), 8, 3 },
{ pinmux(13), 8, 2 },
{ pinmux(13), 8, 1 },
{ pinmux(13), 8, 0 },
{ pinmux(18), 8, 1 }, /* GP7[0] */
{ pinmux(18), 8, 0 },
{ pinmux(17), 8, 7 },
{ pinmux(17), 8, 6 },
{ pinmux(17), 8, 5 },
{ pinmux(17), 8, 4 },
{ pinmux(17), 8, 3 },
{ pinmux(17), 8, 2 },
{ pinmux(17), 8, 1 },
{ pinmux(17), 8, 0 },
{ pinmux(16), 8, 7 },
{ pinmux(16), 8, 6 },
{ pinmux(16), 8, 5 },
{ pinmux(16), 8, 4 },
{ pinmux(16), 8, 3 },
{ pinmux(16), 8, 2 },
{ pinmux(19), 8, 0 }, /* GP8[0] */
{ pinmux(3), 4, 7 },
{ pinmux(3), 4, 6 },
{ pinmux(3), 4, 5 },
{ pinmux(3), 4, 4 },
{ pinmux(3), 4, 3 },
{ pinmux(3), 4, 2 },
{ pinmux(2), 4, 7 },
{ pinmux(19), 8, 1 },
{ pinmux(19), 8, 0 },
{ pinmux(18), 8, 7 },
{ pinmux(18), 8, 6 },
{ pinmux(18), 8, 5 },
{ pinmux(18), 8, 4 },
{ pinmux(18), 8, 3 },
{ pinmux(18), 8, 2 },
};
int gpio_request(unsigned gpio, const char *label)
{
if (gpio >= MAX_NUM_GPIOS)
return -1;
if (gpio_registry[gpio].is_registered)
return -1;
gpio_registry[gpio].is_registered = 1;
strncpy(gpio_registry[gpio].name, label, GPIO_NAME_SIZE);
gpio_registry[gpio].name[GPIO_NAME_SIZE - 1] = 0;
davinci_configure_pin_mux(&gpio_pinmux[gpio], 1);
return 0;
}
int gpio_free(unsigned gpio)
{
if (gpio >= MAX_NUM_GPIOS)
return -1;
if (!gpio_registry[gpio].is_registered)
return -1;
gpio_registry[gpio].is_registered = 0;
gpio_registry[gpio].name[0] = '\0';
/* Do not configure as input or change pin mux here */
return 0;
}
int gpio_direction_input(unsigned gpio)
{
struct davinci_gpio *bank;
bank = GPIO_BANK(gpio);
setbits_le32(&bank->dir, 1U << GPIO_BIT(gpio));
return 0;
}
int gpio_direction_output(unsigned gpio, int value)
{
struct davinci_gpio *bank;
bank = GPIO_BANK(gpio);
clrbits_le32(&bank->dir, 1U << GPIO_BIT(gpio));
gpio_set_value(gpio, value);
return 0;
}
int gpio_get_value(unsigned gpio)
{
struct davinci_gpio *bank;
unsigned int ip;
bank = GPIO_BANK(gpio);
ip = in_le32(&bank->in_data) & (1U << GPIO_BIT(gpio));
return ip ? 1 : 0;
}
int gpio_set_value(unsigned gpio, int value)
{
struct davinci_gpio *bank;
bank = GPIO_BANK(gpio);
if (value)
bank->set_data = 1U << GPIO_BIT(gpio);
else
bank->clr_data = 1U << GPIO_BIT(gpio);
return 0;
}
void gpio_info(void)
{
unsigned gpio, dir, val;
struct davinci_gpio *bank;
for (gpio = 0; gpio < MAX_NUM_GPIOS; ++gpio) {
bank = GPIO_BANK(gpio);
dir = in_le32(&bank->dir) & (1U << GPIO_BIT(gpio));
val = gpio_get_value(gpio);
printf("% 4d: %s: %d [%c] %s\n",
gpio, dir ? " in" : "out", val,
gpio_registry[gpio].is_registered ? 'x' : ' ',
gpio_registry[gpio].name);
}
}

View file

@ -0,0 +1,166 @@
/*
* arch/arm/plat-orion/gpio.c
*
* Marvell Orion SoC GPIO handling.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
/*
* Based on (mostly copied from) plat-orion based Linux 2.6 kernel driver.
* Removed orion_gpiochip struct and kernel level irq handling.
*
* Dieter Kiermaier dk-arm-linux@gmx.de
*/
#include <common.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/arch/kirkwood.h>
#include <asm/arch/gpio.h>
static unsigned long gpio_valid_input[BITS_TO_LONGS(GPIO_MAX)];
static unsigned long gpio_valid_output[BITS_TO_LONGS(GPIO_MAX)];
void __set_direction(unsigned pin, int input)
{
u32 u;
u = readl(GPIO_IO_CONF(pin));
if (input)
u |= 1 << (pin & 31);
else
u &= ~(1 << (pin & 31));
writel(u, GPIO_IO_CONF(pin));
u = readl(GPIO_IO_CONF(pin));
}
void __set_level(unsigned pin, int high)
{
u32 u;
u = readl(GPIO_OUT(pin));
if (high)
u |= 1 << (pin & 31);
else
u &= ~(1 << (pin & 31));
writel(u, GPIO_OUT(pin));
}
void __set_blinking(unsigned pin, int blink)
{
u32 u;
u = readl(GPIO_BLINK_EN(pin));
if (blink)
u |= 1 << (pin & 31);
else
u &= ~(1 << (pin & 31));
writel(u, GPIO_BLINK_EN(pin));
}
int kw_gpio_is_valid(unsigned pin, int mode)
{
if (pin < GPIO_MAX) {
if ((mode & GPIO_INPUT_OK) && !test_bit(pin, gpio_valid_input))
goto err_out;
if ((mode & GPIO_OUTPUT_OK) && !test_bit(pin, gpio_valid_output))
goto err_out;
return 0;
}
err_out:
printf("%s: invalid GPIO %d\n", __func__, pin);
return 1;
}
void kw_gpio_set_valid(unsigned pin, int mode)
{
if (mode == 1)
mode = GPIO_INPUT_OK | GPIO_OUTPUT_OK;
if (mode & GPIO_INPUT_OK)
__set_bit(pin, gpio_valid_input);
else
__clear_bit(pin, gpio_valid_input);
if (mode & GPIO_OUTPUT_OK)
__set_bit(pin, gpio_valid_output);
else
__clear_bit(pin, gpio_valid_output);
}
/*
* GENERIC_GPIO primitives.
*/
int kw_gpio_direction_input(unsigned pin)
{
if (kw_gpio_is_valid(pin, GPIO_INPUT_OK) != 0)
return 1;
/* Configure GPIO direction. */
__set_direction(pin, 1);
return 0;
}
int kw_gpio_direction_output(unsigned pin, int value)
{
if (kw_gpio_is_valid(pin, GPIO_OUTPUT_OK) != 0)
{
printf("%s: invalid GPIO %d\n", __func__, pin);
return 1;
}
__set_blinking(pin, 0);
/* Configure GPIO output value. */
__set_level(pin, value);
/* Configure GPIO direction. */
__set_direction(pin, 0);
return 0;
}
int kw_gpio_get_value(unsigned pin)
{
int val;
if (readl(GPIO_IO_CONF(pin)) & (1 << (pin & 31)))
val = readl(GPIO_DATA_IN(pin)) ^ readl(GPIO_IN_POL(pin));
else
val = readl(GPIO_OUT(pin));
return (val >> (pin & 31)) & 1;
}
void kw_gpio_set_value(unsigned pin, int value)
{
/* Configure GPIO output value. */
__set_level(pin, value);
}
void kw_gpio_set_blink(unsigned pin, int blink)
{
/* Set output value to zero. */
__set_level(pin, 0);
/* Set blinking. */
__set_blinking(pin, blink);
}

View file

@ -0,0 +1,199 @@
/*
* Freescale MPC83xx GPIO handling.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h>
#include <mpc83xx.h>
#include <asm/gpio.h>
#include <asm/io.h>
#ifndef CONFIG_MPC83XX_GPIO_0_INIT_DIRECTION
#define CONFIG_MPC83XX_GPIO_0_INIT_DIRECTION 0
#endif
#ifndef CONFIG_MPC83XX_GPIO_1_INIT_DIRECTION
#define CONFIG_MPC83XX_GPIO_1_INIT_DIRECTION 0
#endif
#ifndef CONFIG_MPC83XX_GPIO_0_INIT_OPEN_DRAIN
#define CONFIG_MPC83XX_GPIO_0_INIT_OPEN_DRAIN 0
#endif
#ifndef CONFIG_MPC83XX_GPIO_1_INIT_OPEN_DRAIN
#define CONFIG_MPC83XX_GPIO_1_INIT_OPEN_DRAIN 0
#endif
#ifndef CONFIG_MPC83XX_GPIO_0_INIT_VALUE
#define CONFIG_MPC83XX_GPIO_0_INIT_VALUE 0
#endif
#ifndef CONFIG_MPC83XX_GPIO_1_INIT_VALUE
#define CONFIG_MPC83XX_GPIO_1_INIT_VALUE 0
#endif
static unsigned int gpio_output_value[MPC83XX_GPIO_CTRLRS];
/*
* Generic_GPIO primitives.
*/
int gpio_request(unsigned gpio, const char *label)
{
if (gpio >= MAX_NUM_GPIOS)
return -1;
return 0;
}
int gpio_free(unsigned gpio)
{
/* Do not set to input */
return 0;
}
/* set GPIO pin 'gpio' as an input */
int gpio_direction_input(unsigned gpio)
{
immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
unsigned int ctrlr;
unsigned int line;
unsigned int line_mask;
/* 32-bits per controller */
ctrlr = gpio >> 5;
line = gpio & (0x1F);
/* Big endian */
line_mask = 1 << (31 - line);
clrbits_be32(&im->gpio[ctrlr].dir, line_mask);
return 0;
}
/* set GPIO pin 'gpio' as an output, with polarity 'value' */
int gpio_direction_output(unsigned gpio, int value)
{
immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
unsigned int ctrlr;
unsigned int line;
unsigned int line_mask;
if (value != 0 && value != 1) {
printf("Error: Value parameter must be 0 or 1.\n");
return -1;
}
gpio_set_value(gpio, value);
/* 32-bits per controller */
ctrlr = gpio >> 5;
line = gpio & (0x1F);
/* Big endian */
line_mask = 1 << (31 - line);
/* Make the line output */
setbits_be32(&im->gpio[ctrlr].dir, line_mask);
return 0;
}
/* read GPIO IN value of pin 'gpio' */
int gpio_get_value(unsigned gpio)
{
immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
unsigned int ctrlr;
unsigned int line;
unsigned int line_mask;
/* 32-bits per controller */
ctrlr = gpio >> 5;
line = gpio & (0x1F);
/* Big endian */
line_mask = 1 << (31 - line);
/* Read the value and mask off the bit */
return (in_be32(&im->gpio[ctrlr].dat) & line_mask) != 0;
}
/* write GPIO OUT value to pin 'gpio' */
int gpio_set_value(unsigned gpio, int value)
{
immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
unsigned int ctrlr;
unsigned int line;
unsigned int line_mask;
if (value != 0 && value != 1) {
printf("Error: Value parameter must be 0 or 1.\n");
return -1;
}
/* 32-bits per controller */
ctrlr = gpio >> 5;
line = gpio & (0x1F);
/* Big endian */
line_mask = 1 << (31 - line);
/* Update the local output buffer soft copy */
gpio_output_value[ctrlr] =
(gpio_output_value[ctrlr] & ~line_mask) | \
(value ? line_mask : 0);
/* Write the output */
out_be32(&im->gpio[ctrlr].dat, gpio_output_value[ctrlr]);
return 0;
}
/* Configure GPIO registers early */
void mpc83xx_gpio_init_f()
{
immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
#if MPC83XX_GPIO_CTRLRS >= 1
out_be32(&im->gpio[0].dir, CONFIG_MPC83XX_GPIO_0_INIT_DIRECTION);
out_be32(&im->gpio[0].odr, CONFIG_MPC83XX_GPIO_0_INIT_OPEN_DRAIN);
out_be32(&im->gpio[0].dat, CONFIG_MPC83XX_GPIO_0_INIT_VALUE);
out_be32(&im->gpio[0].ier, 0xFFFFFFFF); /* Clear all events */
out_be32(&im->gpio[0].imr, 0);
out_be32(&im->gpio[0].icr, 0);
#endif
#if MPC83XX_GPIO_CTRLRS >= 2
out_be32(&im->gpio[1].dir, CONFIG_MPC83XX_GPIO_1_INIT_DIRECTION);
out_be32(&im->gpio[1].odr, CONFIG_MPC83XX_GPIO_1_INIT_OPEN_DRAIN);
out_be32(&im->gpio[1].dat, CONFIG_MPC83XX_GPIO_1_INIT_VALUE);
out_be32(&im->gpio[1].ier, 0xFFFFFFFF); /* Clear all events */
out_be32(&im->gpio[1].imr, 0);
out_be32(&im->gpio[1].icr, 0);
#endif
}
/* Initialize GPIO soft-copies */
void mpc83xx_gpio_init_r()
{
#if MPC83XX_GPIO_CTRLRS >= 1
gpio_output_value[0] = CONFIG_MPC83XX_GPIO_0_INIT_VALUE;
#endif
#if MPC83XX_GPIO_CTRLRS >= 2
gpio_output_value[1] = CONFIG_MPC83XX_GPIO_1_INIT_VALUE;
#endif
}

View file

@ -0,0 +1,113 @@
/*
* (C) Copyright 2011
* eInfochips Ltd. <www.einfochips.com>
* Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
*
* (C) Copyright 2010
* Marvell Semiconductor <www.marvell.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <common.h>
#include <asm/io.h>
#include <asm/errno.h>
#include "mvgpio.h"
#include <asm/gpio.h>
#ifndef MV_MAX_GPIO
#define MV_MAX_GPIO 128
#endif
int gpio_request(unsigned gpio, const char *label)
{
if (gpio >= MV_MAX_GPIO) {
printf("%s: Invalid GPIO requested %d\n", __func__, gpio);
return -1;
}
return 0;
}
int gpio_free(unsigned gpio)
{
return 0;
}
int gpio_direction_input(unsigned gpio)
{
struct gpio_reg *gpio_reg_bank;
if (gpio >= MV_MAX_GPIO) {
printf("%s: Invalid GPIO %d\n", __func__, gpio);
return -1;
}
gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio));
writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gcdr);
return 0;
}
int gpio_direction_output(unsigned gpio, int value)
{
struct gpio_reg *gpio_reg_bank;
if (gpio >= MV_MAX_GPIO) {
printf("%s: Invalid GPIO %d\n", __func__, gpio);
return -1;
}
gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio));
writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gsdr);
gpio_set_value(gpio, value);
return 0;
}
int gpio_get_value(unsigned gpio)
{
struct gpio_reg *gpio_reg_bank;
u32 gpio_val;
if (gpio >= MV_MAX_GPIO) {
printf("%s: Invalid GPIO %d\n", __func__, gpio);
return -1;
}
gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio));
gpio_val = readl(&gpio_reg_bank->gplr);
return GPIO_VAL(gpio, gpio_val);
}
int gpio_set_value(unsigned gpio, int value)
{
struct gpio_reg *gpio_reg_bank;
if (gpio >= MV_MAX_GPIO) {
printf("%s: Invalid GPIO %d\n", __func__, gpio);
return -1;
}
gpio_reg_bank = get_gpio_base(GPIO_TO_REG(gpio));
if (value)
writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gpsr);
else
writel(GPIO_TO_BIT(gpio), &gpio_reg_bank->gpcr);
return 0;
}

View file

@ -0,0 +1,74 @@
/*
* (C) Copyright 2011
* eInfochips Ltd. <www.einfochips.com>
* Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
*
* (C) Copyright 2010
* Marvell Semiconductor <www.marvell.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#ifndef __MVGPIO_H__
#define __MVGPIO_H__
#include <common.h>
#ifdef CONFIG_SHEEVA_88SV331xV5
/*
* GPIO Register map for SHEEVA 88SV331xV5
*/
struct gpio_reg {
u32 gplr; /* Pin Level Register - 0x0000 */
u32 pad0[2];
u32 gpdr; /* Pin Direction Register - 0x000C */
u32 pad1[2];
u32 gpsr; /* Pin Output Set Register - 0x0018 */
u32 pad2[2];
u32 gpcr; /* Pin Output Clear Register - 0x0024 */
u32 pad3[2];
u32 grer; /* Rising-Edge Detect Enable Register - 0x0030 */
u32 pad4[2];
u32 gfer; /* Falling-Edge Detect Enable Register - 0x003C */
u32 pad5[2];
u32 gedr; /* Edge Detect Status Register - 0x0048 */
u32 pad6[2];
u32 gsdr; /* Bitwise Set of GPIO Direction Register - 0x0054 */
u32 pad7[2];
u32 gcdr; /* Bitwise Clear of GPIO Direction Register - 0x0060 */
u32 pad8[2];
u32 gsrer; /* Bitwise Set of Rising-Edge Detect Enable
Register - 0x006C */
u32 pad9[2];
u32 gcrer; /* Bitwise Clear of Rising-Edge Detect Enable
Register - 0x0078 */
u32 pad10[2];
u32 gsfer; /* Bitwise Set of Falling-Edge Detect Enable
Register - 0x0084 */
u32 pad11[2];
u32 gcfer; /* Bitwise Clear of Falling-Edge Detect Enable
Register - 0x0090 */
u32 pad12[2];
u32 apmask; /* Bitwise Mask of Edge Detect Register - 0x009C */
};
#else
#error "CPU core subversion not defined"
#endif
#endif /* __MVGPIO_H__ */

View file

@ -0,0 +1,82 @@
/*
* (C) Copyright 2010
* Marvell Semiconductor <www.marvell.com>
* Written-by: Prafulla Wadaskar <prafulla@marvell.com>,
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <common.h>
#include <asm/io.h>
#include <mvmfp.h>
#include <asm/arch/mfp.h>
/*
* mfp_config
*
* On most of Marvell SoCs (ex. ARMADA100) there is Multi-Funtion-Pin
* configuration registers to configure each GPIO/Function pin on the
* SoC.
*
* This function reads the array of values for
* MFPR_X registers and programms them into respective
* Multi-Function Pin registers.
* It supports - Alternate Function Selection programming.
*
* Whereas,
* The Configureation value is constructed using MFP()
* array consists of 32bit values as defined in MFP(xx,xx..) macro
*/
void mfp_config(u32 *mfp_cfgs)
{
u32 *p_mfpr = NULL;
u32 cfg_val, val;
do {
cfg_val = *mfp_cfgs++;
/* exit if End of configuration table detected */
if (cfg_val == MFP_EOC)
break;
p_mfpr = (u32 *)(MV_MFPR_BASE
+ MFP_REG_GET_OFFSET(cfg_val));
/* Write a mfg register as per configuration */
val = 0;
if (cfg_val & MFP_AF_FLAG)
/* Abstract and program Afternate-Func Selection */
val |= cfg_val & MFP_AF_MASK;
if (cfg_val & MFP_EDGE_FLAG)
/* Abstract and program Edge configuration */
val |= cfg_val & MFP_LPM_EDGE_MASK;
if (cfg_val & MFP_DRIVE_FLAG)
/* Abstract and program Drive configuration */
val |= cfg_val & MFP_DRIVE_MASK;
if (cfg_val & MFP_PULL_FLAG)
/* Abstract and program Pullup/down configuration */
val |= cfg_val & MFP_PULL_MASK;
writel(val, p_mfpr);
} while (1);
/*
* perform a read-back of any MFPR register to make sure the
* previous writings are finished
*/
readl(p_mfpr);
}

View file

@ -0,0 +1,150 @@
/*
* Copyright (C) 2009
* Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
*
* Copyright (C) 2011
* Stefano Babic, DENX Software Engineering, <sbabic@denx.de>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h>
#include <asm/arch/imx-regs.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <errno.h>
enum mxc_gpio_direction {
MXC_GPIO_DIRECTION_IN,
MXC_GPIO_DIRECTION_OUT,
};
#define GPIO_TO_PORT(n) (n / 32)
/* GPIO port description */
static unsigned long gpio_ports[] = {
[0] = GPIO1_BASE_ADDR,
[1] = GPIO2_BASE_ADDR,
[2] = GPIO3_BASE_ADDR,
#if defined(CONFIG_MX25) || defined(CONFIG_MX51) || defined(CONFIG_MX53) || \
defined(CONFIG_MX6Q)
[3] = GPIO4_BASE_ADDR,
#endif
#if defined(CONFIG_MX53) || defined(CONFIG_MX6Q)
[4] = GPIO5_BASE_ADDR,
[5] = GPIO6_BASE_ADDR,
[6] = GPIO7_BASE_ADDR,
#endif
};
static int mxc_gpio_direction(unsigned int gpio,
enum mxc_gpio_direction direction)
{
unsigned int port = GPIO_TO_PORT(gpio);
struct gpio_regs *regs;
u32 l;
if (port >= ARRAY_SIZE(gpio_ports))
return -1;
gpio &= 0x1f;
regs = (struct gpio_regs *)gpio_ports[port];
l = readl(&regs->gpio_dir);
switch (direction) {
case MXC_GPIO_DIRECTION_OUT:
l |= 1 << gpio;
break;
case MXC_GPIO_DIRECTION_IN:
l &= ~(1 << gpio);
}
writel(l, &regs->gpio_dir);
return 0;
}
int gpio_set_value(unsigned gpio, int value)
{
unsigned int port = GPIO_TO_PORT(gpio);
struct gpio_regs *regs;
u32 l;
if (port >= ARRAY_SIZE(gpio_ports))
return -1;
gpio &= 0x1f;
regs = (struct gpio_regs *)gpio_ports[port];
l = readl(&regs->gpio_dr);
if (value)
l |= 1 << gpio;
else
l &= ~(1 << gpio);
writel(l, &regs->gpio_dr);
return 0;
}
int gpio_get_value(unsigned gpio)
{
unsigned int port = GPIO_TO_PORT(gpio);
struct gpio_regs *regs;
u32 val;
if (port >= ARRAY_SIZE(gpio_ports))
return -1;
gpio &= 0x1f;
regs = (struct gpio_regs *)gpio_ports[port];
val = (readl(&regs->gpio_dr) >> gpio) & 0x01;
return val;
}
int gpio_request(unsigned gpio, const char *label)
{
unsigned int port = GPIO_TO_PORT(gpio);
if (port >= ARRAY_SIZE(gpio_ports))
return -1;
return 0;
}
int gpio_free(unsigned gpio)
{
return 0;
}
int gpio_direction_input(unsigned gpio)
{
return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_IN);
}
int gpio_direction_output(unsigned gpio, int value)
{
int ret = mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_OUT);
if (ret < 0)
return ret;
return gpio_set_value(gpio, value);
}

View file

@ -0,0 +1,132 @@
/*
* Freescale i.MX28 GPIO control code
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h>
#include <netdev.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/arch/iomux.h>
#include <asm/arch/imx-regs.h>
#if defined(CONFIG_MX23)
#define PINCTRL_BANKS 3
#define PINCTRL_DOUT(n) (0x0500 + ((n) * 0x10))
#define PINCTRL_DIN(n) (0x0600 + ((n) * 0x10))
#define PINCTRL_DOE(n) (0x0700 + ((n) * 0x10))
#define PINCTRL_PIN2IRQ(n) (0x0800 + ((n) * 0x10))
#define PINCTRL_IRQEN(n) (0x0900 + ((n) * 0x10))
#define PINCTRL_IRQSTAT(n) (0x0c00 + ((n) * 0x10))
#elif defined(CONFIG_MX28)
#define PINCTRL_BANKS 5
#define PINCTRL_DOUT(n) (0x0700 + ((n) * 0x10))
#define PINCTRL_DIN(n) (0x0900 + ((n) * 0x10))
#define PINCTRL_DOE(n) (0x0b00 + ((n) * 0x10))
#define PINCTRL_PIN2IRQ(n) (0x1000 + ((n) * 0x10))
#define PINCTRL_IRQEN(n) (0x1100 + ((n) * 0x10))
#define PINCTRL_IRQSTAT(n) (0x1400 + ((n) * 0x10))
#else
#error "Please select CONFIG_MX23 or CONFIG_MX28"
#endif
#define GPIO_INT_FALL_EDGE 0x0
#define GPIO_INT_LOW_LEV 0x1
#define GPIO_INT_RISE_EDGE 0x2
#define GPIO_INT_HIGH_LEV 0x3
#define GPIO_INT_LEV_MASK (1 << 0)
#define GPIO_INT_POL_MASK (1 << 1)
void mxs_gpio_init(void)
{
int i;
for (i = 0; i < PINCTRL_BANKS; i++) {
writel(0, MXS_PINCTRL_BASE + PINCTRL_PIN2IRQ(i));
writel(0, MXS_PINCTRL_BASE + PINCTRL_IRQEN(i));
/* Use SCT address here to clear the IRQSTAT bits */
writel(0xffffffff, MXS_PINCTRL_BASE + PINCTRL_IRQSTAT(i) + 8);
}
}
int gpio_get_value(unsigned gpio)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DIN(bank);
struct mx28_register_32 *reg =
(struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
return (readl(&reg->reg) >> PAD_PIN(gpio)) & 1;
}
void gpio_set_value(unsigned gpio, int value)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOUT(bank);
struct mx28_register_32 *reg =
(struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
if (value)
writel(1 << PAD_PIN(gpio), &reg->reg_set);
else
writel(1 << PAD_PIN(gpio), &reg->reg_clr);
}
int gpio_direction_input(unsigned gpio)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOE(bank);
struct mx28_register_32 *reg =
(struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
writel(1 << PAD_PIN(gpio), &reg->reg_clr);
return 0;
}
int gpio_direction_output(unsigned gpio, int value)
{
uint32_t bank = PAD_BANK(gpio);
uint32_t offset = PINCTRL_DOE(bank);
struct mx28_register_32 *reg =
(struct mx28_register_32 *)(MXS_PINCTRL_BASE + offset);
writel(1 << PAD_PIN(gpio), &reg->reg_set);
gpio_set_value(gpio, value);
return 0;
}
int gpio_request(unsigned gpio, const char *label)
{
if (PAD_BANK(gpio) >= PINCTRL_BANKS)
return -1;
return 0;
}
int gpio_free(unsigned gpio)
{
return 0;
}

View file

@ -0,0 +1,294 @@
/*
* Copyright 2008 Extreme Engineering Solutions, Inc.
*
* 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.
*
* 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
*/
/*
* Driver for NXP's 4, 8 and 16 bit I2C gpio expanders (eg pca9537, pca9557,
* pca9539, etc)
*/
#include <common.h>
#include <i2c.h>
#include <pca953x.h>
/* Default to an address that hopefully won't corrupt other i2c devices */
#ifndef CONFIG_SYS_I2C_PCA953X_ADDR
#define CONFIG_SYS_I2C_PCA953X_ADDR (~0)
#endif
enum {
PCA953X_CMD_INFO,
PCA953X_CMD_DEVICE,
PCA953X_CMD_OUTPUT,
PCA953X_CMD_INPUT,
PCA953X_CMD_INVERT,
};
#ifdef CONFIG_SYS_I2C_PCA953X_WIDTH
struct pca953x_chip_ngpio {
uint8_t chip;
uint8_t ngpio;
};
static struct pca953x_chip_ngpio pca953x_chip_ngpios[] =
CONFIG_SYS_I2C_PCA953X_WIDTH;
#define NUM_CHIP_GPIOS (sizeof(pca953x_chip_ngpios) / \
sizeof(struct pca953x_chip_ngpio))
/*
* Determine the number of GPIO pins supported. If we don't know we assume
* 8 pins.
*/
static int pca953x_ngpio(uint8_t chip)
{
int i;
for (i = 0; i < NUM_CHIP_GPIOS; i++)
if (pca953x_chip_ngpios[i].chip == chip)
return pca953x_chip_ngpios[i].ngpio;
return 8;
}
#else
static int pca953x_ngpio(uint8_t chip)
{
return 8;
}
#endif
/*
* Modify masked bits in register
*/
static int pca953x_reg_write(uint8_t chip, uint addr, uint mask, uint data)
{
uint8_t valb;
uint16_t valw;
if (pca953x_ngpio(chip) <= 8) {
if (i2c_read(chip, addr, 1, &valb, 1))
return -1;
valb &= ~mask;
valb |= data;
return i2c_write(chip, addr, 1, &valb, 1);
} else {
if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
return -1;
valw &= ~mask;
valw |= data;
return i2c_write(chip, addr << 1, 1, (u8*)&valw, 2);
}
}
static int pca953x_reg_read(uint8_t chip, uint addr, uint *data)
{
uint8_t valb;
uint16_t valw;
if (pca953x_ngpio(chip) <= 8) {
if (i2c_read(chip, addr, 1, &valb, 1))
return -1;
*data = (int)valb;
} else {
if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
return -1;
*data = (int)valw;
}
return 0;
}
/*
* Set output value of IO pins in 'mask' to corresponding value in 'data'
* 0 = low, 1 = high
*/
int pca953x_set_val(uint8_t chip, uint mask, uint data)
{
return pca953x_reg_write(chip, PCA953X_OUT, mask, data);
}
/*
* Set read polarity of IO pins in 'mask' to corresponding value in 'data'
* 0 = read pin value, 1 = read inverted pin value
*/
int pca953x_set_pol(uint8_t chip, uint mask, uint data)
{
return pca953x_reg_write(chip, PCA953X_POL, mask, data);
}
/*
* Set direction of IO pins in 'mask' to corresponding value in 'data'
* 0 = output, 1 = input
*/
int pca953x_set_dir(uint8_t chip, uint mask, uint data)
{
return pca953x_reg_write(chip, PCA953X_CONF, mask, data);
}
/*
* Read current logic level of all IO pins
*/
int pca953x_get_val(uint8_t chip)
{
uint val;
if (pca953x_reg_read(chip, PCA953X_IN, &val) < 0)
return -1;
return (int)val;
}
#ifdef CONFIG_CMD_PCA953X
#ifdef CONFIG_CMD_PCA953X_INFO
/*
* Display pca953x information
*/
static int pca953x_info(uint8_t chip)
{
int i;
uint data;
int nr_gpio = pca953x_ngpio(chip);
int msb = nr_gpio - 1;
printf("pca953x@ 0x%x (%d pins):\n\n", chip, nr_gpio);
printf("gpio pins: ");
for (i = msb; i >= 0; i--)
printf("%x", i);
printf("\n");
for (i = 11 + nr_gpio; i > 0; i--)
printf("-");
printf("\n");
if (pca953x_reg_read(chip, PCA953X_CONF, &data) < 0)
return -1;
printf("conf: ");
for (i = msb; i >= 0; i--)
printf("%c", data & (1 << i) ? 'i' : 'o');
printf("\n");
if (pca953x_reg_read(chip, PCA953X_POL, &data) < 0)
return -1;
printf("invert: ");
for (i = msb; i >= 0; i--)
printf("%c", data & (1 << i) ? '1' : '0');
printf("\n");
if (pca953x_reg_read(chip, PCA953X_IN, &data) < 0)
return -1;
printf("input: ");
for (i = msb; i >= 0; i--)
printf("%c", data & (1 << i) ? '1' : '0');
printf("\n");
if (pca953x_reg_read(chip, PCA953X_OUT, &data) < 0)
return -1;
printf("output: ");
for (i = msb; i >= 0; i--)
printf("%c", data & (1 << i) ? '1' : '0');
printf("\n");
return 0;
}
#endif /* CONFIG_CMD_PCA953X_INFO */
cmd_tbl_t cmd_pca953x[] = {
U_BOOT_CMD_MKENT(device, 3, 0, (void *)PCA953X_CMD_DEVICE, "", ""),
U_BOOT_CMD_MKENT(output, 4, 0, (void *)PCA953X_CMD_OUTPUT, "", ""),
U_BOOT_CMD_MKENT(input, 3, 0, (void *)PCA953X_CMD_INPUT, "", ""),
U_BOOT_CMD_MKENT(invert, 4, 0, (void *)PCA953X_CMD_INVERT, "", ""),
#ifdef CONFIG_CMD_PCA953X_INFO
U_BOOT_CMD_MKENT(info, 2, 0, (void *)PCA953X_CMD_INFO, "", ""),
#endif
};
int do_pca953x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
static uint8_t chip = CONFIG_SYS_I2C_PCA953X_ADDR;
int val;
ulong ul_arg2 = 0;
ulong ul_arg3 = 0;
cmd_tbl_t *c;
c = find_cmd_tbl(argv[1], cmd_pca953x, ARRAY_SIZE(cmd_pca953x));
/* All commands but "device" require 'maxargs' arguments */
if (!c || !((argc == (c->maxargs)) ||
(((int)c->cmd == PCA953X_CMD_DEVICE) &&
(argc == (c->maxargs - 1))))) {
return cmd_usage(cmdtp);
}
/* arg2 used as chip number or pin number */
if (argc > 2)
ul_arg2 = simple_strtoul(argv[2], NULL, 16);
/* arg3 used as pin or invert value */
if (argc > 3)
ul_arg3 = simple_strtoul(argv[3], NULL, 16) & 0x1;
switch ((int)c->cmd) {
#ifdef CONFIG_CMD_PCA953X_INFO
case PCA953X_CMD_INFO:
return pca953x_info(chip);
#endif
case PCA953X_CMD_DEVICE:
if (argc == 3)
chip = (uint8_t)ul_arg2;
printf("Current device address: 0x%x\n", chip);
return 0;
case PCA953X_CMD_INPUT:
pca953x_set_dir(chip, (1 << ul_arg2),
PCA953X_DIR_IN << ul_arg2);
val = (pca953x_get_val(chip) & (1 << ul_arg2)) != 0;
printf("chip 0x%02x, pin 0x%lx = %d\n", chip, ul_arg2, val);
return val;
case PCA953X_CMD_OUTPUT:
pca953x_set_dir(chip, (1 << ul_arg2),
(PCA953X_DIR_OUT << ul_arg2));
return pca953x_set_val(chip, (1 << ul_arg2),
(ul_arg3 << ul_arg2));
case PCA953X_CMD_INVERT:
return pca953x_set_pol(chip, (1 << ul_arg2),
(ul_arg3 << ul_arg2));
default:
/* We should never get here */
return 1;
}
}
U_BOOT_CMD(
pca953x, 5, 1, do_pca953x,
"pca953x gpio access",
"device [dev]\n"
" - show or set current device address\n"
#ifdef CONFIG_CMD_PCA953X_INFO
"pca953x info\n"
" - display info for current chip\n"
#endif
"pca953x output pin 0|1\n"
" - set pin as output and drive low or high\n"
"pca953x invert pin 0|1\n"
" - disable/enable polarity inversion for reads\n"
"pca953x intput pin\n"
" - set pin as input and read value"
);
#endif /* CONFIG_CMD_PCA953X */

View file

@ -0,0 +1,143 @@
/*
* (C) Copyright 2011
* Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*/
/*
* Driver for NXP's pca9698 40 bit I2C gpio expander
*/
#include <common.h>
#include <i2c.h>
#include <asm/errno.h>
#include <pca9698.h>
/*
* The pca9698 registers
*/
#define PCA9698_REG_INPUT 0x00
#define PCA9698_REG_OUTPUT 0x08
#define PCA9698_REG_POLARITY 0x10
#define PCA9698_REG_CONFIG 0x18
#define PCA9698_BUFFER_SIZE 5
#define PCA9698_GPIO_COUNT 40
static int pca9698_read40(u8 addr, u8 offset, u8 *buffer)
{
u8 command = offset | 0x80; /* autoincrement */
return i2c_read(addr, command, 1, buffer, PCA9698_BUFFER_SIZE);
}
static int pca9698_write40(u8 addr, u8 offset, u8 *buffer)
{
u8 command = offset | 0x80; /* autoincrement */
return i2c_write(addr, command, 1, buffer, PCA9698_BUFFER_SIZE);
}
static void pca9698_set_bit(unsigned gpio, u8 *buffer, unsigned value)
{
unsigned byte = gpio / 8;
unsigned bit = gpio % 8;
if (value)
buffer[byte] |= (1 << bit);
else
buffer[byte] &= ~(1 << bit);
}
int pca9698_request(unsigned gpio, const char *label)
{
if (gpio >= PCA9698_GPIO_COUNT)
return -EINVAL;
return 0;
}
void pca9698_free(unsigned gpio)
{
}
int pca9698_direction_input(u8 addr, unsigned gpio)
{
u8 data[PCA9698_BUFFER_SIZE];
int res;
res = pca9698_read40(addr, PCA9698_REG_CONFIG, data);
if (res)
return res;
pca9698_set_bit(gpio, data, 1);
return pca9698_write40(addr, PCA9698_REG_CONFIG, data);
}
int pca9698_direction_output(u8 addr, unsigned gpio, int value)
{
u8 data[PCA9698_BUFFER_SIZE];
int res;
res = pca9698_set_value(addr, gpio, value);
if (res)
return res;
res = pca9698_read40(addr, PCA9698_REG_CONFIG, data);
if (res)
return res;
pca9698_set_bit(gpio, data, 0);
return pca9698_write40(addr, PCA9698_REG_CONFIG, data);
}
int pca9698_get_value(u8 addr, unsigned gpio)
{
unsigned config_byte = gpio / 8;
unsigned config_bit = gpio % 8;
unsigned value;
u8 data[PCA9698_BUFFER_SIZE];
int res;
res = pca9698_read40(addr, PCA9698_REG_INPUT, data);
if (res)
return -1;
value = data[config_byte] & (1 << config_bit);
return !!value;
}
int pca9698_set_value(u8 addr, unsigned gpio, int value)
{
u8 data[PCA9698_BUFFER_SIZE];
int res;
res = pca9698_read40(addr, PCA9698_REG_OUTPUT, data);
if (res)
return res;
pca9698_set_bit(gpio, data, value);
return pca9698_write40(addr, PCA9698_REG_OUTPUT, data);
}

View file

@ -0,0 +1,196 @@
/*
* (C) Copyright 2009 Samsung Electronics
* Minkyu Kang <mk7.kang@samsung.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/io.h>
#include <asm/gpio.h>
#define CON_MASK(x) (0xf << ((x) << 2))
#define CON_SFR(x, v) ((v) << ((x) << 2))
#define DAT_MASK(x) (0x1 << (x))
#define DAT_SET(x) (0x1 << (x))
#define PULL_MASK(x) (0x3 << ((x) << 1))
#define PULL_MODE(x, v) ((v) << ((x) << 1))
#define DRV_MASK(x) (0x3 << ((x) << 1))
#define DRV_SET(x, m) ((m) << ((x) << 1))
#define RATE_MASK(x) (0x1 << (x + 16))
#define RATE_SET(x) (0x1 << (x + 16))
void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg)
{
unsigned int value;
value = readl(&bank->con);
value &= ~CON_MASK(gpio);
value |= CON_SFR(gpio, cfg);
writel(value, &bank->con);
}
void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en)
{
unsigned int value;
s5p_gpio_cfg_pin(bank, gpio, GPIO_OUTPUT);
value = readl(&bank->dat);
value &= ~DAT_MASK(gpio);
if (en)
value |= DAT_SET(gpio);
writel(value, &bank->dat);
}
void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio)
{
s5p_gpio_cfg_pin(bank, gpio, GPIO_INPUT);
}
void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en)
{
unsigned int value;
value = readl(&bank->dat);
value &= ~DAT_MASK(gpio);
if (en)
value |= DAT_SET(gpio);
writel(value, &bank->dat);
}
unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio)
{
unsigned int value;
value = readl(&bank->dat);
return !!(value & DAT_MASK(gpio));
}
void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode)
{
unsigned int value;
value = readl(&bank->pull);
value &= ~PULL_MASK(gpio);
switch (mode) {
case GPIO_PULL_DOWN:
case GPIO_PULL_UP:
value |= PULL_MODE(gpio, mode);
break;
default:
break;
}
writel(value, &bank->pull);
}
void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode)
{
unsigned int value;
value = readl(&bank->drv);
value &= ~DRV_MASK(gpio);
switch (mode) {
case GPIO_DRV_1X:
case GPIO_DRV_2X:
case GPIO_DRV_3X:
case GPIO_DRV_4X:
value |= DRV_SET(gpio, mode);
break;
default:
return;
}
writel(value, &bank->drv);
}
void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode)
{
unsigned int value;
value = readl(&bank->drv);
value &= ~RATE_MASK(gpio);
switch (mode) {
case GPIO_DRV_FAST:
case GPIO_DRV_SLOW:
value |= RATE_SET(gpio);
break;
default:
return;
}
writel(value, &bank->drv);
}
struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio)
{
int bank = gpio / GPIO_PER_BANK;
bank *= sizeof(struct s5p_gpio_bank);
return (struct s5p_gpio_bank *) (s5p_gpio_base(gpio) + bank);
}
int s5p_gpio_get_pin(unsigned gpio)
{
return gpio % GPIO_PER_BANK;
}
/* Common GPIO API */
int gpio_request(unsigned gpio, const char *label)
{
return 0;
}
int gpio_free(unsigned gpio)
{
return 0;
}
int gpio_direction_input(unsigned gpio)
{
s5p_gpio_direction_input(s5p_gpio_get_bank(gpio),
s5p_gpio_get_pin(gpio));
return 0;
}
int gpio_direction_output(unsigned gpio, int value)
{
s5p_gpio_direction_output(s5p_gpio_get_bank(gpio),
s5p_gpio_get_pin(gpio), value);
return 0;
}
int gpio_get_value(unsigned gpio)
{
return (int) s5p_gpio_get_value(s5p_gpio_get_bank(gpio),
s5p_gpio_get_pin(gpio));
}
int gpio_set_value(unsigned gpio, int value)
{
s5p_gpio_set_value(s5p_gpio_get_bank(gpio),
s5p_gpio_get_pin(gpio), value);
return 0;
}

View file

@ -0,0 +1,209 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h>
#include <asm/gpio.h>
/* Flags for each GPIO */
#define GPIOF_OUTPUT (1 << 0) /* Currently set as an output */
#define GPIOF_HIGH (1 << 1) /* Currently set high */
#define GPIOF_RESERVED (1 << 2) /* Is in use / requested */
struct gpio_state {
const char *label; /* label given by requester */
u8 flags; /* flags (GPIOF_...) */
};
/*
* State of GPIOs
* TODO: Put this into sandbox state
*/
static struct gpio_state state[CONFIG_SANDBOX_GPIO_COUNT];
/* Access routines for GPIO state */
static u8 *get_gpio_flags(unsigned gp)
{
/* assert()'s could be disabled, so make sure we handle that */
assert(gp < ARRAY_SIZE(state));
if (gp >= ARRAY_SIZE(state)) {
static u8 invalid_flags;
printf("sandbox_gpio: error: invalid gpio %u\n", gp);
return &invalid_flags;
}
return &state[gp].flags;
}
static int get_gpio_flag(unsigned gp, int flag)
{
return (*get_gpio_flags(gp) & flag) != 0;
}
static int set_gpio_flag(unsigned gp, int flag, int value)
{
u8 *gpio = get_gpio_flags(gp);
if (value)
*gpio |= flag;
else
*gpio &= ~flag;
return 0;
}
static int check_reserved(unsigned gpio, const char *func)
{
if (!get_gpio_flag(gpio, GPIOF_RESERVED)) {
printf("sandbox_gpio: %s: error: gpio %u not reserved\n",
func, gpio);
return -1;
}
return 0;
}
/*
* Back-channel sandbox-internal-only access to GPIO state
*/
int sandbox_gpio_get_value(unsigned gp)
{
if (get_gpio_flag(gp, GPIOF_OUTPUT))
debug("sandbox_gpio: get_value on output gpio %u\n", gp);
return get_gpio_flag(gp, GPIOF_HIGH);
}
int sandbox_gpio_set_value(unsigned gp, int value)
{
return set_gpio_flag(gp, GPIOF_HIGH, value);
}
int sandbox_gpio_get_direction(unsigned gp)
{
return get_gpio_flag(gp, GPIOF_OUTPUT);
}
int sandbox_gpio_set_direction(unsigned gp, int output)
{
return set_gpio_flag(gp, GPIOF_OUTPUT, output);
}
/*
* These functions implement the public interface within U-Boot
*/
/* set GPIO port 'gp' as an input */
int gpio_direction_input(unsigned gp)
{
debug("%s: gp:%u\n", __func__, gp);
if (check_reserved(gp, __func__))
return -1;
return sandbox_gpio_set_direction(gp, 0);
}
/* set GPIO port 'gp' as an output, with polarity 'value' */
int gpio_direction_output(unsigned gp, int value)
{
debug("%s: gp:%u, value = %d\n", __func__, gp, value);
if (check_reserved(gp, __func__))
return -1;
return sandbox_gpio_set_direction(gp, 1) |
sandbox_gpio_set_value(gp, value);
}
/* read GPIO IN value of port 'gp' */
int gpio_get_value(unsigned gp)
{
debug("%s: gp:%u\n", __func__, gp);
if (check_reserved(gp, __func__))
return -1;
return sandbox_gpio_get_value(gp);
}
/* write GPIO OUT value to port 'gp' */
int gpio_set_value(unsigned gp, int value)
{
debug("%s: gp:%u, value = %d\n", __func__, gp, value);
if (check_reserved(gp, __func__))
return -1;
if (!sandbox_gpio_get_direction(gp)) {
printf("sandbox_gpio: error: set_value on input gpio %u\n", gp);
return -1;
}
return sandbox_gpio_set_value(gp, value);
}
int gpio_request(unsigned gp, const char *label)
{
debug("%s: gp:%u, label:%s\n", __func__, gp, label);
if (gp >= ARRAY_SIZE(state)) {
printf("sandbox_gpio: error: invalid gpio %u\n", gp);
return -1;
}
if (get_gpio_flag(gp, GPIOF_RESERVED)) {
printf("sandbox_gpio: error: gpio %u already reserved\n", gp);
return -1;
}
state[gp].label = label;
return set_gpio_flag(gp, GPIOF_RESERVED, 1);
}
int gpio_free(unsigned gp)
{
debug("%s: gp:%u\n", __func__, gp);
if (check_reserved(gp, __func__))
return -1;
state[gp].label = NULL;
return set_gpio_flag(gp, GPIOF_RESERVED, 0);
}
/* Display GPIO information */
void gpio_info(void)
{
unsigned gpio;
puts("Sandbox GPIOs\n");
for (gpio = 0; gpio < ARRAY_SIZE(state); ++gpio) {
const char *label = state[gpio].label;
printf("%4d: %s: %d [%c] %s\n",
gpio,
sandbox_gpio_get_direction(gpio) ? "out" : " in",
sandbox_gpio_get_value(gpio),
get_gpio_flag(gpio, GPIOF_RESERVED) ? 'x' : ' ',
label ? label : "");
}
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (C) 2012 Stefan Roese <sr@denx.de>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*/
/*
* Driver for SPEAr600 GPIO controller
*/
#include <common.h>
#include <asm/arch/hardware.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <errno.h>
static int gpio_direction(unsigned gpio,
enum gpio_direction direction)
{
struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE;
u32 val;
val = readl(&regs->gpiodir);
if (direction == GPIO_DIRECTION_OUT)
val |= 1 << gpio;
else
val &= ~(1 << gpio);
writel(val, &regs->gpiodir);
return 0;
}
int gpio_set_value(unsigned gpio, int value)
{
struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE;
writel(1 << gpio, &regs->gpiodata[DATA_REG_ADDR(gpio)]);
return 0;
}
int gpio_get_value(unsigned gpio)
{
struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE;
u32 val;
val = readl(&regs->gpiodata[DATA_REG_ADDR(gpio)]);
return !!val;
}
int gpio_request(unsigned gpio, const char *label)
{
if (gpio >= SPEAR_GPIO_COUNT)
return -EINVAL;
return 0;
}
int gpio_free(unsigned gpio)
{
return 0;
}
void gpio_toggle_value(unsigned gpio)
{
gpio_set_value(gpio, !gpio_get_value(gpio));
}
int gpio_direction_input(unsigned gpio)
{
return gpio_direction(gpio, GPIO_DIRECTION_IN);
}
int gpio_direction_output(unsigned gpio, int value)
{
int ret = gpio_direction(gpio, GPIO_DIRECTION_OUT);
if (ret < 0)
return ret;
gpio_set_value(gpio, value);
return 0;
}

View file

@ -0,0 +1,262 @@
/*
* NVIDIA Tegra2 GPIO handling.
* (C) Copyright 2010-2012
* NVIDIA Corporation <www.nvidia.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*/
/*
* Based on (mostly copied from) kw_gpio.c based Linux 2.6 kernel driver.
* Tom Warren (twarren@nvidia.com)
*/
#include <common.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/arch/tegra2.h>
#include <asm/gpio.h>
enum {
TEGRA2_CMD_INFO,
TEGRA2_CMD_PORT,
TEGRA2_CMD_OUTPUT,
TEGRA2_CMD_INPUT,
};
static struct gpio_names {
char name[GPIO_NAME_SIZE];
} gpio_names[MAX_NUM_GPIOS];
static char *get_name(int i)
{
return *gpio_names[i].name ? gpio_names[i].name : "UNKNOWN";
}
/* Return config of pin 'gpio' as GPIO (1) or SFPIO (0) */
static int get_config(unsigned gpio)
{
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
u32 u;
int type;
u = readl(&bank->gpio_config[GPIO_PORT(gpio)]);
type = (u >> GPIO_BIT(gpio)) & 1;
debug("get_config: port = %d, bit = %d is %s\n",
GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO");
return type;
}
/* Config pin 'gpio' as GPIO or SFPIO, based on 'type' */
static void set_config(unsigned gpio, int type)
{
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
u32 u;
debug("set_config: port = %d, bit = %d, %s\n",
GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO");
u = readl(&bank->gpio_config[GPIO_PORT(gpio)]);
if (type) /* GPIO */
u |= 1 << GPIO_BIT(gpio);
else
u &= ~(1 << GPIO_BIT(gpio));
writel(u, &bank->gpio_config[GPIO_PORT(gpio)]);
}
/* Return GPIO pin 'gpio' direction - 0 = input or 1 = output */
static int get_direction(unsigned gpio)
{
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
u32 u;
int dir;
u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]);
dir = (u >> GPIO_BIT(gpio)) & 1;
debug("get_direction: port = %d, bit = %d, %s\n",
GPIO_FULLPORT(gpio), GPIO_BIT(gpio), dir ? "OUT" : "IN");
return dir;
}
/* Config GPIO pin 'gpio' as input or output (OE) as per 'output' */
static void set_direction(unsigned gpio, int output)
{
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
u32 u;
debug("set_direction: port = %d, bit = %d, %s\n",
GPIO_FULLPORT(gpio), GPIO_BIT(gpio), output ? "OUT" : "IN");
u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]);
if (output)
u |= 1 << GPIO_BIT(gpio);
else
u &= ~(1 << GPIO_BIT(gpio));
writel(u, &bank->gpio_dir_out[GPIO_PORT(gpio)]);
}
/* set GPIO pin 'gpio' output bit as 0 or 1 as per 'high' */
static void set_level(unsigned gpio, int high)
{
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
u32 u;
debug("set_level: port = %d, bit %d == %d\n",
GPIO_FULLPORT(gpio), GPIO_BIT(gpio), high);
u = readl(&bank->gpio_out[GPIO_PORT(gpio)]);
if (high)
u |= 1 << GPIO_BIT(gpio);
else
u &= ~(1 << GPIO_BIT(gpio));
writel(u, &bank->gpio_out[GPIO_PORT(gpio)]);
}
/*
* Generic_GPIO primitives.
*/
int gpio_request(unsigned gpio, const char *label)
{
if (gpio >= MAX_NUM_GPIOS)
return -1;
if (label != NULL) {
strncpy(gpio_names[gpio].name, label, GPIO_NAME_SIZE);
gpio_names[gpio].name[GPIO_NAME_SIZE - 1] = '\0';
}
/* Configure as a GPIO */
set_config(gpio, 1);
return 0;
}
int gpio_free(unsigned gpio)
{
if (gpio >= MAX_NUM_GPIOS)
return -1;
gpio_names[gpio].name[0] = '\0';
/* Do not configure as input or change pin mux here */
return 0;
}
/* read GPIO OUT value of pin 'gpio' */
static int gpio_get_output_value(unsigned gpio)
{
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
int val;
debug("gpio_get_output_value: pin = %d (port %d:bit %d)\n",
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio));
val = readl(&bank->gpio_out[GPIO_PORT(gpio)]);
return (val >> GPIO_BIT(gpio)) & 1;
}
/* set GPIO pin 'gpio' as an input */
int gpio_direction_input(unsigned gpio)
{
debug("gpio_direction_input: pin = %d (port %d:bit %d)\n",
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio));
/* Configure GPIO direction as input. */
set_direction(gpio, 0);
return 0;
}
/* set GPIO pin 'gpio' as an output, with polarity 'value' */
int gpio_direction_output(unsigned gpio, int value)
{
debug("gpio_direction_output: pin = %d (port %d:bit %d) = %s\n",
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio),
value ? "HIGH" : "LOW");
/* Configure GPIO output value. */
set_level(gpio, value);
/* Configure GPIO direction as output. */
set_direction(gpio, 1);
return 0;
}
/* read GPIO IN value of pin 'gpio' */
int gpio_get_value(unsigned gpio)
{
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
int val;
debug("gpio_get_value: pin = %d (port %d:bit %d)\n",
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio));
val = readl(&bank->gpio_in[GPIO_PORT(gpio)]);
return (val >> GPIO_BIT(gpio)) & 1;
}
/* write GPIO OUT value to pin 'gpio' */
int gpio_set_value(unsigned gpio, int value)
{
debug("gpio_set_value: pin = %d (port %d:bit %d), value = %d\n",
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), value);
/* Configure GPIO output value. */
set_level(gpio, value);
return 0;
}
/*
* Display Tegra GPIO information
*/
void gpio_info(void)
{
unsigned c;
int type;
for (c = 0; c < MAX_NUM_GPIOS; c++) {
type = get_config(c); /* GPIO, not SFPIO */
if (type) {
printf("GPIO_%d:\t%s is an %s, ", c,
get_name(c),
get_direction(c) ? "OUTPUT" : "INPUT");
if (get_direction(c))
printf("value = %d", gpio_get_output_value(c));
else
printf("value = %d", gpio_get_value(c));
printf("\n");
} else
continue;
}
}

View file

@ -0,0 +1,59 @@
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# (C) Copyright 2001
# Erik Theisen, Wave 7 Optics, etheisen@mindspring.com.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# 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 $(TOPDIR)/config.mk
#CFLAGS += -DDEBUG
LIB = $(obj)libhwmon.o
COBJS-$(CONFIG_DTT_ADM1021) += adm1021.o
COBJS-$(CONFIG_DTT_ADT7460) += adt7460.o
COBJS-$(CONFIG_DTT_DS1621) += ds1621.o
COBJS-$(CONFIG_DTT_DS1722) += ds1722.o
COBJS-$(CONFIG_DTT_DS1775) += ds1775.o
COBJS-$(CONFIG_DTT_LM63) += lm63.o
COBJS-$(CONFIG_DTT_LM73) += lm73.o
COBJS-$(CONFIG_DTT_LM75) += lm75.o
COBJS-$(CONFIG_DTT_LM81) += lm81.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,180 @@
/*
* (C) Copyright 2003
* Murray Jensen, CSIRO-MIT, Murray.Jensen@csiro.au
*
* based on dtt/lm75.c which is ...
*
* (C) Copyright 2001
* Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*/
/*
* Analog Devices's ADM1021
* "Low Cost Microprocessor System Temperature Monitor"
*/
#include <common.h>
#include <i2c.h>
#include <dtt.h>
#define DTT_READ_LOC_VALUE 0x00
#define DTT_READ_REM_VALUE 0x01
#define DTT_READ_STATUS 0x02
#define DTT_READ_CONFIG 0x03
#define DTT_READ_CONVRATE 0x04
#define DTT_READ_LOC_HIGHLIM 0x05
#define DTT_READ_LOC_LOWLIM 0x06
#define DTT_READ_REM_HIGHLIM 0x07
#define DTT_READ_REM_LOWLIM 0x08
#define DTT_READ_DEVID 0xfe
#define DTT_WRITE_CONFIG 0x09
#define DTT_WRITE_CONVRATE 0x0a
#define DTT_WRITE_LOC_HIGHLIM 0x0b
#define DTT_WRITE_LOC_LOWLIM 0x0c
#define DTT_WRITE_REM_HIGHLIM 0x0d
#define DTT_WRITE_REM_LOWLIM 0x0e
#define DTT_WRITE_ONESHOT 0x0f
#define DTT_STATUS_BUSY 0x80 /* 1=ADC Converting */
#define DTT_STATUS_LHIGH 0x40 /* 1=Local High Temp Limit Tripped */
#define DTT_STATUS_LLOW 0x20 /* 1=Local Low Temp Limit Tripped */
#define DTT_STATUS_RHIGH 0x10 /* 1=Remote High Temp Limit Tripped */
#define DTT_STATUS_RLOW 0x08 /* 1=Remote Low Temp Limit Tripped */
#define DTT_STATUS_OPEN 0x04 /* 1=Remote Sensor Open-Circuit */
#define DTT_CONFIG_ALERT_MASKED 0x80 /* 0=ALERT Enabled, 1=ALERT Masked */
#define DTT_CONFIG_STANDBY 0x40 /* 0=Run, 1=Standby */
#define DTT_ADM1021_DEVID 0x41
typedef
struct {
uint i2c_addr:7; /* 7bit i2c chip address */
uint conv_rate:3; /* conversion rate */
uint enable_alert:1; /* enable alert output pin */
uint enable_local:1; /* enable internal temp sensor */
uint max_local:8; /* internal temp maximum */
uint min_local:8; /* internal temp minimum */
uint enable_remote:1; /* enable remote temp sensor */
uint max_remote:8; /* remote temp maximum */
uint min_remote:8; /* remote temp minimum */
}
dtt_cfg_t;
dtt_cfg_t dttcfg[] = CONFIG_SYS_DTT_ADM1021;
int
dtt_read (int sensor, int reg)
{
dtt_cfg_t *dcp = &dttcfg[sensor >> 1];
uchar data;
if (i2c_read(dcp->i2c_addr, reg, 1, &data, 1) != 0)
return -1;
return (int)data;
} /* dtt_read() */
int
dtt_write (int sensor, int reg, int val)
{
dtt_cfg_t *dcp = &dttcfg[sensor >> 1];
uchar data;
data = (uchar)(val & 0xff);
if (i2c_write(dcp->i2c_addr, reg, 1, &data, 1) != 0)
return 1;
return 0;
} /* dtt_write() */
int
dtt_init_one(int sensor)
{
dtt_cfg_t *dcp = &dttcfg[sensor >> 1];
int reg, val;
if (((sensor & 1) == 0 ? dcp->enable_local : dcp->enable_remote) == 0)
return 1; /* sensor is disabled (or rather ignored) */
/*
* Setup High Limit register
*/
if ((sensor & 1) == 0) {
reg = DTT_WRITE_LOC_HIGHLIM;
val = dcp->max_local;
}
else {
reg = DTT_WRITE_REM_HIGHLIM;
val = dcp->max_remote;
}
if (dtt_write (sensor, reg, val) != 0)
return 1;
/*
* Setup Low Limit register
*/
if ((sensor & 1) == 0) {
reg = DTT_WRITE_LOC_LOWLIM;
val = dcp->min_local;
}
else {
reg = DTT_WRITE_REM_LOWLIM;
val = dcp->min_remote;
}
if (dtt_write (sensor, reg, val) != 0)
return 1;
/* shouldn't hurt if the rest gets done twice */
/*
* Setup Conversion Rate register
*/
if (dtt_write (sensor, DTT_WRITE_CONVRATE, dcp->conv_rate) != 0)
return 1;
/*
* Setup configuraton register
*/
val = 0; /* running */
if (dcp->enable_alert == 0)
val |= DTT_CONFIG_ALERT_MASKED; /* mask ALERT pin */
if (dtt_write (sensor, DTT_WRITE_CONFIG, val) != 0)
return 1;
return 0;
} /* dtt_init_one() */
int
dtt_get_temp (int sensor)
{
signed char val;
if ((sensor & 1) == 0)
val = dtt_read(sensor, DTT_READ_LOC_VALUE);
else
val = dtt_read(sensor, DTT_READ_REM_VALUE);
return (int) val;
} /* dtt_get_temp() */

View file

@ -0,0 +1,83 @@
/*
* (C) Copyright 2008
* Ricado Ribalda-Universidad Autonoma de Madrid, ricardo.ribalda@uam.es
* This work has been supported by: QTechnology http://qtec.com/
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <common.h>
#include <i2c.h>
#include <dtt.h>
#define ADT7460_ADDRESS 0x2c
#define ADT7460_INVALID 128
#define ADT7460_CONFIG 0x40
#define ADT7460_REM1_TEMP 0x25
#define ADT7460_LOCAL_TEMP 0x26
#define ADT7460_REM2_TEMP 0x27
int dtt_read(int sensor, int reg)
{
u8 dir = reg;
u8 data;
if (i2c_read(ADT7460_ADDRESS, dir, 1, &data, 1) == -1)
return -1;
if (data == ADT7460_INVALID)
return -1;
return data;
}
int dtt_write(int sensor, int reg, int val)
{
u8 dir = reg;
u8 data = val;
if (i2c_write(ADT7460_ADDRESS, dir, 1, &data, 1) == -1)
return -1;
return 0;
}
int dtt_init_one(int sensor)
{
printf("ADT7460 at I2C address 0x%2x\n", ADT7460_ADDRESS);
if (dtt_write(0, ADT7460_CONFIG, 1) == -1) {
puts("Error initialiting ADT7460\n");
return -1;
}
return 0;
}
int dtt_get_temp(int sensor)
{
int aux;
u8 table[] =
{ ADT7460_REM1_TEMP, ADT7460_LOCAL_TEMP, ADT7460_REM2_TEMP };
if (sensor > 2) {
puts("DTT sensor does not exist\n");
return -1;
}
aux = dtt_read(0, table[sensor]);
if (aux == -1) {
puts("DTT temperature read failed\n");
return -1;
}
return aux;
}

View file

@ -0,0 +1,171 @@
/*
* (C) Copyright 2001
* Erik Theisen, Wave 7 Optics, etheisen@mindspring.com.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*/
/*
* Dallas Semiconductor's DS1621/1631 Digital Thermometer and Thermostat.
*/
#include <common.h>
#include <i2c.h>
#include <dtt.h>
/*
* Device code
*/
#define DTT_I2C_DEV_CODE 0x48 /* Dallas Semi's DS1621 */
#define DTT_READ_TEMP 0xAA
#define DTT_READ_COUNTER 0xA8
#define DTT_READ_SLOPE 0xA9
#define DTT_WRITE_START_CONV 0xEE
#define DTT_WRITE_STOP_CONV 0x22
#define DTT_TEMP_HIGH 0xA1
#define DTT_TEMP_LOW 0xA2
#define DTT_CONFIG 0xAC
/*
* Config register bits
*/
#define DTT_CONFIG_1SHOT 0x01
#define DTT_CONFIG_POLARITY 0x02
#define DTT_CONFIG_R0 0x04 /* ds1631 only */
#define DTT_CONFIG_R1 0x08 /* ds1631 only */
#define DTT_CONFIG_NVB 0x10
#define DTT_CONFIG_TLF 0x20
#define DTT_CONFIG_THF 0x40
#define DTT_CONFIG_DONE 0x80
int dtt_read(int sensor, int reg)
{
int dlen;
uchar data[2];
/* Calculate sensor address and command */
sensor = DTT_I2C_DEV_CODE + (sensor & 0x07); /* Calculate addr of ds1621*/
/* Prepare to handle 2 byte result */
switch(reg) {
case DTT_READ_TEMP:
case DTT_TEMP_HIGH:
case DTT_TEMP_LOW:
dlen = 2;
break;
default:
dlen = 1;
}
/* Now try to read the register */
if (i2c_read(sensor, reg, 1, data, dlen) != 0)
return 1;
/* Handle 2 byte result */
if (dlen == 2)
return (short)((data[0] << 8) | data[1]);
return (int)data[0];
}
int dtt_write(int sensor, int reg, int val)
{
int dlen;
uchar data[2];
/* Calculate sensor address and register */
sensor = DTT_I2C_DEV_CODE + (sensor & 0x07);
/* Handle various data sizes. */
switch(reg) {
case DTT_READ_TEMP:
case DTT_TEMP_HIGH:
case DTT_TEMP_LOW:
dlen = 2;
data[0] = (char)((val >> 8) & 0xff); /* MSB first */
data[1] = (char)(val & 0xff);
break;
case DTT_WRITE_START_CONV:
case DTT_WRITE_STOP_CONV:
dlen = 0;
data[0] = (char)0;
data[1] = (char)0;
break;
default:
dlen = 1;
data[0] = (char)(val & 0xff);
}
/* Write value to device */
if (i2c_write(sensor, reg, 1, data, dlen) != 0)
return 1;
/* Poll NV memory busy bit in case write was to register stored in EEPROM */
while(i2c_reg_read(sensor, DTT_CONFIG) & DTT_CONFIG_NVB)
;
return 0;
}
int dtt_init_one(int sensor)
{
int val;
/* Setup High Temp */
val = ((CONFIG_SYS_DTT_MAX_TEMP * 2) << 7) & 0xff80;
if (dtt_write(sensor, DTT_TEMP_HIGH, val) != 0)
return 1;
/* Setup Low Temp - hysteresis */
val = (((CONFIG_SYS_DTT_MAX_TEMP - CONFIG_SYS_DTT_HYSTERESIS) * 2) << 7) & 0xff80;
if (dtt_write(sensor, DTT_TEMP_LOW, val) != 0)
return 1;
/*
* Setup configuraton register
*
* Clear THF & TLF, Reserved = 1, Polarity = Active Low, One Shot = YES
*
* We run in polled mode, since there isn't any way to know if this
* lousy device is ready to provide temperature readings on power up.
*/
val = 0x9;
if (dtt_write(sensor, DTT_CONFIG, val) != 0)
return 1;
return 0;
}
int dtt_get_temp(int sensor)
{
int i;
/* Start a conversion, may take up to 1 second. */
dtt_write(sensor, DTT_WRITE_START_CONV, 0);
for (i = 0; i <= 10; i++) {
udelay(100000);
if (dtt_read(sensor, DTT_CONFIG) & DTT_CONFIG_DONE)
break;
}
return (dtt_read(sensor, DTT_READ_TEMP) / 256);
}

View file

@ -0,0 +1,138 @@
#include <common.h>
#include <asm/ic/ssi.h>
#include <ds1722.h>
static void ds1722_select(int dev)
{
ssi_set_interface(4096, 0, 0, 0);
ssi_chip_select(0);
udelay(1);
ssi_chip_select(dev);
udelay(1);
}
u8 ds1722_read(int dev, int addr)
{
u8 res;
ds1722_select(dev);
ssi_tx_byte(addr);
res = ssi_rx_byte();
ssi_chip_select(0);
return res;
}
void ds1722_write(int dev, int addr, u8 data)
{
ds1722_select(dev);
ssi_tx_byte(0x80|addr);
ssi_tx_byte(data);
ssi_chip_select(0);
}
u16 ds1722_temp(int dev, int resolution)
{
static int useconds[] = {
75000, 150000, 300000, 600000, 1200000
};
char temp;
u16 res;
/* set up the desired resulotion ... */
ds1722_write(dev, 0, 0xe0 | (resolution << 1));
/* wait while the chip measures the tremperature */
udelay(useconds[resolution]);
res = (temp = ds1722_read(dev, 2)) << 8;
if (temp < 0) {
temp = (16 - (ds1722_read(dev, 1) >> 4)) & 0x0f;
} else {
temp = (ds1722_read(dev, 1) >> 4);
}
switch (temp) {
case 0:
/* .0000 */
break;
case 1:
/* .0625 */
res |=1;
break;
case 2:
/* .1250 */
res |=1;
break;
case 3:
/* .1875 */
res |=2;
break;
case 4:
/* .2500 */
res |=3;
break;
case 5:
/* .3125 */
res |=3;
break;
case 6:
/* .3750 */
res |=4;
break;
case 7:
/* .4375 */
res |=4;
break;
case 8:
/* .5000 */
res |=5;
break;
case 9:
/* .5625 */
res |=6;
break;
case 10:
/* .6250 */
res |=6;
break;
case 11:
/* .6875 */
res |=7;
break;
case 12:
/* .7500 */
res |=8;
break;
case 13:
/* .8125 */
res |=8;
break;
case 14:
/* .8750 */
res |=9;
break;
case 15:
/* .9375 */
res |=9;
break;
}
return res;
}
int ds1722_probe(int dev)
{
u16 temp = ds1722_temp(dev, DS1722_RESOLUTION_12BIT);
printf("%d.%d deg C\n\n", (char)(temp >> 8), temp & 0xff);
return 0;
}

View file

@ -0,0 +1,139 @@
/*
* 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
*/
/*
* Dallas Semiconductor's DS1775 Digital Thermometer and Thermostat
*/
#include <common.h>
#include <i2c.h>
#include <dtt.h>
#define DTT_I2C_DEV_CODE CONFIG_SYS_I2C_DTT_ADDR /* Dallas Semi's DS1775 device code */
#define DTT_READ_TEMP 0x0
#define DTT_CONFIG 0x1
#define DTT_TEMP_HYST 0x2
#define DTT_TEMP_OS 0x3
int dtt_read(int sensor, int reg)
{
int dlen;
uchar data[2];
/*
* Calculate sensor address and command
*/
sensor = DTT_I2C_DEV_CODE + (sensor & 0x07); /* Calculate addr of ds1775 */
/*
* Prepare to handle 2 byte result
*/
if ((reg == DTT_READ_TEMP) ||
(reg == DTT_TEMP_OS) || (reg == DTT_TEMP_HYST))
dlen = 2;
else
dlen = 1;
/*
* Now try to read the register
*/
if (i2c_read(sensor, reg, 1, data, dlen) != 0)
return 1;
/*
* Handle 2 byte result
*/
if (dlen == 2)
return ((int)((short)data[1] + (((short)data[0]) << 8)));
return (int) data[0];
}
int dtt_write(int sensor, int reg, int val)
{
int dlen;
uchar data[2];
/*
* Calculate sensor address and register
*/
sensor = DTT_I2C_DEV_CODE + (sensor & 0x07);
/*
* Handle various data sizes
*/
if ((reg == DTT_READ_TEMP) ||
(reg == DTT_TEMP_OS) || (reg == DTT_TEMP_HYST)) {
dlen = 2;
data[0] = (char)((val >> 8) & 0xff); /* MSB first */
data[1] = (char)(val & 0xff);
} else {
dlen = 1;
data[0] = (char)(val & 0xff);
}
/*
* Write value to device
*/
if (i2c_write(sensor, reg, 1, data, dlen) != 0)
return 1;
return 0;
}
int dtt_init_one(int sensor)
{
int val;
/*
* Setup High Temp
*/
val = ((CONFIG_SYS_DTT_MAX_TEMP * 2) << 7) & 0xff80;
if (dtt_write(sensor, DTT_TEMP_OS, val) != 0)
return 1;
udelay(50000); /* Max 50ms */
/*
* Setup Low Temp - hysteresis
*/
val = (((CONFIG_SYS_DTT_MAX_TEMP - CONFIG_SYS_DTT_HYSTERESIS) * 2) << 7) & 0xff80;
if (dtt_write(sensor, DTT_TEMP_HYST, val) != 0)
return 1;
udelay(50000); /* Max 50ms */
/*
* Setup configuraton register
*
* Fault Tolerance limits 4, Thermometer resolution bits is 9,
* Polarity = Active Low,continuous conversion mode, Thermostat
* mode is interrupt mode
*/
val = 0xa;
if (dtt_write(sensor, DTT_CONFIG, val) != 0)
return 1;
udelay(50000); /* Max 50ms */
return 0;
}
int dtt_get_temp(int sensor)
{
return (dtt_read(sensor, DTT_READ_TEMP) / 256);
}

View file

@ -0,0 +1,177 @@
/*
* (C) Copyright 2007-2008
* Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de
* based on lm75.c by Bill Hunter
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*/
/*
* National LM63/LM64 Temperature Sensor
* Main difference: LM 64 has -16 Kelvin temperature offset
*/
#include <common.h>
#include <i2c.h>
#include <dtt.h>
#define DTT_I2C_LM63_ADDR 0x4C /* National LM63 device */
#define DTT_READ_TEMP_RMT_MSB 0x01
#define DTT_CONFIG 0x03
#define DTT_READ_TEMP_RMT_LSB 0x10
#define DTT_TACHLIM_LSB 0x48
#define DTT_TACHLIM_MSB 0x49
#define DTT_FAN_CONFIG 0x4A
#define DTT_PWM_FREQ 0x4D
#define DTT_PWM_LOOKUP_BASE 0x50
struct pwm_lookup_entry {
u8 temp;
u8 pwm;
};
/*
* Device code
*/
int dtt_read(int sensor, int reg)
{
int dlen;
uchar data[2];
/*
* Calculate sensor address and register.
*/
if (!sensor)
sensor = DTT_I2C_LM63_ADDR; /* legacy config */
dlen = 1;
/*
* Now try to read the register.
*/
if (i2c_read(sensor, reg, 1, data, dlen) != 0)
return -1;
return (int)data[0];
} /* dtt_read() */
int dtt_write(int sensor, int reg, int val)
{
int dlen;
uchar data[2];
/*
* Calculate sensor address and register.
*/
if (!sensor)
sensor = DTT_I2C_LM63_ADDR; /* legacy config */
dlen = 1;
data[0] = (char)(val & 0xff);
/*
* Write value to register.
*/
if (i2c_write(sensor, reg, 1, data, dlen) != 0)
return 1;
return 0;
} /* dtt_write() */
static int is_lm64(int sensor)
{
return sensor && (sensor != DTT_I2C_LM63_ADDR);
}
int dtt_init_one(int sensor)
{
int i;
int val;
struct pwm_lookup_entry pwm_lookup[] = CONFIG_DTT_PWM_LOOKUPTABLE;
/*
* Set PWM Frequency to 2.5% resolution
*/
val = 20;
if (dtt_write(sensor, DTT_PWM_FREQ, val) != 0)
return 1;
/*
* Set Tachometer Limit
*/
val = CONFIG_DTT_TACH_LIMIT;
if (dtt_write(sensor, DTT_TACHLIM_LSB, val & 0xff) != 0)
return 1;
if (dtt_write(sensor, DTT_TACHLIM_MSB, (val >> 8) & 0xff) != 0)
return 1;
/*
* Make sure PWM Lookup-Table is writeable
*/
if (dtt_write(sensor, DTT_FAN_CONFIG, 0x20) != 0)
return 1;
/*
* Setup PWM Lookup-Table
*/
for (i = 0; i < sizeof(pwm_lookup) / sizeof(struct pwm_lookup_entry);
i++) {
int address = DTT_PWM_LOOKUP_BASE + 2 * i;
val = pwm_lookup[i].temp;
if (is_lm64(sensor))
val -= 16;
if (dtt_write(sensor, address, val) != 0)
return 1;
val = dtt_read(sensor, address);
val = pwm_lookup[i].pwm;
if (dtt_write(sensor, address + 1, val) != 0)
return 1;
}
/*
* Enable PWM Lookup-Table, PWM Clock 360 kHz, Tachometer Mode 2
*/
val = 0x02;
if (dtt_write(sensor, DTT_FAN_CONFIG, val) != 0)
return 1;
/*
* Enable Tach input
*/
val = dtt_read(sensor, DTT_CONFIG) | 0x04;
if (dtt_write(sensor, DTT_CONFIG, val) != 0)
return 1;
return 0;
}
int dtt_get_temp(int sensor)
{
s16 temp = (dtt_read(sensor, DTT_READ_TEMP_RMT_MSB) << 8)
| (dtt_read(sensor, DTT_READ_TEMP_RMT_LSB));
if (is_lm64(sensor))
temp += 16 << 8;
/* Ignore LSB for now, U-Boot only prints natural numbers */
return temp >> 8;
}

View file

@ -0,0 +1,162 @@
/*
* (C) Copyright 2007-2008
* Larry Johnson, lrj@acm.org
*
* based on dtt/lm75.c which is ...
*
* (C) Copyright 2001
* Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*/
/*
* National Semiconductor LM73 Temperature Sensor
*/
#include <common.h>
#include <i2c.h>
#include <dtt.h>
/*
* Device code
*/
#define DTT_I2C_DEV_CODE 0x48 /* National Semi's LM73 device */
#define DTT_READ_TEMP 0x0
#define DTT_CONFIG 0x1
#define DTT_TEMP_HIGH 0x2
#define DTT_TEMP_LOW 0x3
#define DTT_CONTROL 0x4
#define DTT_ID 0x7
int dtt_read(int const sensor, int const reg)
{
int dlen;
uint8_t data[2];
/*
* Validate 'reg' param and get register size.
*/
switch (reg) {
case DTT_CONFIG:
case DTT_CONTROL:
dlen = 1;
break;
case DTT_READ_TEMP:
case DTT_TEMP_HIGH:
case DTT_TEMP_LOW:
case DTT_ID:
dlen = 2;
break;
default:
return -1;
}
/*
* Try to read the register at the calculated sensor address.
*/
if (0 !=
i2c_read(DTT_I2C_DEV_CODE + (sensor & 0x07), reg, 1, data, dlen))
return -1;
/*
* Handle 2 byte result.
*/
if (2 == dlen)
return (int)((unsigned)data[0] << 8 | (unsigned)data[1]);
return (int)data[0];
} /* dtt_read() */
int dtt_write(int const sensor, int const reg, int const val)
{
int dlen;
uint8_t data[2];
/*
* Validate 'reg' param and handle register size
*/
switch (reg) {
case DTT_CONFIG:
case DTT_CONTROL:
dlen = 1;
data[0] = (uint8_t) val;
break;
case DTT_TEMP_HIGH:
case DTT_TEMP_LOW:
dlen = 2;
data[0] = (uint8_t) (val >> 8); /* MSB first */
data[1] = (uint8_t) val;
break;
default:
return -1;
}
/*
* Write value to register at the calculated sensor address.
*/
return 0 != i2c_write(DTT_I2C_DEV_CODE + (sensor & 0x07), reg, 1, data,
dlen);
} /* dtt_write() */
int dtt_init_one(int const sensor)
{
int val;
/*
* Validate the Identification register
*/
if (0x0190 != dtt_read(sensor, DTT_ID))
return -1;
/*
* Setup THIGH (upper-limit) and TLOW (lower-limit) registers
*/
val = CONFIG_SYS_DTT_MAX_TEMP << 7;
if (dtt_write(sensor, DTT_TEMP_HIGH, val))
return -1;
val = CONFIG_SYS_DTT_MIN_TEMP << 7;
if (dtt_write(sensor, DTT_TEMP_LOW, val))
return -1;
/*
* Setup configuraton register
*/
/* config = alert active low, disabled, and reset */
val = 0x64;
if (dtt_write(sensor, DTT_CONFIG, val))
return -1;
/*
* Setup control/status register
*/
/* control = temp resolution 0.25C */
val = 0x00;
if (dtt_write(sensor, DTT_CONTROL, val))
return -1;
dtt_read(sensor, DTT_CONTROL); /* clear temperature flags */
return 0;
} /* dtt_init_one() */
int dtt_get_temp(int const sensor)
{
int const ret = dtt_read(sensor, DTT_READ_TEMP);
if (ret < 0) {
printf("DTT temperature read failed.\n");
return 0;
}
return (int)((int16_t) ret + 0x0040) >> 7;
} /* dtt_get_temp() */

View file

@ -0,0 +1,159 @@
/*
* (C) Copyright 2001
* Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*/
/*
* On Semiconductor's LM75 Temperature Sensor
*/
#include <common.h>
#include <i2c.h>
#include <dtt.h>
/*
* Device code
*/
#if defined(CONFIG_SYS_I2C_DTT_ADDR)
#define DTT_I2C_DEV_CODE CONFIG_SYS_I2C_DTT_ADDR
#else
#define DTT_I2C_DEV_CODE 0x48 /* ON Semi's LM75 device */
#endif
#define DTT_READ_TEMP 0x0
#define DTT_CONFIG 0x1
#define DTT_TEMP_HYST 0x2
#define DTT_TEMP_SET 0x3
int dtt_read(int sensor, int reg)
{
int dlen;
uchar data[2];
#ifdef CONFIG_DTT_AD7414
/*
* On AD7414 the first value upon bootup is not read correctly.
* This is most likely because of the 800ms update time of the
* temp register in normal update mode. To get current values
* each time we issue the "dtt" command including upon powerup
* we switch into one-short mode.
*
* Issue one-shot mode command
*/
dtt_write(sensor, DTT_CONFIG, 0x64);
#endif
/* Validate 'reg' param */
if((reg < 0) || (reg > 3))
return -1;
/* Calculate sensor address and register. */
sensor = DTT_I2C_DEV_CODE + (sensor & 0x07);
/* Prepare to handle 2 byte result. */
if ((reg == DTT_READ_TEMP) ||
(reg == DTT_TEMP_HYST) ||
(reg == DTT_TEMP_SET))
dlen = 2;
else
dlen = 1;
/* Now try to read the register. */
if (i2c_read(sensor, reg, 1, data, dlen) != 0)
return -1;
/* Handle 2 byte result. */
if (dlen == 2)
return ((int)((short)data[1] + (((short)data[0]) << 8)));
return (int)data[0];
} /* dtt_read() */
int dtt_write(int sensor, int reg, int val)
{
int dlen;
uchar data[2];
/* Validate 'reg' param */
if ((reg < 0) || (reg > 3))
return 1;
/* Calculate sensor address and register. */
sensor = DTT_I2C_DEV_CODE + (sensor & 0x07);
/* Handle 2 byte values. */
if ((reg == DTT_READ_TEMP) ||
(reg == DTT_TEMP_HYST) ||
(reg == DTT_TEMP_SET)) {
dlen = 2;
data[0] = (char)((val >> 8) & 0xff); /* MSB first */
data[1] = (char)(val & 0xff);
} else {
dlen = 1;
data[0] = (char)(val & 0xff);
}
/* Write value to register. */
if (i2c_write(sensor, reg, 1, data, dlen) != 0)
return 1;
return 0;
} /* dtt_write() */
int dtt_init_one(int sensor)
{
int val;
/* Setup TSET ( trip point ) register */
val = ((CONFIG_SYS_DTT_MAX_TEMP * 2) << 7) & 0xff80; /* trip */
if (dtt_write(sensor, DTT_TEMP_SET, val) != 0)
return 1;
/* Setup THYST ( untrip point ) register - Hysteresis */
val = (((CONFIG_SYS_DTT_MAX_TEMP - CONFIG_SYS_DTT_HYSTERESIS) * 2) << 7) & 0xff80;
if (dtt_write(sensor, DTT_TEMP_HYST, val) != 0)
return 1;
/* Setup configuraton register */
#ifdef CONFIG_DTT_AD7414
/* config = alert active low and disabled */
val = 0x60;
#else
/* config = 6 sample integration, int mode, active low, and enable */
val = 0x18;
#endif
if (dtt_write(sensor, DTT_CONFIG, val) != 0)
return 1;
return 0;
} /* dtt_init_one() */
int dtt_get_temp(int sensor)
{
int const ret = dtt_read(sensor, DTT_READ_TEMP);
if (ret < 0) {
printf("DTT temperature read failed.\n");
return 0;
}
return (int)((int16_t) ret / 256);
} /* dtt_get_temp() */

View file

@ -0,0 +1,127 @@
/*
* (C) Copyright 2006
* Heiko Schocher, DENX Software Enginnering <hs@denx.de>
*
* based on dtt/lm75.c which is ...
*
* (C) Copyright 2001
* Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*/
/*
* On Semiconductor's LM81 Temperature Sensor
*/
#include <common.h>
#include <i2c.h>
#include <dtt.h>
/*
* Device code
*/
#define DTT_I2C_DEV_CODE 0x2c /* ON Semi's LM81 device */
#define DTT_READ_TEMP 0x27
#define DTT_CONFIG_TEMP 0x4b
#define DTT_TEMP_MAX 0x39
#define DTT_TEMP_HYST 0x3a
#define DTT_CONFIG 0x40
int dtt_read(int sensor, int reg)
{
int dlen = 1;
uchar data[2];
/*
* Calculate sensor address and register.
*/
sensor = DTT_I2C_DEV_CODE + (sensor & 0x03); /* calculate address of lm81 */
/*
* Now try to read the register.
*/
if (i2c_read(sensor, reg, 1, data, dlen) != 0)
return -1;
return (int)data[0];
} /* dtt_read() */
int dtt_write(int sensor, int reg, int val)
{
uchar data;
/*
* Calculate sensor address and register.
*/
sensor = DTT_I2C_DEV_CODE + (sensor & 0x03); /* calculate address of lm81 */
data = (char)(val & 0xff);
/*
* Write value to register.
*/
if (i2c_write(sensor, reg, 1, &data, 1) != 0)
return 1;
return 0;
} /* dtt_write() */
#define DTT_MANU 0x3e
#define DTT_REV 0x3f
#define DTT_CONFIG 0x40
#define DTT_ADR 0x48
int dtt_init_one(int sensor)
{
int man;
int adr;
int rev;
if (dtt_write (sensor, DTT_CONFIG, 0x01) < 0)
return 1;
/* The LM81 needs 400ms to get the correct values ... */
udelay (400000);
man = dtt_read (sensor, DTT_MANU);
if (man != 0x01)
return 1;
adr = dtt_read (sensor, DTT_ADR);
if (adr < 0)
return 1;
rev = dtt_read (sensor, DTT_REV);
if (adr < 0)
return 1;
debug ("DTT: Found LM81@%x Rev: %d\n", adr, rev);
return 0;
} /* dtt_init_one() */
#define TEMP_FROM_REG(temp) \
((temp)<256?((((temp)&0x1fe) >> 1) * 10) + ((temp) & 1) * 5: \
((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5) \
int dtt_get_temp(int sensor)
{
int val = dtt_read (sensor, DTT_READ_TEMP);
int tmpcnf = dtt_read (sensor, DTT_CONFIG_TEMP);
return (TEMP_FROM_REG((val << 1) + ((tmpcnf & 0x80) >> 7))) / 10;
} /* dtt_get_temp() */

View file

@ -0,0 +1,67 @@
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# 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 $(TOPDIR)/config.mk
LIB := $(obj)libi2c.o
COBJS-$(CONFIG_BFIN_TWI_I2C) += bfin-twi_i2c.o
COBJS-$(CONFIG_DRIVER_DAVINCI_I2C) += davinci_i2c.o
COBJS-$(CONFIG_DW_I2C) += designware_i2c.o
COBJS-$(CONFIG_FSL_I2C) += fsl_i2c.o
COBJS-$(CONFIG_I2C_MVTWSI) += mvtwsi.o
COBJS-$(CONFIG_I2C_MV) += mv_i2c.o
COBJS-$(CONFIG_I2C_MXC) += mxc_i2c.o
COBJS-$(CONFIG_I2C_MXS) += mxs_i2c.o
COBJS-$(CONFIG_DRIVER_OMAP1510_I2C) += omap1510_i2c.o
COBJS-$(CONFIG_DRIVER_OMAP24XX_I2C) += omap24xx_i2c.o
COBJS-$(CONFIG_DRIVER_OMAP34XX_I2C) += omap24xx_i2c.o
COBJS-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o
COBJS-$(CONFIG_PPC4XX_I2C) += ppc4xx_i2c.o
COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o
COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o
COBJS-$(CONFIG_SOFT_I2C) += soft_i2c.o
COBJS-$(CONFIG_TEGRA_I2C) += tegra_i2c.o
COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o
COBJS-$(CONFIG_SH_I2C) += sh_i2c.o
COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o
COBJS-$(CONFIG_IPQ806X_I2C) += ipq_i2c.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
all: $(LIB)
$(LIB): $(obj).depend $(OBJS)
$(call cmd_link_o_target, $(OBJS))
#########################################################################
# defines $(obj).depend target
include $(SRCTREE)/rules.mk
sinclude $(obj).depend
#########################################################################

View file

@ -0,0 +1,378 @@
/*
* i2c.c - driver for Blackfin on-chip TWI/I2C
*
* Copyright (c) 2006-2010 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <i2c.h>
#include <asm/blackfin.h>
#include <asm/mach-common/bits/twi.h>
/* Every register is 32bit aligned, but only 16bits in size */
#define ureg(name) u16 name; u16 __pad_##name;
struct twi_regs {
ureg(clkdiv);
ureg(control);
ureg(slave_ctl);
ureg(slave_stat);
ureg(slave_addr);
ureg(master_ctl);
ureg(master_stat);
ureg(master_addr);
ureg(int_stat);
ureg(int_mask);
ureg(fifo_ctl);
ureg(fifo_stat);
char __pad[0x50];
ureg(xmt_data8);
ureg(xmt_data16);
ureg(rcv_data8);
ureg(rcv_data16);
};
#undef ureg
/* U-Boot I2C framework allows only one active device at a time. */
#ifdef TWI_CLKDIV
#define TWI0_CLKDIV TWI_CLKDIV
#endif
static volatile struct twi_regs *twi = (void *)TWI0_CLKDIV;
#ifdef DEBUG
# define dmemset(s, c, n) memset(s, c, n)
#else
# define dmemset(s, c, n)
#endif
#define debugi(fmt, args...) \
debug( \
"MSTAT:0x%03x FSTAT:0x%x ISTAT:0x%02x\t%-20s:%-3i: " fmt "\n", \
twi->master_stat, twi->fifo_stat, twi->int_stat, \
__func__, __LINE__, ## args)
#ifdef CONFIG_TWICLK_KHZ
# error do not define CONFIG_TWICLK_KHZ ... use CONFIG_SYS_I2C_SPEED
#endif
/*
* The way speed is changed into duty often results in integer truncation
* with 50% duty, so we'll force rounding up to the next duty by adding 1
* to the max. In practice this will get us a speed of something like
* 385 KHz. The other limit is easy to handle as it is only 8 bits.
*/
#define I2C_SPEED_MAX 400000
#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed))
#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1)
#define I2C_DUTY_MIN 0xff /* 8 bit limited */
#define SYS_I2C_DUTY I2C_SPEED_TO_DUTY(CONFIG_SYS_I2C_SPEED)
/* Note: duty is inverse of speed, so the comparisons below are correct */
#if SYS_I2C_DUTY < I2C_DUTY_MAX || SYS_I2C_DUTY > I2C_DUTY_MIN
# error "The Blackfin I2C hardware can only operate 20KHz - 400KHz"
#endif
/* All transfers are described by this data structure */
struct i2c_msg {
u8 flags;
#define I2C_M_COMBO 0x4
#define I2C_M_STOP 0x2
#define I2C_M_READ 0x1
int len; /* msg length */
u8 *buf; /* pointer to msg data */
int alen; /* addr length */
u8 *abuf; /* addr buffer */
};
/* Allow msec timeout per ~byte transfer */
#define I2C_TIMEOUT 10
/**
* wait_for_completion - manage the actual i2c transfer
* @msg: the i2c msg
*/
static int wait_for_completion(struct i2c_msg *msg)
{
uint16_t int_stat;
ulong timebase = get_timer(0);
do {
int_stat = twi->int_stat;
if (int_stat & XMTSERV) {
debugi("processing XMTSERV");
twi->int_stat = XMTSERV;
SSYNC();
if (msg->alen) {
twi->xmt_data8 = *(msg->abuf++);
--msg->alen;
} else if (!(msg->flags & I2C_M_COMBO) && msg->len) {
twi->xmt_data8 = *(msg->buf++);
--msg->len;
} else {
twi->master_ctl |= (msg->flags & I2C_M_COMBO) ? RSTART | MDIR : STOP;
SSYNC();
}
}
if (int_stat & RCVSERV) {
debugi("processing RCVSERV");
twi->int_stat = RCVSERV;
SSYNC();
if (msg->len) {
*(msg->buf++) = twi->rcv_data8;
--msg->len;
} else if (msg->flags & I2C_M_STOP) {
twi->master_ctl |= STOP;
SSYNC();
}
}
if (int_stat & MERR) {
debugi("processing MERR");
twi->int_stat = MERR;
SSYNC();
return msg->len;
}
if (int_stat & MCOMP) {
debugi("processing MCOMP");
twi->int_stat = MCOMP;
SSYNC();
if (msg->flags & I2C_M_COMBO && msg->len) {
twi->master_ctl = (twi->master_ctl & ~RSTART) |
(min(msg->len, 0xff) << 6) | MEN | MDIR;
SSYNC();
} else
break;
}
/* If we were able to do something, reset timeout */
if (int_stat)
timebase = get_timer(0);
} while (get_timer(timebase) < I2C_TIMEOUT);
return msg->len;
}
/**
* i2c_transfer - setup an i2c transfer
* @return: 0 if things worked, non-0 if things failed
*
* Here we just get the i2c stuff all prepped and ready, and then tail off
* into wait_for_completion() for all the bits to go.
*/
static int i2c_transfer(uchar chip, uint addr, int alen, uchar *buffer, int len, u8 flags)
{
uchar addr_buffer[] = {
(addr >> 0),
(addr >> 8),
(addr >> 16),
};
struct i2c_msg msg = {
.flags = flags | (len >= 0xff ? I2C_M_STOP : 0),
.buf = buffer,
.len = len,
.abuf = addr_buffer,
.alen = alen,
};
int ret;
dmemset(buffer, 0xff, len);
debugi("chip=0x%x addr=0x%02x alen=%i buf[0]=0x%02x len=%i flags=0x%02x[%s] ",
chip, addr, alen, buffer[0], len, flags, (flags & I2C_M_READ ? "rd" : "wr"));
/* wait for things to settle */
while (twi->master_stat & BUSBUSY)
if (ctrlc())
return 1;
/* Set Transmit device address */
twi->master_addr = chip;
/* Clear the FIFO before starting things */
twi->fifo_ctl = XMTFLUSH | RCVFLUSH;
SSYNC();
twi->fifo_ctl = 0;
SSYNC();
/* prime the pump */
if (msg.alen) {
len = (msg.flags & I2C_M_COMBO) ? msg.alen : msg.alen + len;
debugi("first byte=0x%02x", *msg.abuf);
twi->xmt_data8 = *(msg.abuf++);
--msg.alen;
} else if (!(msg.flags & I2C_M_READ) && msg.len) {
debugi("first byte=0x%02x", *msg.buf);
twi->xmt_data8 = *(msg.buf++);
--msg.len;
}
/* clear int stat */
twi->master_stat = -1;
twi->int_stat = -1;
twi->int_mask = 0;
SSYNC();
/* Master enable */
twi->master_ctl =
(twi->master_ctl & FAST) |
(min(len, 0xff) << 6) | MEN |
((msg.flags & I2C_M_READ) ? MDIR : 0);
SSYNC();
debugi("CTL=0x%04x", twi->master_ctl);
/* process the rest */
ret = wait_for_completion(&msg);
debugi("ret=%d", ret);
if (ret) {
twi->master_ctl &= ~MEN;
twi->control &= ~TWI_ENA;
SSYNC();
twi->control |= TWI_ENA;
SSYNC();
}
return ret;
}
/**
* i2c_set_bus_speed - set i2c bus speed
* @speed: bus speed (in HZ)
*/
int i2c_set_bus_speed(unsigned int speed)
{
u16 clkdiv = I2C_SPEED_TO_DUTY(speed);
/* Set TWI interface clock */
if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN)
return -1;
twi->clkdiv = (clkdiv << 8) | (clkdiv & 0xff);
/* Don't turn it on */
twi->master_ctl = (speed > 100000 ? FAST : 0);
return 0;
}
/**
* i2c_get_bus_speed - get i2c bus speed
* @speed: bus speed (in HZ)
*/
unsigned int i2c_get_bus_speed(void)
{
/* 10 MHz / (2 * CLKDIV) -> 5 MHz / CLKDIV */
return 5000000 / (twi->clkdiv & 0xff);
}
/**
* i2c_init - initialize the i2c bus
* @speed: bus speed (in HZ)
* @slaveaddr: address of device in slave mode (0 - not slave)
*
* Slave mode isn't actually implemented. It'll stay that way until
* we get a real request for it.
*/
void i2c_init(int speed, int slaveaddr)
{
uint8_t prescale = ((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F;
/* Set TWI internal clock as 10MHz */
twi->control = prescale;
/* Set TWI interface clock as specified */
i2c_set_bus_speed(speed);
/* Enable it */
twi->control = TWI_ENA | prescale;
SSYNC();
debugi("CONTROL:0x%04x CLKDIV:0x%04x", twi->control, twi->clkdiv);
#if CONFIG_SYS_I2C_SLAVE
# error I2C slave support not tested/supported
/* If they want us as a slave, do it */
if (slaveaddr) {
twi->slave_addr = slaveaddr;
twi->slave_ctl = SEN;
}
#endif
}
/**
* i2c_probe - test if a chip exists at a given i2c address
* @chip: i2c chip addr to search for
* @return: 0 if found, non-0 if not found
*/
int i2c_probe(uchar chip)
{
u8 byte;
return i2c_read(chip, 0, 0, &byte, 1);
}
/**
* i2c_read - read data from an i2c device
* @chip: i2c chip addr
* @addr: memory (register) address in the chip
* @alen: byte size of address
* @buffer: buffer to store data read from chip
* @len: how many bytes to read
* @return: 0 on success, non-0 on failure
*/
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
return i2c_transfer(chip, addr, alen, buffer, len, (alen ? I2C_M_COMBO : I2C_M_READ));
}
/**
* i2c_write - write data to an i2c device
* @chip: i2c chip addr
* @addr: memory (register) address in the chip
* @alen: byte size of address
* @buffer: buffer holding data to write to chip
* @len: how many bytes to write
* @return: 0 on success, non-0 on failure
*/
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
return i2c_transfer(chip, addr, alen, buffer, len, 0);
}
/**
* i2c_set_bus_num - change active I2C bus
* @bus: bus index, zero based
* @returns: 0 on success, non-0 on failure
*/
int i2c_set_bus_num(unsigned int bus)
{
switch (bus) {
#if CONFIG_SYS_MAX_I2C_BUS > 0
case 0: twi = (void *)TWI0_CLKDIV; return 0;
#endif
#if CONFIG_SYS_MAX_I2C_BUS > 1
case 1: twi = (void *)TWI1_CLKDIV; return 0;
#endif
#if CONFIG_SYS_MAX_I2C_BUS > 2
case 2: twi = (void *)TWI2_CLKDIV; return 0;
#endif
default: return -1;
}
}
/**
* i2c_get_bus_num - returns index of active I2C bus
*/
unsigned int i2c_get_bus_num(void)
{
switch ((unsigned long)twi) {
#if CONFIG_SYS_MAX_I2C_BUS > 0
case TWI0_CLKDIV: return 0;
#endif
#if CONFIG_SYS_MAX_I2C_BUS > 1
case TWI1_CLKDIV: return 1;
#endif
#if CONFIG_SYS_MAX_I2C_BUS > 2
case TWI2_CLKDIV: return 2;
#endif
default: return -1;
}
}

View file

@ -0,0 +1,332 @@
/*
* TI DaVinci (TMS320DM644x) I2C driver.
*
* Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net>
*
* --------------------------------------------------------
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h>
#include <i2c.h>
#include <asm/arch/hardware.h>
#include <asm/arch/i2c_defs.h>
#define CHECK_NACK() \
do {\
if (tmp & (I2C_TIMEOUT | I2C_STAT_NACK)) {\
REG(I2C_CON) = 0;\
return(1);\
}\
} while (0)
static int wait_for_bus(void)
{
int stat, timeout;
REG(I2C_STAT) = 0xffff;
for (timeout = 0; timeout < 10; timeout++) {
if (!((stat = REG(I2C_STAT)) & I2C_STAT_BB)) {
REG(I2C_STAT) = 0xffff;
return(0);
}
REG(I2C_STAT) = stat;
udelay(50000);
}
REG(I2C_STAT) = 0xffff;
return(1);
}
static int poll_i2c_irq(int mask)
{
int stat, timeout;
for (timeout = 0; timeout < 10; timeout++) {
udelay(1000);
stat = REG(I2C_STAT);
if (stat & mask) {
return(stat);
}
}
REG(I2C_STAT) = 0xffff;
return(stat | I2C_TIMEOUT);
}
void flush_rx(void)
{
while (1) {
if (!(REG(I2C_STAT) & I2C_STAT_RRDY))
break;
REG(I2C_DRR);
REG(I2C_STAT) = I2C_STAT_RRDY;
udelay(1000);
}
}
void i2c_init(int speed, int slaveadd)
{
u_int32_t div, psc;
if (REG(I2C_CON) & I2C_CON_EN) {
REG(I2C_CON) = 0;
udelay (50000);
}
psc = 2;
div = (CONFIG_SYS_HZ_CLOCK / ((psc + 1) * speed)) - 10; /* SCLL + SCLH */
REG(I2C_PSC) = psc; /* 27MHz / (2 + 1) = 9MHz */
REG(I2C_SCLL) = (div * 50) / 100; /* 50% Duty */
REG(I2C_SCLH) = div - REG(I2C_SCLL);
REG(I2C_OA) = slaveadd;
REG(I2C_CNT) = 0;
/* Interrupts must be enabled or I2C module won't work */
REG(I2C_IE) = I2C_IE_SCD_IE | I2C_IE_XRDY_IE |
I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | I2C_IE_NACK_IE;
/* Now enable I2C controller (get it out of reset) */
REG(I2C_CON) = I2C_CON_EN;
udelay(1000);
}
int i2c_set_bus_speed(unsigned int speed)
{
i2c_init(speed, CONFIG_SYS_I2C_SLAVE);
return 0;
}
int i2c_probe(u_int8_t chip)
{
int rc = 1;
if (chip == REG(I2C_OA)) {
return(rc);
}
REG(I2C_CON) = 0;
if (wait_for_bus()) {return(1);}
/* try to read one byte from current (or only) address */
REG(I2C_CNT) = 1;
REG(I2C_SA) = chip;
REG(I2C_CON) = (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP);
udelay (50000);
if (!(REG(I2C_STAT) & I2C_STAT_NACK)) {
rc = 0;
flush_rx();
REG(I2C_STAT) = 0xffff;
} else {
REG(I2C_STAT) = 0xffff;
REG(I2C_CON) |= I2C_CON_STP;
udelay(20000);
if (wait_for_bus()) {return(1);}
}
flush_rx();
REG(I2C_STAT) = 0xffff;
REG(I2C_CNT) = 0;
return(rc);
}
int i2c_read(u_int8_t chip, u_int32_t addr, int alen, u_int8_t *buf, int len)
{
u_int32_t tmp;
int i;
if ((alen < 0) || (alen > 2)) {
printf("%s(): bogus address length %x\n", __FUNCTION__, alen);
return(1);
}
if (wait_for_bus()) {return(1);}
if (alen != 0) {
/* Start address phase */
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX;
REG(I2C_CNT) = alen;
REG(I2C_SA) = chip;
REG(I2C_CON) = tmp;
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
CHECK_NACK();
switch (alen) {
case 2:
/* Send address MSByte */
if (tmp & I2C_STAT_XRDY) {
REG(I2C_DXR) = (addr >> 8) & 0xff;
} else {
REG(I2C_CON) = 0;
return(1);
}
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
CHECK_NACK();
/* No break, fall through */
case 1:
/* Send address LSByte */
if (tmp & I2C_STAT_XRDY) {
REG(I2C_DXR) = addr & 0xff;
} else {
REG(I2C_CON) = 0;
return(1);
}
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK | I2C_STAT_ARDY);
CHECK_NACK();
if (!(tmp & I2C_STAT_ARDY)) {
REG(I2C_CON) = 0;
return(1);
}
}
}
/* Address phase is over, now read 'len' bytes and stop */
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP;
REG(I2C_CNT) = len & 0xffff;
REG(I2C_SA) = chip;
REG(I2C_CON) = tmp;
for (i = 0; i < len; i++) {
tmp = poll_i2c_irq(I2C_STAT_RRDY | I2C_STAT_NACK | I2C_STAT_ROVR);
CHECK_NACK();
if (tmp & I2C_STAT_RRDY) {
buf[i] = REG(I2C_DRR);
} else {
REG(I2C_CON) = 0;
return(1);
}
}
tmp = poll_i2c_irq(I2C_STAT_SCD | I2C_STAT_NACK);
CHECK_NACK();
if (!(tmp & I2C_STAT_SCD)) {
REG(I2C_CON) = 0;
return(1);
}
flush_rx();
REG(I2C_STAT) = 0xffff;
REG(I2C_CNT) = 0;
REG(I2C_CON) = 0;
return(0);
}
int i2c_write(u_int8_t chip, u_int32_t addr, int alen, u_int8_t *buf, int len)
{
u_int32_t tmp;
int i;
if ((alen < 0) || (alen > 2)) {
printf("%s(): bogus address length %x\n", __FUNCTION__, alen);
return(1);
}
if (len < 0) {
printf("%s(): bogus length %x\n", __FUNCTION__, len);
return(1);
}
if (wait_for_bus()) {return(1);}
/* Start address phase */
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | I2C_CON_STP;
REG(I2C_CNT) = (alen == 0) ? len & 0xffff : (len & 0xffff) + alen;
REG(I2C_SA) = chip;
REG(I2C_CON) = tmp;
switch (alen) {
case 2:
/* Send address MSByte */
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
CHECK_NACK();
if (tmp & I2C_STAT_XRDY) {
REG(I2C_DXR) = (addr >> 8) & 0xff;
} else {
REG(I2C_CON) = 0;
return(1);
}
/* No break, fall through */
case 1:
/* Send address LSByte */
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
CHECK_NACK();
if (tmp & I2C_STAT_XRDY) {
REG(I2C_DXR) = addr & 0xff;
} else {
REG(I2C_CON) = 0;
return(1);
}
}
for (i = 0; i < len; i++) {
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
CHECK_NACK();
if (tmp & I2C_STAT_XRDY) {
REG(I2C_DXR) = buf[i];
} else {
return(1);
}
}
tmp = poll_i2c_irq(I2C_STAT_SCD | I2C_STAT_NACK);
CHECK_NACK();
if (!(tmp & I2C_STAT_SCD)) {
REG(I2C_CON) = 0;
return(1);
}
flush_rx();
REG(I2C_STAT) = 0xffff;
REG(I2C_CNT) = 0;
REG(I2C_CON) = 0;
return(0);
}

View file

@ -0,0 +1,346 @@
/*
* (C) Copyright 2009
* Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 <common.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include "designware_i2c.h"
static struct i2c_regs *const i2c_regs_p =
(struct i2c_regs *)CONFIG_SYS_I2C_BASE;
/*
* set_speed - Set the i2c speed mode (standard, high, fast)
* @i2c_spd: required i2c speed mode
*
* Set the i2c speed mode (standard, high, fast)
*/
static void set_speed(int i2c_spd)
{
unsigned int cntl;
unsigned int hcnt, lcnt;
unsigned int high, low;
unsigned int enbl;
/* to set speed cltr must be disabled */
enbl = readl(&i2c_regs_p->ic_enable);
enbl &= ~IC_ENABLE_0B;
writel(enbl, &i2c_regs_p->ic_enable);
cntl = (readl(&i2c_regs_p->ic_con) & (~IC_CON_SPD_MSK));
switch (i2c_spd) {
case IC_SPEED_MODE_MAX:
cntl |= IC_CON_SPD_HS;
high = MIN_HS_SCL_HIGHTIME;
low = MIN_HS_SCL_LOWTIME;
break;
case IC_SPEED_MODE_STANDARD:
cntl |= IC_CON_SPD_SS;
high = MIN_SS_SCL_HIGHTIME;
low = MIN_SS_SCL_LOWTIME;
break;
case IC_SPEED_MODE_FAST:
default:
cntl |= IC_CON_SPD_FS;
high = MIN_FS_SCL_HIGHTIME;
low = MIN_FS_SCL_LOWTIME;
break;
}
writel(cntl, &i2c_regs_p->ic_con);
hcnt = (IC_CLK * high) / NANO_TO_MICRO;
writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt);
lcnt = (IC_CLK * low) / NANO_TO_MICRO;
writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt);
/* re-enable i2c ctrl back now that speed is set */
enbl |= IC_ENABLE_0B;
writel(enbl, &i2c_regs_p->ic_enable);
}
/*
* i2c_set_bus_speed - Set the i2c speed
* @speed: required i2c speed
*
* Set the i2c speed.
*/
int i2c_set_bus_speed(int speed)
{
if (speed >= I2C_MAX_SPEED)
set_speed(IC_SPEED_MODE_MAX);
else if (speed >= I2C_FAST_SPEED)
set_speed(IC_SPEED_MODE_FAST);
else
set_speed(IC_SPEED_MODE_STANDARD);
return 0;
}
/*
* i2c_get_bus_speed - Gets the i2c speed
*
* Gets the i2c speed.
*/
int i2c_get_bus_speed(void)
{
u32 cntl;
cntl = (readl(&i2c_regs_p->ic_con) & IC_CON_SPD_MSK);
if (cntl == IC_CON_SPD_HS)
return I2C_MAX_SPEED;
else if (cntl == IC_CON_SPD_FS)
return I2C_FAST_SPEED;
else if (cntl == IC_CON_SPD_SS)
return I2C_STANDARD_SPEED;
return 0;
}
/*
* i2c_init - Init function
* @speed: required i2c speed
* @slaveadd: slave address for the device
*
* Initialization function.
*/
void i2c_init(int speed, int slaveadd)
{
unsigned int enbl;
/* Disable i2c */
enbl = readl(&i2c_regs_p->ic_enable);
enbl &= ~IC_ENABLE_0B;
writel(enbl, &i2c_regs_p->ic_enable);
writel((IC_CON_SD | IC_CON_SPD_FS | IC_CON_MM), &i2c_regs_p->ic_con);
writel(IC_RX_TL, &i2c_regs_p->ic_rx_tl);
writel(IC_TX_TL, &i2c_regs_p->ic_tx_tl);
i2c_set_bus_speed(speed);
writel(IC_STOP_DET, &i2c_regs_p->ic_intr_mask);
writel(slaveadd, &i2c_regs_p->ic_sar);
/* Enable i2c */
enbl = readl(&i2c_regs_p->ic_enable);
enbl |= IC_ENABLE_0B;
writel(enbl, &i2c_regs_p->ic_enable);
}
/*
* i2c_setaddress - Sets the target slave address
* @i2c_addr: target i2c address
*
* Sets the target slave address.
*/
static void i2c_setaddress(unsigned int i2c_addr)
{
writel(i2c_addr, &i2c_regs_p->ic_tar);
}
/*
* i2c_flush_rxfifo - Flushes the i2c RX FIFO
*
* Flushes the i2c RX FIFO
*/
static void i2c_flush_rxfifo(void)
{
while (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE)
readl(&i2c_regs_p->ic_cmd_data);
}
/*
* i2c_wait_for_bb - Waits for bus busy
*
* Waits for bus busy
*/
static int i2c_wait_for_bb(void)
{
unsigned long start_time_bb = get_timer(0);
while ((readl(&i2c_regs_p->ic_status) & IC_STATUS_MA) ||
!(readl(&i2c_regs_p->ic_status) & IC_STATUS_TFE)) {
/* Evaluate timeout */
if (get_timer(start_time_bb) > (unsigned long)(I2C_BYTE_TO_BB))
return 1;
}
return 0;
}
/* check parameters for i2c_read and i2c_write */
static int check_params(uint addr, int alen, uchar *buffer, int len)
{
if (buffer == NULL) {
printf("Buffer is invalid\n");
return 1;
}
if (alen > 1) {
printf("addr len %d not supported\n", alen);
return 1;
}
if (addr + len > 256) {
printf("address out of range\n");
return 1;
}
return 0;
}
static int i2c_xfer_init(uchar chip, uint addr)
{
if (i2c_wait_for_bb())
return 1;
i2c_setaddress(chip);
writel(addr, &i2c_regs_p->ic_cmd_data);
return 0;
}
static int i2c_xfer_finish(void)
{
ulong start_stop_det = get_timer(0);
while (1) {
if ((readl(&i2c_regs_p->ic_raw_intr_stat) & IC_STOP_DET)) {
readl(&i2c_regs_p->ic_clr_stop_det);
break;
} else if (get_timer(start_stop_det) > I2C_STOPDET_TO) {
break;
}
}
if (i2c_wait_for_bb()) {
printf("Timed out waiting for bus\n");
return 1;
}
i2c_flush_rxfifo();
/* Wait for read/write operation to complete on actual memory */
udelay(10000);
return 0;
}
/*
* i2c_read - Read from i2c memory
* @chip: target i2c address
* @addr: address to read from
* @alen:
* @buffer: buffer for read data
* @len: no of bytes to be read
*
* Read from i2c memory.
*/
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
unsigned long start_time_rx;
if (check_params(addr, alen, buffer, len))
return 1;
if (i2c_xfer_init(chip, addr))
return 1;
start_time_rx = get_timer(0);
while (len) {
writel(IC_CMD, &i2c_regs_p->ic_cmd_data);
if (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) {
*buffer++ = (uchar)readl(&i2c_regs_p->ic_cmd_data);
len--;
start_time_rx = get_timer(0);
} else if (get_timer(start_time_rx) > I2C_BYTE_TO) {
return 1;
}
}
return i2c_xfer_finish();
}
/*
* i2c_write - Write to i2c memory
* @chip: target i2c address
* @addr: address to read from
* @alen:
* @buffer: buffer for read data
* @len: no of bytes to be read
*
* Write to i2c memory.
*/
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
int nb = len;
unsigned long start_time_tx;
if (check_params(addr, alen, buffer, len))
return 1;
if (i2c_xfer_init(chip, addr))
return 1;
start_time_tx = get_timer(0);
while (len) {
if (readl(&i2c_regs_p->ic_status) & IC_STATUS_TFNF) {
writel(*buffer, &i2c_regs_p->ic_cmd_data);
buffer++;
len--;
start_time_tx = get_timer(0);
} else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) {
printf("Timed out. i2c write Failed\n");
return 1;
}
}
return i2c_xfer_finish();
}
/*
* i2c_probe - Probe the i2c chip
*/
int i2c_probe(uchar chip)
{
u32 tmp;
int ret;
/*
* Try to read the first location of the chip.
*/
ret = i2c_read(chip, 0, 1, (uchar *)&tmp, 1);
if (ret)
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
return ret;
}

View file

@ -0,0 +1,146 @@
/*
* (C) Copyright 2009
* Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 __DW_I2C_H_
#define __DW_I2C_H_
struct i2c_regs {
u32 ic_con;
u32 ic_tar;
u32 ic_sar;
u32 ic_hs_maddr;
u32 ic_cmd_data;
u32 ic_ss_scl_hcnt;
u32 ic_ss_scl_lcnt;
u32 ic_fs_scl_hcnt;
u32 ic_fs_scl_lcnt;
u32 ic_hs_scl_hcnt;
u32 ic_hs_scl_lcnt;
u32 ic_intr_stat;
u32 ic_intr_mask;
u32 ic_raw_intr_stat;
u32 ic_rx_tl;
u32 ic_tx_tl;
u32 ic_clr_intr;
u32 ic_clr_rx_under;
u32 ic_clr_rx_over;
u32 ic_clr_tx_over;
u32 ic_clr_rd_req;
u32 ic_clr_tx_abrt;
u32 ic_clr_rx_done;
u32 ic_clr_activity;
u32 ic_clr_stop_det;
u32 ic_clr_start_det;
u32 ic_clr_gen_call;
u32 ic_enable;
u32 ic_status;
u32 ic_txflr;
u32 ix_rxflr;
u32 reserved_1;
u32 ic_tx_abrt_source;
};
#define IC_CLK 166
#define NANO_TO_MICRO 1000
/* High and low times in different speed modes (in ns) */
#define MIN_SS_SCL_HIGHTIME 4000
#define MIN_SS_SCL_LOWTIME 5000
#define MIN_FS_SCL_HIGHTIME 800
#define MIN_FS_SCL_LOWTIME 1700
#define MIN_HS_SCL_HIGHTIME 60
#define MIN_HS_SCL_LOWTIME 160
/* Worst case timeout for 1 byte is kept as 2ms */
#define I2C_BYTE_TO (CONFIG_SYS_HZ/500)
#define I2C_STOPDET_TO (CONFIG_SYS_HZ/500)
#define I2C_BYTE_TO_BB (I2C_BYTE_TO * 16)
/* i2c control register definitions */
#define IC_CON_SD 0x0040
#define IC_CON_RE 0x0020
#define IC_CON_10BITADDRMASTER 0x0010
#define IC_CON_10BITADDR_SLAVE 0x0008
#define IC_CON_SPD_MSK 0x0006
#define IC_CON_SPD_SS 0x0002
#define IC_CON_SPD_FS 0x0004
#define IC_CON_SPD_HS 0x0006
#define IC_CON_MM 0x0001
/* i2c target address register definitions */
#define TAR_ADDR 0x0050
/* i2c slave address register definitions */
#define IC_SLAVE_ADDR 0x0002
/* i2c data buffer and command register definitions */
#define IC_CMD 0x0100
/* i2c interrupt status register definitions */
#define IC_GEN_CALL 0x0800
#define IC_START_DET 0x0400
#define IC_STOP_DET 0x0200
#define IC_ACTIVITY 0x0100
#define IC_RX_DONE 0x0080
#define IC_TX_ABRT 0x0040
#define IC_RD_REQ 0x0020
#define IC_TX_EMPTY 0x0010
#define IC_TX_OVER 0x0008
#define IC_RX_FULL 0x0004
#define IC_RX_OVER 0x0002
#define IC_RX_UNDER 0x0001
/* fifo threshold register definitions */
#define IC_TL0 0x00
#define IC_TL1 0x01
#define IC_TL2 0x02
#define IC_TL3 0x03
#define IC_TL4 0x04
#define IC_TL5 0x05
#define IC_TL6 0x06
#define IC_TL7 0x07
#define IC_RX_TL IC_TL0
#define IC_TX_TL IC_TL0
/* i2c enable register definitions */
#define IC_ENABLE_0B 0x0001
/* i2c status register definitions */
#define IC_STATUS_SA 0x0040
#define IC_STATUS_MA 0x0020
#define IC_STATUS_RFF 0x0010
#define IC_STATUS_RFNE 0x0008
#define IC_STATUS_TFE 0x0004
#define IC_STATUS_TFNF 0x0002
#define IC_STATUS_ACT 0x0001
/* Speed Selection */
#define IC_SPEED_MODE_STANDARD 1
#define IC_SPEED_MODE_FAST 2
#define IC_SPEED_MODE_MAX 3
#define I2C_MAX_SPEED 3400000
#define I2C_FAST_SPEED 400000
#define I2C_STANDARD_SPEED 100000
#endif /* __DW_I2C_H_ */

View file

@ -0,0 +1,495 @@
/*
* Copyright 2006,2009 Freescale Semiconductor, Inc.
*
* 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.
*
* 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 <common.h>
#ifdef CONFIG_HARD_I2C
#include <command.h>
#include <i2c.h> /* Functional interface */
#include <asm/io.h>
#include <asm/fsl_i2c.h> /* HW definitions */
/* The maximum number of microseconds we will wait until another master has
* released the bus. If not defined in the board header file, then use a
* generic value.
*/
#ifndef CONFIG_I2C_MBB_TIMEOUT
#define CONFIG_I2C_MBB_TIMEOUT 100000
#endif
/* The maximum number of microseconds we will wait for a read or write
* operation to complete. If not defined in the board header file, then use a
* generic value.
*/
#ifndef CONFIG_I2C_TIMEOUT
#define CONFIG_I2C_TIMEOUT 10000
#endif
#define I2C_READ_BIT 1
#define I2C_WRITE_BIT 0
DECLARE_GLOBAL_DATA_PTR;
/* Initialize the bus pointer to whatever one the SPD EEPROM is on.
* Default is bus 0. This is necessary because the DDR initialization
* runs from ROM, and we can't switch buses because we can't modify
* the global variables.
*/
#ifndef CONFIG_SYS_SPD_BUS_NUM
#define CONFIG_SYS_SPD_BUS_NUM 0
#endif
static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = CONFIG_SYS_SPD_BUS_NUM;
#if defined(CONFIG_I2C_MUX)
static unsigned int i2c_bus_num_mux __attribute__ ((section ("data"))) = 0;
#endif
static unsigned int i2c_bus_speed[2] = {CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED};
static const struct fsl_i2c *i2c_dev[2] = {
(struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET),
#ifdef CONFIG_SYS_I2C2_OFFSET
(struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C2_OFFSET)
#endif
};
/* I2C speed map for a DFSR value of 1 */
/*
* Map I2C frequency dividers to FDR and DFSR values
*
* This structure is used to define the elements of a table that maps I2C
* frequency divider (I2C clock rate divided by I2C bus speed) to a value to be
* programmed into the Frequency Divider Ratio (FDR) and Digital Filter
* Sampling Rate (DFSR) registers.
*
* The actual table should be defined in the board file, and it must be called
* fsl_i2c_speed_map[].
*
* The last entry of the table must have a value of {-1, X}, where X is same
* FDR/DFSR values as the second-to-last entry. This guarantees that any
* search through the array will always find a match.
*
* The values of the divider must be in increasing numerical order, i.e.
* fsl_i2c_speed_map[x+1].divider > fsl_i2c_speed_map[x].divider.
*
* For this table, the values are based on a value of 1 for the DFSR
* register. See the application note AN2919 "Determining the I2C Frequency
* Divider Ratio for SCL"
*
* ColdFire I2C frequency dividers for FDR values are different from
* PowerPC. The protocol to use the I2C module is still the same.
* A different table is defined and are based on MCF5xxx user manual.
*
*/
static const struct {
unsigned short divider;
u8 fdr;
} fsl_i2c_speed_map[] = {
#ifdef __M68K__
{20, 32}, {22, 33}, {24, 34}, {26, 35},
{28, 0}, {28, 36}, {30, 1}, {32, 37},
{34, 2}, {36, 38}, {40, 3}, {40, 39},
{44, 4}, {48, 5}, {48, 40}, {56, 6},
{56, 41}, {64, 42}, {68, 7}, {72, 43},
{80, 8}, {80, 44}, {88, 9}, {96, 41},
{104, 10}, {112, 42}, {128, 11}, {128, 43},
{144, 12}, {160, 13}, {160, 48}, {192, 14},
{192, 49}, {224, 50}, {240, 15}, {256, 51},
{288, 16}, {320, 17}, {320, 52}, {384, 18},
{384, 53}, {448, 54}, {480, 19}, {512, 55},
{576, 20}, {640, 21}, {640, 56}, {768, 22},
{768, 57}, {960, 23}, {896, 58}, {1024, 59},
{1152, 24}, {1280, 25}, {1280, 60}, {1536, 26},
{1536, 61}, {1792, 62}, {1920, 27}, {2048, 63},
{2304, 28}, {2560, 29}, {3072, 30}, {3840, 31},
{-1, 31}
#endif
};
/**
* Set the I2C bus speed for a given I2C device
*
* @param dev: the I2C device
* @i2c_clk: I2C bus clock frequency
* @speed: the desired speed of the bus
*
* The I2C device must be stopped before calling this function.
*
* The return value is the actual bus speed that is set.
*/
static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev,
unsigned int i2c_clk, unsigned int speed)
{
unsigned short divider = min(i2c_clk / speed, (unsigned short) -1);
/*
* We want to choose an FDR/DFSR that generates an I2C bus speed that
* is equal to or lower than the requested speed. That means that we
* want the first divider that is equal to or greater than the
* calculated divider.
*/
#ifdef __PPC__
u8 dfsr, fdr = 0x31; /* Default if no FDR found */
/* a, b and dfsr matches identifiers A,B and C respectively in AN2919 */
unsigned short a, b, ga, gb;
unsigned long c_div, est_div;
#ifdef CONFIG_FSL_I2C_CUSTOM_DFSR
dfsr = CONFIG_FSL_I2C_CUSTOM_DFSR;
#else
/* Condition 1: dfsr <= 50/T */
dfsr = (5 * (i2c_clk / 1000)) / 100000;
#endif
#ifdef CONFIG_FSL_I2C_CUSTOM_FDR
fdr = CONFIG_FSL_I2C_CUSTOM_FDR;
speed = i2c_clk / divider; /* Fake something */
#else
debug("Requested speed:%d, i2c_clk:%d\n", speed, i2c_clk);
if (!dfsr)
dfsr = 1;
est_div = ~0;
for (ga = 0x4, a = 10; a <= 30; ga++, a += 2) {
for (gb = 0; gb < 8; gb++) {
b = 16 << gb;
c_div = b * (a + ((3*dfsr)/b)*2);
if ((c_div > divider) && (c_div < est_div)) {
unsigned short bin_gb, bin_ga;
est_div = c_div;
bin_gb = gb << 2;
bin_ga = (ga & 0x3) | ((ga & 0x4) << 3);
fdr = bin_gb | bin_ga;
speed = i2c_clk / est_div;
debug("FDR:0x%.2x, div:%ld, ga:0x%x, gb:0x%x, "
"a:%d, b:%d, speed:%d\n",
fdr, est_div, ga, gb, a, b, speed);
/* Condition 2 not accounted for */
debug("Tr <= %d ns\n",
(b - 3 * dfsr) * 1000000 /
(i2c_clk / 1000));
}
}
if (a == 20)
a += 2;
if (a == 24)
a += 4;
}
debug("divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr);
debug("FDR:0x%.2x, speed:%d\n", fdr, speed);
#endif
writeb(dfsr, &dev->dfsrr); /* set default filter */
writeb(fdr, &dev->fdr); /* set bus speed */
#else
unsigned int i;
for (i = 0; i < ARRAY_SIZE(fsl_i2c_speed_map); i++)
if (fsl_i2c_speed_map[i].divider >= divider) {
u8 fdr;
fdr = fsl_i2c_speed_map[i].fdr;
speed = i2c_clk / fsl_i2c_speed_map[i].divider;
writeb(fdr, &dev->fdr); /* set bus speed */
break;
}
#endif
return speed;
}
unsigned int get_i2c_clock(int bus)
{
if (bus)
return gd->i2c2_clk; /* I2C2 clock */
else
return gd->i2c1_clk; /* I2C1 clock */
}
void
i2c_init(int speed, int slaveadd)
{
const struct fsl_i2c *dev;
unsigned int temp;
int bus_num, i;
#ifdef CONFIG_SYS_I2C_INIT_BOARD
/* Call board specific i2c bus reset routine before accessing the
* environment, which might be in a chip on that bus. For details
* about this problem see doc/I2C_Edge_Conditions.
*/
i2c_init_board();
#endif
#ifdef CONFIG_SYS_I2C2_OFFSET
bus_num = 2;
#else
bus_num = 1;
#endif
for (i = 0; i < bus_num; i++) {
dev = i2c_dev[i];
writeb(0, &dev->cr); /* stop I2C controller */
udelay(5); /* let it shutdown in peace */
temp = set_i2c_bus_speed(dev, get_i2c_clock(i), speed);
if (gd->flags & GD_FLG_RELOC)
i2c_bus_speed[i] = temp;
writeb(slaveadd << 1, &dev->adr);/* write slave address */
writeb(0x0, &dev->sr); /* clear status register */
writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */
}
#ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT
/* Call board specific i2c bus reset routine AFTER the bus has been
* initialized. Use either this callpoint or i2c_init_board;
* which is called before i2c_init operations.
* For details about this problem see doc/I2C_Edge_Conditions.
*/
i2c_board_late_init();
#endif
}
static int
i2c_wait4bus(void)
{
unsigned long long timeval = get_ticks();
const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT);
while (readb(&i2c_dev[i2c_bus_num]->sr) & I2C_SR_MBB) {
if ((get_ticks() - timeval) > timeout)
return -1;
}
return 0;
}
static __inline__ int
i2c_wait(int write)
{
u32 csr;
unsigned long long timeval = get_ticks();
const unsigned long long timeout = usec2ticks(CONFIG_I2C_TIMEOUT);
do {
csr = readb(&i2c_dev[i2c_bus_num]->sr);
if (!(csr & I2C_SR_MIF))
continue;
/* Read again to allow register to stabilise */
csr = readb(&i2c_dev[i2c_bus_num]->sr);
writeb(0x0, &i2c_dev[i2c_bus_num]->sr);
if (csr & I2C_SR_MAL) {
debug("i2c_wait: MAL\n");
return -1;
}
if (!(csr & I2C_SR_MCF)) {
debug("i2c_wait: unfinished\n");
return -1;
}
if (write == I2C_WRITE_BIT && (csr & I2C_SR_RXAK)) {
debug("i2c_wait: No RXACK\n");
return -1;
}
return 0;
} while ((get_ticks() - timeval) < timeout);
debug("i2c_wait: timed out\n");
return -1;
}
static __inline__ int
i2c_write_addr (u8 dev, u8 dir, int rsta)
{
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX
| (rsta ? I2C_CR_RSTA : 0),
&i2c_dev[i2c_bus_num]->cr);
writeb((dev << 1) | dir, &i2c_dev[i2c_bus_num]->dr);
if (i2c_wait(I2C_WRITE_BIT) < 0)
return 0;
return 1;
}
static __inline__ int
__i2c_write(u8 *data, int length)
{
int i;
for (i = 0; i < length; i++) {
writeb(data[i], &i2c_dev[i2c_bus_num]->dr);
if (i2c_wait(I2C_WRITE_BIT) < 0)
break;
}
return i;
}
static __inline__ int
__i2c_read(u8 *data, int length)
{
int i;
writeb(I2C_CR_MEN | I2C_CR_MSTA | ((length == 1) ? I2C_CR_TXAK : 0),
&i2c_dev[i2c_bus_num]->cr);
/* dummy read */
readb(&i2c_dev[i2c_bus_num]->dr);
for (i = 0; i < length; i++) {
if (i2c_wait(I2C_READ_BIT) < 0)
break;
/* Generate ack on last next to last byte */
if (i == length - 2)
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_TXAK,
&i2c_dev[i2c_bus_num]->cr);
/* Do not generate stop on last byte */
if (i == length - 1)
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX,
&i2c_dev[i2c_bus_num]->cr);
data[i] = readb(&i2c_dev[i2c_bus_num]->dr);
}
return i;
}
int
i2c_read(u8 dev, uint addr, int alen, u8 *data, int length)
{
int i = -1; /* signal error */
u8 *a = (u8*)&addr;
if (i2c_wait4bus() >= 0
&& i2c_write_addr(dev, I2C_WRITE_BIT, 0) != 0
&& __i2c_write(&a[4 - alen], alen) == alen)
i = 0; /* No error so far */
if (length
&& i2c_write_addr(dev, I2C_READ_BIT, 1) != 0)
i = __i2c_read(data, length);
writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr);
if (i2c_wait4bus()) /* Wait until STOP */
debug("i2c_read: wait4bus timed out\n");
if (i == length)
return 0;
return -1;
}
int
i2c_write(u8 dev, uint addr, int alen, u8 *data, int length)
{
int i = -1; /* signal error */
u8 *a = (u8*)&addr;
if (i2c_wait4bus() >= 0
&& i2c_write_addr(dev, I2C_WRITE_BIT, 0) != 0
&& __i2c_write(&a[4 - alen], alen) == alen) {
i = __i2c_write(data, length);
}
writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr);
if (i2c_wait4bus()) /* Wait until STOP */
debug("i2c_write: wait4bus timed out\n");
if (i == length)
return 0;
return -1;
}
int
i2c_probe(uchar chip)
{
/* For unknow reason the controller will ACK when
* probing for a slave with the same address, so skip
* it.
*/
if (chip == (readb(&i2c_dev[i2c_bus_num]->adr) >> 1))
return -1;
return i2c_read(chip, 0, 0, NULL, 0);
}
int i2c_set_bus_num(unsigned int bus)
{
#if defined(CONFIG_I2C_MUX)
if (bus < CONFIG_SYS_MAX_I2C_BUS) {
i2c_bus_num = bus;
} else {
int ret;
ret = i2x_mux_select_mux(bus);
if (ret)
return ret;
i2c_bus_num = 0;
}
i2c_bus_num_mux = bus;
#else
#ifdef CONFIG_SYS_I2C2_OFFSET
if (bus > 1) {
#else
if (bus > 0) {
#endif
return -1;
}
i2c_bus_num = bus;
#endif
return 0;
}
int i2c_set_bus_speed(unsigned int speed)
{
unsigned int i2c_clk = (i2c_bus_num == 1) ? gd->i2c2_clk : gd->i2c1_clk;
writeb(0, &i2c_dev[i2c_bus_num]->cr); /* stop controller */
i2c_bus_speed[i2c_bus_num] =
set_i2c_bus_speed(i2c_dev[i2c_bus_num], i2c_clk, speed);
writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr); /* start controller */
return 0;
}
unsigned int i2c_get_bus_num(void)
{
#if defined(CONFIG_I2C_MUX)
return i2c_bus_num_mux;
#else
return i2c_bus_num;
#endif
}
unsigned int i2c_get_bus_speed(void)
{
return i2c_bus_speed[i2c_bus_num];
}
#endif /* CONFIG_HARD_I2C */

View file

@ -0,0 +1,414 @@
/*
* Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
#include <common.h>
#include <i2c.h>
#include "ipq_i2c.h"
#include <asm/io.h>
#include <asm/errno.h>
#include <asm/arch-ipq806x/gsbi.h>
#include <asm/arch-ipq806x/gpio.h>
#include <asm/arch-ipq806x/iomap.h>
#include <asm/arch-ipq806x/clock.h>
static int gsbi_port;
static int gsbi_base_addr;
static int i2c_hw_initialized;
static int i2c_board_initialized;
static uint32_t i2c_master_clk_ctl;
/*
* Reset entire QUP and all mini cores
*/
static void i2c_reset(void)
{
writel(0x1, GSBI_QUP_BASE(gsbi_port) + QUP_SW_RESET_OFFSET);
udelay(5);
}
static int check_bit_state(uint32_t reg_addr, int bit_num, int val,
int us_delay)
{
unsigned int count = TIMEOUT_CNT;
unsigned int bit_val = ((readl(reg_addr) >> bit_num) & 0x01);
while (bit_val != val) {
count--;
if (count == 0) {
return -ETIMEDOUT;
}
udelay(us_delay);
bit_val = ((readl(reg_addr) >> bit_num) & 0x01);
}
return SUCCESS;
}
/*
* Check whether GSBIn_QUP State is valid
*/
static int check_qup_state_valid(void)
{
return check_bit_state(GSBI_QUP_BASE(gsbi_port) + QUP_STATE_OFFSET,
QUP_STATE_VALID_BIT,
QUP_STATE_VALID, 1);
}
/*
* Configure GSBIn Core state
*/
static int config_i2c_state(unsigned int state)
{
uint32_t val;
int ret = SUCCESS;
ret = check_qup_state_valid();
if (ret != SUCCESS)
return ret;
/* Set the state */
val = readl(GSBI_QUP_BASE(gsbi_port) + QUP_STATE_OFFSET);
val = ((val & ~QUP_STATE_MASK) | state);
writel(val, GSBI_QUP_BASE(gsbi_port) + QUP_STATE_OFFSET);
ret = check_qup_state_valid();
return ret;
}
/*
* Configure I2C IO Mode.
*/
void config_i2c_mode(void)
{
int cfg;
cfg = readl(GSBI_QUP_BASE(gsbi_port) + QUP_IO_MODES_OFFSET);
cfg |= (INPUT_FIFO_MODE |
OUTPUT_FIFO_MODE |
OUTPUT_BIT_SHIFT_EN);
writel(cfg, GSBI_QUP_BASE(gsbi_port) + QUP_IO_MODES_OFFSET);
}
void i2c_ipq_board_init(void)
{
gsbi_base_addr = gboard_param->i2c_gsbi_base;
gsbi_port = gboard_param->i2c_gsbi;
/*
* Currently I2C GPIO lines (12(SDA), 13(SCLK)) are configured by
* RPM firmware for PMIC. Trustzone firmware configures the TLMM
* RPU in such a way that only RPM will be able to configure those
* GPIOs. If Krait non secure (u-boot) tries to write to those GPIO
* registers will lead to Master port L2 error.
* GSBI4 uses GPIO 12 and 13 lines for I2C.
* So for GSBI4 we will not update the GPIO configs. RPM would have
* already configured this.`
*/
/* Configure the GPIOs */
if (gsbi_port != GSBI_4)
ipq_configure_gpio(gboard_param->i2c_gpio, NO_OF_I2C_GPIOS);
/* Configure the I2C clock */
i2c_clock_config(gboard_param->i2c_gsbi,
gboard_param->i2c_mnd_value.m_value,
gboard_param->i2c_mnd_value.n_value,
gboard_param->i2c_mnd_value.d_value,
gboard_param->clk_dummy);
i2c_hw_initialized = 0;
i2c_board_initialized = 1;
}
/*
* GSBIn I2C Hardware Initialisation
*/
static int i2c_hw_init(void)
{
int ret, cfg;
/* GSBI module configuration */
i2c_reset();
/* Set the GSBIn QUP state */
ret = config_i2c_state(QUP_STATE_RESET);
if (ret)
return ret;
/* Configure GSBI_CTRL register to set protocol_mode to I2C_UART:110 */
writel(GSBI_PROTOCOL_CODE_I2C_UART <<
GSBI_CTRL_REG_PROTOCOL_CODE_S,
GSBI_CTRL_REG(gsbi_base_addr));
/* Configure Mini core to I2C core */
cfg = readl(GSBI_QUP_BASE(gsbi_port) + QUP_CONFIG_OFFSET);
cfg |= (QUP_CONFIG_MINI_CORE_I2C |
I2C_BIT_WORD);
writel(cfg, GSBI_QUP_BASE(gsbi_port) + QUP_CONFIG_OFFSET);
/* Configure I2C mode */
config_i2c_mode();
/* Enable QUP Error Flags */
writel(ERROR_FLAGS_EN,
GSBI_QUP_BASE(gsbi_port) + QUP_ERROR_FLAGS_EN_OFFSET);
/* Clear the MASTER_CTL_STATUS */
writel(I2C_MASTER_STATUS_CLEAR,
GSBI_QUP_BASE(gsbi_port) + QUP_I2C_MASTER_STATUS_OFFSET);
/* Set to RUN STATE */
ret = config_i2c_state(QUP_STATE_RUN);
if (ret)
return ret;
/* Configure the I2C Master clock */
i2c_master_clk_ctl = ((QUP_INPUT_CLK / (CONFIG_I2C_CLK_FREQ * 2)) - 3) & 0xff;
writel(i2c_master_clk_ctl, GSBI_QUP_BASE(gsbi_port) + QUP_I2C_MASTER_CLK_CTL_OFFSET);
i2c_hw_initialized = 1;
return SUCCESS;
}
/*
* Function to check wheather Input or Output FIFO
* has data to be serviced. For invalid slaves, this
* flag will not be set.
*/
static int check_fifo_status(uint dir)
{
unsigned int count = TIMEOUT_CNT;
unsigned int status_flag;
unsigned int val;
if (dir == READ) {
do {
val = readl(GSBI_QUP_BASE(gsbi_port)
+ QUP_OPERATIONAL_OFFSET);
count--;
if (count == 0)
return -ETIMEDOUT;
status_flag = val & INPUT_SERVICE_FLAG;
udelay(10);
} while (!status_flag);
} else if (dir == WRITE) {
do {
val = readl(GSBI_QUP_BASE(gsbi_port)
+ QUP_OPERATIONAL_OFFSET);
count--;
if (count == 0)
return -ETIMEDOUT;
status_flag = val & OUTPUT_FIFO_FULL;
udelay(10);
} while (status_flag);
/*
* Clear the flag and Acknowledge that the
* software has or will write the data.
*/
if (readl(GSBI_QUP_BASE(gsbi_port) + QUP_OPERATIONAL_OFFSET)
& OUTPUT_SERVICE_FLAG) {
writel(OUTPUT_SERVICE_FLAG, GSBI_QUP_BASE(gsbi_port)
+ QUP_OPERATIONAL_OFFSET);
}
}
return SUCCESS;
}
/*
* Check whether the values in the OUTPUT FIFO are shifted out.
*/
static int check_write_done(void)
{
unsigned int count = TIMEOUT_CNT;
unsigned int status_flag;
unsigned int val;
do {
val = readl(GSBI_QUP_BASE(gsbi_port)
+ QUP_OPERATIONAL_OFFSET);
count--;
if (count == 0)
return -ETIMEDOUT;
status_flag = val & OUTPUT_FIFO_NOT_EMPTY;
udelay(10);
} while (status_flag);
return SUCCESS;
}
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
int ret = 0;
unsigned int data = 0;
if (!i2c_board_initialized) {
i2c_ipq_board_init();
}
if(!i2c_hw_initialized) {
i2c_hw_init();
}
writel(0xFF, GSBI_QUP_BASE(gsbi_port)
+ QUP_ERROR_FLAGS_OFFSET);
writel(0, GSBI_QUP_BASE(gsbi_port)
+ QUP_I2C_MASTER_STATUS_OFFSET);
/* Set to RUN state */
ret = config_i2c_state(QUP_STATE_RUN);
if (ret != SUCCESS)
goto out;
/* Configure the I2C Master clock */
writel(i2c_master_clk_ctl, GSBI_QUP_BASE(gsbi_port) + QUP_I2C_MASTER_CLK_CTL_OFFSET);
/* Send a write request to the chip */
writel((QUP_I2C_START_SEQ | QUP_I2C_ADDR(chip)),
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
writel((QUP_I2C_DATA_SEQ | QUP_I2C_DATA(addr)),
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
ret = check_write_done();
if (ret != SUCCESS)
goto out;
ret = check_fifo_status(WRITE);
if (ret != SUCCESS)
goto out;
/* Send read request */
writel((QUP_I2C_START_SEQ |
(QUP_I2C_ADDR(chip)|
QUP_I2C_SLAVE_READ)),
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
writel((QUP_I2C_RECV_SEQ | len),
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
while (len--) {
ret = check_fifo_status(READ);
if (ret != SUCCESS)
goto out;
/* Read the data from the FIFO */
data = readl(GSBI_QUP_BASE(gsbi_port) + QUP_INPUT_FIFO_OFFSET);
*buffer = QUP_I2C_DATA(data);
/*
* Clear the flag and Acknowledge that the
* software has or will read the data.
*/
writel(INPUT_SERVICE_FLAG,
GSBI_QUP_BASE(gsbi_port) + QUP_OPERATIONAL_OFFSET);
buffer++;
}
/* Set to PAUSE state */
ret = config_i2c_state(QUP_STATE_PAUSE);
if (ret != SUCCESS)
goto out;
out:
/*
* Put the I2C Core back in the Reset State to end the transfer.
*/
(void)config_i2c_state(QUP_STATE_RESET);
return ret;
}
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
int ret = 0;
int idx = 0;
if (!i2c_board_initialized) {
i2c_ipq_board_init();
}
if(!i2c_hw_initialized) {
i2c_hw_init();
}
/* Set to RUN state */
ret = config_i2c_state(QUP_STATE_RUN);
if (ret != SUCCESS)
goto out;
/* Configure the I2C Master clock */
writel(i2c_master_clk_ctl, GSBI_QUP_BASE(gsbi_port) + QUP_I2C_MASTER_CLK_CTL_OFFSET);
/* Send the write request */
writel((QUP_I2C_START_SEQ | QUP_I2C_ADDR(chip)),
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
writel((QUP_I2C_DATA_SEQ | QUP_I2C_DATA(addr)),
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
while (len) {
if (len == 1) {
writel((QUP_I2C_STOP_SEQ | QUP_I2C_DATA(buffer[idx])),
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
} else {
writel((QUP_I2C_DATA_SEQ | QUP_I2C_DATA(buffer[idx])),
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
}
len--;
idx++;
ret = check_fifo_status(WRITE);
if (ret != SUCCESS)
goto out;
}
ret = check_write_done();
if (ret != SUCCESS)
goto out;
/* Set to PAUSE state */
ret = config_i2c_state(QUP_STATE_PAUSE);
if (ret != SUCCESS)
goto out;
return ret;
out:
/*
* Put the I2C Core back in the Reset State to end the transfer.
*/
(void)config_i2c_state(QUP_STATE_RESET);
return ret;
}
/*
* Probe the given I2C chip address.
* Returns 0 if a chip responded.
*/
int i2c_probe(uchar chip)
{
uchar buf;
return i2c_read(chip, 0x0, 0x1, &buf, 0x1);
}
void i2c_init(int speed, int slaveaddr)
{
debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
}

View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 2014 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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.
*/
static int gsbi_base[] = {
0x12440000,
0x12480000,
0x16200000,
0x16300000,
0x1A200000,
0x16500000,
0x16600000
};
/* GSBI QUP devices base address */
static int qup_offset[] = {
0x20000,
0x20000,
0x80000,
0x80000,
0x80000,
0x80000,
0x80000,
};
/* QUP Registers Base Address */
#define GSBI_QUP_BASE(x) (gsbi_base[x-1] + qup_offset[x-1])
#define GSBI_RESET(N) (0x009029DC + (32*(N-1)))
#define I2C_STATUS_ERROR_MASK 0x38000FC
/* QUP Core register offsets */
#define QUP_CONFIG_OFFSET 0x0
#define QUP_STATE_OFFSET 0x4
#define QUP_IO_MODES_OFFSET 0x8
#define QUP_SW_RESET_OFFSET 0xc
#define QUP_TIME_OUT_OFFSET 0x10
#define QUP_TIME_OUT_CURRENT_OFFSET 0x14
#define QUP_OPERATIONAL_OFFSET 0x18
#define QUP_ERROR_FLAGS_OFFSET 0x1c
#define QUP_ERROR_FLAGS_EN_OFFSET 0x20
#define QUP_TEST_CTRL_OFFSET 0x24
#define QUP_MX_OUTPUT_COUNT_OFFSET 0x100
#define QUP_MX_OUTPUT_CNT_CURRENT_OFFSET 0x104
#define QUP_OUTPUT_DEBUG_OFFSET 0x108
#define QUP_OUTPUT_FIFO_WORD_CNT_OFFSET 0x10c
#define QUP_OUTPUT_FIFO_OFFSET 0x110
#define QUP_MX_WRITE_COUNT_OFFSET 0x150
#define QUP_WRITE_CNT_CURRENT_OFFSET 0x154
#define QUP_MX_INPUT_COUNT_OFFSET 0x200
#define QUP_MX_READ_COUNT_OFFSET 0x208
#define QUP_MX_READ_CNT_CURRENT_OFFSET 0x20c
#define QUP_INPUT_DEBUG_OFFSET 0x210
#define QUP_INPUT_FIFO_WORD_CNT_OFFSET 0x214
#define QUP_INPUT_FIFO_OFFSET 0x218
#define QUP_I2C_MASTER_CLK_CTL_OFFSET 0x400
#define QUP_I2C_MASTER_STATUS_OFFSET 0x404
#define QUP_MAX_OFFSET 0xfff
#define SUCCESS 0
#define TIMEOUT_CNT 100
#define QUP_STATE_RESET 0x0
#define QUP_STATE_RUN 0x1D
#define QUP_STATE_PAUSE 0x1F
#define QUP_STATE_VALID_BIT 2
#define QUP_STATE_VALID 1
#define QUP_STATE_MASK 0x3
#define QUP_CONFIG_MINI_CORE_I2C (2 << 8)
#define I2C_BIT_WORD 0xF
#define INPUT_FIFO_MODE (0x0 << 12)
#define OUTPUT_FIFO_MODE (0x0 << 10)
#define INPUT_BLOCK_MODE (0x01 << 12)
#define OUTPUT_BLOCK_MODE (0x01 << 10)
#define PACK_EN (0x01 << 15)
#define UNPACK_EN (0x01 << 14)
#define OUTPUT_BIT_SHIFT_EN (0x01 << 16)
#define ERROR_FLAGS_EN 0x7C
#define I2C_MASTER_STATUS_CLEAR 0xFFFFFC
#define QUP_DATA_AVAILABLE_FOR_READ (1 << 5)
#define QUP_OUTPUT_FIFO_NOT_EMPTY (1 << 4)
#define OUTPUT_SERVICE_FLAG (1 << 8)
#define INPUT_SERVICE_FLAG (1 << 9)
#define QUP_OUTPUT_FIFO_FULL (1 << 6)
#define MAX_OUTPUT_DONE_FLAG (1 << 10)
#define MAX_INPUT_DONE_FLAG (1 << 11)
#define QUP_DATA_AVAILABLE_FOR_READ (1 << 5)
#define OUTPUT_FIFO_NOT_EMPTY (1 << 4)
#define OUTPUT_FIFO_FULL (1 << 6)
#define QUP_I2C_START_SEQ (0x1 << 8)
#define QUP_I2C_DATA_SEQ (0x2 << 8)
#define QUP_I2C_STOP_SEQ (0x3 << 8)
#define QUP_I2C_RECV_SEQ (0x4 << 8)
/* Tags for input FIFO */
#define QUP_I2C_MIDATA_SEQ (0x5 << 8)
#define QUP_I2C_MISTOP_SEQ (0x6 << 8)
#define QUP_I2C_MINACK_SEQ (0x7 << 8)
#define QUP_I2C_ADDR(x) ((x & 0xFF) << 1)
#define QUP_I2C_DATA(x) (x & 0xFF)
#define QUP_I2C_MI_TAG(x) (x & 0xFF00)
#define QUP_I2C_SLAVE_READ (0x1)
enum dir {
READ,
WRITE,
};
typedef enum
{
QUPI2C_NOP,
QUPI2C_START,
QUPI2C_MO_DATA,
QUPI2C_MO_STOP,
QUPI2C_MI_REC,
QUPI2C_MI_DATA,
QUPI2C_MI_STOP,
QUPI2C_MI_NACK,
QUPI2C_SO_DATA,
QUPI2C_SI_START,
QUPI2C_SI_DATA
} qupi2c_TagType;
#define qupi2c_NumberOfNops(c) ((QUPI2C_NOP << 8) | c)
#define qupi2c_StartWrite(c) ((QUPI2C_START << 8) | (c << 1) | 0)
#define qupi2c_StartRead(c) ((QUPI2C_START << 8) | (c << 1) | 1)
#define qupi2c_ByteWrite(c) ((QUPI2C_MO_DATA << 8) | c)
#define qupi2c_LastByteWrite(c) ((QUPI2C_MO_STOP << 8) | c)
#define qupi2c_NumberofReads(c) ((QUPI2C_MI_REC << 8) | c)
/* I2C some pre-defined frequencies */
#define I2C_CLK_1KHZ 1
#define I2C_CLK_100KHZ 100
#define I2C_CLK_400KHZ 400
#define I2C_CLK_1MHZ 1000
#define QUP_INPUT_CLK_TCXO 19200
#define QUP_INPUT_CLK_PLL8 24000
#define I2C_INPUT_CLK_TCXO_DIV4 ((I2C_INPUT_CLK_TCXO)/4)
#define QUP_INPUT_CLK QUP_INPUT_CLK_PLL8
#define CONFIG_I2C_CLK_FREQ I2C_CLK_100KHZ

View file

@ -0,0 +1,487 @@
/*
* (C) Copyright 2000
* Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it
*
* (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Marius Groeger <mgroeger@sysgo.de>
*
* (C) Copyright 2003 Pengutronix e.K.
* Robert Schwebel <r.schwebel@pengutronix.de>
*
* (C) Copyright 2011 Marvell Inc.
* Lei Wen <leiwen@marvell.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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
*
* Back ported to the 8xx platform (from the 8260 platform) by
* Murray.Jensen@cmst.csiro.au, 27-Jan-01.
*/
#include <common.h>
#include <asm/io.h>
#ifdef CONFIG_HARD_I2C
#include <i2c.h>
#include "mv_i2c.h"
#ifdef DEBUG_I2C
#define PRINTD(x) printf x
#else
#define PRINTD(x)
#endif
/* All transfers are described by this data structure */
struct i2c_msg {
u8 condition;
u8 acknack;
u8 direction;
u8 data;
};
struct mv_i2c {
u32 ibmr;
u32 pad0;
u32 idbr;
u32 pad1;
u32 icr;
u32 pad2;
u32 isr;
u32 pad3;
u32 isar;
};
static struct mv_i2c *base;
static void i2c_board_init(struct mv_i2c *base)
{
#ifdef CONFIG_SYS_I2C_INIT_BOARD
u32 icr;
/*
* call board specific i2c bus reset routine before accessing the
* environment, which might be in a chip on that bus. For details
* about this problem see doc/I2C_Edge_Conditions.
*
* disable I2C controller first, otherwhise it thinks we want to
* talk to the slave port...
*/
icr = readl(&base->icr);
writel(readl(&base->icr) & ~(ICR_SCLE | ICR_IUE), &base->icr);
i2c_init_board();
writel(icr, &base->icr);
#endif
}
#ifdef CONFIG_I2C_MULTI_BUS
static u32 i2c_regs[CONFIG_MV_I2C_NUM] = CONFIG_MV_I2C_REG;
static unsigned int bus_initialized[CONFIG_MV_I2C_NUM];
static unsigned int current_bus;
int i2c_set_bus_num(unsigned int bus)
{
if ((bus < 0) || (bus >= CONFIG_MV_I2C_NUM)) {
printf("Bad bus: %d\n", bus);
return -1;
}
base = (struct mv_i2c *)i2c_regs[bus];
current_bus = bus;
if (!bus_initialized[current_bus]) {
i2c_board_init(base);
bus_initialized[current_bus] = 1;
}
return 0;
}
unsigned int i2c_get_bus_num(void)
{
return current_bus;
}
#endif
/*
* i2c_reset: - reset the host controller
*
*/
static void i2c_reset(void)
{
writel(readl(&base->icr) & ~ICR_IUE, &base->icr); /* disable unit */
writel(readl(&base->icr) | ICR_UR, &base->icr); /* reset the unit */
udelay(100);
writel(readl(&base->icr) & ~ICR_IUE, &base->icr); /* disable unit */
i2c_clk_enable();
writel(CONFIG_SYS_I2C_SLAVE, &base->isar); /* set our slave address */
writel(I2C_ICR_INIT, &base->icr); /* set control reg values */
writel(I2C_ISR_INIT, &base->isr); /* set clear interrupt bits */
writel(readl(&base->icr) | ICR_IUE, &base->icr); /* enable unit */
udelay(100);
}
/*
* i2c_isr_set_cleared: - wait until certain bits of the I2C status register
* are set and cleared
*
* @return: 1 in case of success, 0 means timeout (no match within 10 ms).
*/
static int i2c_isr_set_cleared(unsigned long set_mask,
unsigned long cleared_mask)
{
int timeout = 1000, isr;
do {
isr = readl(&base->isr);
udelay(10);
if (timeout-- < 0)
return 0;
} while (((isr & set_mask) != set_mask)
|| ((isr & cleared_mask) != 0));
return 1;
}
/*
* i2c_transfer: - Transfer one byte over the i2c bus
*
* This function can tranfer a byte over the i2c bus in both directions.
* It is used by the public API functions.
*
* @return: 0: transfer successful
* -1: message is empty
* -2: transmit timeout
* -3: ACK missing
* -4: receive timeout
* -5: illegal parameters
* -6: bus is busy and couldn't be aquired
*/
int i2c_transfer(struct i2c_msg *msg)
{
int ret;
if (!msg)
goto transfer_error_msg_empty;
switch (msg->direction) {
case I2C_WRITE:
/* check if bus is not busy */
if (!i2c_isr_set_cleared(0, ISR_IBB))
goto transfer_error_bus_busy;
/* start transmission */
writel(readl(&base->icr) & ~ICR_START, &base->icr);
writel(readl(&base->icr) & ~ICR_STOP, &base->icr);
writel(msg->data, &base->idbr);
if (msg->condition == I2C_COND_START)
writel(readl(&base->icr) | ICR_START, &base->icr);
if (msg->condition == I2C_COND_STOP)
writel(readl(&base->icr) | ICR_STOP, &base->icr);
if (msg->acknack == I2C_ACKNAK_SENDNAK)
writel(readl(&base->icr) | ICR_ACKNAK, &base->icr);
if (msg->acknack == I2C_ACKNAK_SENDACK)
writel(readl(&base->icr) & ~ICR_ACKNAK, &base->icr);
writel(readl(&base->icr) & ~ICR_ALDIE, &base->icr);
writel(readl(&base->icr) | ICR_TB, &base->icr);
/* transmit register empty? */
if (!i2c_isr_set_cleared(ISR_ITE, 0))
goto transfer_error_transmit_timeout;
/* clear 'transmit empty' state */
writel(readl(&base->isr) | ISR_ITE, &base->isr);
/* wait for ACK from slave */
if (msg->acknack == I2C_ACKNAK_WAITACK)
if (!i2c_isr_set_cleared(0, ISR_ACKNAK))
goto transfer_error_ack_missing;
break;
case I2C_READ:
/* check if bus is not busy */
if (!i2c_isr_set_cleared(0, ISR_IBB))
goto transfer_error_bus_busy;
/* start receive */
writel(readl(&base->icr) & ~ICR_START, &base->icr);
writel(readl(&base->icr) & ~ICR_STOP, &base->icr);
if (msg->condition == I2C_COND_START)
writel(readl(&base->icr) | ICR_START, &base->icr);
if (msg->condition == I2C_COND_STOP)
writel(readl(&base->icr) | ICR_STOP, &base->icr);
if (msg->acknack == I2C_ACKNAK_SENDNAK)
writel(readl(&base->icr) | ICR_ACKNAK, &base->icr);
if (msg->acknack == I2C_ACKNAK_SENDACK)
writel(readl(&base->icr) & ~ICR_ACKNAK, &base->icr);
writel(readl(&base->icr) & ~ICR_ALDIE, &base->icr);
writel(readl(&base->icr) | ICR_TB, &base->icr);
/* receive register full? */
if (!i2c_isr_set_cleared(ISR_IRF, 0))
goto transfer_error_receive_timeout;
msg->data = readl(&base->idbr);
/* clear 'receive empty' state */
writel(readl(&base->isr) | ISR_IRF, &base->isr);
break;
default:
goto transfer_error_illegal_param;
}
return 0;
transfer_error_msg_empty:
PRINTD(("i2c_transfer: error: 'msg' is empty\n"));
ret = -1; goto i2c_transfer_finish;
transfer_error_transmit_timeout:
PRINTD(("i2c_transfer: error: transmit timeout\n"));
ret = -2; goto i2c_transfer_finish;
transfer_error_ack_missing:
PRINTD(("i2c_transfer: error: ACK missing\n"));
ret = -3; goto i2c_transfer_finish;
transfer_error_receive_timeout:
PRINTD(("i2c_transfer: error: receive timeout\n"));
ret = -4; goto i2c_transfer_finish;
transfer_error_illegal_param:
PRINTD(("i2c_transfer: error: illegal parameters\n"));
ret = -5; goto i2c_transfer_finish;
transfer_error_bus_busy:
PRINTD(("i2c_transfer: error: bus is busy\n"));
ret = -6; goto i2c_transfer_finish;
i2c_transfer_finish:
PRINTD(("i2c_transfer: ISR: 0x%04x\n", readl(&base->isr)));
i2c_reset();
return ret;
}
/* ------------------------------------------------------------------------ */
/* API Functions */
/* ------------------------------------------------------------------------ */
void i2c_init(int speed, int slaveaddr)
{
#ifdef CONFIG_I2C_MULTI_BUS
current_bus = 0;
base = (struct mv_i2c *)i2c_regs[current_bus];
#else
base = (struct mv_i2c *)CONFIG_MV_I2C_REG;
#endif
i2c_board_init(base);
}
/*
* i2c_probe: - Test if a chip answers for a given i2c address
*
* @chip: address of the chip which is searched for
* @return: 0 if a chip was found, -1 otherwhise
*/
int i2c_probe(uchar chip)
{
struct i2c_msg msg;
i2c_reset();
msg.condition = I2C_COND_START;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = (chip << 1) + 1;
if (i2c_transfer(&msg))
return -1;
msg.condition = I2C_COND_STOP;
msg.acknack = I2C_ACKNAK_SENDNAK;
msg.direction = I2C_READ;
msg.data = 0x00;
if (i2c_transfer(&msg))
return -1;
return 0;
}
/*
* i2c_read: - Read multiple bytes from an i2c device
*
* The higher level routines take into account that this function is only
* called with len < page length of the device (see configuration file)
*
* @chip: address of the chip which is to be read
* @addr: i2c data address within the chip
* @alen: length of the i2c data address (1..2 bytes)
* @buffer: where to write the data
* @len: how much byte do we want to read
* @return: 0 in case of success
*/
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
struct i2c_msg msg;
u8 addr_bytes[3]; /* lowest...highest byte of data address */
PRINTD(("i2c_read(chip=0x%02x, addr=0x%02x, alen=0x%02x, "
"len=0x%02x)\n", chip, addr, alen, len));
i2c_reset();
/* dummy chip address write */
PRINTD(("i2c_read: dummy chip address write\n"));
msg.condition = I2C_COND_START;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = (chip << 1);
msg.data &= 0xFE;
if (i2c_transfer(&msg))
return -1;
/*
* send memory address bytes;
* alen defines how much bytes we have to send.
*/
/*addr &= ((1 << CONFIG_SYS_EEPROM_PAGE_WRITE_BITS)-1); */
addr_bytes[0] = (u8)((addr >> 0) & 0x000000FF);
addr_bytes[1] = (u8)((addr >> 8) & 0x000000FF);
addr_bytes[2] = (u8)((addr >> 16) & 0x000000FF);
while (--alen >= 0) {
PRINTD(("i2c_read: send memory word address byte %1d\n", alen));
msg.condition = I2C_COND_NORMAL;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = addr_bytes[alen];
if (i2c_transfer(&msg))
return -1;
}
/* start read sequence */
PRINTD(("i2c_read: start read sequence\n"));
msg.condition = I2C_COND_START;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = (chip << 1);
msg.data |= 0x01;
if (i2c_transfer(&msg))
return -1;
/* read bytes; send NACK at last byte */
while (len--) {
if (len == 0) {
msg.condition = I2C_COND_STOP;
msg.acknack = I2C_ACKNAK_SENDNAK;
} else {
msg.condition = I2C_COND_NORMAL;
msg.acknack = I2C_ACKNAK_SENDACK;
}
msg.direction = I2C_READ;
msg.data = 0x00;
if (i2c_transfer(&msg))
return -1;
*buffer = msg.data;
PRINTD(("i2c_read: reading byte (0x%08x)=0x%02x\n",
(unsigned int)buffer, *buffer));
buffer++;
}
i2c_reset();
return 0;
}
/*
* i2c_write: - Write multiple bytes to an i2c device
*
* The higher level routines take into account that this function is only
* called with len < page length of the device (see configuration file)
*
* @chip: address of the chip which is to be written
* @addr: i2c data address within the chip
* @alen: length of the i2c data address (1..2 bytes)
* @buffer: where to find the data to be written
* @len: how much byte do we want to read
* @return: 0 in case of success
*/
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
{
struct i2c_msg msg;
u8 addr_bytes[3]; /* lowest...highest byte of data address */
PRINTD(("i2c_write(chip=0x%02x, addr=0x%02x, alen=0x%02x, "
"len=0x%02x)\n", chip, addr, alen, len));
i2c_reset();
/* chip address write */
PRINTD(("i2c_write: chip address write\n"));
msg.condition = I2C_COND_START;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = (chip << 1);
msg.data &= 0xFE;
if (i2c_transfer(&msg))
return -1;
/*
* send memory address bytes;
* alen defines how much bytes we have to send.
*/
addr_bytes[0] = (u8)((addr >> 0) & 0x000000FF);
addr_bytes[1] = (u8)((addr >> 8) & 0x000000FF);
addr_bytes[2] = (u8)((addr >> 16) & 0x000000FF);
while (--alen >= 0) {
PRINTD(("i2c_write: send memory word address\n"));
msg.condition = I2C_COND_NORMAL;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = addr_bytes[alen];
if (i2c_transfer(&msg))
return -1;
}
/* write bytes; send NACK at last byte */
while (len--) {
PRINTD(("i2c_write: writing byte (0x%08x)=0x%02x\n",
(unsigned int)buffer, *buffer));
if (len == 0)
msg.condition = I2C_COND_STOP;
else
msg.condition = I2C_COND_NORMAL;
msg.acknack = I2C_ACKNAK_WAITACK;
msg.direction = I2C_WRITE;
msg.data = *(buffer++);
if (i2c_transfer(&msg))
return -1;
}
i2c_reset();
return 0;
}
#endif /* CONFIG_HARD_I2C */

Some files were not shown because too many files have changed in this diff Show more