| /*- |
| * Copyright (c) 2008 John Hay. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| #include <sys/cdefs.h> |
| __FBSDID("$FreeBSD$"); |
| #include <sys/param.h> |
| #include <sys/ata.h> |
| #include <sys/linker_set.h> |
| |
| #include <stdarg.h> |
| |
| #include "lib.h" |
| #include "cf_ata.h" |
| |
| #include <machine/armreg.h> |
| #include <arm/xscale/ixp425/ixp425reg.h> |
| #include <dev/ic/ns16550.h> |
| |
| struct board_config { |
| const char *desc; |
| int (*probe)(int boardtype_hint); |
| void (*init)(void); |
| }; |
| /* set of registered boards */ |
| SET_DECLARE(boards, struct board_config); |
| #define BOARD_CONFIG(name, _desc) \ |
| static struct board_config name##config = { \ |
| .desc = _desc, \ |
| .probe = name##_probe, \ |
| .init = name##_init, \ |
| }; \ |
| DATA_SET(boards, name##config) |
| |
| static u_int cputype; |
| #define cpu_is_ixp43x() (cputype == CPU_ID_IXP435) |
| static u_int8_t *ubase; |
| |
| static u_int8_t uart_getreg(u_int8_t *, int); |
| static void uart_setreg(u_int8_t *, int, u_int8_t); |
| |
| static void cf_init(void); |
| static void cf_clr(void); |
| |
| #ifdef DEBUG |
| #define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__) |
| #else |
| #define DPRINTF(fmt, ...) |
| #endif |
| |
| const char * |
| board_init(void) |
| { |
| struct board_config **pbp; |
| |
| cputype = cpu_id() & CPU_ID_CPU_MASK; |
| |
| SET_FOREACH(pbp, boards) |
| /* XXX pass down redboot board type */ |
| if ((*pbp)->probe(0)) { |
| (*pbp)->init(); |
| return (*pbp)->desc; |
| } |
| /* XXX panic, unknown board type */ |
| return "???"; |
| } |
| |
| /* |
| * This should be called just before starting the kernel. This is so |
| * that one can undo incompatible hardware settings. |
| */ |
| void |
| clr_board(void) |
| { |
| cf_clr(); |
| } |
| |
| /* |
| * General support functions. |
| */ |
| |
| /* |
| * DELAY should delay for the number of microseconds. |
| * The idea is that the inner loop should take 1us, so val is the |
| * number of usecs to delay. |
| */ |
| void |
| DELAY(int val) |
| { |
| volatile int sub; |
| volatile int subsub; |
| |
| sub = val; |
| while(sub) { |
| subsub = 3; |
| while(subsub) |
| subsub--; |
| sub--; |
| } |
| } |
| |
| u_int32_t |
| swap32(u_int32_t a) |
| { |
| return (((a & 0xff) << 24) | ((a & 0xff00) << 8) | |
| ((a & 0xff0000) >> 8) | ((a & 0xff000000) >> 24)); |
| } |
| |
| u_int16_t |
| swap16(u_int16_t val) |
| { |
| return (val << 8) | (val >> 8); |
| } |
| |
| /* |
| * uart related funcs |
| */ |
| static u_int8_t |
| uart_getreg(u_int8_t *bas, int off) |
| { |
| return *((volatile u_int32_t *)(bas + (off << 2))) & 0xff; |
| } |
| |
| static void |
| uart_setreg(u_int8_t *bas, int off, u_int8_t val) |
| { |
| *((volatile u_int32_t *)(bas + (off << 2))) = (u_int32_t)val; |
| } |
| |
| int |
| getc(int seconds) |
| { |
| int c, delay, limit; |
| |
| c = 0; |
| delay = 10000; |
| limit = seconds * 1000000/10000; |
| while ((uart_getreg(ubase, REG_LSR) & LSR_RXRDY) == 0 && --limit) |
| DELAY(delay); |
| |
| if ((uart_getreg(ubase, REG_LSR) & LSR_RXRDY) == LSR_RXRDY) |
| c = uart_getreg(ubase, REG_DATA); |
| |
| return c; |
| } |
| |
| void |
| putchar(int ch) |
| { |
| int delay, limit; |
| |
| delay = 500; |
| limit = 20; |
| while ((uart_getreg(ubase, REG_LSR) & LSR_THRE) == 0 && --limit) |
| DELAY(delay); |
| uart_setreg(ubase, REG_DATA, ch); |
| |
| limit = 40; |
| while ((uart_getreg(ubase, REG_LSR) & LSR_TEMT) == 0 && --limit) |
| DELAY(delay); |
| } |
| |
| void |
| xputchar(int ch) |
| { |
| if (ch == '\n') |
| putchar('\r'); |
| putchar(ch); |
| } |
| |
| void |
| putstr(const char *str) |
| { |
| while(*str) |
| xputchar(*str++); |
| } |
| |
| void |
| puthex8(u_int8_t ch) |
| { |
| const char *hex = "0123456789abcdef"; |
| |
| putchar(hex[ch >> 4]); |
| putchar(hex[ch & 0xf]); |
| } |
| |
| void |
| puthexlist(const u_int8_t *str, int length) |
| { |
| while(length) { |
| puthex8(*str); |
| putchar(' '); |
| str++; |
| length--; |
| } |
| } |
| |
| /* |
| * |
| * CF/IDE functions. |
| * |
| */ |
| |
| struct { |
| u_int64_t dsize; |
| u_int64_t total_secs; |
| u_int8_t heads; |
| u_int8_t sectors; |
| u_int32_t cylinders; |
| |
| u_int32_t *cs1to; |
| u_int32_t *cs2to; |
| |
| u_int8_t *cs1; |
| u_int8_t *cs2; |
| |
| u_int32_t use_lba; |
| u_int32_t use_stream8; |
| u_int32_t debug; |
| |
| u_int8_t status; |
| u_int8_t error; |
| } dskinf; |
| |
| static void cfenable16(void); |
| static void cfdisable16(void); |
| static u_int8_t cfread8(u_int32_t off); |
| static u_int16_t cfread16(u_int32_t off); |
| static void cfreadstream8(void *buf, int length); |
| static void cfreadstream16(void *buf, int length); |
| static void cfwrite8(u_int32_t off, u_int8_t val); |
| static u_int8_t cfaltread8(u_int32_t off); |
| static void cfaltwrite8(u_int32_t off, u_int8_t val); |
| static int cfwait(u_int8_t mask); |
| static int cfaltwait(u_int8_t mask); |
| static int cfcmd(u_int32_t cmd, u_int32_t cylinder, u_int32_t head, |
| u_int32_t sector, u_int32_t count, u_int32_t feature); |
| static void cfreset(void); |
| #ifdef DEBUG |
| static int cfgetparams(void); |
| #endif |
| static void cfprintregs(void); |
| |
| static void |
| cf_init(void) |
| { |
| u_int8_t status; |
| #ifdef DEBUG |
| int rval; |
| #endif |
| |
| /* NB: board init routines setup other parts of dskinf */ |
| dskinf.use_stream8 = 0; |
| dskinf.use_lba = 0; |
| dskinf.debug = 1; |
| |
| DPRINTF("cs1 %x, cs2 %x\n", dskinf.cs1, dskinf.cs2); |
| |
| /* Setup the CF window */ |
| *dskinf.cs1to |= (EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN); |
| DPRINTF("t1 %x, ", *dskinf.cs1to); |
| |
| *dskinf.cs2to |= (EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN); |
| DPRINTF("t2 %x\n", *dskinf.cs2to); |
| |
| /* Detect if there is a disk. */ |
| cfwrite8(CF_DRV_HEAD, CF_D_IBM); |
| DELAY(1000); |
| status = cfread8(CF_STATUS); |
| if (status != 0x50) |
| printf("cf-ata0 %x\n", (u_int32_t)status); |
| if (status == 0xff) { |
| printf("cf_ata0: No disk!\n"); |
| return; |
| } |
| |
| cfreset(); |
| |
| if (dskinf.use_stream8) { |
| DPRINTF("setting %d bit mode.\n", 8); |
| cfwrite8(CF_FEATURE, 0x01); /* Enable 8 bit transfers */ |
| cfwrite8(CF_COMMAND, ATA_SETFEATURES); |
| cfaltwait(CF_S_READY); |
| } |
| |
| #ifdef DEBUG |
| rval = cfgetparams(); |
| if (rval) |
| return; |
| #endif |
| dskinf.use_lba = 1; |
| dskinf.debug = 0; |
| } |
| |
| static void |
| cf_clr(void) |
| { |
| cfwrite8(CF_DRV_HEAD, CF_D_IBM); |
| cfaltwait(CF_S_READY); |
| cfwrite8(CF_FEATURE, 0x81); /* Enable 8 bit transfers */ |
| cfwrite8(CF_COMMAND, ATA_SETFEATURES); |
| cfaltwait(CF_S_READY); |
| } |
| |
| static void |
| cfenable16(void) |
| { |
| u_int32_t val; |
| |
| val = *dskinf.cs1to; |
| *dskinf.cs1to = val &~ EXP_BYTE_EN; |
| DELAY(100); |
| #if 0 |
| DPRINTF("%s: cs1 timing reg %x\n", *dskinf.cs1to, __func__); |
| #endif |
| } |
| |
| static void |
| cfdisable16(void) |
| { |
| u_int32_t val; |
| |
| DELAY(100); |
| val = *dskinf.cs1to; |
| *dskinf.cs1to = val | EXP_BYTE_EN; |
| #if 0 |
| DPRINTF("%s: cs1 timing reg %x\n", *dskinf.cs1to, __func__); |
| #endif |
| } |
| |
| static u_int8_t |
| cfread8(u_int32_t off) |
| { |
| volatile u_int8_t *vp; |
| |
| vp = (volatile u_int8_t *)(dskinf.cs1 + off); |
| return *vp; |
| } |
| |
| static void |
| cfreadstream8(void *buf, int length) |
| { |
| u_int8_t *lbuf; |
| u_int8_t tmp; |
| |
| lbuf = buf; |
| while (length) { |
| tmp = cfread8(CF_DATA); |
| *lbuf = tmp; |
| #ifdef DEBUG |
| if (dskinf.debug && (length > (512 - 32))) { |
| if ((length % 16) == 0) |
| xputchar('\n'); |
| puthex8(tmp); |
| putchar(' '); |
| } |
| #endif |
| lbuf++; |
| length--; |
| } |
| #ifdef DEBUG |
| if (dskinf.debug) |
| xputchar('\n'); |
| #endif |
| } |
| |
| static u_int16_t |
| cfread16(u_int32_t off) |
| { |
| volatile u_int16_t *vp; |
| |
| vp = (volatile u_int16_t *)(dskinf.cs1 + off); |
| return swap16(*vp); |
| } |
| |
| static void |
| cfreadstream16(void *buf, int length) |
| { |
| u_int16_t *lbuf; |
| |
| length = length / 2; |
| cfenable16(); |
| lbuf = buf; |
| while (length--) { |
| *lbuf = cfread16(CF_DATA); |
| lbuf++; |
| } |
| cfdisable16(); |
| } |
| |
| static void |
| cfwrite8(u_int32_t off, u_int8_t val) |
| { |
| volatile u_int8_t *vp; |
| |
| vp = (volatile u_int8_t *)(dskinf.cs1 + off); |
| *vp = val; |
| } |
| |
| #if 0 |
| static void |
| cfwrite16(u_int32_t off, u_int16_t val) |
| { |
| volatile u_int16_t *vp; |
| |
| vp = (volatile u_int16_t *)(dskinf.cs1 + off); |
| *vp = val; |
| } |
| #endif |
| |
| static u_int8_t |
| cfaltread8(u_int32_t off) |
| { |
| volatile u_int8_t *vp; |
| |
| off &= 0x0f; |
| vp = (volatile u_int8_t *)(dskinf.cs2 + off); |
| return *vp; |
| } |
| |
| static void |
| cfaltwrite8(u_int32_t off, u_int8_t val) |
| { |
| volatile u_int8_t *vp; |
| |
| /* |
| * This is documented in the Intel appnote 302456. |
| */ |
| off &= 0x0f; |
| vp = (volatile u_int8_t *)(dskinf.cs2 + off); |
| *vp = val; |
| } |
| |
| static int |
| cfwait(u_int8_t mask) |
| { |
| u_int8_t status; |
| u_int32_t tout; |
| |
| tout = 0; |
| while (tout <= 5000000) { |
| status = cfread8(CF_STATUS); |
| if (status == 0xff) { |
| printf("%s: master: no status, reselecting\n", |
| __func__); |
| cfwrite8(CF_DRV_HEAD, CF_D_IBM); |
| DELAY(1); |
| status = cfread8(CF_STATUS); |
| } |
| if (status == 0xff) |
| return -1; |
| dskinf.status = status; |
| if (!(status & CF_S_BUSY)) { |
| if (status & CF_S_ERROR) { |
| dskinf.error = cfread8(CF_ERROR); |
| printf("%s: error, status 0x%x error 0x%x\n", |
| __func__, status, dskinf.error); |
| } |
| if ((status & mask) == mask) { |
| DPRINTF("%s: status 0x%x mask 0x%x tout %u\n", |
| __func__, status, mask, tout); |
| return (status & CF_S_ERROR); |
| } |
| } |
| if (tout > 1000) { |
| tout += 1000; |
| DELAY(1000); |
| } else { |
| tout += 10; |
| DELAY(10); |
| } |
| } |
| return -1; |
| } |
| |
| static int |
| cfaltwait(u_int8_t mask) |
| { |
| u_int8_t status; |
| u_int32_t tout; |
| |
| tout = 0; |
| while (tout <= 5000000) { |
| status = cfaltread8(CF_ALT_STATUS); |
| if (status == 0xff) { |
| printf("cfaltwait: master: no status, reselecting\n"); |
| cfwrite8(CF_DRV_HEAD, CF_D_IBM); |
| DELAY(1); |
| status = cfread8(CF_STATUS); |
| } |
| if (status == 0xff) |
| return -1; |
| dskinf.status = status; |
| if (!(status & CF_S_BUSY)) { |
| if (status & CF_S_ERROR) |
| dskinf.error = cfread8(CF_ERROR); |
| if ((status & mask) == mask) { |
| DPRINTF("cfaltwait: tout %u\n", tout); |
| return (status & CF_S_ERROR); |
| } |
| } |
| if (tout > 1000) { |
| tout += 1000; |
| DELAY(1000); |
| } else { |
| tout += 10; |
| DELAY(10); |
| } |
| } |
| return -1; |
| } |
| |
| static int |
| cfcmd(u_int32_t cmd, u_int32_t cylinder, u_int32_t head, u_int32_t sector, |
| u_int32_t count, u_int32_t feature) |
| { |
| if (cfwait(0) < 0) { |
| printf("cfcmd: timeout\n"); |
| return -1; |
| } |
| cfwrite8(CF_FEATURE, feature); |
| cfwrite8(CF_CYL_L, cylinder); |
| cfwrite8(CF_CYL_H, cylinder >> 8); |
| if (dskinf.use_lba) |
| cfwrite8(CF_DRV_HEAD, CF_D_IBM | CF_D_LBA | head); |
| else |
| cfwrite8(CF_DRV_HEAD, CF_D_IBM | head); |
| cfwrite8(CF_SECT_NUM, sector); |
| cfwrite8(CF_SECT_CNT, count); |
| cfwrite8(CF_COMMAND, cmd); |
| return 0; |
| } |
| |
| static void |
| cfreset(void) |
| { |
| u_int8_t status; |
| u_int32_t tout; |
| |
| cfwrite8(CF_DRV_HEAD, CF_D_IBM); |
| DELAY(1); |
| #ifdef DEBUG |
| cfprintregs(); |
| #endif |
| cfread8(CF_STATUS); |
| cfaltwrite8(CF_ALT_DEV_CTR, CF_A_IDS | CF_A_RESET); |
| DELAY(10000); |
| cfaltwrite8(CF_ALT_DEV_CTR, CF_A_IDS); |
| DELAY(10000); |
| cfread8(CF_ERROR); |
| DELAY(3000); |
| |
| for (tout = 0; tout < 310000; tout++) { |
| cfwrite8(CF_DRV_HEAD, CF_D_IBM); |
| DELAY(1); |
| status = cfread8(CF_STATUS); |
| if (!(status & CF_S_BUSY)) |
| break; |
| DELAY(100); |
| } |
| DELAY(1); |
| if (status & CF_S_BUSY) { |
| cfprintregs(); |
| printf("cfreset: Status stayed busy after reset.\n"); |
| } |
| DPRINTF("cfreset: finished, tout %u\n", tout); |
| } |
| |
| #ifdef DEBUG |
| static int |
| cfgetparams(void) |
| { |
| u_int8_t *buf; |
| |
| buf = (u_int8_t *)(0x170000); |
| p_memset((char *)buf, 0, 1024); |
| /* Select the drive. */ |
| cfwrite8(CF_DRV_HEAD, CF_D_IBM); |
| DELAY(1); |
| cfcmd(ATA_ATA_IDENTIFY, 0, 0, 0, 0, 0); |
| if (cfaltwait(CF_S_READY | CF_S_DSC | CF_S_DRQ)) { |
| printf("cfgetparams: ATA_IDENTIFY failed.\n"); |
| return -1; |
| } |
| if (dskinf.use_stream8) |
| cfreadstream8(buf, 512); |
| else |
| cfreadstream16(buf, 512); |
| if (dskinf.debug) |
| cfprintregs(); |
| #if 0 |
| memcpy(&dskinf.ata_params, buf, sizeof(struct ata_params)); |
| dskinf.cylinders = dskinf.ata_params.cylinders; |
| dskinf.heads = dskinf.ata_params.heads; |
| dskinf.sectors = dskinf.ata_params.sectors; |
| printf("dsk0: sec %x, hd %x, cyl %x, stat %x, err %x\n", |
| (u_int32_t)dskinf.ata_params.sectors, |
| (u_int32_t)dskinf.ata_params.heads, |
| (u_int32_t)dskinf.ata_params.cylinders, |
| (u_int32_t)dskinf.status, |
| (u_int32_t)dskinf.error); |
| #endif |
| dskinf.status = cfread8(CF_STATUS); |
| if (dskinf.debug) |
| printf("cfgetparams: ata_params * %x, stat %x\n", |
| (u_int32_t)buf, (u_int32_t)dskinf.status); |
| return 0; |
| } |
| #endif /* DEBUG */ |
| |
| static void |
| cfprintregs(void) |
| { |
| u_int8_t rv; |
| |
| putstr("cfprintregs: regs error "); |
| rv = cfread8(CF_ERROR); |
| puthex8(rv); |
| putstr(", count "); |
| rv = cfread8(CF_SECT_CNT); |
| puthex8(rv); |
| putstr(", sect "); |
| rv = cfread8(CF_SECT_NUM); |
| puthex8(rv); |
| putstr(", cyl low "); |
| rv = cfread8(CF_CYL_L); |
| puthex8(rv); |
| putstr(", cyl high "); |
| rv = cfread8(CF_CYL_H); |
| puthex8(rv); |
| putstr(", drv head "); |
| rv = cfread8(CF_DRV_HEAD); |
| puthex8(rv); |
| putstr(", status "); |
| rv = cfread8(CF_STATUS); |
| puthex8(rv); |
| putstr("\n"); |
| } |
| |
| int |
| avila_read(char *dest, unsigned source, unsigned length) |
| { |
| if (dskinf.use_lba == 0 && source == 0) |
| source++; |
| if (dskinf.debug) |
| printf("avila_read: 0x%x, sect %d num secs %d\n", |
| (u_int32_t)dest, source, length); |
| while (length) { |
| cfwait(CF_S_READY); |
| /* cmd, cyl, head, sect, count, feature */ |
| cfcmd(ATA_READ, (source >> 8) & 0xffff, source >> 24, |
| source & 0xff, 1, 0); |
| |
| cfwait(CF_S_READY | CF_S_DRQ | CF_S_DSC); |
| if (dskinf.use_stream8) |
| cfreadstream8(dest, 512); |
| else |
| cfreadstream16(dest, 512); |
| length--; |
| source++; |
| dest += 512; |
| } |
| return 0; |
| } |
| |
| /* |
| * Gateworks Avila Support. |
| */ |
| static int |
| avila_probe(int boardtype_hint) |
| { |
| volatile u_int32_t *cs; |
| /* |
| * Redboot only configure the chip selects that are needed, so |
| * use that to figure out if it is an Avila or ADI board. The |
| * Avila boards use CS2 and ADI does not. |
| */ |
| cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS2_OFFSET); |
| return (*cs != 0); |
| } |
| |
| static void |
| avila_init(void) |
| { |
| /* Config the serial port. RedBoot should do the rest. */ |
| ubase = (u_int8_t *)(IXP425_UART0_HWBASE); |
| |
| dskinf.cs1to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS1_OFFSET); |
| dskinf.cs2to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS2_OFFSET); |
| dskinf.cs1 = (u_int8_t *)IXP425_EXP_BUS_CS1_HWBASE; |
| dskinf.cs2 = (u_int8_t *)IXP425_EXP_BUS_CS2_HWBASE; |
| |
| cf_init(); |
| } |
| BOARD_CONFIG(avila, "Gateworks Avila"); |
| |
| /* |
| * Gateworks Cambria Support. |
| */ |
| static int |
| cambria_probe(int boardtype_hint) |
| { |
| return cpu_is_ixp43x(); |
| } |
| |
| static void |
| cambria_init(void) |
| { |
| /* Config the serial port. RedBoot should do the rest. */ |
| ubase = (u_int8_t *)(IXP425_UART0_HWBASE); |
| |
| dskinf.cs1to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS3_OFFSET); |
| dskinf.cs2to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS4_OFFSET); |
| dskinf.cs1 = (u_int8_t *)CAMBRIA_CFSEL0_HWBASE; |
| dskinf.cs2 = (u_int8_t *)CAMBRIA_CFSEL1_HWBASE; |
| |
| cf_init(); |
| } |
| BOARD_CONFIG(cambria, "Gateworks Cambria"); |
| |
| /* |
| * Pronghorn Metro Support. |
| */ |
| static int |
| pronghorn_probe(int boardtype_hint) |
| { |
| volatile u_int32_t *cs; |
| /* |
| * Redboot only configure the chip selects that are needed, so |
| * use that to figure out if it is an Avila or ADI board. The |
| * Avila boards use CS2 and ADI does not. |
| */ |
| cs = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS2_OFFSET); |
| return (*cs == 0); |
| } |
| |
| static void |
| pronghorn_init(void) |
| { |
| /* Config the serial port. RedBoot should do the rest. */ |
| ubase = (u_int8_t *)(IXP425_UART1_HWBASE); |
| |
| dskinf.cs1to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS3_OFFSET); |
| dskinf.cs2to = (u_int32_t *)(IXP425_EXP_HWBASE + EXP_TIMING_CS4_OFFSET); |
| dskinf.cs1 = (u_int8_t *)IXP425_EXP_BUS_CS3_HWBASE; |
| dskinf.cs2 = (u_int8_t *)IXP425_EXP_BUS_CS4_HWBASE; |
| |
| cf_init(); |
| } |
| BOARD_CONFIG(pronghorn, "Pronghorn Metro"); |