diff options
author | 2015-08-28 09:58:54 +0800 | |
---|---|---|
committer | 2015-09-01 12:44:00 +0800 | |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/SLOF/lib | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/SLOF/lib')
152 files changed, 21172 insertions, 0 deletions
diff --git a/qemu/roms/SLOF/lib/Makefile b/qemu/roms/SLOF/lib/Makefile new file mode 100644 index 000000000..ed8a3597e --- /dev/null +++ b/qemu/roms/SLOF/lib/Makefile @@ -0,0 +1,35 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +SUBDIRS = libc libipmi libbootmsg libbases libnvram libelf libhvcall libvirtio \ + libusb libveth libe1k libbcm + +all: subdirs + +.PHONY : subdirs $(SUBDIRS) clean distclean + + +subdirs: $(SUBDIRS) + +$(SUBDIRS): + $(MAKE) -C $@ $(MAKEARG) + +# Rules for making clean: +clean: + @for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir clean || exit 1; \ + done + +distclean: + @for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir distclean || exit 1; \ + done diff --git a/qemu/roms/SLOF/lib/libbases/Makefile b/qemu/roms/SLOF/lib/libbases/Makefile new file mode 100644 index 000000000..add4ed18c --- /dev/null +++ b/qemu/roms/SLOF/lib/libbases/Makefile @@ -0,0 +1,40 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) -I. -I../../include +LDFLAGS = -nostdlib + +all: + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + + +# Include dependency file if available: +-include Makefile.dep + diff --git a/qemu/roms/SLOF/lib/libbases/libbases.code b/qemu/roms/SLOF/lib/libbases/libbases.code new file mode 100644 index 000000000..128b94ab2 --- /dev/null +++ b/qemu/roms/SLOF/lib/libbases/libbases.code @@ -0,0 +1,43 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +#include <southbridge.h> +#include <nvram.h> + +// : get-nvram-base ( -- base ) +PRIM(get_X2d_nvram_X2d_base) + PUSH; + TOS.u = SB_NVRAM_adr; +MIRP + +// : get-nvram-size ( -- size ) +PRIM(get_X2d_nvram_X2d_size) + PUSH; + TOS.u = get_nvram_size(); +MIRP + +// : get-flash-base ( -- base ) +PRIM(get_X2d_flash_X2d_base) + PUSH; + TOS.u = SB_FLASH_adr; +MIRP + +// : get-flash-size ( -- size ) +PRIM(get_X2d_flash_X2d_size) + PUSH; + TOS.u = FLASH_LENGTH; +MIRP + +// : get-mbx-base ( -- base ) +PRIM(get_X2d_mbx_X2d_base) + PUSH; + TOS.u = SB_MAILBOX_adr; +MIRP diff --git a/qemu/roms/SLOF/lib/libbases/libbases.in b/qemu/roms/SLOF/lib/libbases/libbases.in new file mode 100644 index 000000000..844a55de1 --- /dev/null +++ b/qemu/roms/SLOF/lib/libbases/libbases.in @@ -0,0 +1,17 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +cod(get-nvram-base) +cod(get-nvram-size) +cod(get-flash-base) +cod(get-flash-size) +cod(get-mbx-base) diff --git a/qemu/roms/SLOF/lib/libbcm/Makefile b/qemu/roms/SLOF/lib/libbcm/Makefile new file mode 100644 index 000000000..1aead4c5e --- /dev/null +++ b/qemu/roms/SLOF/lib/libbcm/Makefile @@ -0,0 +1,53 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008, 2013 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \ + -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) + +#CFLAGS += -O2 -I. -I../common -I$(TOP)/clients/net-snk/include -I$(TOP)/lib/libc/include -fno-builtin -ffreestanding -msoft-float -Wall -nostdinc + +LDFLAGS = -nostdlib + +TARGET = ../libbcm.a + + +all: $(TARGET) Makefile.dep + +SRCS = bcm57xx.c + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep diff --git a/qemu/roms/SLOF/lib/libbcm/bcm.code b/qemu/roms/SLOF/lib/libbcm/bcm.code new file mode 100644 index 000000000..308ebbaa2 --- /dev/null +++ b/qemu/roms/SLOF/lib/libbcm/bcm.code @@ -0,0 +1,57 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * libbcm Forth wrapper + */ + +#include <bcm57xx.h> + +// : bcm57xx-open ( -- false | [ driver true ] ) +PRIM(BCM57XX_X2d_OPEN) +{ + net_driver_t *net_driver = bcm57xx_open(); + if (net_driver) { + PUSH; + TOS.u = (unsigned long)net_driver; PUSH; + TOS.n = -1; + } else { + PUSH; + TOS.n = 0; + } +} +MIRP + +// : bcm57xx-close ( driver -- ) +PRIM(BCM57XX_X2d_CLOSE) +{ + net_driver_t *driver = TOS.a; POP; + bcm57xx_close(driver); +} +MIRP + + +// : bcm57xx-read ( addr len -- actual ) +PRIM(BCM57XX_X2d_READ) +{ + int len = TOS.u; POP; + TOS.n = bcm57xx_read(TOS.a, len); +} +MIRP + +// : bcm57xx-write ( addr len -- actual ) +PRIM(BCM57XX_X2d_WRITE) +{ + int len = TOS.u; POP; + TOS.n = bcm57xx_write(TOS.a, len); +} +MIRP diff --git a/qemu/roms/SLOF/lib/libbcm/bcm.in b/qemu/roms/SLOF/lib/libbcm/bcm.in new file mode 100644 index 000000000..ee50a6e65 --- /dev/null +++ b/qemu/roms/SLOF/lib/libbcm/bcm.in @@ -0,0 +1,20 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * libbcm bindings for Forth - definitions + */ + +cod(BCM57XX-OPEN) +cod(BCM57XX-CLOSE) +cod(BCM57XX-READ) +cod(BCM57XX-WRITE) diff --git a/qemu/roms/SLOF/lib/libbcm/bcm57xx.c b/qemu/roms/SLOF/lib/libbcm/bcm57xx.c new file mode 100644 index 000000000..2ecb517f1 --- /dev/null +++ b/qemu/roms/SLOF/lib/libbcm/bcm57xx.c @@ -0,0 +1,3461 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * + ****************************************************************************** + * reference: + * Broadcom 57xx + * Host Programmer Interface Specification for the + * NetXtreme Family of Highly-Integrated Media Access Controlers + */ +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <byteorder.h> +#include <helpers.h> +#include <netdriver.h> +#include "bcm57xx.h" + +/* + * local defines + ****************************************************************************** + */ + + +// #define BCM_VLAN_TAG ( (uint32_t) 0x1 ) + +// number of tx/rx rings +// NOTE: 5714 only uses 1 rx/tx ring, but memory +// for the other rings is cleaned anyways for +// sanity & future use +#define BCM_MAX_TX_RING 16 +#define BCM_MAX_RXRET_RING 16 +#define BCM_MAX_RXPROD_RCB 3 + +// bd descriptions +#define BCM_RXPROD_RING_SIZE 512 // don't change +#define BCM_RXRET_RING_SIZE 512 // don't change +#define BCM_TX_RING_SIZE 512 // don't change +#define BCM_BUF_SIZE 1536 // don't change +#define BCM_MTU_MAX_LEN 1522 +#define BCM_MAX_RX_BUF 64 +#define BCM_MAX_TX_BUF 16 + +// number of MAC addresses in NIC +#define BCM_NUM_MAC_ADDR 4 +#define BCM_NUM_MAC5704_ADDR 12 +// offset of mac address field(s) in bcm register space +#define MAC5704_ADDR_OFFS ( (uint16_t) 0x0530 ) + +// offset of NIC memory start address from base address +#define BCM_MEMORY_OFFS ( (uint64_t) 0x8000 ) + +// offset of statistics block in NIC memory +#define BCM_STATISTIC_OFFS ( (uint64_t) 0x0300 ) +// size of statistic block in NIC memory +#define BCM_STATISTIC_SIZE 0x800 + +// offsets of NIC rx/tx rings in NIC memory +#define BCM_NIC_TX_OFFS ( (uint16_t) 0x4000 ) +#define BCM_NIC_RX_OFFS ( (uint16_t) 0x6000 ) +#define BCM_NIC_TX_SIZE ( (uint16_t) ( ( BCM_TX_RING_SIZE * BCM_RCB_SIZE_u16 ) / 4 ) ) + +// device mailboxes +#define BCM_FW_MBX ( (uint16_t) 0x0b50 ) +#define BCM_FW_MBX_CMD ( (uint16_t) 0x0b78 ) +#define BCM_FW_MBX_LEN ( (uint16_t) 0x0b7c ) +#define BCM_FW_MBX_DATA ( (uint16_t) 0x0b80 ) +#define BCM_NICDRV_STATE_MBX ( (uint16_t) 0x0c04 ) + +// device mailbox commands +#define BCM_NICDRV_ALIVE ( (uint32_t) 0x00000001 ) +#define BCM_NICDRV_PAUSE_FW ( (uint32_t) 0x00000002 ) + +// device values +#define BCM_MAGIC_NUMBER ( (uint32_t) 0x4b657654 ) + +// device states +#define NIC_FWDRV_STATE_START ( (uint32_t) 0x00000001 ) +#define NIC_FWDRV_STATE_START_DONE ( (uint32_t) 0x80000001 ) +#define NIC_FWDRV_STATE_UNLOAD ( (uint32_t) 0x00000002 ) +#define NIC_FWDRV_STATE_UNLOAD_DONE ( (uint32_t) 0x80000002 ) +#define NIC_FWDRV_STATE_SUSPEND ( (uint32_t) 0x00000004 ) + +// timer prescaler value +#define BCM_TMR_PRESCALE ( (uint32_t) 0x41 ) + +// offset of transmit rcb's in NIC memory +#define BCM_TX_RCB_OFFS ( (uint16_t) 0x0100 ) +// offset of receive return rcb's in NIC memory +#define BCM_RXRET_RCB_OFFS ( (uint16_t) 0x0200 ) + +// register offsets for ring indices +#define TX_PROD_IND ( (uint16_t) 0x0304 ) +#define TX_CONS_IND ( (uint16_t) 0x3cc0 ) +#define RXPROD_PROD_IND ( (uint16_t) 0x026c ) +#define RXPROD_CONS_IND ( (uint16_t) 0x3c54 ) +#define RXRET_PROD_IND ( (uint16_t) 0x3c80 ) +#define RXRET_CONS_IND ( (uint16_t) 0x0284 ) +// NIC producer index only needed for initialization +#define TX_NIC_PROD_IND ( (uint16_t) 0x0384 ) + +/* + * predefined register values used during initialization + * may be adapted by user + */ +#define DMA_RW_CTRL_VAL_5714 ( (uint32_t) 0x76144000 ) +#define DMA_RW_CTRL_VAL ( (uint32_t) 0x760F0000 ) +#define TX_MAC_LEN_VAL ( (uint32_t) 0x00002620 ) + +#define RX_LST_PLC_CFG_VAL ( (uint32_t) 0x00000109 ) +#define RX_LST_PLC_STAT_EN_VAL ( (uint32_t) 0x007e000f ) +#define NVM_ADDR_MSK ( (uint32_t) 0x000fffff ) + +// Number of Receive Rules /w or /wo SOL enabled +#define RX_RULE_CFG_VAL ( (uint32_t) 0x00000008 ) +#define NUM_RX_RULE ( (uint32_t) 16 ) +#define NUM_RX_RULE_ASF ( (uint32_t) ( NUM_RX_RULE - 4 ) ) + +// RCB register offsets +#define BCM_RXPROD_RCB_JUM ( (uint16_t) 0x2440 ) +#define BCM_RXPROD_RCB_STD ( (uint16_t) 0x2450 ) +#define BCM_RXPROD_RCB_MIN ( (uint16_t) 0x2460 ) + +// macros needed for new addressing method +#define BCM_RCB_HOSTADDR_HI_u16( rcb ) ( (uint16_t) rcb + 0x00 ) +#define BCM_RCB_HOSTADDR_LOW_u16( rcb ) ( (uint16_t) rcb + 0x04 ) +#define BCM_RCB_LENFLAG_u16( rcb ) ( (uint16_t) rcb + 0x08 ) +#define BCM_RCB_NICADDR_u16( rcb ) ( (uint16_t) rcb + 0x0c ) +#define BCM_RCB_SIZE_u16 ( (uint16_t) 0x0010 ) + +// RCB flags +#define RCB_FLAG_RING_DISABLED BIT32( 1 ) + +// BCM device ID masks +#define BCM_DEV_5714 ( (uint64_t) 0x1 ) +#define BCM_DEV_5704 ( (uint64_t) 0x2 ) +#define BCM_DEV_5703 ( (uint64_t) 0x4 ) +#define BCM_DEV_SERDES ( (uint64_t) 0x80000000 ) +#define BCM_DEV_COPPER ( (uint64_t) 0x40000000 ) + +#define IS_5714 ( ( bcm_device_u64 & BCM_DEV_5714 ) != 0 ) +#define IS_5704 ( ( bcm_device_u64 & BCM_DEV_5704 ) != 0 ) +#define IS_5703 ( ( bcm_device_u64 & BCM_DEV_5703 ) != 0 ) +#define IS_SERDES ( ( bcm_device_u64 & BCM_DEV_SERDES ) != 0 ) +#define IS_COPPER_PHY ( ( bcm_device_u64 & BCM_DEV_COPPER ) != 0 ) + +#define BUFFERED_FLASH_PAGE_POS 9 +#define BUFFERED_FLASH_BYTE_ADDR_MASK ((<<BUFFERED_FLASH_PAGE_POS) - 1) +#define BUFFERED_FLASH_PAGE_SIZE 264 +#define BUFFERED_FLASH_PHY_SIZE 512 +#define MANUFACTURING_INFO_SIZE 140 +#define CRC32_POLYNOMIAL 0xEDB88320 + +/* + * local types + ****************************************************************************** + */ +typedef struct { + uint32_t m_dev_u32; + uint64_t m_devmsk_u64; +} bcm_dev_t; + +/* + * BCM common data structures + * BCM57xx Programmer's Guide: Section 5 + */ + +/* + * 64bit host address in a way the NIC is able to understand it + */ +typedef struct { + uint32_t m_hi_u32; + uint32_t m_lo_u32; +} bcm_addr64_t; +/* + * ring control block + */ +typedef struct { + bcm_addr64_t m_hostaddr_st; + uint32_t m_lenflags_u32; // upper 16b: len, lower 16b: flags + uint32_t m_nicaddr_u32; +} bcm_rcb_t; + +/* + * tx buffer descriptor + */ +typedef struct { + bcm_addr64_t m_hostaddr_st; + uint32_t m_lenflags_u32; // upper 16b: len, lower 16b: flags + uint32_t m_VLANtag_u32; // lower 16b: vtag +} bcm_txbd_t; + +/* + * rx buffer descriptor + */ +typedef struct { + bcm_addr64_t m_hostaddr_st; + uint32_t m_idxlen_u32; // upper 16b: idx, lower 16b: len + uint32_t m_typeflags_u32; // upper 16b: type, lower 16b: flags + uint32_t m_chksum_u32; // upper 16b: ip, lower 16b: tcp/udp + uint32_t m_errvlan_u32; // upper 16b: err, lower 16b: vlan tag + uint32_t m_reserved_u32; + uint32_t m_opaque_u32; +} bcm_rxbd_t; + +/* + * bcm status block + * NOTE: in fact the status block is not used and configured + * so that it is not updated by the NIC. Still it has to be + * set up so the NIC is satisfied + */ +typedef struct { + uint32_t m_st_word_u32; + uint32_t m_st_tag_u32; + uint16_t m_rxprod_cons_u16; + uint16_t m_unused_u16; + uint32_t m_unused_u32; + uint16_t m_tx_cons_u16; + uint16_t m_rxret_prod_u16; +} bcm_status_t; + +/* + * local constants + ****************************************************************************** + */ +static const bcm_dev_t bcm_dev[] = { + { 0x166b, BCM_DEV_5714 }, + { 0x1668, BCM_DEV_5714 }, + { 0x1669, BCM_DEV_5714 }, + { 0x166a, BCM_DEV_5714 }, + { 0x1648, BCM_DEV_5704 }, + { 0x1649, BCM_DEV_5704 | BCM_DEV_SERDES }, + { 0x16a8, BCM_DEV_5704 | BCM_DEV_SERDES }, + { 0x16a7, BCM_DEV_5703 | BCM_DEV_SERDES }, + { 0x16c7, BCM_DEV_5703 | BCM_DEV_SERDES }, + { 0 , 0 } +}; + +/* + * local variables + ****************************************************************************** + */ +static uint64_t bcm_device_u64; +static uint32_t bcm_rxret_ring_sz; +static uint64_t bcm_baseaddr_u64; +static uint64_t bcm_memaddr_u64; + +/* + * rings & their buffers + */ +// the rings made of buffer descriptors +static bcm_txbd_t bcm_tx_ring[BCM_TX_RING_SIZE]; +static bcm_rxbd_t bcm_rxprod_ring[BCM_RXPROD_RING_SIZE]; +static bcm_rxbd_t bcm_rxret_ring[BCM_RXRET_RING_SIZE*2]; + +// the buffers used in the rings +static uint8_t bcm_tx_buffer_pu08[BCM_MAX_TX_BUF][BCM_BUF_SIZE]; +static uint8_t bcm_rx_buffer_pu08[BCM_MAX_RX_BUF][BCM_BUF_SIZE]; + +// tx ring index of first/last bd +static uint32_t bcm_tx_start_u32; +static uint32_t bcm_tx_stop_u32; +static uint32_t bcm_tx_bufavail_u32; + +/* + * status block + */ +static bcm_status_t bcm_status; + +/* + * implementation + ****************************************************************************** + */ + + +/* + * global functions + ****************************************************************************** + */ + + +/* + * local helper functions + ****************************************************************************** + */ +#if 0 +static char * +memcpy( char *dest, const char *src, size_t n ) +{ + char *ret = dest; + while( n-- ) { + *dest++ = *src++; + } + + return( ret ); +} +#endif + +static char * +memset_ci( char *dest, int c, size_t n ) +{ + char *ret = dest; + + while( n-- ) { + wr08( dest, c ); + dest++; + } + + return( ret ); +} + +#if 0 +static char * +memset( char *dest, int c, size_t n ) +{ + char *ret = dest; + while( n-- ) { + *dest++ = (char) c; + } + + return( ret ); +} +#endif + +static uint32_t +bcm_nvram_logical_to_physical_address(uint32_t address) +{ + uint32_t page_no = address / BUFFERED_FLASH_PAGE_SIZE; + uint32_t page_addr = address % BUFFERED_FLASH_PAGE_SIZE; + + return (page_no << BUFFERED_FLASH_PAGE_POS) + page_addr; +} + +/* + * read/write functions to access NIC registers & memory + * NOTE: all functions are executed with cache inhibitation (dead slow :-) ) + */ +static uint32_t +bcm_read_mem32( uint16_t f_offs_u16 ) +{ // caution: shall only be used after initialization! + return rd32( bcm_memaddr_u64 + (uint64_t) f_offs_u16 ); +} + +/* not used so far +static uint16_t +bcm_read_mem16( uint16_t f_offs_u16 ) +{ // caution: shall only be used after initialization! + return rd16( bcm_memaddr_u64 + (uint64_t) f_offs_u16 ); +}*/ +/* not used so far +static uint8_t +bcm_read_mem08( uint16_t f_offs_u16 ) +{ // caution: shall only be used after initialization! + return rd08( bcm_memaddr_u64 + (uint64_t) f_offs_u16 ); +}*/ + +static uint32_t +bcm_read_reg32_indirect( uint16_t f_offs_u16 ) +{ // caution: shall only be used after initialization! + SLOF_pci_config_write32(REG_BASE_ADDR_REG, f_offs_u16); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + REG_BASE_ADDR_REG, + f_offs_u16 );*/ + return bswap_32(SLOF_pci_config_read32(REG_DATA_REG)); + /*return (uint32_t) bswap_32( snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + REG_DATA_REG ) ) ;*/ +} + +static uint32_t +bcm_read_reg32( uint16_t f_offs_u16 ) +{ // caution: shall only be used after initialization! + if(f_offs_u16 >= 0x200 && f_offs_u16 <0x400) + return bcm_read_reg32_indirect( f_offs_u16 + 0x5600 ); + return rd32( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); +} + +static uint16_t +bcm_read_reg16( uint16_t f_offs_u16 ) +{ // caution: shall only be used after initialization! + return rd16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); +} +/* not used so far +static uint8_t +bcm_read_reg08( uint16_t f_offs_u16 ) +{ // caution: shall only be used after initialization! + return rd08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); +}*/ + +static void +bcm_write_mem32_indirect( uint16_t f_offs_u16, uint32_t f_val_u32 ) +{ // caution: shall only be used after initialization! + SLOF_pci_config_write32(MEM_BASE_ADDR_REG, f_offs_u16); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + MEM_BASE_ADDR_REG, + f_offs_u16 );*/ + SLOF_pci_config_write32(MEM_DATA_REG, bswap_32(f_val_u32)); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + MEM_DATA_REG, + bswap_32 ( f_val_u32 ) );*/ +} + +static void +bcm_write_mem32( uint16_t f_offs_u16, uint32_t f_val_u32 ) +{ // caution: shall only be used after initialization! + if(f_offs_u16 >= BCM_RXRET_RCB_OFFS && + f_offs_u16 < BCM_RXRET_RCB_OFFS + (BCM_MAX_RXRET_RING*BCM_RCB_SIZE_u16)) + bcm_write_mem32_indirect( f_offs_u16, f_val_u32 ); + else if(f_offs_u16 >= BCM_TX_RCB_OFFS && + f_offs_u16 < BCM_TX_RCB_OFFS + (BCM_MAX_TX_RING*BCM_RCB_SIZE_u16)) + bcm_write_mem32_indirect( f_offs_u16, f_val_u32 ); + else + wr32( bcm_memaddr_u64 + (uint64_t) f_offs_u16, f_val_u32 ); +} +/* not used so far +static void +bcm_write_mem16( uint16_t f_offs_u16, uint16_t f_val_u16 ) +{ // caution: shall only be used after initialization! + wr16( bcm_memaddr_u64 + (uint64_t) f_offs_u16, f_val_u16 ); +}*/ +/* not used so far +static void +bcm_write_mem08( uint16_t f_offs_u16, uint8_t f_val_u08 ) +{ // caution: shall only be used after initialization! + wr08( bcm_memaddr_u64 + (uint64_t) f_offs_u16, f_val_u08 ); +}*/ + +static void +bcm_write_reg32_indirect( uint16_t f_offs_u16, uint32_t f_val_u32 ) +{ // caution: shall only be used after initialization! + SLOF_pci_config_write32(REG_BASE_ADDR_REG, f_offs_u16); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + REG_BASE_ADDR_REG, + f_offs_u16 );*/ + SLOF_pci_config_write32(REG_DATA_REG, bswap_32(f_val_u32)); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + REG_DATA_REG, + bswap_32 ( f_val_u32 ) );*/ +} + +static void +bcm_write_reg32( uint16_t f_offs_u16, uint32_t f_val_u32 ) +{ // caution: shall only be used after initialization! + if(f_offs_u16 >= 0x200 && f_offs_u16 <0x400) + bcm_write_reg32_indirect( f_offs_u16 + 0x5600, f_val_u32 ); + else + wr32( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, f_val_u32 ); +} + +static void +bcm_write_reg16( uint16_t f_offs_u16, uint16_t f_val_u16 ) +{ // caution: shall only be used after initialization! + wr16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, f_val_u16 ); +} +/* not used so far +static void +bcm_write_reg08( uint16_t f_offs_u16, uint8_t f_val_u08 ) +{ // caution: shall only be used after initialization! + wr08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, f_val_u08 ); +}*/ + +static void +bcm_setb_reg32( uint16_t f_offs_u16, uint32_t f_mask_u32 ) +{ + uint32_t v; + + v = bcm_read_reg32( f_offs_u16 ); + v |= f_mask_u32; + bcm_write_reg32( f_offs_u16, v ); +} +/* not used so far +static void +bcm_setb_reg16( uint16_t f_offs_u16, uint16_t f_mask_u16 ) +{ + uint16_t v; + v = rd16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); + v |= f_mask_u16; + wr16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, v ); +}*/ +/* not used so far +static void +bcm_setb_reg08( uint16_t f_offs_u16, uint8_t f_mask_u08 ) +{ + uint8_t v; + v = rd08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); + v |= f_mask_u08; + wr08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, v ); +}*/ + +static void +bcm_clrb_reg32( uint16_t f_offs_u16, uint32_t f_mask_u32 ) +{ + uint32_t v; + + v = bcm_read_reg32( f_offs_u16 ); + v &= ~f_mask_u32; + bcm_write_reg32( f_offs_u16, v ); +} + +static void +bcm_clrb_reg16( uint16_t f_offs_u16, uint16_t f_mask_u16 ) +{ + uint16_t v; + + v = bcm_read_reg16( f_offs_u16 ); + v &= ~f_mask_u16; + bcm_write_reg16( f_offs_u16, v ); +} +/* not used so far +static void +bcm_clrb_reg08( uint16_t f_offs_u16, uint8_t f_mask_u08 ) +{ + uint8_t v; + v = rd08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); + v &= ~f_mask_u32; + wr08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, v ); +}*/ + +static void +bcm_clr_wait_bit32( uint16_t r, uint32_t b ) +{ + uint32_t i; + + bcm_clrb_reg32( r, b ); + + i = 1000; + while( --i ) { + + if( ( bcm_read_reg32( r ) & b ) == 0 ) { + break; + } + + SLOF_usleep( 10 ); + } +#ifdef BCM_DEBUG + if( ( bcm_read_reg32( r ) & b ) != 0 ) { + printf( "bcm57xx: bcm_clear_wait_bit32 failed (0x%04X)!\n", r ); + } +#endif +} + +/* + * (g)mii bus access + */ +#if 0 +// not used so far +static int32_t +bcm_mii_write16( uint32_t f_reg_u32, uint16_t f_value_u16 ) +{ + static const uint32_t WR_VAL = ( ( ((uint32_t) 0x1) << 21 ) | BIT32( 29 ) | BIT32( 26 ) ); + int32_t l_autopoll_i32 = 0; + uint32_t l_wrval_u32; + uint32_t i; + + /* + * only 0x00-0x1f are valid registers + */ + if( f_reg_u32 > (uint32_t) 0x1f ) { + return -1; + } + + /* + * disable auto polling if enabled + */ + if( ( bcm_read_reg32( MI_MODE_R ) & BIT32( 4 ) ) != 0 ) { + l_autopoll_i32 = (int32_t) !0; + bcm_clrb_reg32( MI_MODE_R, BIT32( 4 ) ); + SLOF_usleep( 40 ); + } + + /* + * construct & write mi com register value + */ + l_wrval_u32 = ( WR_VAL | ( f_reg_u32 << 16 ) | (uint32_t) f_value_u16 ); + bcm_write_reg32( MI_COM_R, l_wrval_u32 ); + + /* + * wait for transaction to complete + */ + i = 25; + while( ( --i ) && + ( ( bcm_read_reg32( MI_COM_R ) & BIT32( 29 ) ) != 0 ) ) { + SLOF_usleep( 10 ); + } + + /* + * re-enable auto polling if necessary + */ + if( l_autopoll_i32 ) { + bcm_setb_reg32( MI_MODE_R, BIT32( 4 ) ); + } + + // return on error + if( i == 0 ) { + return -1; + } + + return 0; +} +#endif + +static int32_t +bcm_mii_read16( uint32_t f_reg_u32, uint16_t *f_value_pu16 ) +{ + static const uint32_t RD_VAL = ( ( ((uint32_t) 0x1) << 21 ) | BIT32( 29 ) | BIT32( 27 ) ); + int32_t l_autopoll_i32 = 0; + uint32_t l_rdval_u32; + uint32_t i; + uint16_t first_not_busy; + + /* + * only 0x00-0x1f are valid registers + */ + if( f_reg_u32 > (uint32_t) 0x1f ) { + return -1; + } + + /* + * disable auto polling if enabled + */ + if( ( bcm_read_reg32( MI_MODE_R ) & BIT32( 4 ) ) != 0 ) { + l_autopoll_i32 = ( int32_t ) !0; + bcm_clrb_reg32( MI_MODE_R, BIT32( 4 ) ); + SLOF_usleep( 40 ); + } + + /* + * construct & write mi com register value + */ + l_rdval_u32 = ( RD_VAL | ( f_reg_u32 << 16 ) ); + bcm_write_reg32( MI_COM_R, l_rdval_u32 ); + + /* + * wait for transaction to complete + * ERRATA workaround: must read two "not busy" states to indicate transaction complete + */ + i = 25; + first_not_busy = 0; + l_rdval_u32 = bcm_read_reg32( MI_COM_R ); + while( ( --i ) && + ( (first_not_busy == 0) || ( ( l_rdval_u32 & BIT32( 29 ) ) != 0 ) ) ) { + /* Is this the first clear BUSY state? */ + if ( ( l_rdval_u32 & BIT32( 29 ) ) == 0 ) + first_not_busy++; + SLOF_usleep( 10 ); + l_rdval_u32 = bcm_read_reg32( MI_COM_R ); + } + + /* + * re-enable autopolling if necessary + */ + if( l_autopoll_i32 ) { + bcm_setb_reg32( MI_MODE_R, BIT32( 4 ) ); + } + + /* + * return on read transaction error + * (check read failed bit) + */ + if( ( i == 0 ) || + ( ( l_rdval_u32 & BIT32( 28 ) ) != 0 ) ) { + return -1; + } + + /* + * return read value + */ + *f_value_pu16 = (uint16_t) ( l_rdval_u32 & (uint32_t) 0xffff ); + + return 0; +} + +/* + * ht2000 dump (not complete) + */ +#if 0 +static void +bcm_dump( void ) +{ + uint32_t i, j; + + printf( "*** DUMP ***********************************************************************\n\n" ); + + printf( "* PCI Configuration Registers:\n" ); + for( i = 0, j = 0; i < 0x40; i += 4 ) { + + printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); + + if( ( ++j & 0x3 ) == 0 ) { + printf( "\n" ); + } + + } + + printf( "\n* Private PCI Configuration Registers:\n" ); + for( i = 0x68, j = 0; i < 0x88; i += 4 ) { + + printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); + + if( ( ++j & 0x3 ) == 0 ) { + printf( "\n" ); + } + + } + + printf( "\n* VPD Config:\n" ); + printf( "%04X: %08X \n", 0x94, bcm_read_reg32( 0x94 ) ); + + printf( "\n* Dual MAC Control Registers:\n" ); + for( i = 0xb8, j = 0; i < 0xd0; i += 4 ) { + + printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); + + if( ( ++j & 0x3 ) == 0 ) { + printf( "\n" ); + } + + } + + printf( "\n* Ethernet MAC Control Registers:\n" ); + for( i = 0x400, j = 0; i < 0x590; i += 4 ) { + + printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); + + if( ( ++j & 0x3 ) == 0 ) { + printf( "\n" ); + } + + } + + printf( "\n* Send Data Initiator Control:\n" ); + for( i = 0xc00, j = 0; i < 0xc10; i += 4 ) { + + printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); + + if( ( ++j & 0x3 ) == 0 ) { + printf( "\n" ); + } + + } + + printf( "\n* Send Data Completion Control:\n" ); + printf( "%04X: %08X ", 0x1000, bcm_read_reg32( 0x1000 ) ); + printf( "%04X: %08X \n", 0x1008, bcm_read_reg32( 0x1008 ) ); + + printf( "\n* Send BD Ring Selector Control:\n" ); + printf( "%04X: %08X ", 0x1400, bcm_read_reg32( 0x1400 ) ); + printf( "%04X: %08X ", 0x1404, bcm_read_reg32( 0x1404 ) ); + printf( "%04X: %08X \n", 0x1408, bcm_read_reg32( 0x1408 ) ); + + printf( "\n* Send BD Initiator Control:\n" ); + printf( "%04X: %08X ", 0x1800, bcm_read_reg32( 0x1800 ) ); + printf( "%04X: %08X \n", 0x1804, bcm_read_reg32( 0x1804 ) ); + + printf( "\n* Send BD Completion Control:\n" ); + printf( "%04X: %08X ", 0x1c00, bcm_read_reg32( 0x1c00 ) ); + + printf( "\n* Receive List Placement Control:\n" ); + for( i = 0x2000, j = 0; i < 0x2020; i += 4 ) { + + printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); + + if( ( ++j & 0x3 ) == 0 ) { + printf( "\n" ); + } + + } + + printf( "\n* Receive Data & Receive BD Initiator Control:\n" ); + printf( "%04X: %08X ", 0x2400, bcm_read_reg32( 0x2400 ) ); + printf( "%04X: %08X \n", 0x2404, bcm_read_reg32( 0x2404 ) ); + + printf( "\n* Jumbo Receive BD Ring RCB:\n" ); + for( i = 0x2440, j = 0; i < 0x2450; i += 4 ) { + + printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); + + if( ( ++j & 0x3 ) == 0 ) { + printf( "\n" ); + } + + } + + printf( "\n* Standard Receive BD Ring RCB:\n" ); + for( i = 0x2450, j = 0; i < 0x2460; i += 4 ) { + + printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); + + if( ( ++j & 0x3 ) == 0 ) { + printf( "\n" ); + } + + } + + printf( "\n* Mini Receive BD Ring RCB:\n" ); + for( i = 0x2460, j = 0; i < 0x2470; i += 4 ) { + + printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); + + if( ( ++j & 0x3 ) == 0 ) { + printf( "\n" ); + } + + } + + printf( "\nRDI Timer Mode Register:\n" ); + printf( "%04X: %08X \n", 0x24f0, bcm_read_reg32( 0x24f0 ) ); + + printf( "\n* Receive BD Initiator Control:\n" ); + for( i = 0x2c00, j = 0; i < 0x2c20; i += 4 ) { + + printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); + + if( ( ++j & 0x3 ) == 0 ) { + printf( "\n" ); + } + + } + + printf( "\n* Receive BD Completion Control:\n" ); + for( i = 0x3000, j = 0; i < 0x3014; i += 4 ) { + + printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); + + if( ( ++j & 0x3 ) == 0 ) { + printf( "\n" ); + } + + } +} +#endif + + + +/* + * NVRAM access + */ + +static int +bcm_nvram_lock( void ) +{ + int i; + + /* + * Acquire NVRam lock (REQ0) & wait for arbitration won (ARB0_WON) + */ +// bcm_setb_reg32( SW_ARB_R, BIT32( 0 ) ); + bcm_setb_reg32( SW_ARB_R, BIT32( 1 ) ); + + i = 2000; + while( ( --i ) && +// ( bcm_read_reg32( SW_ARB_R ) & BIT32( 8 ) ) == 0 ) { + ( bcm_read_reg32( SW_ARB_R ) & BIT32( 9 ) ) == 0 ) { + SLOF_msleep( 1 ); + } + + // return on error + if( i == 0 ) { +#ifdef BCM_DEBUG + printf("bcm57xx: failed to lock nvram"); +#endif + return -1; + } + + return 0; +} + +static void +bcm_nvram_unlock( void ) +{ + /* + * release NVRam lock (CLR0) + */ +// bcm_setb_reg32( SW_ARB_R, BIT32( 4 ) ); + bcm_setb_reg32( SW_ARB_R, BIT32( 5 ) ); +} + +static void +bcm_nvram_init( void ) +{ + /* + * enable access to NVRAM registers + */ + if(IS_5714) { + bcm_setb_reg32( NVM_ACC_R, BIT32( 1 ) | BIT32( 0 ) ); + } + + /* + * disable bit-bang method 19& disable interface bypass + */ + bcm_clrb_reg32( NVM_CFG1_R, BIT32( 31 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 14 ) | BIT32( 16 ) ); + bcm_setb_reg32( NVM_CFG1_R, BIT32 ( 13 ) | BIT32 ( 17 )); + + /* + * enable Auto SEEPROM Access + */ + bcm_setb_reg32( MISC_LOCAL_CTRL_R, BIT32 ( 24 ) ); + + /* + * NVRAM write enable + */ + bcm_setb_reg32( MODE_CTRL_R, BIT32 ( 21 ) ); +} + +static int32_t +bcm_nvram_read( uint32_t f_addr_u32, uint32_t *f_val_pu32, uint32_t lock ) +{ + uint32_t i; + + /* + * parameter check + */ + if( f_addr_u32 > NVM_ADDR_MSK ) { + return -1; + } + + /* + * Acquire NVRam lock (REQ0) & wait for arbitration won (ARB0_WON) + */ + if( lock && (bcm_nvram_lock() == -1) ) { + return -1; + } + + /* + * setup address to read + */ + bcm_write_reg32( NVM_ADDR_R, + bcm_nvram_logical_to_physical_address(f_addr_u32) ); +// bcm_write_reg32( NVM_ADDR_R, f_addr_u32 ); + + /* + * get the command going + */ + bcm_write_reg32( NVM_COM_R, BIT32( 8 ) | BIT32( 7 ) | + BIT32( 4 ) | BIT32( 3 ) ); + + /* + * wait for command completion + */ + i = 2000; + while( ( --i ) && + ( ( bcm_read_reg32( NVM_COM_R ) & BIT32( 3 ) ) == 0 ) ) { + SLOF_msleep( 1 ); + } + + /* + * read back data if no error + */ + if( i != 0 ) { + /* + * read back data + */ + *f_val_pu32 = bcm_read_reg32( NVM_READ_R ); + } + + if(lock) + bcm_nvram_unlock(); + + // error + if( i == 0 ) { +#ifdef BCM_DEBUG + printf("bcm57xx: reading from NVRAM failed\n"); +#endif + return -1; + } + + // success + return 0; +} + +static int32_t +bcm_nvram_write( uint32_t f_addr_u32, uint32_t f_value_u32, uint32_t lock ) +{ + uint32_t i; + + /* + * parameter check + */ + if( f_addr_u32 > NVM_ADDR_MSK ) { + return -1; + } + + /* + * Acquire NVRam lock (REQ0) & wait for arbitration won (ARB0_WON) + */ + if( lock && (bcm_nvram_lock() == -1) ) { + return -1; + } + + /* + * setup address to write + */ + bcm_write_reg32( NVM_ADDR_R, bcm_nvram_logical_to_physical_address( f_addr_u32 ) ); + + /* + * setup write data + */ + bcm_write_reg32( NVM_WRITE_R, f_value_u32 ); + + /* + * get the command going + */ + bcm_write_reg32( NVM_COM_R, BIT32( 8 ) | BIT32( 7 ) | + BIT32( 5 ) | BIT32( 4 ) | BIT32( 3 ) ); + + /* + * wait for command completion + */ + i = 2000; + while( ( --i ) && + ( ( bcm_read_reg32( NVM_COM_R ) & BIT32( 3 ) ) == 0 ) ) { + SLOF_msleep( 1 ); + } + + /* + * release NVRam lock (CLR0) + */ + if(lock) + bcm_nvram_unlock(); + + // error + if( i == 0 ) { +#ifdef BCM_DEBUG + printf("bcm57xx: writing to NVRAM failed\n"); +#endif + return -1; + } + + // success + return 0; +} + +/* + * PHY initialization + */ +static int32_t +bcm_mii_phy_init( void ) +{ + static const uint32_t PHY_STAT_R = (uint32_t) 0x01; + static const uint32_t AUX_STAT_R = (uint32_t) 0x19; + static const uint32_t MODE_GMII = BIT32( 3 ); + static const uint32_t MODE_MII = BIT32( 2 ); + static const uint32_t NEG_POLARITY = BIT32( 10 ); + static const uint32_t MII_MSK = ( MODE_GMII | MODE_MII ); + static const uint16_t GIGA_ETH = ( BIT16( 10 ) | BIT16( 9 ) ); + int32_t i; + uint16_t v; + + /* + * enable MDI communication + */ + bcm_write_reg32( MDI_CTRL_R, (uint32_t) 0x0 ); + + /* + * check link up + */ + i = 2500; + do { + SLOF_msleep( 1 ); + // register needs to be read twice! + bcm_mii_read16( PHY_STAT_R, &v ); + bcm_mii_read16( PHY_STAT_R, &v ); + } while( ( --i ) && + ( ( v & BIT16( 2 ) ) == 0 ) ); + + if( i == 0 ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: link is down\n" ); +#endif + return -1; + } + +#ifdef BCM_DEBUG + printf( "bcm57xx: link is up\n" ); +#endif + if( !IS_COPPER_PHY ) { + return 0; + } + + /* + * setup GMII or MII interface + */ + i = bcm_read_reg32( ETH_MAC_MODE_R ); + /* + * read status register twice, since the first + * read fails once between here and the moon... + */ + bcm_mii_read16( AUX_STAT_R, &v ); + bcm_mii_read16( AUX_STAT_R, &v ); + + if( ( v & GIGA_ETH ) == GIGA_ETH ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: running PHY in GMII mode (1000BaseT)\n" ); +#endif + // GMII device + if( ( i & MII_MSK ) != MODE_GMII ) { + i &= ~MODE_MII; + i |= MODE_GMII; + } + + } else { +#ifdef BCM_DEBUG + printf( "bcm57xx: running PHY in MII mode (10/100BaseT)\n" ); +#endif + // MII device + if( ( i & MII_MSK ) != MODE_MII ) { + i &= ~MODE_GMII; + i |= MODE_MII; + } + + } + + if( IS_5704 && !IS_SERDES ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: set the link ready signal for 5704C to negative polarity\n" ); +#endif + i |= NEG_POLARITY; // set the link ready signal for 5704C to negative polarity + } + + bcm_write_reg32( ETH_MAC_MODE_R, i ); + + return 0; +} + +static int32_t +bcm_tbi_phy_init( void ) +{ + int32_t i; +#if 0 + /* + * set TBI mode full duplex + */ + bcm_clrb_reg32( ETH_MAC_MODE_R, BIT32( 1 ) ); + bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 2 ) | BIT32( 3 ) ); + + /* + * enable MDI communication + */ + bcm_write_reg32( MDI_CTRL_R, (uint32_t) 0x0 ); + + /* Disable link change interrupt. */ + bcm_write_reg32( ETH_MAC_EVT_EN_R, 0 ); + + /* + * set link polarity + */ + bcm_clrb_reg32( ETH_MAC_MODE_R, BIT32( 10 ) ); + + /* + * wait for sync/config changes + */ + for( i = 0; i < 100; i++ ) { + bcm_write_reg32( ETH_MAC_STAT_R, + BIT32( 3 ) | BIT32( 4 ) ); + + SLOF_usleep( 20 ); + + if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & + ( BIT32( 3 ) | BIT32( 4 ) ) ) == 0 ) { + break; + } + + } +#endif + /* + * wait for sync to come up + */ + for( i = 0; i < 100; i++ ) { + + if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & BIT32( 0 ) ) != 0 ) { + break; + } + + SLOF_usleep( 20 ); + } + + if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & BIT32( 0 ) ) == 0) { +#ifdef BCM_DEBUG + printf( "bcm57xx: link is down\n" ); +#endif + return -1; + } +#if 0 + /* + * clear all attentions + */ + bcm_write_reg32( ETH_MAC_STAT_R, (uint32_t) ~0 ); +#endif + +#ifdef BCM_DEBUG + printf( "bcm57xx: link is up\n" ); +#endif + return 0; +} + +static int32_t +bcm_phy_init( void ) +{ + static const uint16_t SRAM_HW_CFG = (uint16_t) 0x0b58; + uint32_t l_val_u32; + int32_t l_ret_i32 = 0; + + /* + * get HW configuration from SRAM + */ + l_val_u32 = bcm_read_mem32( SRAM_HW_CFG ); + l_val_u32 &= ( BIT32( 5 ) | BIT32( 4 ) ); + + switch( l_val_u32 ) { + case 0x10: { + #ifdef BCM_DEBUG + printf( "bcm57xx: copper PHY detected\n" ); + #endif + + bcm_device_u64 |= BCM_DEV_COPPER; + l_ret_i32 = bcm_mii_phy_init(); + } break; + + case 0x20: { + #ifdef BCM_DEBUG + printf( "bcm57xx: fiber PHY detected\n" ); + #endif + + if( !IS_SERDES ) { + #ifdef BCM_DEBUG + printf( "bcm57xx: running PHY in gmii/mii mode\n" ); + #endif + l_ret_i32 = bcm_mii_phy_init(); + } else { + #ifdef BCM_DEBUG + printf( "bcm57xx: running PHY in tbi mode\n" ); + #endif + l_ret_i32 = bcm_tbi_phy_init(); + } + + } break; + + default: { + #ifdef BCM_DEBUG + printf( "bcm57xx: unknown PHY type detected, terminating\n" ); + #endif + l_ret_i32 = -1; + } + + } + + return l_ret_i32; +} + +/* + * ring initialization + */ +static void +bcm_init_rxprod_ring( void ) +{ + uint32_t v; + uint32_t i; + + /* + * clear out the whole rx prod ring for sanity + */ + memset( (void *) &bcm_rxprod_ring, + 0, + BCM_RXPROD_RING_SIZE * sizeof( bcm_rxbd_t ) ); + mb(); + + /* + * assign buffers & indices to the ring members + */ + for( i = 0; i < BCM_MAX_RX_BUF; i++ ) { + bcm_rxprod_ring[i].m_hostaddr_st.m_hi_u32 = + (uint32_t) ( (uint64_t) &bcm_rx_buffer_pu08[i] >> 32 ); + bcm_rxprod_ring[i].m_hostaddr_st.m_lo_u32 = + (uint32_t) ( (uint64_t) &bcm_rx_buffer_pu08[i] & + (uint64_t) 0xffffffff ); + bcm_rxprod_ring[i].m_idxlen_u32 = ( i << 16 ); + bcm_rxprod_ring[i].m_idxlen_u32 += BCM_BUF_SIZE; + } + + /* + * clear rcb registers & disable rings + * NOTE: mini & jumbo rings are not supported, + * still rcb's are cleaned out for sanity + */ + bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_JUM ), RCB_FLAG_RING_DISABLED ); + bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_JUM ), 0 ); + bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_JUM ), 0 ); + bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_JUM ), 0 ); + + bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_STD ), RCB_FLAG_RING_DISABLED ); + bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_STD ), 0 ); + bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_STD ), 0 ); + bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_STD ), 0 ); + + bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_MIN ), RCB_FLAG_RING_DISABLED ); + bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_MIN ), 0 ); + bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_MIN ), 0 ); + bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_MIN ), 0 ); + + /* + * clear rx producer index of std producer ring + */ + bcm_write_reg32( RXPROD_PROD_IND, 0 ); + + /* + * setup rx standard rcb using recommended NIC addr (hard coded) + */ + bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_STD ), + (uint32_t) ( (uint64_t) &bcm_rxprod_ring >> 32 ) ); + bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_STD ), + (uint32_t) ( (uint64_t) &bcm_rxprod_ring & (uint64_t) 0xffffffff ) ); + bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_STD ), + (uint32_t) BCM_NIC_RX_OFFS ); + + if( IS_5704 || IS_5703 ) { + // 5704: length field = max buffer len + v = (uint32_t) BCM_BUF_SIZE << 16; + } else { + // 5714: length field = number of ring entries + v = (uint32_t) BCM_RXPROD_RING_SIZE << 16; + } + + v &= (uint32_t) ~RCB_FLAG_RING_DISABLED; + bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_STD ), v ); +} + +static void +bcm_init_rxret_ring( void ) +{ + uint32_t i; + uint16_t v; + + /* + * clear out the whole rx ret ring for sanity + */ + memset( (void *) &bcm_rxret_ring, + 0, + 2 * BCM_RXRET_RING_SIZE * sizeof( bcm_rxbd_t ) ); + mb(); + + /* + * setup return ring size dependent on installed device + */ + bcm_rxret_ring_sz = BCM_RXRET_RING_SIZE; + if( IS_5704 || IS_5703 ) { + bcm_rxret_ring_sz *= 2; + } + + /* + * clear rcb memory & disable rings + * NOTE: 5714 only supports one return ring, + * still all possible rcb's are cleaned out for sanity + */ + v = BCM_RXRET_RCB_OFFS; + for( i = 0; i < BCM_MAX_RXRET_RING; i++ ) { + bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED ); + bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 ); + bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 ); + bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 ); + + v += BCM_RCB_SIZE_u16; + } + + /* + * clear rx consumer index of return ring + */ + bcm_write_reg32( RXRET_CONS_IND, 0 ); + + /* + * setup rx ret rcb + * NOTE: NIC address not aplicable in return rings + */ + bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXRET_RCB_OFFS ), + (uint32_t) ( (uint64_t) &bcm_rxret_ring >> 32 ) ); + bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXRET_RCB_OFFS ), + (uint32_t) ( (uint64_t) &bcm_rxret_ring & + (uint64_t) 0xffffffff ) ); + bcm_write_mem32( BCM_RCB_NICADDR_u16( BCM_RXRET_RCB_OFFS ), 0 ); + + i = bcm_rxret_ring_sz; + i <<= 16; + i &= (uint32_t) ~RCB_FLAG_RING_DISABLED; + bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXRET_RCB_OFFS ), i ); +} + +static void +bcm_init_tx_ring( void ) +{ + uint32_t i; + uint16_t v; + + /* + * clear out the whole tx ring for sanity + */ + memset( (void *) &bcm_tx_ring, + 0, + BCM_TX_RING_SIZE * sizeof( bcm_txbd_t ) ); + mb(); + + /* + * assign buffers to the ring members & setup invariant flags + */ + for( i = 0; i < BCM_MAX_TX_BUF; i++ ) { + bcm_tx_ring[i].m_hostaddr_st.m_hi_u32 = + (uint32_t) ( (uint64_t) &bcm_tx_buffer_pu08[i] >> 32 ); + bcm_tx_ring[i].m_hostaddr_st.m_lo_u32 = + (uint32_t) ( (uint64_t) &bcm_tx_buffer_pu08[i] & + (uint64_t) 0xffffffff ); + // flags: indicate last packet & coal now + // -last packet is always true (only one send packet supported) + // -coal now needed to always get the consumed bd's (since + // only a few bd's are set up which permanently are recycled) + bcm_tx_ring[i].m_lenflags_u32 = ( BIT32( 2 ) | BIT32( 7 ) ); + bcm_tx_ring[i].m_VLANtag_u32 = (uint32_t) 0; // not used + } + + /* + * clear rcb memory & disable rings + * NOTE: 5714 only supports one send ring, + * still all possible rcb's are cleaned out for sanity + */ + v = BCM_TX_RCB_OFFS; + for( i = 0; i < BCM_MAX_TX_RING; i++ ) { + bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED ); + bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 ); + bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 ); + bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 ); + + v += BCM_RCB_SIZE_u16; + } + + /* + * clear host/nic producer indices + */ + bcm_write_reg32( TX_NIC_PROD_IND, 0 ); + bcm_write_reg32( TX_PROD_IND, 0 ); + + /* + * setup tx rcb using recommended NIC addr (hard coded) + */ + bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( BCM_TX_RCB_OFFS ), + (uint32_t) ( (uint64_t) &bcm_tx_ring >> 32 ) ); + bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( BCM_TX_RCB_OFFS ), + (uint32_t) ( (uint64_t) &bcm_tx_ring & + (uint64_t) 0xffffffff ) ); + bcm_write_mem32( BCM_RCB_NICADDR_u16( BCM_TX_RCB_OFFS ), + (uint32_t) BCM_NIC_TX_OFFS ); + + if( IS_5704 || IS_5703 ) { + // 5704: length field = max buffer len + i = (uint32_t) BCM_BUF_SIZE << 16; + } else { + // 5714: length field = number of ring entries + i = (uint32_t) BCM_TX_RING_SIZE << 16; + } + + i &= ( uint32_t ) ~RCB_FLAG_RING_DISABLED; + bcm_write_mem32( BCM_RCB_LENFLAG_u16( BCM_TX_RCB_OFFS ), i ); + + /* + * remember the next bd index to be used + * & number of available buffers + */ + bcm_tx_stop_u32 = BCM_MAX_TX_BUF; + bcm_tx_bufavail_u32 = BCM_MAX_TX_BUF; +} + +static int32_t +bcm_mac_init( uint8_t *f_mac_pu08 ) +{ + static const uint16_t MEM_MAC_LO = (uint16_t) 0x0c18; + static const uint16_t MEM_MAC_HI = (uint16_t) 0x0c14; + + uint32_t NVR_MAC_LO = (uint16_t) 0x80; + uint32_t NVR_MAC_HI = (uint16_t) 0x7c; + + bcm_addr64_t l_mac_st; + uint32_t i; + uint32_t v; + + /* + * Use MAC address from device tree if possible + */ + for( i = 0, v = 0; i < 6; i++ ) { + v += (uint32_t) f_mac_pu08[i]; + } + + if( v != 0 ) { + l_mac_st.m_hi_u32 = ( ( (uint32_t) f_mac_pu08[0]) << 8 ); + l_mac_st.m_hi_u32 |= ( ( (uint32_t) f_mac_pu08[1]) << 0 ); + l_mac_st.m_lo_u32 = ( ( (uint32_t) f_mac_pu08[2]) << 24 ); + l_mac_st.m_lo_u32 |= ( ( (uint32_t) f_mac_pu08[3]) << 16 ); + l_mac_st.m_lo_u32 |= ( ( (uint32_t) f_mac_pu08[4]) << 8 ); + l_mac_st.m_lo_u32 |= ( ( (uint32_t) f_mac_pu08[5]) << 0 ); + } else { + /* + * try to read MAC address from MAC mailbox + */ + l_mac_st.m_hi_u32 = bcm_read_mem32( MEM_MAC_HI ); + + if( ( l_mac_st.m_hi_u32 >> 16 ) == (uint32_t) 0x484b ) { + l_mac_st.m_hi_u32 &= (uint32_t) 0xffff; + l_mac_st.m_lo_u32 = bcm_read_mem32( MEM_MAC_LO ); + } else { + int32_t l_err_i32; + + /* + * otherwise retrieve MAC address from NVRam + */ + if( ( bcm_read_reg32( MAC_FUNC_R ) & BIT32( 2 ) ) != 0 ) { + // secondary MAC is in use, address in NVRAM changes + NVR_MAC_LO += 0x50; + NVR_MAC_HI += 0x50; + } + + l_err_i32 = bcm_nvram_read( NVR_MAC_LO, &l_mac_st.m_lo_u32, 1 ); + l_err_i32 += bcm_nvram_read( NVR_MAC_HI, &l_mac_st.m_hi_u32, 1 ); + + // return on read error + if( l_err_i32 < 0 ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: failed to retrieve MAC address\n" ); +#endif + return -1; + } + } + } + + /* + * write the mac addr into the NIC's register area + */ + bcm_write_reg32( MAC_ADDR_OFFS_HI(0), l_mac_st.m_hi_u32 ); + bcm_write_reg32( MAC_ADDR_OFFS_LO(0), l_mac_st.m_lo_u32 ); + for( i = 1; i < BCM_NUM_MAC_ADDR; i++ ) { + bcm_write_reg32( MAC_ADDR_OFFS_HI(i), 0 ); + bcm_write_reg32( MAC_ADDR_OFFS_LO(i), 0 ); + } + + /* + * WY 26.01.07 + * not needed anymore, s.a. + if( IS_5704 != 0 ) { + + v = MAC5704_ADDR_OFFS; + for( i = 0; i < BCM_NUM_MAC5704_ADDR; i++ ) { + bcm_write_reg32( v, l_mac_st.m_hi_u32 ); + v += sizeof( uint32_t ); + bcm_write_reg32( v, l_mac_st.m_lo_u32 ); + v += sizeof( uint32_t ); + } + + } + */ + + /* + * return MAC address as string + */ + f_mac_pu08[0] = (uint8_t) ( ( l_mac_st.m_hi_u32 >> 8 ) & (uint32_t) 0xff ); + f_mac_pu08[1] = (uint8_t) ( ( l_mac_st.m_hi_u32 ) & (uint32_t) 0xff ); + f_mac_pu08[2] = (uint8_t) ( ( l_mac_st.m_lo_u32 >> 24 ) & (uint32_t) 0xff ); + f_mac_pu08[3] = (uint8_t) ( ( l_mac_st.m_lo_u32 >> 16 ) & (uint32_t) 0xff ); + f_mac_pu08[4] = (uint8_t) ( ( l_mac_st.m_lo_u32 >> 8 ) & (uint32_t) 0xff ); + f_mac_pu08[5] = (uint8_t) ( ( l_mac_st.m_lo_u32 ) & (uint32_t) 0xff ); + +#ifdef BCM_DEBUG + do { + int32_t i; + printf( "bcm57xx: retrieved MAC address " ); + + for( i = 0; i < 6; i++ ) { + printf( "%02X", f_mac_pu08[i] ); + + if( i != 5 ) { + printf( ":" ); + } + + } + + printf( "\n" ); + } while( 0 ); +#endif + + return 0; +} + + +/* + ****************************************************************************** + * ASF Firmware + ****************************************************************************** + */ + + +#ifdef BCM_DEBUG +#ifdef BCM_SHOW_ASF_REGS +static void +bcm_asf_check_register( void ) +{ + uint32_t i; + + i = bcm_read_reg32( ASF_CTRL_R ); + printf( "bcm57xx: ASF control : %x\n", i ); + + i = bcm_read_reg32( ASF_WATCHDOG_TIMER_R ); + printf( "bcm57xx: ASF Watchdog Timer : %x\n", i ); + + i = bcm_read_reg32( ASF_HEARTBEAT_TIMER_R ); + printf( "bcm57xx: ASF Heartbeat Timer : %x\n", i ); + + i = bcm_read_reg32( ASF_POLL_TIMER_R ); + printf( "bcm57xx: ASF Poll Timer : %x\n", i ); + + i = bcm_read_reg32( POLL_LEGACY_TIMER_R ); + printf( "bcm57xx: Poll Legacy Timer : %x\n", i ); + + i = bcm_read_reg32( RETRANSMISSION_TIMER_R ); + printf( "bcm57xx: Retransmission Timer : %x\n", i ); + + i = bcm_read_reg32( TIME_STAMP_COUNTER_R ); + printf( "bcm57xx: Time Stamp Counter : %x\n", i ); + + i = bcm_read_reg32( RX_CPU_MODE_R ); + printf( "bcm57xx: RX RISC Mode : %x\n", i ); + + i = bcm_read_reg32( RX_CPU_STATE_R ); + printf( "bcm57xx: RX RISC State : %x\n", i ); + + i = bcm_read_reg32( RX_CPU_PC_R ); + printf( "bcm57xx: RX RISC Prg. Counter : %x\n", i ); +} +#endif +#endif + +static int +bcm_fw_halt( void ) +{ + int i; + + bcm_write_mem32( BCM_FW_MBX_CMD, BCM_NICDRV_PAUSE_FW ); + bcm_setb_reg32( RX_CPU_EVENT_R, BIT32( 14 ) ); + + /* Wait for RX cpu to ACK the event. */ + for (i = 0; i < 100; i++) { + if(bcm_read_reg32( RX_CPU_EVENT_R ) & BIT32( 14 )) + break; + SLOF_msleep(1); + } + if( i>= 100) + return -1; + return 0; +} + + +#ifdef BCM_SW_AUTONEG +static void +bcm_sw_autoneg( void ) { + uint32_t i, j, k; + uint32_t SerDesCfg; + uint32_t SgDigControl; + uint32_t SgDigStatus; + uint32_t ExpectedSgDigControl; + int AutoNegJustInitiated = 0; + + // step 1: init TX 1000BX Autoneg. Register to zero + bcm_write_reg32(TX_1000BX_AUTONEG_R, 0); + + // step 2&3: set TBI mode + bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 2 ) | BIT32( 3 ) ); + SLOF_usleep(10); + + // step 4: enable link attention + bcm_setb_reg32( ETH_MAC_EVT_EN_R, BIT32( 12 ) ); + + // step 5: preserve voltage regulator bits + SerDesCfg = bcm_read_reg32(SERDES_CTRL_R) & ( BIT32( 20 ) | BIT32( 21 ) + | BIT32( 22 ) | BIT32( 23 ) ); + + // step 6: preserve voltage regulator bits + SgDigControl = bcm_read_reg32(HW_AUTONEG_CTRL_R); + + // step 7: if device is NOT set-up for auto negotiation, then go to step 26 + // goto bcm_setup_phy_step26; + + // We want to use auto negotiation + + // step 8: we don't want to use flow control + ExpectedSgDigControl = 0x81388400; // no flow control + + // step 9: compare SgDigControl with 0x81388400 + if(SgDigControl == ExpectedSgDigControl) { + goto bcm_setup_phy_step17; + } +#ifdef BCM_DEBUG + printf("bcm57xx: SgDigControl = %08X\n", SgDigControl); +#endif + // step 10 + bcm_write_reg32(SERDES_CTRL_R, SerDesCfg | 0xC011880); + + // step 11: restart auto negotiation + bcm_write_reg32(HW_AUTONEG_CTRL_R, ExpectedSgDigControl | BIT32( 30 ) ); + + // step 12: read back HW_AUTONEG_CTRL_R + bcm_read_reg32(HW_AUTONEG_CTRL_R); + + // step 13 + SLOF_usleep( 5 ); + + // step 14,15,16: same as step 11, but don't restart auto neg. + bcm_write_reg32(HW_AUTONEG_CTRL_R, ExpectedSgDigControl); + AutoNegJustInitiated = 1; + goto bcm_setup_phy_step30; + + // step 17: + bcm_setup_phy_step17: + if( ( bcm_read_reg32(ETH_MAC_STAT_R) & ( BIT32( 1 ) | BIT32( 0 ) ) ) == 0 ) { + goto bcm_setup_phy_step30; + } + + // step 18: Get HW Autoneg. Status + SgDigStatus = bcm_read_reg32(HW_AUTONEG_STAT_R); + + // step 19: + if( ( SgDigStatus & BIT32(1) ) + && ( bcm_read_reg32(ETH_MAC_STAT_R) & BIT32(0) ) ) { + // resolve the current flow control? + AutoNegJustInitiated = 0; + goto bcm_setup_phy_step30; + } + + // step 20 + if( SgDigStatus & BIT32(1) ) { + goto bcm_setup_phy_step30; + } + if( AutoNegJustInitiated != 0) { + AutoNegJustInitiated = 0; + goto bcm_setup_phy_step29; + } + + // step 21, 22, 23, 24: fallback to 1000Mbps-FullDuplex forced mode + if( ( bcm_read_reg32( MAC_FUNC_R ) & BIT32( 2 ) ) == 0 ) { + // port 0 + bcm_write_reg32( SERDES_CTRL_R, 0xC010880 ); + } + else { // port 1 + bcm_write_reg32( SERDES_CTRL_R, 0x4010880 ); + } + // set to 1000Mbps-FullDuplex + bcm_write_reg32(HW_AUTONEG_CTRL_R, 0x1388400); + // read back + bcm_read_reg32(HW_AUTONEG_CTRL_R); + SLOF_usleep( 40 ); + + // step 25: a little bit reduces... + goto bcm_setup_phy_step30; + + // step 26: check if auto negotiation bit is NOT set +// bcm_setup_phy_step26: + if( ( SgDigControl & BIT32(31) )== 0 ) { + printf("No autoneg.\n"); + goto bcm_setup_phy_step29; + } + + // step 27: + if( ( bcm_read_reg32( MAC_FUNC_R ) & BIT32( 2 ) ) == 0 ) { + // port 0 + bcm_write_reg32( SERDES_CTRL_R, 0xC010880 ); + } + else { // port 1 + bcm_write_reg32( SERDES_CTRL_R, 0x4010880 ); + } + + // step 28: disable auto neg. and force 1000FD mode + bcm_write_reg32(HW_AUTONEG_CTRL_R, 0x1388400); + + // step 29-31: omitted for 5704S + bcm_setup_phy_step29: + bcm_setup_phy_step30: + + // step 32: clear link attentions + i = bcm_read_reg32( ETH_MAC_STAT_R ) | BIT32( 3 ) | BIT32( 4 ); + k = 100; + do { + bcm_write_reg32( ETH_MAC_STAT_R, i ); + j = bcm_read_reg32( ETH_MAC_STAT_R ); + if( ( j & BIT32( 3 ) ) != 0 ) + i = i & ~(BIT32( 3 )); + if( ( j & BIT32( 4 ) ) != 0 ) + i = i & ~(BIT32( 4 )); + --k; + } while( i & k); + + // step 33 + if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & BIT32( 0 ) ) == 0 ) { + goto bcm_setup_phy_step35; + } + + // step 34 + i = bcm_read_reg32( ETH_MAC_MODE_R ); + i|= BIT32( 17 ); + bcm_write_reg32( ETH_MAC_MODE_R, i ); + + SLOF_usleep( 1 ); + + i = bcm_read_reg32( ETH_MAC_STAT_R ); + i&= ~BIT32( 17 ); + bcm_write_reg32( ETH_MAC_STAT_R, i ); + + // step 35 & 36: done + bcm_setup_phy_step35: +#ifdef BCM_DEBUG + printf("bcm57xx: SetupPhy\n"); +#endif + return; +} +#endif + +static int +bcm_handle_events( void ) { +#ifdef BCM_DEBUG +#ifdef BCM_SHOW_ASF_REGS + // ASF REGISTER CHECK + // ------------------ + // check if watchdog timer expired + if( bcm_read_reg32( ASF_WATCHDOG_TIMER_R ) == 0 ) { + // Show ASF registers + bcm_asf_check_register(); + + // rearm watchdog timer + bcm_write_reg32( ASF_WATCHDOG_TIMER_R, 5 ); + } +#endif +#endif + +#ifdef BCM_SW_AUTONEG + // AUTO NEGOTIATION + // ---------------- + + // Check event for Auto Negotiation + if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & + ( BIT32( 12 ) | BIT32( 3 ) | BIT32( 0 ) ) ) != 0 ) { + // link timer procedure + bcm_sw_autoneg(); + } +#endif + + // ASF FW HEARTBEAT + // ---------------- + + // check if heartsbeat timer expired + if( bcm_read_reg32( ASF_HEARTBEAT_TIMER_R ) <= 2) { + int i; + + // Send heartbeat event + bcm_write_mem32( BCM_FW_MBX_CMD, BCM_NICDRV_ALIVE ); + bcm_write_mem32( BCM_FW_MBX_LEN, 4 ); + bcm_write_mem32( BCM_FW_MBX_DATA, 5 ); + bcm_setb_reg32( RX_CPU_EVENT_R, BIT32( 14 ) ); + + // Wait for RX cpu to ACK the event. + for (i = 100; i > 0; i--) { + if(bcm_read_reg32( RX_CPU_EVENT_R ) & BIT32( 14 )) + break; + SLOF_msleep(1); + } + if( i == 0) { +#ifdef BCM_DEBUG + printf( "bcm57xx: RX cpu did not acknowledge heartbeat event\n" ); +#endif + return -1; + } + + // rearm heartbeat timer + bcm_write_reg32( ASF_HEARTBEAT_TIMER_R, 5 ); + } + return 0; +} + +/* + * interface + ****************************************************************************** + */ + +/* + * bcm_receive + */ +static int +bcm_receive( char *f_buffer_pc, int f_len_i ) +{ + uint32_t l_rxret_prod_u32 = bcm_read_reg32( RXRET_PROD_IND ); + uint32_t l_rxret_cons_u32 = bcm_read_reg32( RXRET_CONS_IND ); + uint32_t l_rxprod_prod_u32 = bcm_read_reg32( RXPROD_PROD_IND ); + int l_ret_i; +#ifdef BCM_DEBUG +#ifdef BCM_SHOW_RCV_DATA + int i, j; +#endif +#endif + + /* + * NOTE: dummy read to ensure data has already been DMA'd is + * done by the indice reads + */ + + bcm_handle_events(); + + /* + * if producer index == consumer index then nothing was received + */ + if( l_rxret_prod_u32 == l_rxret_cons_u32 ) { + return 0; + } + + /* + * discard erroneous packets + */ + if( ( bcm_rxret_ring[l_rxret_cons_u32].m_typeflags_u32 & BIT32( 10 ) ) != 0 ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: erroneous frame received\n" ); + printf( " : frame discarded\n" ); +#endif + l_ret_i = 0; + } else { + /* + * get packet length, throw away checksum (last 4 bytes) + */ + l_ret_i = (int) ( bcm_rxret_ring[l_rxret_cons_u32].m_idxlen_u32 & + (uint32_t) 0xffff ) - (int) 4; + + /* + * discard oversized packets + */ + if( l_ret_i > f_len_i ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: receive packet length error:\n" ); + printf( " : incoming 0x%X bytes, available buffer 0x%X bytes\n", l_ret_i, f_len_i ); + printf( " : frame discarded\n" ); +#endif + l_ret_i = 0; + } + + } + + /* + * copy & update data & indices + */ + if( l_ret_i != 0 ) { + uint64_t l_cpyaddr_u64; + + l_cpyaddr_u64 = + ( (uint64_t) bcm_rxret_ring[l_rxret_cons_u32].m_hostaddr_st.m_hi_u32 << 32 ); + l_cpyaddr_u64 += + ( (uint64_t) bcm_rxret_ring[l_rxret_cons_u32].m_hostaddr_st.m_lo_u32 ); + +// FIXME: + if(l_cpyaddr_u64 == 0) { +#ifdef BCM_DEBUG + printf("bcm57xx: NULL address\n"); +#endif + return 0; + } +// + memcpy( (void *) f_buffer_pc, + (void *) l_cpyaddr_u64, + (size_t) l_ret_i ); + + } + + /* + * replenish bd to producer ring + */ + bcm_rxprod_ring[l_rxprod_prod_u32] = + bcm_rxret_ring[l_rxret_cons_u32]; + bcm_rxprod_ring[l_rxprod_prod_u32].m_idxlen_u32 = + ( l_rxprod_prod_u32 << 16 ); + bcm_rxprod_ring[l_rxprod_prod_u32].m_idxlen_u32 += + (uint32_t) BCM_BUF_SIZE; + + /* + * update producer ring's producer index + */ + l_rxprod_prod_u32 = ( l_rxprod_prod_u32 + 1 ) & ( BCM_RXPROD_RING_SIZE - 1 ); + + /* + * move to the next bd in return ring + */ + l_rxret_cons_u32 = ( l_rxret_cons_u32 + 1 ) & ( bcm_rxret_ring_sz - 1 ); + + /* + * synchronize before new indices are send to NIC + */ + mb(); + + /* + * write back new indices + */ + bcm_write_reg32( RXRET_CONS_IND, l_rxret_cons_u32 ); + bcm_write_reg32( RXPROD_PROD_IND, l_rxprod_prod_u32 ); + +#ifdef BCM_DEBUG +#ifdef BCM_SHOW_RCV + if( l_ret_i != 0 ) { + printf( "bcm57xx: received bytes: %d\n", l_ret_i ); + } +#ifdef BCM_SHOW_RCV_DATA + for( i = 0, j = 0; i < l_ret_i; i++ ) { + printf( "%02X ", ( uint32_t ) f_buffer_pc[i] ); + + if( ( ++j % 0x18 ) == 0 ) { + printf( "\n" ); + } + } + + if( ( i % 0x18 ) != 0 ) { + printf( "\n" ); + } +#endif +#endif +#endif + + /* + * return packet length + */ + return l_ret_i; +} + +static int +bcm_xmit( char *f_buffer_pc, int f_len_i ) +{ + uint32_t l_tx_cons_u32 = bcm_read_reg32( TX_CONS_IND ); + uint32_t l_tx_prod_u32 = bcm_read_reg32( TX_PROD_IND ); + uint64_t l_cpyaddr_u64; + +#ifdef BCM_DEBUG +#ifdef BCM_SHOW_XMIT_DATA + int i, j; +#endif +#ifdef BCM_SHOW_IDX + printf( "\n" ); + printf( "bcm57xx: TX_PROD_IND : 0x%03X\n", l_tx_prod_u32 ); + printf( "bcm57xx: TX_CONS_IND : 0x%03X\n", l_tx_cons_u32 ); + printf( "bcm57xx: RXPROD_PROD_IND: 0x%03X\n", bcm_read_reg32( RXPROD_PROD_IND ) ); + printf( "bcm57xx: RXPROD_CONS_IND: 0x%03X\n", bcm_read_reg32( RXPROD_CONS_IND ) ); + printf( "bcm57xx: RXRET_PROD_IND : 0x%03X\n", bcm_read_reg32( RXRET_PROD_IND ) ); + printf( "bcm57xx: RXRET_CONS_IND : 0x%03X\n", bcm_read_reg32( RXRET_CONS_IND ) ); + printf( "bcm57xx: available txb : 0x%03X\n", bcm_tx_bufavail_u32 ); +#endif +#ifdef BCM_SHOW_STATS + printf( "bcm57xx: bcm_status.m_st_word_u32: %08X\n", bcm_status.m_st_word_u32 ); + printf( "bcm57xx: bcm_status.m_st_tag_u32 : %08X\n", bcm_status.m_st_tag_u32 ); + printf( "bcm57xx: bcm_status.m_rxprod_cons_u16: %04X\n", ( uint32_t ) bcm_status.m_rxprod_cons_u16 ); + printf( "bcm57xx: bcm_status.m_unused_u16: %04X\n", ( uint32_t ) bcm_status.m_unused_u16 ); + printf( "bcm57xx: bcm_status.m_unused_u32: %08X\n", bcm_status.m_unused_u32 ); + printf( "bcm57xx: bcm_status.m_tx_cons_u16: %04X\n", ( uint32_t ) bcm_status.m_tx_cons_u16 ); + printf( "bcm57xx: bcm_status.m_rxret_prod_u16: %04X\n", ( uint32_t ) bcm_status.m_rxret_prod_u16 ); +#endif +#endif + + bcm_handle_events(); + + /* + * make all consumed bd's available in the ring again + * this way only a few buffers are needed instead of + * having 512 buffers allocated + */ + while( bcm_tx_start_u32 != l_tx_cons_u32 ) { + bcm_tx_ring[bcm_tx_stop_u32] = bcm_tx_ring[bcm_tx_start_u32]; + bcm_tx_stop_u32 = ( bcm_tx_stop_u32 + 1 ) & ( BCM_TX_RING_SIZE - 1 ); + bcm_tx_start_u32 = ( bcm_tx_start_u32 + 1 ) & ( BCM_TX_RING_SIZE - 1 ); + bcm_tx_bufavail_u32++; + } + + /* + * check for tx buffer availability + */ + if( bcm_tx_bufavail_u32 == 0 ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: no more transmit buffers available\n" ); +#endif + return 0; + } + + /* + * setup next available bd in tx ring + */ + bcm_tx_ring[l_tx_prod_u32].m_lenflags_u32 = ( BIT32( 2 ) | BIT32( 7 ) /*| BIT32( 6 )*/ ); + bcm_tx_ring[l_tx_prod_u32].m_lenflags_u32 += ( (uint32_t) f_len_i << 16 ); +// bcm_tx_ring[l_tx_prod_u32].m_VLANtag_u32 = BCM_VLAN_TAG; + + l_cpyaddr_u64 = ( (uint64_t) bcm_tx_ring[l_tx_prod_u32].m_hostaddr_st.m_hi_u32 << 32 ); + l_cpyaddr_u64 += ( (uint64_t) bcm_tx_ring[l_tx_prod_u32].m_hostaddr_st.m_lo_u32 ); + +#ifdef BCM_DEBUG +#ifdef BCM_SHOW_XMIT_STATS + printf("bcm57xx: xmit: l_cpyaddr_u64: 0x%lx\n", l_cpyaddr_u64 ); + printf(" f_buffer_pc : 0x%lx\n", f_buffer_pc ); + printf(" f_len_i : %d\n", f_len_i ); +#endif +#endif + memcpy( (void *) l_cpyaddr_u64, (void *) f_buffer_pc, (size_t) f_len_i ); + + /* + * update tx producer index & available buffers + */ + l_tx_prod_u32 = ( l_tx_prod_u32 + 1 ) & ( BCM_TX_RING_SIZE - 1 ); + bcm_tx_bufavail_u32--; + + /* + * synchronize before new index is send to NIC + */ + mb(); + + bcm_write_reg32( TX_PROD_IND, l_tx_prod_u32 ); + +#ifdef BCM_DEBUG +#ifdef BCM_SHOW_XMIT + printf( "bcm57xx: sent bytes: %d\n", f_len_i ); +#ifdef BCM_SHOW_XMIT_DATA + for( i = 0, j = 0; i < f_len_i; i++ ) { + printf( "%02X ", ( uint32_t ) f_buffer_pc[i] ); + + if( ( ++j % 0x18 ) == 0 ) { + printf( "\n" ); + } + + } + if( ( i % 0x18 ) != 0 ) { + printf( "\n" ); + } +#endif +#endif + +#ifdef BCM_SHOW_STATS + // coalesce status block now + bcm_setb_reg32( HOST_COAL_MODE_R, BIT32( 3 ) | BIT32( 1 ) ); +#endif + +#endif + return f_len_i; +} + +static int +check_driver( uint16_t vendor_id, uint16_t device_id ) +{ + uint64_t i; + + /* + * checks whether the driver is handling this device + * by verifying vendor & device id + * vendor id 0x14e4 == Broadcom + */ + if( vendor_id != 0x14e4 ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: netdevice not supported, illegal vendor id\n" ); +#endif + return -1; + } + + for( i = 0; bcm_dev[i].m_dev_u32 != 0; i++ ) { + if( bcm_dev[i].m_dev_u32 == (uint32_t) device_id ) { + // success + break; + } + } + + if(bcm_dev[i].m_dev_u32 == 0) { +#ifdef BCM_DEBUG + printf( "bcm57xx: netdevice not supported, illegal device ID\n" ); +#endif + return -1; + } + + /* + * initialize static variables + */ + bcm_device_u64 = bcm_dev[i].m_devmsk_u64; + bcm_rxret_ring_sz = 0; + bcm_baseaddr_u64 = 0; + bcm_memaddr_u64 = 0; + + bcm_tx_start_u32 = 0; + bcm_tx_stop_u32 = 0; + bcm_tx_bufavail_u32 = 0; + + return 0; +} + +static void +bcm_wol_activate(void) +{ +#ifdef BCM_DEBUG + uint16_t reg_pwr_cap; +#endif + uint16_t reg_pwr_crtl; + uint32_t wol_mode; + + wol_mode = bcm_read_reg32( WOL_MODE_R ); + bcm_write_reg32( WOL_MODE_R, wol_mode | BIT32(0) ); + +#ifdef BCM_DEBUG + printf( "bcm57xx: WOL activating..." ); +#endif + +// bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_WOL ); +// SLOF_msleep( 100 ); + +#ifdef BCM_DEBUG + reg_pwr_cap = SLOF_pci_config_read16(0x4a); + /*reg_pwr_cap = snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, + 2, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + 0x4a );*/ + printf( "bcm57xx: PM Capability Register: %04X\n", reg_pwr_cap ); +#endif + /* get curretn power control register */ + reg_pwr_crtl = SLOF_pci_config_read16(0x4c); + /*reg_pwr_crtl = snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, + 2, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + 0x4c );*/ + +#ifdef BCM_DEBUG + printf( "bcm57xx: PM Control/Status Register: %04X\n", reg_pwr_crtl ); +#endif + + /* switch to power state D0 */ + reg_pwr_crtl |= 0x8000; + reg_pwr_crtl &= ~(0x0003); + SLOF_pci_config_write16(0x4c, reg_pwr_crtl); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 2, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + 0x4c, + reg_pwr_crtl );*/ + SLOF_msleep(10); + +/* + bcm_write_mem32( BCM_NICDRV_WOL_MBX, BCM_WOL_MAGIC_NUMBER | + NIC_WOLDRV_STATE_SHUTDOWN | + NIC_WOLDRV_WOL | + NIC_WOLDRV_SET_MAGIC_PKT ); +*/ + + /* switch to power state D3hot */ +/* + reg_pwr_crtl |= 0x0103; + SLOF_pci_config_write16(0x4c, reg_pwr_crtl); + snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 2, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + 0x4c, + reg_pwr_crtl ); + SLOF_msleep(10); +*/ + +#ifdef BCM_DEBUG + reg_pwr_crtl = SLOF_pci_config_read16(0x4c); + /*reg_pwr_crtl = snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, + 2, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + 0x4c );*/ + + printf( "bcm57xx: PM Control/Status Register: %04X\n", reg_pwr_crtl ); +#endif + +#ifdef BCM_DEBUG + printf( "bcm57xx: WOL activated" ); +#endif +} + +static int +bcm_init( net_driver_t *driver ) +{ + static const uint32_t lc_Maxwait_u32 = (uint32_t) 1000; + uint32_t l_baseaddrL_u32; + uint32_t l_baseaddrH_u32; + uint32_t i; + uint8_t *mac_addr = driver->mac_addr; + + if(driver->running != 0) { + return 0; + } +#ifdef BCM_DEBUG + printf( "bcm57xx: detected device " ); + if( IS_5703 ) { + printf( "5703S\n" ); + } else if( IS_5704 ) { + printf( "5704" ); + + if( IS_SERDES ) { + printf( "S\n" ); + } else { + printf( "C\n" ); + } + + } else if( IS_5714 ) { + printf( "5714\n" ); + } +#endif + /* + * setup register & memory base addresses of NIC + */ + l_baseaddrL_u32 = (uint32_t) ~0xf & + (uint32_t) SLOF_pci_config_read32(PCI_BAR1_R); + /*l_baseaddrL_u32 = ( (uint32_t) ~0xf & + (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_BAR1_R ) );*/ + + l_baseaddrH_u32 = (uint32_t) SLOF_pci_config_read32(PCI_BAR2_R); + /*l_baseaddrH_u32 = + (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_BAR2_R );*/ + bcm_baseaddr_u64 = (uint64_t) l_baseaddrH_u32; + bcm_baseaddr_u64 <<= 32; + bcm_baseaddr_u64 += (uint64_t) l_baseaddrL_u32; + bcm_baseaddr_u64 = + (uint64_t) SLOF_translate_my_address((void *)bcm_baseaddr_u64); + /*snk_kernel_interface->translate_addr(((void *)&(bcm_baseaddr_u64)));*/ + bcm_memaddr_u64 = bcm_baseaddr_u64 + BCM_MEMORY_OFFS; + +#ifdef BCM_DEBUG + printf( "bcm57xx: device's register base high address = 0x%08X\n", l_baseaddrH_u32 ); + printf( "bcm57xx: device's register base low address = 0x%08X\n", l_baseaddrL_u32 ); + printf( "bcm57xx: device's register address = 0x%llx\n", bcm_baseaddr_u64 ); +#endif + + /* + * 57xx hardware initialization + * BCM57xx Programmer's Guide: Section 8, "Initialization" + * steps 1 through 101 + */ + + // step 1: enable bus master & memory space in command reg + i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) ); + SLOF_pci_config_write16(PCI_COM_R, i); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 2, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_COM_R, + ( int ) i );*/ + // step 2: disable & mask interrupts & enable pci byte/word swapping & enable indirect addressing mode + i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); + + SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_MISC_HCTRL_R, + ( int ) i );*/ + + /* + * from now on access may be made through the local + * read/write functions + */ + + // step 3: Save ahche line size register + // omitted, because register is not used for 5704 + + // step 4: acquire the nvram lock + if( bcm_nvram_lock() != 0 ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: locking NVRAM failed\n" ); +#endif + return -1; + } + + // step 5: prepare the chip for writing TG3_MAGIC_NUMBER + bcm_setb_reg32( MEMARB_MODE_R, BIT32( 1 ) ); + i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); + SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_MISC_HCTRL_R, + ( int ) i );*/ + bcm_write_reg32( MODE_CTRL_R, BIT32( 23 ) | BIT32( 20 ) | + BIT32( 17 ) | BIT32( 16 ) | + BIT32( 14 ) | BIT32( 13 ) | + BIT32( 5 ) | BIT32( 4 ) | + BIT32( 2 ) | BIT32( 1 ) ); + + // step 6: write TG3_MAGIC_NUMBER + bcm_write_mem32( BCM_FW_MBX, BCM_MAGIC_NUMBER ); + + // step 7: reset core clocks + + if( IS_5714 ) { + bcm_setb_reg32( MISC_CFG_R, BIT32( 26 ) | BIT32( 0 ) ); + } else { + bcm_setb_reg32( MISC_CFG_R, BIT32( 0 ) ); + } + // step 8 + SLOF_msleep( 20 ); + + // step 9: disable & mask interrupts & enable indirect addressing mode & + // enable pci byte/word swapping initialize the misc host control register + i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); + SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_MISC_HCTRL_R, + ( int ) i );*/ + + // step 10: set but master et cetera + i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) ); + SLOF_pci_config_write16(PCI_COM_R, i); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 2, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_COM_R, + ( int ) i );*/ + + // step 11: disable PCI-X relaxed ordering + bcm_clrb_reg16( PCI_X_COM_R, BIT16( 1 ) ); + + // step 12: enable the MAC memory arbiter + bcm_setb_reg32( MEMARB_MODE_R, BIT32( 1 ) ); + + // step 13: omitted, only for BCM5700 + // step 14: s. step 10 + i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); + SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_MISC_HCTRL_R, + ( int ) i );*/ + // step 15: set byte swapping (incl. step 27/28/29/30) + // included prohibition of tx/rx interrupts + bcm_write_reg32( MODE_CTRL_R, BIT32( 23 ) | BIT32( 20 ) | + BIT32( 17 ) | BIT32( 16 ) | + BIT32( 14 ) | BIT32( 13 ) | + BIT32( 5 ) | BIT32( 4 ) | + BIT32( 2 ) | BIT32( 1 ) ); + // step 16: omitted + i = 1000; + while( ( --i ) && + ( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) ) { +#ifdef BCM_DEBUG + printf( "." ); +#endif + SLOF_msleep( 1 ); + } + + // return on error + if( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) { + printf( "bootcode not loaded: %x\n", bcm_read_mem32( BCM_FW_MBX ) ); +#ifdef BCM_DEBUG + printf( "failed\n" ); +#endif + return -1; + } + + + // if ASF Firmware enabled + bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START ); + SLOF_msleep( 10 ); + + // step 17: write ethernet mac mode register + /* + * WY 07.02.07 + * omitted for correct SOL function + */ + /* + if( IS_SERDES ) { + bcm_write_reg32( ETH_MAC_MODE_R, (uint32_t) 0xc ); + } else { + bcm_write_reg32( ETH_MAC_MODE_R, (uint32_t) 0x0 ); + } + */ + + // step 18/19: omitted + // step 20: enable hw bugfix for 5704 + if( IS_5704 || IS_5703 ) { + bcm_setb_reg32( MSG_DATA_R, BIT32( 26 ) | + BIT32( 28 ) | + BIT32( 29 ) ); + } + + // step 21: omitted + // step 22: omitted + // step 23: 5704 clear statistics block + if( IS_5703 || IS_5704 ) { + memset_ci( (void *) ( bcm_memaddr_u64 + BCM_STATISTIC_OFFS ), + 0, + BCM_STATISTIC_SIZE ); + } + + // step 24/25: omitted + // step 26: set DMA Read/Write Control register + // NOTE: recommended values from the spec are used here + if( IS_5714 ) { + bcm_write_reg32( DMA_RW_CTRL_R, DMA_RW_CTRL_VAL_5714 ); + } else { + uint32_t l_PCIState_u32 = bcm_read_reg32( PCI_STATE_R ); + uint32_t l_DMAVal_u32 = DMA_RW_CTRL_VAL; + + if( ( l_PCIState_u32 & BIT32( 2 ) ) != 0 ) { // PCI + l_DMAVal_u32 |= (uint32_t) 0x300000; + } else { // PCI-X + l_DMAVal_u32 |= (uint32_t) 0x900000; + + if( ( bcm_read_reg32( PCI_CLK_CTRL_R ) & (uint32_t) 0x1f ) + >= (uint32_t) 6 ) { + l_DMAVal_u32 |= (uint32_t) 0x4000; + } + + } + + bcm_write_reg32( DMA_RW_CTRL_R, l_DMAVal_u32 ); + } + + // step 27/28/29: s. step 14 + + // step 30: Configure TCP/UDP pseudo header checksum offloading + // already done in step 14: offloading disabled + + // step 31: setup timer prescaler + i = bcm_read_reg32( MISC_CFG_R ); + i &= (uint32_t) ~0xfe; // clear bits 7-1 first + i |= ( BCM_TMR_PRESCALE << 1 ); + bcm_write_reg32( MISC_CFG_R, i ); + + // step 32: 5703/4 configure Mbuf pool address/length + // step 33: 5703/4 configure MAC DMA resource pool + // step 34: configure MAC memory pool watermarks + // step 35: 5703/4 configure DMA resource watermarks + // using recommended settings (hard coded) + if( IS_5703 || IS_5704 ) { + + if( IS_5703 ) { + bcm_write_reg32( MBUF_POOL_ADDR_R, (uint32_t) 0x8000 ); + bcm_write_reg32( MBUF_POOL_LEN_R, (uint32_t) 0x18000 ); + } else { + bcm_write_reg32( MBUF_POOL_ADDR_R, (uint32_t) 0x10000 ); + bcm_write_reg32( MBUF_POOL_LEN_R, (uint32_t) 0x10000 ); + } + + bcm_write_reg32( DMA_DESC_POOL_ADDR_R, (uint32_t) 0x2000 ); + bcm_write_reg32( DMA_DESC_POOL_LEN_R, (uint32_t) 0x2000 ); + + bcm_write_reg32( DMA_RMBUF_LOW_WMARK_R, (uint32_t) 0x50 ); + bcm_write_reg32( MAC_RXMBUF_LOW_WMARK_R, (uint32_t) 0x20 ); + bcm_write_reg32( MBUF_HIGH_WMARK_R, (uint32_t) 0x60 ); + + bcm_write_reg32( DMA_DESC_LOW_WM_R, (uint32_t) 5 ); + bcm_write_reg32( DMA_DESC_HIGH_WM_R, (uint32_t) 10 ); + } else { + bcm_write_reg32( DMA_RMBUF_LOW_WMARK_R, (uint32_t) 0x00 ); + bcm_write_reg32( MAC_RXMBUF_LOW_WMARK_R, (uint32_t) 0x10 ); + bcm_write_reg32( MBUF_HIGH_WMARK_R, (uint32_t) 0x60 ); + } + + // step 35: omitted + // step 36: Configure flow control behaviour + // using recommended settings (hard coded) + bcm_write_reg32( LOW_WMARK_MAX_RXFRAM_R, (uint32_t) 0x02 ); + + // step 37/38: enable buffer manager & wait for successful start + bcm_setb_reg32( BUF_MAN_MODE_R, BIT32( 2 ) | BIT32( 1 ) ); + + i = lc_Maxwait_u32; + while( ( --i ) && + ( ( bcm_read_reg32( BUF_MAN_MODE_R ) & BIT32( 1 ) ) == 0 ) ) { + SLOF_usleep( 10 ); + } + + // return on error + if( i == 0 ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: init step 38: enable buffer manager failed\n" ); +#endif + return -1; + } + + // step 39: enable internal hardware queues + bcm_write_reg32( FTQ_RES_R, (uint32_t) ~0 ); + bcm_write_reg32( FTQ_RES_R, (uint32_t) 0 ); + + // step 40/41/42: initialize rx producer ring + bcm_init_rxprod_ring(); + + // step 43: set rx producer ring replenish threshold + // using recommended setting of maximum allocated BD's/8 + bcm_write_reg32( STD_RXPR_REP_THR_R, (uint32_t) BCM_MAX_RX_BUF / 8 ); + + // step 44/45/46: initialize send rings + bcm_init_tx_ring(); + bcm_init_rxret_ring(); + + // steps 47-50 done in ring init functions + // step 51: configure MAC unicast address + bcm_nvram_init(); + if( bcm_mac_init( (uint8_t *) mac_addr ) < 0 ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: init step 51: configure MAC unicast address failed\n" ); +#endif + return -1; + } + memcpy(driver->mac_addr, mac_addr, 6); + + // step 52: configure backoff random seed for transmit + // using recommended algorithm + i = (uint32_t) mac_addr[0] + (uint32_t) mac_addr[1] + + (uint32_t) mac_addr[2] + (uint32_t) mac_addr[3] + + (uint32_t) mac_addr[4] + (uint32_t) mac_addr[5]; + i &= (uint32_t) 0x03ff; + bcm_write_reg32( ETH_TX_RND_BO_R, i ); + + // step 53: configure message transfer unit MTU size + bcm_write_reg32( RX_MTU_SIZE_R, (uint32_t) BCM_MTU_MAX_LEN ); + + // step 54: configure IPG for transmit + // using recommended value (through #define) + bcm_write_reg32( TX_MAC_LEN_R, TX_MAC_LEN_VAL ); + + // step 55: configure receive rules + + // set RX rule default class + bcm_write_reg32( RX_RULE_CFG_R, RX_RULE_CFG_VAL ); + + // step 56: configure the number of receive lists + bcm_write_reg32( RX_LST_PLACE_CFG_R, RX_LST_PLC_CFG_VAL ); + bcm_write_reg32( RX_LST_PLACE_STAT_EN_R, RX_LST_PLC_STAT_EN_VAL ); + +/* + // rule 1: accept frames for our MAC address + bcm_write_reg32( RX_RULE_CTRL_R ( 0 ), + BIT32( 31 ) | // enable rule + BIT32( 30 ) | // and with next + BIT32( 26 ) | // split value register + BIT32( 8 ) ); // class 1 + bcm_write_reg32( RX_RULE_VAL_R ( 0 ), + (uint32_t) 0xffff0000 | + ( bcm_read_reg32( MAC_ADDR_OFFS_HI(0) ) & + (uint32_t) 0xffff ) ); + + bcm_write_reg32( RX_RULE_CTRL_R ( 1 ), + BIT32( 31 ) | // enable rule + BIT32( 8 ) | // class 1 + BIT32( 1 ) ); // offset 2 + bcm_write_reg32( RX_RULE_VAL_R ( 1 ), + bcm_read_reg32( MAC_ADDR_OFFS_LO(0) ) ); + + // rule 2: accept broadcast frames + bcm_write_reg32( RX_RULE_CTRL_R ( 2 ), + BIT32( 31 ) | // enable rule + BIT32( 30 ) | // and with next + BIT32( 26 ) | // split value register + BIT32( 8 ) ); // class 1 + bcm_write_reg32( RX_RULE_VAL_R ( 2 ), + (uint32_t) ~0 ); + + bcm_write_reg32( RX_RULE_CTRL_R ( 3 ), + BIT32( 31 ) | // enable rule + BIT32( 8 ) | // class 1 + BIT32( 1 ) ); // offset 2 + bcm_write_reg32( RX_RULE_VAL_R ( 3 ), + (uint32_t) ~0 ); +*/ + for( i=0; i<NUM_RX_RULE_ASF; ++i) { + bcm_write_reg32( RX_RULE_CTRL_R ( i ), 0 ); + bcm_write_reg32( RX_RULE_VAL_R ( i ), 0 ); + } + + // step 57-60: enable rx/tx statistics + // omitted, no need for statistics (so far) + + // step 61/62: disable host coalescing engine/wait 20ms + bcm_write_reg32( HOST_COAL_MODE_R, (uint32_t) 0 ); + + i = lc_Maxwait_u32 * 2; + while( ( --i ) && + ( bcm_read_reg32( HOST_COAL_MODE_R ) != 0 ) ) { + SLOF_usleep( 10 ); + } + + // return on error + if( i == 0 ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: init step 62: disable host coal. engine failed\n" ); +#endif + return -1; + } + + // step 63-66: initialize coalescing engine + // NOTE: status block is unused in this driver, + // therefore the coal. engine status block + // automatic update is disabled (by writing + // 0 to every counter + bcm_write_reg32( RX_COAL_TICKS_R, 0 ); + bcm_write_reg32( TX_COAL_TICKS_R, 0 ); + bcm_write_reg32( RX_COAL_MAX_BD_R, 0 ); + bcm_write_reg32( TX_COAL_MAX_BD_R, 0 ); + bcm_write_reg32( RX_COAL_TICKS_INT_R, 0 ); + bcm_write_reg32( TX_COAL_TICKS_INT_R, 0 ); + bcm_write_reg32( RX_COAL_MAX_BD_INT_R, 0 ); + bcm_write_reg32( TX_COAL_MAX_BD_INT_R, 0 ); + + // step 67: initialize host status block address + // NOTE: status block is not needed in this driver, + // still it needs to be set up + i = (uint32_t) ( (uint64_t) &bcm_status >> 32 ); + bcm_write_reg32( STB_HOST_ADDR_HI_R, i ); + i = (uint32_t) ( (uint64_t) &bcm_status & (uint64_t) 0xffffffff ); + bcm_write_reg32( STB_HOST_ADDR_LO_R, i ); + + // 5704/3 adaption + if( IS_5703 || IS_5704 ) { + // step 68: 5704, for now omitted + // step 69: 5704 set the statistics coalescing tick counter + bcm_write_reg32( STAT_TICK_CNT_R, 0 ); + // step 70: 5704 configure statistics block address in NIC memory + // using recommended values (hard coded) + bcm_write_reg32( STAT_NIC_ADDR_R, (uint32_t) 0x300 ); + // step 71: 5704 configure status block address in NIC memory + // using recommended values (hard coded) + bcm_write_reg32( STB_NIC_ADDR_R, (uint32_t) 0xb00 ); + } + + // step 72: enable host coalescing engine + bcm_setb_reg32( HOST_COAL_MODE_R, BIT32( 12 ) | BIT32( 11 ) | BIT32( 1 ) ); + + // step 73: enable rx bd completion functional block + bcm_write_reg32( RX_BD_COMPL_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); + + // step 74: enable rx list placement functional block + bcm_write_reg32( RX_LST_PLACE_MODE_R, BIT32( 1 ) ); + // 5704/3 adaption + if( IS_5703 || IS_5704 ) { + // step 75: 5704/3 enable receive list selector func block + bcm_write_reg32( RX_LST_SEL_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); + } + + // step 76: enable DMA engines + bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 23 ) | BIT32( 22 ) | BIT32( 21 ) ); + /* + * WY 26.10.07 This is wrong for 5714, better leave it alone + if( IS_5714 ) { + bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 20 ) ); + } + */ + + // step 77: omitted, statistics are not used + // step 78: Configure the General Misc Local Control register + // NOTE: as known so far nothing needs to be done here, + // default values should work fine + //bcm_setb_reg32( MISC_LOCAL_CTRL_R, 0 ); + + // step 79: clear interrupts in INT_MBX0_R low word + bcm_write_reg32( INT_MBX0_R, 0 ); + // 5704/3 adaption + // step 80: 5704/3 enable DMA completion functional block + if( IS_5703 || IS_5704 ) { + bcm_write_reg32( DMA_COMPL_MODE_R, BIT32( 1 ) ); + } + + // step 81/82: configure write/read DMA mode registers + // disable MSI + bcm_write_reg32( RD_DMA_MODE_R, BIT32( 10 ) | BIT32( 9 ) | BIT32( 8 ) | + BIT32( 7 ) | BIT32( 6 ) | BIT32( 5 ) | + BIT32( 4 ) | BIT32( 3 ) | BIT32( 2 ) | + BIT32( 1 ) ); + bcm_write_reg32( WR_DMA_MODE_R, BIT32( 9 ) | BIT32( 8 ) | BIT32( 7 ) | + BIT32( 6 ) | BIT32( 5 ) | BIT32( 4 ) | + BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) ); + bcm_clrb_reg32( MSI_MODE_R, BIT32( 1 ) ); + SLOF_usleep( 100 ); + + // step 83-91: enable all these functional blocks... + bcm_write_reg32( RX_DAT_COMPL_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); + + if( IS_5703 || IS_5704 ) { + bcm_write_reg32( MBUF_CLSTR_FREE_MODE_R, BIT32( 1 ) ); + } + + bcm_write_reg32( TX_DAT_COMPL_MODE_R, BIT32( 1 ) ); + bcm_write_reg32( TX_BD_COMPL_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); + bcm_write_reg32( RX_BD_INIT_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); + bcm_write_reg32( RX_DAT_BD_INIT_MODE_R, BIT32( 1 ) ); + bcm_write_reg32( TX_DAT_INIT_MODE_R, BIT32( 1 ) | BIT32( 3 ) ); + bcm_write_reg32( TX_BD_INIT_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); + bcm_write_reg32( TX_BD_RING_SEL_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); + + // step 92: omitted + // step 93/94: Enable Tx/Rx MAC + bcm_setb_reg32( TX_MAC_MODE_R, BIT32( 1 ) ); +// bcm_setb_reg32( RX_MAC_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); // set BIT32( 8 ) for promiscious mode! + bcm_setb_reg32( RX_MAC_MODE_R, BIT32( 1 ) ); // set BIT32( 8 ) for promiscious mode! + // set BIT32( 10) for VLAN + + // step 95: disable auto polling: + // bcm_phy_init takes care of this + // step 96: omitted + // step 97: omitted, may change though, but is not important + // step 98: activate link & enable MAC functional block + // NOTE autopolling is enabled so bit 0 needs not to be set + //bcm_setb_reg32( MI_STATUS_R, BIT32( 0 ) ); + + // step 99: setup PHY + // return if link is down + if( bcm_phy_init() < 0 ) { +#ifdef BCM_DEBUG + printf( "bcm57xx: init step 99: PHY initialization failed\n" ); +#endif + return -1; + } + + // step 100: setup multicast filters + bcm_write_reg32( MAC_HASH0_R, (uint32_t) 0 ); + bcm_write_reg32( MAC_HASH1_R, (uint32_t) 0 ); + bcm_write_reg32( MAC_HASH2_R, (uint32_t) 0 ); + bcm_write_reg32( MAC_HASH3_R, (uint32_t) 0 ); +/* + // accept all multicast frames + bcm_write_reg32( MAC_HASH0_R, (uint32_t) 0xffffffff ); + bcm_write_reg32( MAC_HASH1_R, (uint32_t) 0xffffffff ); + bcm_write_reg32( MAC_HASH2_R, (uint32_t) 0xffffffff ); + bcm_write_reg32( MAC_HASH3_R, (uint32_t) 0xffffffff ); +*/ + // step 101: omitted, no interrupts used + + // make initial receive buffers available for NIC + // this step has to be done here after RX DMA engine has started (step 94) + bcm_write_reg32( RXPROD_PROD_IND, BCM_MAX_RX_BUF ); + + // if ASF Firmware enabled + bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START_DONE ); + SLOF_msleep( 10 ); + + // enable heartbeat timer + + bcm_write_reg32( ASF_HEARTBEAT_TIMER_R, 0x5 ); + + driver->running = 1; + // off we go.. + return 0; +} + +static int +bcm_reset( void ) +{ + uint32_t i; + +#ifdef BCM_DEBUG + printf( "bcm57xx: resetting controller.." ); +#endif + + bcm_write_mem32( BCM_FW_MBX, BCM_MAGIC_NUMBER ); + + if( IS_5714 ) { + bcm_setb_reg32( MISC_CFG_R, BIT32( 26 ) | BIT32( 0 ) ); + } else { + bcm_setb_reg32( MISC_CFG_R, BIT32( 0 ) ); + } + + SLOF_msleep( 20 ); + + /* + * after reset local read/write functions cannot be used annymore + * until bus master & stuff is set up again + */ + + i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) ); + SLOF_pci_config_write16(PCI_COM_R, i); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 2, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_COM_R, + ( int ) i );*/ + + // step 9 & 13: disable & mask interrupts & enable indirect addressing mode & + // enable pci byte/word swapping initialize the misc host control register + i = ( BIT32( 7 ) | BIT32( 5 ) | BIT32( 4 ) | + BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); + SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_MISC_HCTRL_R, + ( int ) i );*/ + + // step 16: poll for bootcode completion by waiting for the one's + // complement of the magic number previously written + i = 1000; + while( ( --i ) && + ( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) ) { +#ifdef BCM_DEBUG + printf( "." ); +#else + SLOF_msleep( 1 ); +#endif + } + + // return on error + if( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) { +#ifdef BCM_DEBUG + printf( "failed\n" ); +#endif + return -1; + } + +#ifdef BCM_DEBUG + printf( "done\n" ); +#endif + return 0; +} + +static int +bcm_term( void ) +{ + uint32_t i; + uint16_t v; + +#ifdef BCM_DEBUG + printf( "bcm57xx: driver shutdown.." ); +#endif + + /* + * halt ASF firmware + */ + bcm_fw_halt(); + + /* + * unload ASF firmware + */ + bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_UNLOAD ); + + /* + * disable RX producer rings + */ + bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_JUM ), RCB_FLAG_RING_DISABLED ); + bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_JUM ), 0 ); + bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_JUM ), 0 ); + bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_JUM ), 0 ); + + bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_STD ), RCB_FLAG_RING_DISABLED ); + bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_STD ), 0 ); + bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_STD ), 0 ); + bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_STD ), 0 ); + + bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_MIN ), RCB_FLAG_RING_DISABLED ); + bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_MIN ), 0 ); + bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_MIN ), 0 ); + bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_MIN ), 0 ); + + /* + * disable RX return rings + */ + v = BCM_RXRET_RCB_OFFS; + for( i = 0; i < BCM_MAX_RXRET_RING; i++ ) { + bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED ); + bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 ); + bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 ); + bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 ); + + v += BCM_RCB_SIZE_u16; + } + + /* + * disable TX rings + */ + v = BCM_TX_RCB_OFFS; + for( i = 0; i < BCM_MAX_TX_RING; i++ ) { + bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED ); + bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 ); + bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 ); + bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 ); + + v += BCM_RCB_SIZE_u16; + } + + /* + * remove receive rules + */ + bcm_write_reg32( RX_RULE_CTRL_R ( 0 ), 0 ); + bcm_write_reg32( RX_RULE_VAL_R ( 0 ), 0 ); + bcm_write_reg32( RX_RULE_CTRL_R ( 1 ), 0 ); + bcm_write_reg32( RX_RULE_VAL_R ( 1 ), 0 ); + + /* + * shutdown sequence + * BCM57xx Programmer's Guide: Section 8, "Shutdown" + * the enable bit of every state machine of the 57xx + * has to be reset. + */ + + /* + * receive path shutdown sequence + */ + bcm_clr_wait_bit32( RX_MAC_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( RX_LST_PLACE_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( RX_BD_INIT_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( RX_DAT_BD_INIT_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( RX_DAT_COMPL_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( RX_BD_COMPL_MODE_R, BIT32( 1 ) ); + + if( IS_5704 || IS_5703 ) { + bcm_clr_wait_bit32( RX_LST_SEL_MODE_R, BIT32( 1 ) ); + } + + /* + * transmit path & memory shutdown sequence + */ + bcm_clr_wait_bit32( TX_BD_RING_SEL_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( TX_BD_INIT_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( TX_DAT_INIT_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( RD_DMA_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( TX_DAT_COMPL_MODE_R, BIT32( 1 ) ); + + if( IS_5704 ) { + bcm_clr_wait_bit32( DMA_COMPL_MODE_R, BIT32( 1 ) ); + } + + bcm_clr_wait_bit32( TX_BD_COMPL_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( ETH_MAC_MODE_R, BIT32( 21 ) ); + bcm_clr_wait_bit32( TX_MAC_MODE_R, BIT32( 1 ) ); + + bcm_clr_wait_bit32( HOST_COAL_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( WR_DMA_MODE_R, BIT32( 1 ) ); + + if( IS_5704 || IS_5703 ) { + bcm_clr_wait_bit32( MBUF_CLSTR_FREE_MODE_R, BIT32( 1 ) ); + } + + bcm_write_reg32( FTQ_RES_R, (uint32_t) ~0 ); + bcm_write_reg32( FTQ_RES_R, (uint32_t) 0 ); + + if( IS_5704 || IS_5703 ) { + bcm_clr_wait_bit32( BUF_MAN_MODE_R, BIT32( 1 ) ); + bcm_clr_wait_bit32( MEMARB_MODE_R, BIT32( 1 ) ); + } + +#ifdef BCM_DEBUG + printf( "done.\n" ); +#endif + /* + * controller reset + */ + if( bcm_reset() != 0 ) { + return -1; + } + + /* + * restart ASF firmware + */ + bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_UNLOAD ); + SLOF_msleep( 10 ); + bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_UNLOAD_DONE ); + SLOF_msleep( 100 ); + bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START ); + SLOF_msleep( 10 ); + bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START_DONE ); + + /* + * activate Wake-on-LAN + */ + bcm_wol_activate(); + + /* + * PCI shutdown + */ + bcm_clrb_reg32( PCI_MISC_HCTRL_R, BIT32( 3 ) | BIT32( 2 ) ); + + /* + * from now on local rw functions cannot be used anymore + */ + +// bcm_clrb_reg32( PCI_COM_R, BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) ); + + SLOF_pci_config_write32(PCI_COM_R, BIT32(8) | BIT32(6)); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 2, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_COM_R, + BIT32(8) | BIT32(6) );*/ + + // no more networking... + return 0; +} + +static int +bcm_getmac(uint32_t addr, char mac[6]) +{ + uint32_t t1, t2; + uint64_t t3; + + if (bcm_nvram_read(addr, &t1, 1) != 0) + return -1; + if (bcm_nvram_read(addr+4, &t2, 1) != 0) + return -1; + t3 = ((uint64_t)t1 << 32) + t2; + + mac[0] = (t3 >> 40) & 0xFF; + mac[1] = (t3 >> 32) & 0xFF; + mac[2] = (t3 >> 24) & 0xFF; + mac[3] = (t3 >> 16) & 0xFF; + mac[4] = (t3 >> 8) & 0xFF; + mac[5] = (t3 >> 0) & 0xFF; + + return 0; +} + +static char* +print_itoa(char *text, uint32_t value) +{ + if(value >= 10) + text = print_itoa(text, value / 10); + *text = '0' + (value % 10); + ++text; + return text; +} + +static int +bcm_get_version(char *text) +{ + uint32_t t1; + + if (bcm_nvram_read(0x94, &t1, 1) != 0) + return -1; + + text = print_itoa(text, (t1 >> 8) & 0xFF); + text[0] = '.'; + text = print_itoa(&text[1], t1 & 0xFF); + text[0] = '\n'; + return 0; +} + +static uint32_t +util_gen_crc( char *pcDatabuf, uint32_t ulDatalen, uint32_t ulCrc_in) +{ + unsigned char data; + uint32_t idx, bit, crc = ulCrc_in; + + for(idx = 0; idx < ulDatalen; idx++) { + data = *pcDatabuf++; + for(bit = 0; bit < 8; bit++, data >>= 1) { + crc = (crc >> 1) ^ (((crc ^ data) & 1) ? + CRC32_POLYNOMIAL : 0); + } + } + return bswap_32(~crc); +} + +static int +bcm_setmac(char mac_addr1[6], char mac_addr2[6]) +{ + uint64_t mac1 = 0, mac2 = 0; + uint32_t manu[MANUFACTURING_INFO_SIZE/4]; + int addr, i; + uint32_t crc, val1, val2, val3, val4; + +#ifdef BCM_DEBUG + printf("Flashing MAC 1: %02X:%02X:%02X:%02X:%02X:%02X\n", + ((unsigned int) mac_addr1[0]) & 0xFF, + ((unsigned int) mac_addr1[1]) & 0xFF, + ((unsigned int) mac_addr1[2]) & 0xFF, + ((unsigned int) mac_addr1[3]) & 0xFF, + ((unsigned int) mac_addr1[4]) & 0xFF, + ((unsigned int) mac_addr1[5]) & 0xFF); + + printf("Flashing MAC 2: %02X:%02X:%02X:%02X:%02X:%02X\n", + ((unsigned int) mac_addr2[0]) & 0xFF, + ((unsigned int) mac_addr2[1]) & 0xFF, + ((unsigned int) mac_addr2[2]) & 0xFF, + ((unsigned int) mac_addr2[3]) & 0xFF, + ((unsigned int) mac_addr2[4]) & 0xFF, + ((unsigned int) mac_addr2[5]) & 0xFF); +#endif + + mac1 |= ((uint64_t) mac_addr1[0]) & 0xFF; mac1 = mac1 << 8; + mac1 |= ((uint64_t) mac_addr1[1]) & 0xFF; mac1 = mac1 << 8; + mac1 |= ((uint64_t) mac_addr1[2]) & 0xFF; mac1 = mac1 << 8; + mac1 |= ((uint64_t) mac_addr1[3]) & 0xFF; mac1 = mac1 << 8; + mac1 |= ((uint64_t) mac_addr1[4]) & 0xFF; mac1 = mac1 << 8; + mac1 |= ((uint64_t) mac_addr1[5]) & 0xFF; + + mac2 |= ((uint64_t) mac_addr2[0]) & 0xFF; mac2 = mac2 << 8; + mac2 |= ((uint64_t) mac_addr2[1]) & 0xFF; mac2 = mac2 << 8; + mac2 |= ((uint64_t) mac_addr2[2]) & 0xFF; mac2 = mac2 << 8; + mac2 |= ((uint64_t) mac_addr2[3]) & 0xFF; mac2 = mac2 << 8; + mac2 |= ((uint64_t) mac_addr2[4]) & 0xFF; mac2 = mac2 << 8; + mac2 |= ((uint64_t) mac_addr2[5]) & 0xFF; + + /* Extract the manufacturing data, starts at 0x74 */ + if(bcm_nvram_lock() == -1) { + return -1; + } + + addr = 0x74; + for (i = 0; i < (MANUFACTURING_INFO_SIZE/4); i++) { + if (bcm_nvram_read(addr, &manu[i], 0) != 0) { + printf("\nREAD FAILED\n"); + bcm_nvram_unlock(); + return -1; + } + addr+=4; + } + bcm_nvram_unlock(); + + /* Store the new MAC address in the manufacturing data */ + val1 = mac1 >> 32; + val2 = mac1 & 0xFFFFFFFF; + val3 = mac2 >> 32; + val4 = mac2 & 0xFFFFFFFF; + manu[(0x7C-0x74)/4] = val1; + manu[(0x80-0x74)/4] = val2; + manu[(0xCC-0x74)/4] = val3; + manu[(0xD0-0x74)/4] = val4; + + /* Calculate the new manufacturing datas CRC */ + crc = util_gen_crc(((char *)manu), + MANUFACTURING_INFO_SIZE - 4, 0xFFFFFFFF); + + /* Now write the new MAC addresses and CRC */ + if ((bcm_nvram_write(0x7C, val1, 1) != 0) || + (bcm_nvram_write(0x80, val2, 1) != 0) || + (bcm_nvram_write(0xCC, val3, 1) != 0) || + (bcm_nvram_write(0xD0, val4, 1) != 0) || + (bcm_nvram_write(0xFC, crc, 1) != 0) ) + { + /* Disastor ! */ +#ifdef BCM_DEBUG + printf("failed to write MAC address\n"); +#endif + return -1; + } + + /* Success !!!! */ + return 0; +} + +static int +bcm_ioctl( int request, void* data ) +{ + uint32_t l_baseaddrL_u32; + uint32_t l_baseaddrH_u32; + uint32_t i; + int ret_val = 0; + char mac_addr[6]; + ioctl_net_data_t *ioctl_data = (ioctl_net_data_t*) data; + + if(request != SIOCETHTOOL) { + return -1; + } + +#ifdef BCM_DEBUG + printf( "bcm57xx: detected device " ); + if( IS_5703 ) { + printf( "5703S" ); + } else if( IS_5704 ) { + printf( "5704" ); + if( IS_SERDES ) { + printf( "S\n" ); + } else { + printf( "C\n" ); + } + } else if( IS_5714 ) { + printf( "5714\n" ); + } +#endif + /* + * setup register & memory base addresses of NIC + */ + l_baseaddrL_u32 = (uint32_t) ~0xf & + SLOF_pci_config_read32(PCI_BAR1_R); + /*l_baseaddrL_u32 = ( (uint32_t) ~0xf & + (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_BAR1_R ) );*/ + + l_baseaddrH_u32 = SLOF_pci_config_read32(PCI_BAR2_R); + /*l_baseaddrH_u32 = + (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_BAR2_R );*/ + + bcm_baseaddr_u64 = (uint64_t) l_baseaddrH_u32; + bcm_baseaddr_u64 <<= 32; + bcm_baseaddr_u64 += (uint64_t) l_baseaddrL_u32; + bcm_baseaddr_u64 = + (uint64_t)SLOF_translate_my_address((void *)bcm_baseaddr_u64); + /*snk_kernel_interface->translate_addr(((void *)&(bcm_baseaddr_u64)));*/ + bcm_memaddr_u64 = bcm_baseaddr_u64 + BCM_MEMORY_OFFS; + + /* + * 57xx hardware initialization + * BCM57xx Programmer's Guide: Section 8, "Initialization" + * steps 1 through 101 + */ + + // step 1: enable bus master & memory space in command reg + i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) ); + SLOF_pci_config_write16(PCI_COM_R, i); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 2, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_COM_R, + ( int ) i );*/ + + // step 2: disable & mask interrupts & enable pci byte/word swapping & enable indirect addressing mode + i = ( BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); + SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); + /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, + 4, + bcm_pcicfg_bus, + bcm_pcicfg_devfn, + PCI_MISC_HCTRL_R, + ( int ) i );*/ + + bcm_nvram_init(); + + switch(ioctl_data->subcmd) { + case ETHTOOL_GMAC: + switch(ioctl_data->data.mac.idx) { + case 0: + ret_val = bcm_getmac(0x7C, ioctl_data->data.mac.address); + break; + case 1: + ret_val = bcm_getmac(0xCC, ioctl_data->data.mac.address); + break; + default: + ret_val = -1; + break; + } + break; + case ETHTOOL_SMAC: + switch(ioctl_data->data.mac.idx) { + case 0: + ret_val = bcm_getmac(0xCC, mac_addr); + if(ret_val == 0) + ret_val = bcm_setmac(ioctl_data->data.mac.address, mac_addr); + break; + case 1: + ret_val = bcm_getmac(0x7C, mac_addr); + if(ret_val == 0) + ret_val = bcm_setmac(mac_addr, ioctl_data->data.mac.address); + break; + default: + ret_val = -1; + break; + } + break; + case ETHTOOL_VERSION: { + char *text = ioctl_data->data.version.text; + memcpy(text, " BCM57xx Boot code level: ", 27); + ret_val = bcm_get_version(&text[27]); + break; + } + default: + ret_val = -1; + break; + } + + bcm_term(); + return ret_val; +} + +net_driver_t *bcm57xx_open(void) +{ + net_driver_t *driver; + uint16_t vendor_id, device_id; + + vendor_id = SLOF_pci_config_read16(0); + device_id = SLOF_pci_config_read16(2); + if (check_driver(vendor_id, device_id)) + return NULL; + + driver = SLOF_alloc_mem(sizeof(*driver)); + if (!driver) { + printf("Unable to allocate virtio-net driver\n"); + return NULL; + } + memset(driver, 0, sizeof(*driver)); + + if (bcm_init(driver)) + goto FAIL; + + return driver; + +FAIL: SLOF_free_mem(driver, sizeof(*driver)); + return NULL; + + return 0; +} + +void bcm57xx_close(net_driver_t *driver) +{ + if (driver->running == 0) + return; + + bcm_term(); + driver->running = 0; + SLOF_free_mem(driver, sizeof(*driver)); +} + +int bcm57xx_read(char *buf, int len) +{ + if (buf) + return bcm_receive(buf, len); + return -1; +} + +int bcm57xx_write(char *buf, int len) +{ + if (buf) + return bcm_xmit(buf, len); + return -1; +} diff --git a/qemu/roms/SLOF/lib/libbcm/bcm57xx.h b/qemu/roms/SLOF/lib/libbcm/bcm57xx.h new file mode 100644 index 000000000..efaba60c6 --- /dev/null +++ b/qemu/roms/SLOF/lib/libbcm/bcm57xx.h @@ -0,0 +1,323 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <cache.h> +#include <netdriver.h> + +// Debug switches +//#define BCM_DEBUG // main debug switch, w/o it the other ones don't work +//#define BCM_SHOW_RCV +//#define BCM_SHOW_RCV_DATA +//#define BCM_SHOW_XMIT +//#define BCM_SHOW_XMIT_DATA +//#define BCM_SHOW_XMIT_STATS +//#define BCM_SHOW_IDX +//#define BCM_SHOW_STATS +//#define BCM_SHOW_ASF_REGS + +// Switch to enable SW AUTO-NEG +// don't try, it's still incomplete +//#define BCM_SW_AUTONEG + +/* + * used register offsets + */ +// PCI command register +#define PCI_COM_R ( (uint16_t) 0x0004 ) +// PCI Cache Line Size register +#define PCI_CACHELS_R ( (uint16_t) 0x000c ) +// PCI bar1 register +#define PCI_BAR1_R ( (uint16_t) 0x0010 ) +// PCI bar2 register +#define PCI_BAR2_R ( (uint16_t) 0x0014 ) +// PCI bar1 register +#define PCI_SUBID_R ( (uint16_t) 0x002e ) +// PCI-X Comand register +#define PCI_X_COM_R ( (uint16_t) 0x0042 ) +// Message Data Register +#define MSG_DATA_R ( (uint16_t) 0x0064 ) +// PCI misc host contrl register +#define PCI_MISC_HCTRL_R ( (uint16_t) 0x0068 ) +// DMA Read/Write Control register +#define DMA_RW_CTRL_R ( (uint16_t) 0x006c ) +// PCI State register +#define PCI_STATE_R ( (uint16_t) 0x0070 ) +// PCI_Clock Control register +#define PCI_CLK_CTRL_R ( (uint16_t) 0x0074 ) +// Register Base Address Register +#define REG_BASE_ADDR_REG ( (uint16_t) 0x0078 ) +// Memory Window Base Address Register +#define MEM_BASE_ADDR_REG ( (uint16_t) 0x007c ) +// Register Data Register +#define REG_DATA_REG ( (uint16_t) 0x0080 ) +// Memory Window Data Register +#define MEM_DATA_REG ( (uint16_t) 0x0084 ) +// MAC Function register +#define MAC_FUNC_R ( (uint16_t) 0x00b8 ) +// Interrupt Mailbox 0 register +#define INT_MBX0_R ( (uint16_t) 0x0204 ) +// Ethernet MAC Mode register +#define ETH_MAC_MODE_R ( (uint16_t) 0x0400 ) +// Ethernet MAC Addresses registers +#define MAC_ADDR_OFFS_HI( idx ) ( (uint16_t) ( (idx*2 + 0)*sizeof( uint32_t ) + 0x0410 ) ) +#define MAC_ADDR_OFFS_LO( idx ) ( (uint16_t) ( (idx*2 + 1)*sizeof( uint32_t ) + 0x0410 ) ) +// Ethernet MAC Status register +#define ETH_MAC_STAT_R ( (uint16_t) 0x0404 ) +// Ethernet MAC Event Enable register +#define ETH_MAC_EVT_EN_R ( (uint16_t) 0x0408 ) +// Ethernet Transmit Random Backoff register +#define ETH_TX_RND_BO_R ( (uint16_t) 0x0438 ) +// Receive MTU Size register +#define RX_MTU_SIZE_R ( (uint16_t) 0x043c ) +// Transmit 1000BASE-X Auto Negotiation register +#define TX_1000BX_AUTONEG_R ( (uint16_t) 0x0444 ) +// Receive 1000BASE-X Auto Negotiation register +#define RX_1000BX_AUTONEG_R ( (uint16_t) 0x0448 ) +// MI Communication register +#define MI_COM_R ( (uint16_t) 0x044c ) +// MI Status Register +#define MI_STATUS_R ( (uint16_t) 0x0450 ) +// MI Mode register +#define MI_MODE_R ( (uint16_t) 0x0454 ) +// Transmit MAC Mode register +#define TX_MAC_MODE_R ( (uint16_t) 0x045c ) +// Transmit MAC Length register +#define TX_MAC_LEN_R ( (uint16_t) 0x0464 ) +// Receive MAC Mode register +#define RX_MAC_MODE_R ( (uint16_t) 0x0468 ) +// MAC Hash 0 register* VPD Config: +#define MAC_HASH0_R ( (uint16_t) 0x0470 ) +// MAC Hash 1 register +#define MAC_HASH1_R ( (uint16_t) 0x0474 ) +// MAC Hash 2 register +#define MAC_HASH2_R ( (uint16_t) 0x0478 ) +// MAC Hash 3 register +#define MAC_HASH3_R ( (uint16_t) 0x047c ) +// Receive Rules Control register +#define RX_RULE_CTRL_R( idx ) ( (uint16_t) ( idx*8 + 0x0480 ) ) +// Receive Rules Value register +#define RX_RULE_VAL_R( idx ) ( (uint16_t) ( idx*8 + 0x0484 ) ) +// Receive Rules Configuration register +#define RX_RULE_CFG_R ( (uint16_t) 0x0500 ) +// Low Watermark Max Receive Frames register +#define LOW_WMARK_MAX_RXFRAM_R ( (uint16_t) 0x0504 ) +// SerDes Control Register +#define SERDES_CTRL_R ( (uint16_t) 0x0590 ) +// Hardware Auto Negotiation Control Register +#define HW_AUTONEG_CTRL_R ( (uint16_t) 0x05B0 ) +// Hardware Auto Negotiation Status Register +#define HW_AUTONEG_STAT_R ( (uint16_t) 0x05B4 ) +// Send Data Initiator Mode register +#define TX_DAT_INIT_MODE_R ( (uint16_t) 0x0c00 ) +// Send Data Completion Mode register +#define TX_DAT_COMPL_MODE_R ( (uint16_t) 0x1000 ) +// Send BD Ring Selector Mode register +#define TX_BD_RING_SEL_MODE_R ( (uint16_t) 0x1400 ) +// Send BD Initiator Mode register +#define TX_BD_INIT_MODE_R ( (uint16_t) 0x1800 ) +// Send BD Completion Mode register +#define TX_BD_COMPL_MODE_R ( (uint16_t) 0x1c00 ) +// Receive List Placement Mode register +#define RX_LST_PLACE_MODE_R ( (uint16_t) 0x2000 ) +// Receive List Placement Configuration register +#define RX_LST_PLACE_CFG_R ( (uint16_t) 0x2010 ) +// Receive List Placement Statistics Enable Mask register +#define RX_LST_PLACE_STAT_EN_R ( (uint16_t) 0x2018 ) +// Receive Data & Receive BD Initiator Mode register +#define RX_DAT_BD_INIT_MODE_R ( (uint16_t) 0x2400 ) +// Receive Data Completion Mode register +#define RX_DAT_COMPL_MODE_R ( (uint16_t) 0x2800 ) +// Receive BD Initiator Mode register +#define RX_BD_INIT_MODE_R ( (uint16_t) 0x2c00 ) +// Standard Receive Producer Ring Replenish Threshold register +#define STD_RXPR_REP_THR_R ( (uint16_t) 0x2c18 ) +// Receive BD Completion Mode register +#define RX_BD_COMPL_MODE_R ( (uint16_t) 0x3000 ) +// Receive List Selector Mode register +#define RX_LST_SEL_MODE_R ( (uint16_t) 0x3400 ) +// MBUF Cluster Free Mode register +#define MBUF_CLSTR_FREE_MODE_R ( (uint16_t) 0x3800 ) +// Host Coalescing Mode register +#define HOST_COAL_MODE_R ( (uint16_t) 0x3c00 ) +// Receive Coalescing Ticks register +#define RX_COAL_TICKS_R ( (uint16_t) 0x3c08 ) +// Send Coalescing Ticks register +#define TX_COAL_TICKS_R ( (uint16_t) 0x3c0c ) +// Receive Max Coalesced BD Count register +#define RX_COAL_MAX_BD_R ( (uint16_t) 0x3c10 ) +// Send Max Coalesced BD Count register +#define TX_COAL_MAX_BD_R ( (uint16_t) 0x3c14 ) +// Receive Coalescing Ticks During Int register +#define RX_COAL_TICKS_INT_R ( (uint16_t) 0x3c18 ) +// Send Coalescing Ticks During Int register +#define TX_COAL_TICKS_INT_R ( (uint16_t) 0x3c1c ) +// Receive Max Coalesced BD Count During Int register +#define RX_COAL_MAX_BD_INT_R ( (uint16_t) 0x3c18 ) +// Send Max Coalesced BD Count During Int register +#define TX_COAL_MAX_BD_INT_R ( (uint16_t) 0x3c1c ) +// Statistics Ticks Counter register +#define STAT_TICK_CNT_R ( (uint16_t) 0x3c28 ) +// Status Block Host Address Low register +#define STB_HOST_ADDR_HI_R ( (uint16_t) 0x3c38 ) +// Status Block Host Address High register +#define STB_HOST_ADDR_LO_R ( (uint16_t) 0x3c3c ) +// Statistics Base Address register +#define STAT_NIC_ADDR_R ( (uint16_t) 0x3c40 ) +// Status Block Base Address register +#define STB_NIC_ADDR_R ( (uint16_t) 0x3c44 ) +// Memory Arbiter Mode register +#define MEMARB_MODE_R ( (uint16_t) 0x4000 ) +// Buffer Manager Mode register +#define BUF_MAN_MODE_R ( (uint16_t) 0x4400 ) +// MBuf Pool Address register +#define MBUF_POOL_ADDR_R ( (uint16_t) 0x4408 ) +// MBuf Pool Length register +#define MBUF_POOL_LEN_R ( (uint16_t) 0x440c ) +// Read DMA Mbuf Low Watermark register +#define DMA_RMBUF_LOW_WMARK_R ( (uint16_t) 0x4410 ) +// MAC Rx Mbuf Low Watermark register +#define MAC_RXMBUF_LOW_WMARK_R ( (uint16_t) 0x4414 ) +// Mbuf High Watermark register +#define MBUF_HIGH_WMARK_R ( (uint16_t) 0x4418 ) +// DMA Descriptor Pool Address register +#define DMA_DESC_POOL_ADDR_R ( (uint16_t) 0x442c ) +// DMA Descriptor Pool Length register +#define DMA_DESC_POOL_LEN_R ( (uint16_t) 0x4430 ) +// DMA Descriptor Low Watermark register +#define DMA_DESC_LOW_WM_R ( (uint16_t) 0x4434 ) +// DMA Descriptor HIGH Watermark register +#define DMA_DESC_HIGH_WM_R ( (uint16_t) 0x4438 ) +// Read DMA Mode register +#define RD_DMA_MODE_R ( (uint16_t) 0x4800 ) +// Write DMA Mode register +#define WR_DMA_MODE_R ( (uint16_t) 0x4c00 ) +// FTQ Reset register +#define FTQ_RES_R ( (uint16_t) 0x5c00 ) +// MSI Mode register +#define MSI_MODE_R ( (uint16_t) 0x6000 ) +// DMA completion Mode register +#define DMA_COMPL_MODE_R ( (uint16_t) 0x6400 ) +// Mode Control register +#define MODE_CTRL_R ( (uint16_t) 0x6800 ) +// Misc Configuration register +#define MISC_CFG_R ( (uint16_t) 0x6804 ) +// Misc Local Control register +#define MISC_LOCAL_CTRL_R ( (uint16_t) 0x6808 ) +// RX-Risc Mode Register +#define RX_CPU_MODE_R ( (uint16_t) 0x5000 ) +// RX-Risc State Register +#define RX_CPU_STATE_R ( (uint16_t) 0x5004 ) +// RX-Risc Program Counter +#define RX_CPU_PC_R ( (uint16_t) 0x501c ) +// RX-Risc Event Register +#define RX_CPU_EVENT_R ( (uint16_t) 0x6810 ) +// MDI Control register +#define MDI_CTRL_R ( (uint16_t) 0x6844 ) +// WOL Mode register +#define WOL_MODE_R ( (uint16_t) 0x6880 ) +// WOL Config register +#define WOL_CFG_R ( (uint16_t) 0x6884 ) +// WOL Status register +#define WOL_STATUS_R ( (uint16_t) 0x6888 ) + +// ASF Control register +#define ASF_CTRL_R ( (uint16_t) 0x6c00 ) +// ASF Watchdog Timer register +#define ASF_WATCHDOG_TIMER_R ( (uint16_t) 0x6c0c ) +// ASF Heartbeat Timer register +#define ASF_HEARTBEAT_TIMER_R ( (uint16_t) 0x6c10 ) +// Poll ASF Timer register +#define ASF_POLL_TIMER_R ( (uint16_t) 0x6c14 ) +// Poll Legacy Timer register +#define POLL_LEGACY_TIMER_R ( (uint16_t) 0x6c18 ) +// Retransmission Timer register +#define RETRANSMISSION_TIMER_R ( (uint16_t) 0x6c1c ) +// Time Stamp Counter register +#define TIME_STAMP_COUNTER_R ( (uint16_t) 0x6c20 ) + +// NVM Command register +#define NVM_COM_R ( (uint16_t) 0x7000 ) +// NVM Write register +#define NVM_WRITE_R ( (uint16_t) 0x7008 ) +// NVM Address register +#define NVM_ADDR_R ( (uint16_t) 0x700c ) +// NVM Read registertg3_phy_copper_begin +#define NVM_READ_R ( (uint16_t) 0x7010 ) +// NVM Access register +#define NVM_ACC_R ( (uint16_t) 0x7024 ) +// NVM Config 1 register +#define NVM_CFG1_R ( (uint16_t) 0x7014 ) +// Software arbitration register +#define SW_ARB_R ( (uint16_t) 0x7020 ) + +/* + * useful def's + */ +#define rd08(a) ci_read_8((uint8_t *)(a)) +#define rd16(a) ci_read_16((uint16_t *)(a)) +#define rd32(a) ci_read_32((uint32_t *)(a)) +#define wr08(a,v) ci_write_8((uint8_t *)(a), (v)) +#define wr16(a,v) ci_write_16((uint16_t *)(a), (v)) +#define wr32(a,v) ci_write_32((uint32_t *)(a), (v)) + +#define BIT08( bit ) ( (uint8_t) 0x1 << (bit) ) +#define BIT16( bit ) ( (uint16_t) 0x1 << (bit) ) +#define BIT32( bit ) ( (uint32_t) 0x1 << (bit) ) + +/* + * type definition + */ + +/* + * Constants for different kinds of IOCTL requests + */ + +#define SIOCETHTOOL 0x1000 + +/* + * special structure and constants for IOCTL requests of type ETHTOOL + */ + +#define ETHTOOL_GMAC 0x03 +#define ETHTOOL_SMAC 0x04 +#define ETHTOOL_VERSION 0x05 + +typedef struct { + int idx; + char address[6]; +} ioctl_ethtool_mac_t; + +typedef struct { + unsigned int length; + char *text; +} ioctl_ethtool_version_t; + + +/* + * default structure and constants for IOCTL requests + */ + +#define IF_NAME_SIZE 0xFF + +typedef struct { + char if_name[IF_NAME_SIZE]; + int subcmd; + union { + ioctl_ethtool_mac_t mac; + ioctl_ethtool_version_t version; + } data; +} ioctl_net_data_t; + +extern net_driver_t *bcm57xx_open(void); +extern void bcm57xx_close(net_driver_t *driver); +extern int bcm57xx_read(char *buf, int len); +extern int bcm57xx_write(char *buf, int len); diff --git a/qemu/roms/SLOF/lib/libbootmsg/Makefile b/qemu/roms/SLOF/lib/libbootmsg/Makefile new file mode 100644 index 000000000..642c970ac --- /dev/null +++ b/qemu/roms/SLOF/lib/libbootmsg/Makefile @@ -0,0 +1,75 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) -I. -I../../include +LDFLAGS = -nostdlib + +TARGET = ../libbootmsg.a + + +all: $(TARGET) + +ifeq ($(CPUARCH),cbea) +SRCS = +SRCSS = bootmsg_lvl.S +else +ifeq ($(CPUARCH),ppc970) +SRCS = +SRCSS = bootmsg_lvl.S +else +ifeq ($(CPUARCH),p5) +SRCS = +SRCSS = bootmsg_lvl.S +else +ifeq ($(CPUARCH),ppcp7) +SRCS = +SRCSS = bootmsg_lvl.S +else +SRCS = bootmsg.c +SRCSS = +endif +endif +endif +endif + +OBJS = $(SRCS:%.c=%.o) $(SRCSS:%.S=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +%.o: %.S + $(CC) $(CPPFLAGS) $(ASFLAGS) -c $< -o $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -MM $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep + diff --git a/qemu/roms/SLOF/lib/libbootmsg/bootmsg.code b/qemu/roms/SLOF/lib/libbootmsg/bootmsg.code new file mode 100644 index 000000000..ae370af08 --- /dev/null +++ b/qemu/roms/SLOF/lib/libbootmsg/bootmsg.code @@ -0,0 +1,61 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +#include <libbootmsg.h> + +// : cp ( cp-id -- ) +PRIM(bootmsg_X2d_cp) + int cpid = TOS.n; POP; + bootmsg_cp(cpid); +MIRP + +// : bootmsg-warning ( cp-id lvl pstr -- ) +PRIM(bootmsg_X2d_warning) + char* str = TOS.a; POP; + short lvl = TOS.n; POP; + short cpid = TOS.n; POP; + bootmsg_warning(cpid, (const char*)str, lvl); +MIRP + +// : bootmsg-error ( cp-id pstr -- ) +PRIM(bootmsg_X2d_error) + char* str = TOS.a; POP; + short cpid = TOS.n; POP; + bootmsg_error(cpid, (const char*)str); +MIRP + +// : bootmsg-debugcp ( cp-id lvl pstr -- ) +PRIM(bootmsg_X2d_debugcp) + char* str = TOS.a; POP; + short lvl = TOS.n; POP; + short cpid = TOS.n; POP; + bootmsg_debugcp(cpid, (const char*)str, lvl); +MIRP + +// : bootmsg-setlevel ( area lvl -- ) +PRIM(bootmsg_X2d_setlevel) + char lvl = TOS.n; POP; + short area = TOS.n; POP; + bootmsg_setlevel(area, lvl); +MIRP + +// : bootmsg-checklevel ( area lvl -- [true|false] ) +PRIM(bootmsg_X2d_checklevel) + char lvl = TOS.n; POP; + short area = TOS.n; POP; + PUSH; + TOS.n = (bootmsg_checklevel(area, lvl)) ? -1 : 0; +MIRP + +// : bootmsg-nvupdate ( -- ) +PRIM(bootmsg_X2d_nvupdate) + bootmsg_nvupdate(); +MIRP diff --git a/qemu/roms/SLOF/lib/libbootmsg/bootmsg.in b/qemu/roms/SLOF/lib/libbootmsg/bootmsg.in new file mode 100644 index 000000000..73e01e3d5 --- /dev/null +++ b/qemu/roms/SLOF/lib/libbootmsg/bootmsg.in @@ -0,0 +1,19 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +cod(bootmsg-cp) +cod(bootmsg-warning) +cod(bootmsg-error) +cod(bootmsg-debugcp) +cod(bootmsg-setlevel) +cod(bootmsg-nvupdate) +cod(bootmsg-checklevel) diff --git a/qemu/roms/SLOF/lib/libbootmsg/bootmsg_lvl.S b/qemu/roms/SLOF/lib/libbootmsg/bootmsg_lvl.S new file mode 100644 index 000000000..2e4c1359a --- /dev/null +++ b/qemu/roms/SLOF/lib/libbootmsg/bootmsg_lvl.S @@ -0,0 +1,204 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +#define _ASM_ +#include "macros.h" +#include "southbridge.h" +#include "nvramlog.h" + +#define bootmsg_area_size 128 + + .text + .align 3 + +// Declare the warning level for all 128 possibilities of AC/pCKG kombinations + WRNG_LVL: + .rept bootmsg_area_size + .byte 0x0 + .endr + + +//***************************************************************************** +// Check UserWarningLevel against SystemWarningLevel +// input : r3=cp-id, r5=level +// change: r6,r7 +// output: CR0 ( compared user vs system level ) +// example: +// bl GET_WRNG_LVL +// ble print_warning +// bgt do_not_print_warning +ENTRY(GET_WRNG_LVL) + mflr r7 // save linkage register + bl 0f // get current +0: mflr r6 // Instruction Address + mtlr r7 // restore linkage register + addi r6,r6,WRNG_LVL-0b // calc addr of WRNG_LVL array + rldic r7,r3,56,57 // calc index into array + lbzux r7,r6,r7 // read the warning level + cmpw r5,r7 // and compare it + blr + +//***************************************************************************** +// Print CheckPoint +// input : r3=cp-id +// change: r3, r4, r5, r6, r7, r11 +// output: none +ENTRY(bootmsg_cp) + mflr r11 + mr r9, r3 // save checkpoint ID + li r3, 'C' + bl io_putchar // print character + mr r3, r9 + bl io_printhex16 // print checkpoint ID + .rept 5 + li r3,'\b' + bl io_putchar // print backspaces + .endr + mtlr r11 + blr + +//***************************************************************************** +// Print a general BootMessage +// input : r3=cp-id, r4=string, r5=char (type C,W,E) +// change: r3,r4,r5,r6,r7,r9,r10,r11,r12 +// output: none +ENTRY(print_msg) + mflr r11 // Save linkage register + mr r9, r3 // Save ID + mr r10, r4 // Save ptr to string + mr r12, r5 // Save type (char [CWE]) + li r3, '\n' // make it a new line + bl io_putchar + li r3, '\r' + bl io_putchar + mr r3, r12 // restore type + bl io_putchar // print character + mr r3, r9 // restore ID + bl io_printhex16 // print checkpoint ID + li r3, ' ' // print a space + bl io_putchar + mr r3, r10 // restore ptr to string + bl io_print // print message + li r3, '\n' // add a new line + bl io_putchar + li r3, '\r' + bl io_putchar + mtlr r11 // restore linkage register + blr + +//***************************************************************************** +// Print an Error Boot Message +// input : r3=cp-id, r4=string-ptr +// change : r3,r4,r5,r6,r7,r9,r10,r11,r12 +// output : none +ENTRY(bootmsg_error) + li r5, 'E' // E is for Error + b print_msg // and print this message + +//***************************************************************************** +// Print a Warning Boot Message +// input : r3=cp-id, r4=string-ptr, r5=level +// change : r3,r4,r5,r6,r7,r9,r10,r11,r12 +// output : none +ENTRY(bootmsg_warning) + mflr r11 // save linkage register + bl GET_WRNG_LVL // check UserLevel against SystemLevel + mtlr r11 // restore linkage register + li r5, 'W' // 'W' is for Warning + ble print_msg // if UserLevel<=SystemLevel print and return + blr // else return + +//***************************************************************************** +// Print a Debug Checkpoint +// input : r3=cp-id, r4=string-ptr, r5=level +// change : r3,r4,r5,r6,r7,r9,r10,r11,r12 +// output : none +// r3=cp-id, r4=string, r5=level +ENTRY(bootmsg_debugcp) + mflr r11 // save linkage register + addi r5,r5,0x20 // add checkpoint offset + bl GET_WRNG_LVL // check UserLevel against SystemLevel + mtlr r11 // restore linkage register + li r5, 'D' // 'D' is for Debug CheckPoint + ble print_msg // if UserLevel<=SystemLevel print and return + blr // else return + +//***************************************************************************** +// Check warning level +// input : r3=cp-id, r4=level +// change : r3,r4,r5,r6,r7,r9,r10,r11 +// output : r3 (true, false) +// r3=cp-id, r4=level +ENTRY(bootmsg_checklevel) + mflr r11 + mr r5, r4 + slwi r3, r3, 8 + bl GET_WRNG_LVL // check UserLevel against SystemLevel + li r3, 0 // return 0 + bgt 0f // IF ( UserLevel < SystemLevel ) + li r3, 1 // | return 1 +0: mtlr r11 // FI + blr + +// r3=area|pkg, r4=level +ENTRY(bootmsg_setlevel) + mflr r5 + bl WarningMsg // calc current IA + WarningMsg: + mflr r6 // get current IA + addi r6,r6,WRNG_LVL-WarningMsg + andi. r3, r3, 0x7F + add r6,r3,r6 // address | + stb r4,0(r6) // store level |_ stwbrx r4,r3,r6 + +#if !defined(DISABLE_NVRAM) && !defined(RTAS_NVRAM) + LOAD64(r6, SB_NVRAM_FWONLY_adr + 8 ) + add r6,r6,r3 + stb r4,0(r6) +#endif + mtlr r5 + blr + +ENTRY(bootmsg_nvupdate) +#if !defined(DISABLE_NVRAM) && !defined(RTAS_NVRAM) + mflr r10 + LOAD64(r3, SB_NVRAM_FWONLY_adr) + lwz r4, 0(r3) + cmpwi r4, 0x424E // find bootmsg area header + bne 0f + + LOAD64(r5, bootmsg_area_size/8) + mtctr r5 + bl WngMsg + WngMsg: + mflr r5 + addi r5,r5,WRNG_LVL-WngMsg-8 + +1: + ldu r4, 8(r3) + stdu r4, 8(r5) + bdnz+ 1b + b 2f + +0: + LOAD64(r5, bootmsg_area_size) + mtctr r5 + li r4, 0x424E // clear bootmsg log area + stw r4, 0(r3) + li r4, 0 + +1: stdu r4, 8(r3) + bdnz+ 1b + +2: // the end + mtlr r10 +#endif + blr diff --git a/qemu/roms/SLOF/lib/libbootmsg/libbootmsg.h b/qemu/roms/SLOF/lib/libbootmsg/libbootmsg.h new file mode 100644 index 000000000..9d0bd157d --- /dev/null +++ b/qemu/roms/SLOF/lib/libbootmsg/libbootmsg.h @@ -0,0 +1,21 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +#ifndef _LIBBOOTMSG_H +#define _LIBBOOTMSG_H +void bootmsg_cp(short p); +void bootmsg_error(short p, const char *str); +void bootmsg_warning(short p, const char *str, short lvl); +void bootmsg_debugcp(short p, const char *str, short lvl); +void bootmsg_setlevel(short p, short level); +int bootmsg_checklevel(short p, short level); +void *bootmsg_nvupdate(void); +#endif /* _LIBBOOTMSG_H */ diff --git a/qemu/roms/SLOF/lib/libc/Makefile b/qemu/roms/SLOF/lib/libc/Makefile new file mode 100644 index 000000000..0c762ec8b --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/Makefile @@ -0,0 +1,61 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +LIBCCMNDIR = $(shell pwd) +STRINGCMNDIR = $(LIBCCMNDIR)/string +CTYPECMNDIR = $(LIBCCMNDIR)/ctype +STDLIBCMNDIR = $(LIBCCMNDIR)/stdlib +STDIOCMNDIR = $(LIBCCMNDIR)/stdio +GETOPTCMNDIR = $(LIBCCMNDIR)/getopt + +include $(TOPCMNDIR)/make.rules + + +CPPFLAGS = -I$(LIBCCMNDIR)/include +LDFLAGS= -nostdlib + +TARGET = ../libc.a + + +all: $(TARGET) + +# Use the following target to build a native version of the lib +# (for example for debugging purposes): +native: + $(MAKE) CROSS="" CC=$(HOSTCC) NATIVEBUILD=1 + + +include $(STRINGCMNDIR)/Makefile.inc +include $(CTYPECMNDIR)/Makefile.inc +include $(STDLIBCMNDIR)/Makefile.inc +include $(STDIOCMNDIR)/Makefile.inc +include $(GETOPTCMNDIR)/Makefile.inc + +OBJS = $(STRING_OBJS) $(CTYPE_OBJS) $(STDLIB_OBJS) $(STDIO_OBJS) $(GETOPT_OBJS) + +ifneq ($(NATIVEBUILD),1) +# These parts of the libc use assembler, so they can only be compiled when +# we are _not_ building a native version. +endif + + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean diff --git a/qemu/roms/SLOF/lib/libc/README.txt b/qemu/roms/SLOF/lib/libc/README.txt new file mode 100644 index 000000000..eaafdf4af --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/README.txt @@ -0,0 +1,49 @@ + + Standard C library for the SLOF firmware project + ================================================ + +To use this library, link your target against the "libc.a" archive. + +However, there are some prerequisites before you can use certain parts of the +library: + +1) If you want to use malloc() and the like, you have to supply an implemen- + tation of sbrk() in your own code. malloc() uses sbrk() to get new, free + memory regions. + + Prototype: void *sbrk(int incr); + Description: sbrk() increments the available data space by incr bytes and + returns a pointer to the start of the new area. + + See the man-page of sbrk for details about this function. + +2) Before you can use the stdio output functions like printf(), puts() and the + like, you have to provide a standard write() function in your code. + printf() and the like use write() to print out the strings to the standard + output. + + Prototype: ssize_t write(int fd, const void *buf, size_t cnt); + Description: Write cnt byte from the buffer buf to the stream associated + with the file descriptor fd. + + The stdio functions will print their output to the stdout channel which is + assigned with the file descriptor 1 by default. Note that the stdio + functions will not use open() before calling write(), so if the stdout + cannel needs to be opened first, you should do that in your start-up code + before using the libc functions for the first time. + +3) Before you can use the stdio input functions like scanf() and the + like, you have to provide a standard read() function in your code. + scanf() and the like use read() to get the characters from the standard + input. + + Prototype: ssize_t read(int fd, void *buf, size_t cnt); + Description: Read cnt byte from the stream associated with the file + descriptor fd and put them into the buffer buf. + + The stdio functions will get their input from the stdin channel which is + assigned with the file descriptor 0 by default. Note that the stdio + functions will not use open() before calling read(), so if the stdin + cannel needs to be opened first, you should do that in your start-up code + before using the libc functions for the first time. + diff --git a/qemu/roms/SLOF/lib/libc/ctype/Makefile.inc b/qemu/roms/SLOF/lib/libc/ctype/Makefile.inc new file mode 100644 index 000000000..25513a9a7 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/ctype/Makefile.inc @@ -0,0 +1,20 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + + +CTYPE_SRC_C = isdigit.c isprint.c isspace.c isxdigit.c tolower.c toupper.c +CTYPE_SRC_ASM = +CTYPE_SRCS = $(CTYPE_SRC_C:%=$(CTYPECMNDIR)/%) $(CTYPE_SRC_ASM:%=$(CTYPECMNDIR)/%) +CTYPE_OBJS = $(CTYPE_SRC_C:%.c=%.o) $(CTYPE_SRC_ASM:%.S=%.o) + +%.o : $(CTYPECMNDIR)/%.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ diff --git a/qemu/roms/SLOF/lib/libc/ctype/isdigit.c b/qemu/roms/SLOF/lib/libc/ctype/isdigit.c new file mode 100644 index 000000000..62d08a1cf --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/ctype/isdigit.c @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <ctype.h> + +int isdigit(int ch) +{ + switch (ch) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return 1; + + default: + return 0; + } +} diff --git a/qemu/roms/SLOF/lib/libc/ctype/isprint.c b/qemu/roms/SLOF/lib/libc/ctype/isprint.c new file mode 100644 index 000000000..c74880f1c --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/ctype/isprint.c @@ -0,0 +1,18 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <ctype.h> + +int isprint(int ch) +{ + return (ch >= 32 && ch < 127); +} diff --git a/qemu/roms/SLOF/lib/libc/ctype/isspace.c b/qemu/roms/SLOF/lib/libc/ctype/isspace.c new file mode 100644 index 000000000..51230192f --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/ctype/isspace.c @@ -0,0 +1,29 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <ctype.h> + +int isspace(int ch) +{ + switch (ch) { + case ' ': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + return 1; + + default: + return 0; + } +} diff --git a/qemu/roms/SLOF/lib/libc/ctype/isxdigit.c b/qemu/roms/SLOF/lib/libc/ctype/isxdigit.c new file mode 100644 index 000000000..9d323f3c0 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/ctype/isxdigit.c @@ -0,0 +1,21 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <ctype.h> + +int isxdigit(int ch) +{ + return ( + (ch >= '0' && ch <= '9') | + (ch >= 'A' && ch <= 'F') | + (ch >= 'a' && ch <= 'f') ); +} diff --git a/qemu/roms/SLOF/lib/libc/ctype/tolower.c b/qemu/roms/SLOF/lib/libc/ctype/tolower.c new file mode 100644 index 000000000..f775e9096 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/ctype/tolower.c @@ -0,0 +1,18 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <ctype.h> + +int tolower(int c) +{ + return (((c >= 'A') && (c <= 'Z')) ? (c - 'A' + 'a' ) : c); +} diff --git a/qemu/roms/SLOF/lib/libc/ctype/toupper.c b/qemu/roms/SLOF/lib/libc/ctype/toupper.c new file mode 100644 index 000000000..9bcee523d --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/ctype/toupper.c @@ -0,0 +1,21 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include "ctype.h" + +int toupper (int cha) +{ + if((cha >= 'a') && (cha <= 'z')) + return(cha - 'a' + 'A'); + return(cha); +} diff --git a/qemu/roms/SLOF/lib/libc/getopt/Makefile.inc b/qemu/roms/SLOF/lib/libc/getopt/Makefile.inc new file mode 100644 index 000000000..8a2e32f00 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/getopt/Makefile.inc @@ -0,0 +1,17 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +GETOPT_SRC_C = getopt.c +GETOPT_OBJS = $(GETOPT_SRC_C:%.c=%.o) + +%.o : $(GETOPTCMNDIR)/%.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ diff --git a/qemu/roms/SLOF/lib/libc/getopt/getopt.c b/qemu/roms/SLOF/lib/libc/getopt/getopt.c new file mode 100644 index 000000000..be626ddc2 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/getopt/getopt.c @@ -0,0 +1,470 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * includes + ******************************************************************************* + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +/* + * global variables, types & constants + * may be removed if already defined + ******************************************************************************* + */ +int opterr = 1; +int optopt = 0; +int optind = 1; +char *optarg = NULL; + +/* + * internal values needed by getopt + * DO NOT CHANGE or REMOVE + */ +enum { + OPTIONAL_ARG = 0, + MANDATORY_ARG = 1, + NO_ARG = 2 +}; + +/* + * variables needed by getopt & getopt_long! + * DO NOT REMOVE + */ +static char *optstart = NULL; + +int +getopt(int argc, char **argv, const char *options) +{ + char *optptr; + char *argptr; + int optman; + int idx; + int ret = 0; + int argpresent; + + /* + * reset used global values + */ + optopt = 0; + optarg = NULL; + + /* + * reset getopt if a new argv pointer is passed + */ + if (optstart != argv[0]) { + optopt = 0; + optind = 1; + optarg = NULL; + optstart = argv[0]; + } + + /* + * return if no more arguments are available + */ + if (optind >= argc) { + return -1; + } + + /* + * start parsing argv[optind] + */ + idx = 0; + + /* + * return if the option does not begin with a '-' or has more than 2 characters + */ + if (argv[optind][idx] != '-') { + + if (opterr != 0) { + printf("unknown option \'%s\', expecting \'-\'\n", + argv[optind]); + } + + optopt = (int) argv[optind][idx]; + optind++; + + return '?'; + } + + /* + * continue to the next character in argv[optind] + */ + idx++; + + /* + * identify the option + * make sure if an option contains a ':' to invalidate the option + */ + optptr = strchr(argv[optind], ':'); + + if (optptr == NULL) { + optptr = strchr(options, (int) argv[optind][idx]); + } else { + optptr = NULL; + } + + /* + * check whether the option is present + */ + if (optptr == NULL) { + /* + * unknown option detected + */ + if (opterr != 0) { + printf("unknown option \'%s\'\n", argv[optind]); + } + + optopt = (int) argv[optind][idx]; + optind++; + + return '?'; + } + + /* + * the option is present in the option string + * setup return value + */ + ret = (int) *optptr; + + /* + * get option argument if needed + */ + optptr++; + + /* + * determine between mandatory and optional argument + */ + optman = NO_ARG; + + if (*optptr == ':') { + optman--; // now set to MANDATORY_ARG + } + + if (optman == MANDATORY_ARG) { + optptr++; + + if (*optptr == ':') { + optman--; // now set to OPTIONAL_ARG + } + + } + + /* + * if strlen( argv[optind ) is greater than 2, + * the argument is in the same argv + */ + if (strlen(argv[optind]) > 2) { + argptr = &argv[optind][2]; + + /* + * do not allow '-' in an argument + */ + if (strchr(argptr, '-') != NULL) { + + if (opterr != 0) { + printf + ("illegal argument value \'%s\' for option \'-%c\'\n", + argptr, ret); + } + + optopt = ret; + + return '?'; + } + + } else { + /* + * move on to the next argv + * it now either contains an argument or the next option + */ + optind++; + + /* + * make sure not to overflow + */ + if (optind < argc) { + argptr = argv[optind]; + } else { + argptr = NULL; + } + + } + + /* + * do the needed actions for the argument state + */ + switch (optman) { + case OPTIONAL_ARG: + + if (argptr == NULL) { + break; + } + + if (*argptr != '-') { + /* + * argument present + */ + optarg = argptr; + optind++; + + } + + + break; + + case MANDATORY_ARG: + argpresent = (argptr != NULL); + + if (argpresent) { + argpresent = (*argptr != '-'); + } + + if (argpresent) { + /* + * argument present + */ + optarg = argptr; + optind++; + } else { + /* + * mandatory argument missing + */ + if (opterr != 0) { + printf + ("missing argument for option \'-%c\'\n", + ret); + } + + optopt = ret; + + /* + * if the first character of options is a ':' + * return a ':' instead of a '?' in case of + * a missing argument + */ + if (*options == ':') { + ret = ':'; + } else { + ret = '?'; + } + + } + + + break; + + case NO_ARG: + + if (strlen(argv[optind - 1]) > 2) { + + if (opterr != 0) { + printf + ("too many arguments for option \'-%c\'\n", + ret); + } + + optopt = ret; + ret = '?'; + } + + + break; + + } + + return ret; +} + +int +getopt_long(int argc, char **argv, const char *shortopts, + const struct option *longopts, int *indexptr) +{ + struct option *optptr = (struct option *) longopts; + int optidx = 0; + int idx; + int ret = 0; + int argpresent; + + /* + * reset used global values + */ + optopt = 0; + optarg = NULL; + + /* + * reset indexptr + */ + *indexptr = -1; + + /* + * reset getopt if a new argv pointer is passed + */ + if (optstart != argv[0]) { + optopt = 0; + optind = 1; + optarg = NULL; + optstart = argv[0]; + } + + /* + * return if no more arguments are available + */ + if (optind >= argc) { + return -1; + } + + /* + * start parsing argv[optind] + */ + idx = 0; + + /* + * return if the option does not begin with a '-' + */ + if (argv[optind][idx] != '-') { + printf("unknown option \'%s\', expecting \'-\'\n", + argv[optind]); + + optind++; + + return '?'; + } + + /* + * move on to the next character in argv[optind] + */ + idx++; + + /* + * return getopt() in case of a short option + */ + if (argv[optind][idx] != '-') { + return getopt(argc, argv, shortopts); + } + + /* + * handle a long option + */ + idx++; + + while (optptr->name != NULL) { + + if (strcmp(&argv[optind][idx], optptr->name) == 0) { + break; + } + + optptr++; + optidx++; + } + + /* + * no matching option found + */ + if (optptr->name == NULL) { + printf("unknown option \'%s\'\n", argv[optind]); + + optind++; + + return '?'; + } + + /* + * option was found, set up index pointer + */ + *indexptr = optidx; + + /* + * get argument + */ + optind++; + + switch (optptr->has_arg) { + case no_argument: + /* + * nothing to do + */ + + break; + + case required_argument: + argpresent = (optind != argc); + + if (argpresent) { + argpresent = (argv[optind][0] != '-'); + } + + if (argpresent) { + /* + * argument present + */ + optarg = argv[optind]; + optind++; + } else { + /* + * mandatory argument missing + */ + printf("missing argument for option \'%s\'\n", + argv[optind - 1]); + + ret = '?'; + } + + + break; + + case optional_argument: + + if (optind == argc) { + break; + } + + if (argv[optind][0] != '-') { + /* + * argument present + */ + optarg = argv[optind]; + optind++; + } + + + break; + + default: + printf("unknown argument option for option \'%s\'\n", + argv[optind - 1]); + + ret = '?'; + + break; + + } + + /* + * setup return values + */ + if (ret != '?') { + + if (optptr->flag == NULL) { + ret = optptr->val; + } else { + *optptr->flag = optptr->val; + ret = 0; + } + + } + + return ret; +} diff --git a/qemu/roms/SLOF/lib/libc/include/ctype.h b/qemu/roms/SLOF/lib/libc/include/ctype.h new file mode 100644 index 000000000..9051a7563 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/ctype.h @@ -0,0 +1,24 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _CTYPE_H +#define _CTYPE_H + +int isdigit(int c); +int isxdigit(int c); +int isprint(int c); +int isspace(int c); + +int tolower(int c); +int toupper(int c); + +#endif diff --git a/qemu/roms/SLOF/lib/libc/include/errno.h b/qemu/roms/SLOF/lib/libc/include/errno.h new file mode 100644 index 000000000..d5859347e --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/errno.h @@ -0,0 +1,34 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _ERRNO_H +#define _ERRNO_H + +extern int errno; + +/* + * Error number definitions + */ +#define EPERM 1 /* not permitted */ +#define ENOENT 2 /* file or directory not found */ +#define EIO 5 /* input/output error */ +#define ENOMEM 12 /* not enough space */ +#define EACCES 13 /* permission denied */ +#define EFAULT 14 /* bad address */ +#define EBUSY 16 /* resource busy */ +#define EEXIST 17 /* file already exists */ +#define ENODEV 19 /* device not found */ +#define EINVAL 22 /* invalid argument */ +#define EDOM 33 /* math argument out of domain of func */ +#define ERANGE 34 /* math result not representable */ + +#endif diff --git a/qemu/roms/SLOF/lib/libc/include/getopt.h b/qemu/roms/SLOF/lib/libc/include/getopt.h new file mode 100644 index 000000000..5956986a5 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/getopt.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef GETOPT_H +#define GETOPT_H + +extern char *optarg; +extern int optind; +extern int opterr; +extern int optopt; + +struct option { + const char *name; + int has_arg; + int *flag; + int val; +}; + +enum { + no_argument = 0, + required_argument, + optional_argument +}; + +int getopt(int argc, char **, const char *); +int getopt_long(int argc, char **, const char *, const struct option *, int *); + +#endif /* GETOPT_H */ diff --git a/qemu/roms/SLOF/lib/libc/include/limits.h b/qemu/roms/SLOF/lib/libc/include/limits.h new file mode 100644 index 000000000..4726835c2 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/limits.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _LIMITS_H +#define _LIMITS_H + +#define UCHAR_MAX 255 +#define SCHAR_MAX 127 +#define SCHAR_MIN (-128) + +#define USHRT_MAX 65535 +#define SHRT_MAX 32767 +#define SHRT_MIN (-32768) + +#define UINT_MAX (4294967295U) +#define INT_MAX 2147483647 +#define INT_MIN (-2147483648) + +#define ULONG_MAX ((unsigned long)-1L) +#define LONG_MAX (ULONG_MAX/2) +#define LONG_MIN ((-LONG_MAX)-1) + +#endif diff --git a/qemu/roms/SLOF/lib/libc/include/stdarg.h b/qemu/roms/SLOF/lib/libc/include/stdarg.h new file mode 100644 index 000000000..d3d12f778 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/stdarg.h @@ -0,0 +1,22 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _STDARG_H +#define _STDARG_H + +typedef __builtin_va_list va_list; + +#define va_start(v,l) __builtin_va_start(v,l) +#define va_arg(v,l) __builtin_va_arg(v,l) +#define va_end(v) __builtin_va_end(v) + +#endif diff --git a/qemu/roms/SLOF/lib/libc/include/stdbool.h b/qemu/roms/SLOF/lib/libc/include/stdbool.h new file mode 100644 index 000000000..5b7d36a7e --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/stdbool.h @@ -0,0 +1,20 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _STDBOOL_H +#define _STDBOOL_H + +#ifndef __cplusplus +typedef enum { false = 0, true } bool; +#endif + +#endif diff --git a/qemu/roms/SLOF/lib/libc/include/stddef.h b/qemu/roms/SLOF/lib/libc/include/stddef.h new file mode 100644 index 000000000..ba2d96098 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/stddef.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _STDDEF_H +#define _STDDEF_H + + +#define NULL ((void *)0) + + +typedef unsigned int size_t; + + +#endif + + diff --git a/qemu/roms/SLOF/lib/libc/include/stdint.h b/qemu/roms/SLOF/lib/libc/include/stdint.h new file mode 100644 index 000000000..518a72305 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/stdint.h @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _STDINT_H +#define _STDINT_H + +typedef unsigned char uint8_t; +typedef signed char int8_t; + +typedef unsigned short uint16_t; +typedef signed short int16_t; + +typedef unsigned int uint32_t; +typedef signed int int32_t; + +typedef unsigned long long uint64_t; +typedef signed long long int64_t; + +#endif diff --git a/qemu/roms/SLOF/lib/libc/include/stdio.h b/qemu/roms/SLOF/lib/libc/include/stdio.h new file mode 100644 index 000000000..84cddea07 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/stdio.h @@ -0,0 +1,63 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _STDIO_H +#define _STDIO_H + +#include <stdarg.h> +#include "stddef.h" + +#define EOF (-1) + +#define _IONBF 0 +#define _IOLBF 1 +#define _IOFBF 2 +#define BUFSIZ 80 + +typedef struct { + int fd; + int mode; + int pos; + char *buf; + int bufsiz; +} FILE; + +extern FILE stdin_data; +extern FILE stdout_data; +extern FILE stderr_data; + +#define stdin (&stdin_data) +#define stdout (&stdout_data) +#define stderr (&stderr_data) + +int fileno(FILE *stream); +int printf(const char *format, ...) __attribute__((format (printf, 1, 2))); +int fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3))); +int sprintf(char *str, const char *format, ...) __attribute__((format (printf, 2, 3))); +int vfprintf(FILE *stream, const char *format, va_list); +int vsprintf(char *str, const char *format, va_list); +int vsnprintf(char *str, size_t size, const char *format, va_list); +void setbuf(FILE *stream, char *buf); +int setvbuf(FILE *stream, char *buf, int mode , size_t size); + +int putc(int ch, FILE *stream); +int putchar(int ch); +int puts(char *str); + +int scanf(const char *format, ...) __attribute__((format (scanf, 1, 2))); +int fscanf(FILE *stream, const char *format, ...) __attribute__((format (scanf, 2, 3))); +int vfscanf(FILE *stream, const char *format, va_list); +int vsscanf(const char *str, const char *format, va_list); +int getc(FILE *stream); +int getchar(void); + +#endif diff --git a/qemu/roms/SLOF/lib/libc/include/stdlib.h b/qemu/roms/SLOF/lib/libc/include/stdlib.h new file mode 100644 index 000000000..dff57f577 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/stdlib.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _STDLIB_H +#define _STDLIB_H + +#include "stddef.h" + +#define RAND_MAX 32767 + + +void *malloc(size_t size); +void *realloc(void *ptr, size_t size); +void free(void *ptr); +void *memalign(size_t boundary, size_t size); + +int atoi(const char *str); +long atol(const char *str); +unsigned long int strtoul(const char *nptr, char **endptr, int base); +long int strtol(const char *nptr, char **endptr, int base); + +int rand(void); + +#endif diff --git a/qemu/roms/SLOF/lib/libc/include/string.h b/qemu/roms/SLOF/lib/libc/include/string.h new file mode 100644 index 000000000..0163c9a67 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/string.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _STRING_H +#define _STRING_H + +#include "stddef.h" + +char *strcpy(char *dest, const char *src); +char *strncpy(char *dest, const char *src, size_t n); +char *strcat(char *dest, const char *src); +int strcmp(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, size_t n); +int strcasecmp(const char *s1, const char *s2); +int strncasecmp(const char *s1, const char *s2, size_t n); +char *strchr(const char *s, int c); +char *strrchr(const char *s, int c); +size_t strlen(const char *s); +char *strstr(const char *hay, const char *needle); +char *strtok(char *src, const char *pattern); + +void *memset(void *s, int c, size_t n); +void *memchr(const void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); + +#endif diff --git a/qemu/roms/SLOF/lib/libc/include/unistd.h b/qemu/roms/SLOF/lib/libc/include/unistd.h new file mode 100644 index 000000000..07210d69a --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/include/unistd.h @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _UNISTD_H +#define _UNISTD_H + +#include <stddef.h> + +typedef long ssize_t; + +extern int open(const char *name, int flags); +extern int close(int fd); +extern ssize_t read(int fd, void *buf, size_t count); +extern ssize_t write(int fd, const void *buf, size_t count); +extern ssize_t lseek(int fd, long offset, int whence); + +extern void *sbrk(int increment); + +#endif diff --git a/qemu/roms/SLOF/lib/libc/stdio/Makefile.inc b/qemu/roms/SLOF/lib/libc/stdio/Makefile.inc new file mode 100644 index 000000000..ac5302d01 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/Makefile.inc @@ -0,0 +1,23 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + + +STDIO_SRC_C = fscanf.c sprintf.c vfprintf.c vsnprintf.c vsprintf.c fprintf.c \ + printf.c setvbuf.c putc.c puts.c putchar.c scanf.c stdchnls.c \ + vfscanf.c vsscanf.c fileno.c + +STDIO_SRC_ASM = +STDIO_SRCS = $(STDIO_SRC_C:%=$(STDIOCMNDIR)/%) $(STDIO_SRC_ASM:%=$(STDIOCMNDIR)/%) +STDIO_OBJS = $(STDIO_SRC_C:%.c=%.o) $(STDIO_SRC_ASM:%.S=%.o) + +%.o : $(STDIOCMNDIR)/%.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ diff --git a/qemu/roms/SLOF/lib/libc/stdio/fileno.c b/qemu/roms/SLOF/lib/libc/stdio/fileno.c new file mode 100644 index 000000000..6e239511d --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/fileno.c @@ -0,0 +1,19 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> + +int +fileno(FILE *stream) +{ + return stream->fd; +} diff --git a/qemu/roms/SLOF/lib/libc/stdio/fprintf.c b/qemu/roms/SLOF/lib/libc/stdio/fprintf.c new file mode 100644 index 000000000..866df3934 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/fprintf.c @@ -0,0 +1,26 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "stdio.h" + + +int fprintf(FILE *stream, const char* fmt, ...) +{ + int count; + va_list ap; + + va_start(ap, fmt); + count = vfprintf(stream, fmt, ap); + va_end(ap); + + return count; +} diff --git a/qemu/roms/SLOF/lib/libc/stdio/fscanf.c b/qemu/roms/SLOF/lib/libc/stdio/fscanf.c new file mode 100644 index 000000000..321b1630a --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/fscanf.c @@ -0,0 +1,26 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> + +int +fscanf(FILE *stream, const char *fmt, ...) +{ + int count; + va_list ap; + + va_start(ap, fmt); + count = vfscanf(stream, fmt, ap); + va_end(ap); + + return count; +} diff --git a/qemu/roms/SLOF/lib/libc/stdio/printf.c b/qemu/roms/SLOF/lib/libc/stdio/printf.c new file mode 100644 index 000000000..01f4592dd --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/printf.c @@ -0,0 +1,27 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "stdio.h" + + +int printf(const char* fmt, ...) +{ + int count; + va_list ap; + + va_start(ap, fmt); + count = vfprintf(stdout, fmt, ap); + va_end(ap); + + return count; +} + diff --git a/qemu/roms/SLOF/lib/libc/stdio/putc.c b/qemu/roms/SLOF/lib/libc/stdio/putc.c new file mode 100644 index 000000000..230e9d196 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/putc.c @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "stdio.h" +#include "unistd.h" + +int +putc(int ch, FILE *stream) +{ + unsigned char outchar = ch; + + if (write(stream->fd, &outchar, 1) == 1) + return outchar; + else + return EOF; +} diff --git a/qemu/roms/SLOF/lib/libc/stdio/putchar.c b/qemu/roms/SLOF/lib/libc/stdio/putchar.c new file mode 100644 index 000000000..5c750d90a --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/putchar.c @@ -0,0 +1,21 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include "stdio.h" + + +int +putchar(int ch) +{ + return putc(ch, stdout); +} diff --git a/qemu/roms/SLOF/lib/libc/stdio/puts.c b/qemu/roms/SLOF/lib/libc/stdio/puts.c new file mode 100644 index 000000000..3f48dbfda --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/puts.c @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include "stdio.h" +#include "string.h" +#include "unistd.h" + + +int +puts(char *str) +{ + int ret; + + ret = write(stdout->fd, str, strlen(str)); + write(stdout->fd, "\r\n", 2); + + return ret; +} diff --git a/qemu/roms/SLOF/lib/libc/stdio/scanf.c b/qemu/roms/SLOF/lib/libc/stdio/scanf.c new file mode 100644 index 000000000..96b639980 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/scanf.c @@ -0,0 +1,26 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> + +int +scanf(const char *fmt, ...) +{ + int count; + va_list ap; + + va_start(ap, fmt); + count = vfscanf(stdin, fmt, ap); + va_end(ap); + + return count; +} diff --git a/qemu/roms/SLOF/lib/libc/stdio/setvbuf.c b/qemu/roms/SLOF/lib/libc/stdio/setvbuf.c new file mode 100644 index 000000000..9b62dd8ff --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/setvbuf.c @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> + +int setvbuf(FILE *stream, char *buf, int mode , size_t size) +{ + if (mode != _IONBF && mode != _IOLBF && mode != _IOFBF) + return -1; + stream->buf = buf; + stream->mode = mode; + stream->bufsiz = size; + return 0; +} + +void setbuf(FILE *stream, char *buf) +{ + setvbuf(stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ); +} diff --git a/qemu/roms/SLOF/lib/libc/stdio/sprintf.c b/qemu/roms/SLOF/lib/libc/stdio/sprintf.c new file mode 100644 index 000000000..9c4540e2e --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/sprintf.c @@ -0,0 +1,30 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> + + +int sprintf(char *buff, const char *format, ...) +{ + va_list ar; + int count; + + if ((buff==NULL) || (format==NULL)) + return(-1); + + va_start(ar, format); + count = vsprintf(buff, format, ar); + va_end(ar); + + return(count); +} + diff --git a/qemu/roms/SLOF/lib/libc/stdio/stdchnls.c b/qemu/roms/SLOF/lib/libc/stdio/stdchnls.c new file mode 100644 index 000000000..41ed958bf --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/stdchnls.c @@ -0,0 +1,23 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include "stdio.h" + +static char stdin_buffer[BUFSIZ], stdout_buffer[BUFSIZ]; + +FILE stdin_data = { .fd = 0, .mode = _IOLBF, .pos = 0, + .buf = stdin_buffer, .bufsiz = BUFSIZ }; +FILE stdout_data = { .fd = 1, .mode = _IOLBF, .pos = 0, + .buf = stdout_buffer, .bufsiz = BUFSIZ }; +FILE stderr_data = { .fd = 2, .mode = _IONBF, .pos = 0, + .buf = NULL, .bufsiz = 0 }; diff --git a/qemu/roms/SLOF/lib/libc/stdio/vfprintf.c b/qemu/roms/SLOF/lib/libc/stdio/vfprintf.c new file mode 100644 index 000000000..765feeace --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/vfprintf.c @@ -0,0 +1,27 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "stdio.h" +#include "unistd.h" + + +int vfprintf(FILE *stream, const char *fmt, va_list ap) +{ + int count; + char buffer[320]; + + count = vsnprintf(buffer, sizeof(buffer), fmt, ap); + write(stream->fd, buffer, count); + + return count; +} + diff --git a/qemu/roms/SLOF/lib/libc/stdio/vfscanf.c b/qemu/roms/SLOF/lib/libc/stdio/vfscanf.c new file mode 100644 index 000000000..4ddd210a9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/vfscanf.c @@ -0,0 +1,266 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "string.h" +#include "ctype.h" +#include "stdlib.h" +#include "stdio.h" +#include "unistd.h" + + +static int +_getc(FILE * stream) +{ + int count; + char c; + + if (stream->mode == _IONBF || stream->buf == NULL) { + if (read(stream->fd, &c, 1) == 1) + return (int) c; + else + return EOF; + } + + if (stream->pos == 0 || stream->pos >= BUFSIZ || + stream->buf[stream->pos] == '\0') { + count = read(stream->fd, stream->buf, BUFSIZ); + if (count < 0) + count = 0; + if (count < BUFSIZ) + stream->buf[count] = '\0'; + stream->pos = 0; + } + + return stream->buf[stream->pos++]; +} + +static void +_ungetc(int ch, FILE * stream) +{ + if (stream->mode != _IONBF && stream->pos > 0) + stream->pos--; +} + +static int +_is_voidage(int ch) +{ + if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\0') + return 1; + else + return 0; +} + + +static int +_scanf(FILE * stream, const char *fmt, va_list * ap) +{ + int i = 0; + int length = 0; + + fmt++; + + while (*fmt != '\0') { + + char tbuf[256]; + char ch; + + switch (*fmt) { + case 'd': + case 'i': + ch = _getc(stream); + if (length == 0) { + while (!_is_voidage(ch) && isdigit(ch)) { + tbuf[i] = ch; + ch = _getc(stream); + i++; + } + } else { + while (!_is_voidage(ch) && i < length + && isdigit(ch)) { + tbuf[i] = ch; + ch = _getc(stream); + i++; + } + } + /* We tried to understand what this is good for... + * but we did not. We know for sure that it does not + * work on SLOF if this is active. */ + /* _ungetc(ch, stream); */ + tbuf[i] = '\0'; + + /* ch = _getc(stream); */ + if (!_is_voidage(ch)) + _ungetc(ch, stream); + + if (strlen(tbuf) == 0) + return 0; + + *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 10); + break; + case 'X': + case 'x': + ch = _getc(stream); + if (length == 0) { + while (!_is_voidage(ch) && isxdigit(ch)) { + tbuf[i] = ch; + ch = _getc(stream); + i++; + } + } else { + while (!_is_voidage(ch) && i < length + && isxdigit(ch)) { + tbuf[i] = ch; + ch = _getc(stream); + i++; + } + } + /* _ungetc(ch, stream); */ + tbuf[i] = '\0'; + + /* ch = _getc(stream); */ + if (!_is_voidage(ch)) + _ungetc(ch, stream); + + if (strlen(tbuf) == 0) + return 0; + + *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 16); + break; + case 'O': + case 'o': + ch = _getc(stream); + if (length == 0) { + while (!_is_voidage(ch) + && !(ch < '0' || ch > '7')) { + tbuf[i] = ch; + ch = _getc(stream); + i++; + } + } else { + while (!_is_voidage(ch) && i < length + && !(ch < '0' || ch > '7')) { + tbuf[i] = ch; + ch = _getc(stream); + i++; + } + } + /* _ungetc(ch, stream); */ + tbuf[i] = '\0'; + + /* ch = _getc(stream); */ + if (!_is_voidage(ch)) + _ungetc(ch, stream); + + if (strlen(tbuf) == 0) + return 0; + + *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 8); + break; + case 'c': + ch = _getc(stream); + while (_is_voidage(ch)) + ch = _getc(stream); + + *(va_arg(*ap, char *)) = ch; + + ch = _getc(stream); + if (!_is_voidage(ch)) + _ungetc(ch, stream); + + break; + case 's': + ch = _getc(stream); + if (length == 0) { + while (!_is_voidage(ch)) { + tbuf[i] = ch; + ch = _getc(stream); + i++; + } + } else { + while (!_is_voidage(ch) && i < length) { + tbuf[i] = ch; + ch = _getc(stream); + i++; + } + } + /* _ungetc(ch, stream); */ + tbuf[i] = '\0'; + + /* ch = _getc(stream); */ + if (!_is_voidage(ch)) + _ungetc(ch, stream); + + strcpy(va_arg(*ap, char *), tbuf); + break; + default: + if (*fmt >= '0' && *fmt <= '9') + length += *fmt - '0'; + break; + } + fmt++; + } + + return 1; +} + + + +int +vfscanf(FILE * stream, const char *fmt, va_list ap) +{ + int args = 0; + + while (*fmt != '\0') { + + if (*fmt == '%') { + + char formstr[20]; + int i = 0; + + do { + formstr[i] = *fmt; + fmt++; + i++; + } while (! + (*fmt == 'd' || *fmt == 'i' || *fmt == 'x' + || *fmt == 'X' || *fmt == 'p' || *fmt == 'c' + || *fmt == 's' || *fmt == '%' || *fmt == 'O' + || *fmt == 'o')); + formstr[i++] = *fmt; + formstr[i] = '\0'; + if (*fmt != '%') { + if (_scanf(stream, formstr, &ap) <= 0) + return args; + else + args++; + } + + } + + fmt++; + + } + + return args; +} + +int +getc(FILE * stream) +{ + return _getc(stream); +} + +int +getchar(void) +{ + return _getc(stdin); +} diff --git a/qemu/roms/SLOF/lib/libc/stdio/vsnprintf.c b/qemu/roms/SLOF/lib/libc/stdio/vsnprintf.c new file mode 100644 index 000000000..e78fb3d8e --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/vsnprintf.c @@ -0,0 +1,242 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" + +const static unsigned long long convert[] = { + 0x0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFFFFULL, 0xFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL +}; + + + +static int +print_itoa(char **buffer,unsigned long value, unsigned short int base) +{ + const char zeichen[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + static char sign = 0; + + if(base <= 2 || base > 16) + return 0; + + if(value < 0) { + sign = 1; + value *= -1; + } + + if(value < base) { + if(sign) { + **buffer = '-'; + *buffer += 1; + sign = 0; + } + **buffer = zeichen[value]; + *buffer += 1; + } else { + print_itoa(buffer, value / base, base); + **buffer = zeichen[(value % base)]; + *buffer += 1; + } + + return 1; +} + + +static unsigned int +print_intlen(unsigned long value, unsigned short int base) +{ + int i = 0; + + while(value > 0) { + value /= base; + i++; + } + if(i == 0) i = 1; + return i; +} + + +static int +print_fill(char **buffer, char *sizec, unsigned long size, unsigned short int base, char c, int optlen) +{ + int i, sizei, len; + + sizei = strtoul(sizec, NULL, 10); + len = print_intlen(size, base) + optlen; + if(sizei > len) { + for(i = 0; i < (sizei - len); i++) { + **buffer = c; + *buffer += 1; + } + } + + return 0; +} + + +static int +print_format(char **buffer, const char *format, void *var) +{ + unsigned long start; + unsigned int i = 0, sizei = 0, len = 0, length_mod = sizeof(int); + unsigned long value = 0; + unsigned long signBit; + char *form, sizec[32]; + char sign = ' '; + + form = (char *) format; + start = (unsigned long) *buffer; + + form++; + if(*form == '0' || *form == '.') { + sign = '0'; + form++; + } + + while(*form != '\0') { + switch(*form) { + case 'u': + case 'd': + case 'i': + sizec[i] = '\0'; + value = (unsigned long) var; + signBit = 0x1ULL << (length_mod * 8 - 1); + if (signBit & value) { + **buffer = '-'; + *buffer += 1; + value = (-(unsigned long)value) & convert[length_mod]; + } + print_fill(buffer, sizec, value, 10, sign, 0); + print_itoa(buffer, value, 10); + break; + case 'X': + case 'x': + sizec[i] = '\0'; + value = (unsigned long) var & convert[length_mod]; + print_fill(buffer, sizec, value, 16, sign, 0); + print_itoa(buffer, value, 16); + break; + case 'O': + case 'o': + sizec[i] = '\0'; + value = (long int) var & convert[length_mod]; + print_fill(buffer, sizec, value, 8, sign, 0); + print_itoa(buffer, value, 8); + break; + case 'p': + sizec[i] = '\0'; + print_fill(buffer, sizec, (unsigned long) var, 16, ' ', 2); + **buffer = '0'; + *buffer += 1; + **buffer = 'x'; + *buffer += 1; + print_itoa(buffer,(unsigned long) var, 16); + break; + case 'c': + sizec[i] = '\0'; + print_fill(buffer, sizec, 1, 10, ' ', 0); + **buffer = (unsigned long) var; + *buffer += 1; + break; + case 's': + sizec[i] = '\0'; + sizei = strtoul(sizec, NULL, 10); + len = strlen((char *) var); + if(sizei > len) { + for(i = 0; i < (sizei - len); i++) { + **buffer = ' '; + *buffer += 1; + } + } + for(i = 0; i < strlen((char *) var); i++) { + **buffer = ((char *) var)[i]; + *buffer += 1; + } + break; + case 'l': + form++; + if(*form == 'l') { + length_mod = sizeof(long long int); + } else { + form--; + length_mod = sizeof(long int); + } + break; + case 'h': + form++; + if(*form == 'h') { + length_mod = sizeof(signed char); + } else { + form--; + length_mod = sizeof(short int); + } + break; + default: + if(*form >= '0' && *form <= '9') + sizec[i++] = *form; + } + form++; + } + + + return (long int) (*buffer - start); +} + + +/* + * The vsnprintf function prints a formated strings into a buffer. + * BUG: buffer size checking does not fully work yet + */ +int +vsnprintf(char *buffer, size_t bufsize, const char *format, va_list arg) +{ + char *ptr, *bstart; + + bstart = buffer; + ptr = (char *) format; + + while(*ptr != '\0' && (buffer - bstart) < bufsize) + { + if(*ptr == '%') { + char formstr[20]; + int i=0; + + do { + formstr[i] = *ptr; + ptr++; + i++; + } while(!(*ptr == 'd' || *ptr == 'i' || *ptr == 'u' || *ptr == 'x' || *ptr == 'X' + || *ptr == 'p' || *ptr == 'c' || *ptr == 's' || *ptr == '%' + || *ptr == 'O' || *ptr == 'o' )); + formstr[i++] = *ptr; + formstr[i] = '\0'; + if(*ptr == '%') { + *buffer++ = '%'; + } else { + print_format(&buffer, formstr, va_arg(arg, void *)); + } + ptr++; + } else { + + *buffer = *ptr; + + buffer++; + ptr++; + } + } + + *buffer = '\0'; + + return (buffer - bstart); +} diff --git a/qemu/roms/SLOF/lib/libc/stdio/vsprintf.c b/qemu/roms/SLOF/lib/libc/stdio/vsprintf.c new file mode 100644 index 000000000..0dfd737bc --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/vsprintf.c @@ -0,0 +1,19 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "stdio.h" + +int +vsprintf(char *buffer, const char *format, va_list arg) +{ + return vsnprintf(buffer, 0x7fffffff, format, arg); +} diff --git a/qemu/roms/SLOF/lib/libc/stdio/vsscanf.c b/qemu/roms/SLOF/lib/libc/stdio/vsscanf.c new file mode 100644 index 000000000..b9603e98b --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdio/vsscanf.c @@ -0,0 +1,131 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" + + +static void +_scanf(const char **buffer, const char *fmt, va_list *ap) +{ + int i; + int length = 0; + + fmt++; + + while(*fmt != '\0') { + + char tbuf[256]; + + switch(*fmt) { + case 'd': + case 'i': + if(length == 0) length = 256; + + for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) { + tbuf[i] = **buffer; + *buffer += 1; + } + tbuf[i] = '\0'; + + *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 10); + break; + case 'X': + case 'x': + if(length == 0) length = 256; + + for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) { + tbuf[i] = **buffer; + *buffer += 1; + } + tbuf[i] = '\0'; + + *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 16); + break; + case 'O': + case 'o': + if(length == 0) length = 256; + + for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) { + tbuf[i] = **buffer; + *buffer += 1; + } + tbuf[i] = '\0'; + + *(va_arg(*ap, int *)) = strtol(tbuf, NULL, 8); + break; + case 'c': + *(va_arg(*ap, char *)) = **buffer; + *buffer += 1; + if(length > 1) + for(i = 1; i < length; i++) + *buffer += 1; + break; + case 's': + if(length == 0) length = 256; + + for(i = 0; **buffer != ' ' && **buffer != '\t' && **buffer != '\n' && i < length; i++) { + tbuf[i] = **buffer; + *buffer += 1; + } + + tbuf[i] = '\0'; + + strcpy(va_arg(*ap, char *), tbuf); + break; + default: + if(*fmt >= '0' && *fmt <= '9') + length += *fmt - '0'; + break; + } + fmt++; + } + +} + + +int +vsscanf(const char *buffer, const char *fmt, va_list ap) +{ + + while(*fmt != '\0') { + + if(*fmt == '%') { + + char formstr[20]; + int i=0; + + do { + formstr[i] = *fmt; + fmt++; + i++; + } while(!(*fmt == 'd' || *fmt == 'i' || *fmt == 'x' || *fmt == 'X' + || *fmt == 'p' || *fmt == 'c' || *fmt == 's' || *fmt == '%' + || *fmt == 'O' || *fmt == 'o' )); + formstr[i++] = *fmt; + formstr[i] = '\0'; + if(*fmt != '%') { + while(*buffer == ' ' || *buffer == '\t' || *buffer == '\n') + buffer++; + _scanf(&buffer, formstr, &ap); + } + + } + + fmt++; + + } + + return 0; +} + diff --git a/qemu/roms/SLOF/lib/libc/stdlib/Makefile.inc b/qemu/roms/SLOF/lib/libc/stdlib/Makefile.inc new file mode 100644 index 000000000..702f6d7cf --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/Makefile.inc @@ -0,0 +1,22 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + + +STDLIB_SRC_C = error.c atoi.c atol.c strtoul.c strtol.c rand.c \ + malloc.c memalign.c realloc.c free.c + +STDLIB_SRC_ASM = +STDLIB_SRCS = $(STDLIB_SRC_C:%=$(STDLIBCMNDIR)/%) $(STDLIB_SRC_ASM:%=$(STDLIBCMNDIR)/%) +STDLIB_OBJS = $(STDLIB_SRC_C:%.c=%.o) $(STDLIB_SRC_ASM:%.S=%.o) + +%.o : $(STDLIBCMNDIR)/%.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ diff --git a/qemu/roms/SLOF/lib/libc/stdlib/atoi.c b/qemu/roms/SLOF/lib/libc/stdlib/atoi.c new file mode 100644 index 000000000..d2fb33b88 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/atoi.c @@ -0,0 +1,18 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdlib.h> + +int atoi(const char *str) +{ + return strtol(str, NULL, 0); +} diff --git a/qemu/roms/SLOF/lib/libc/stdlib/atol.c b/qemu/roms/SLOF/lib/libc/stdlib/atol.c new file mode 100644 index 000000000..a6aa47ba5 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/atol.c @@ -0,0 +1,18 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdlib.h> + +long atol(const char *str) +{ + return strtol(str, NULL, 0); +} diff --git a/qemu/roms/SLOF/lib/libc/stdlib/error.c b/qemu/roms/SLOF/lib/libc/stdlib/error.c new file mode 100644 index 000000000..81020ca55 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/error.c @@ -0,0 +1,15 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +int errno; + diff --git a/qemu/roms/SLOF/lib/libc/stdlib/free.c b/qemu/roms/SLOF/lib/libc/stdlib/free.c new file mode 100644 index 000000000..900545099 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/free.c @@ -0,0 +1,26 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include "stdlib.h" +#include "malloc_defs.h" + +void +free(void *ptr) +{ + struct chunk *header; + + header = (struct chunk *) ptr; + header--; + header->inuse = 0; + +} diff --git a/qemu/roms/SLOF/lib/libc/stdlib/malloc.c b/qemu/roms/SLOF/lib/libc/stdlib/malloc.c new file mode 100644 index 000000000..b2a3138eb --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/malloc.c @@ -0,0 +1,157 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include "stddef.h" +#include "stdlib.h" +#include "unistd.h" +#include "malloc_defs.h" + + +static int clean(void); + + +/* act points to the end of the initialized heap and the start of uninitialized heap */ +static char *act; + +/* Pointers to start and end of heap: */ +static char *heap_start, *heap_end; + + +/* + * Standard malloc function + */ +void * +malloc(size_t size) +{ + char *header; + void *data; + size_t blksize; /* size of memory block including the chunk */ + + blksize = size + sizeof(struct chunk); + + /* has malloc been called for the first time? */ + if (act == 0) { + size_t initsize; + /* add some space so we have a good initial playground */ + initsize = (blksize + 0x1000) & ~0x0fff; + /* get initial memory region with sbrk() */ + heap_start = sbrk(initsize); + if (heap_start == (void*)-1) + return NULL; + heap_end = heap_start + initsize; + act = heap_start; + } + + header = act; + data = act + sizeof(struct chunk); + + /* Check if there is space left in the uninitialized part of the heap */ + if (act + blksize > heap_end) { + //search at begin of heap + header = heap_start; + + while ((((struct chunk *) header)->inuse != 0 + || ((struct chunk *) header)->length < size) + && header < act) { + header = header + sizeof(struct chunk) + + ((struct chunk *) header)->length; + } + + // check if heap is full + if (header >= act) { + if (clean()) { + // merging of free blocks succeeded, so try again + return malloc(size); + } else if (sbrk(blksize) == heap_end) { + // succeeded to get more memory, so try again + heap_end += blksize; + return malloc(size); + } else { + // No more memory available + return 0; + } + } + + // Check if we need to split this memory block into two + if (((struct chunk *) header)->length > blksize) { + //available memory is too big + int alt; + + alt = ((struct chunk *) header)->length; + ((struct chunk *) header)->inuse = 1; + ((struct chunk *) header)->length = size; + data = header + sizeof(struct chunk); + + //mark the rest of the heap + header = data + size; + ((struct chunk *) header)->inuse = 0; + ((struct chunk *) header)->length = + alt - blksize; + } else { + //new memory matched exactly in available memory + ((struct chunk *) header)->inuse = 1; + data = header + sizeof(struct chunk); + } + + } else { + + ((struct chunk *) header)->inuse = 1; + ((struct chunk *) header)->length = size; + + act += blksize; + } + + return data; +} + + +/* + * Merge free memory blocks in initialized heap if possible + */ +static int +clean(void) +{ + char *header; + char *firstfree = 0; + char check = 0; + + header = heap_start; + //if (act == 0) // This should never happen + // act = heap_end; + + while (header < act) { + + if (((struct chunk *) header)->inuse == 0) { + if (firstfree == 0) { + /* First free block in a row, only save address */ + firstfree = header; + + } else { + /* more than one free block in a row, merge them! */ + ((struct chunk *) firstfree)->length += + ((struct chunk *) header)->length + + sizeof(struct chunk); + check = 1; + } + } else { + firstfree = 0; + + } + + header = header + sizeof(struct chunk) + + ((struct chunk *) header)->length; + + } + + return check; +} diff --git a/qemu/roms/SLOF/lib/libc/stdlib/malloc_defs.h b/qemu/roms/SLOF/lib/libc/stdlib/malloc_defs.h new file mode 100644 index 000000000..19330267e --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/malloc_defs.h @@ -0,0 +1,16 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +struct chunk { + unsigned inuse : 4; + unsigned length : 28; +} __attribute__((packed)); diff --git a/qemu/roms/SLOF/lib/libc/stdlib/memalign.c b/qemu/roms/SLOF/lib/libc/stdlib/memalign.c new file mode 100644 index 000000000..3b678aaf5 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/memalign.c @@ -0,0 +1,26 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include "stdlib.h" + + +void * +memalign(size_t blocksize, size_t bytes) +{ + void *x; + + x = malloc(bytes + blocksize); + x = (void *) (((unsigned long) x + blocksize - 1) & ~(blocksize - 1)); + + return (void *) x; +} diff --git a/qemu/roms/SLOF/lib/libc/stdlib/rand.c b/qemu/roms/SLOF/lib/libc/stdlib/rand.c new file mode 100644 index 000000000..87e3efd29 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/rand.c @@ -0,0 +1,24 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdlib.h> + + +static unsigned long _rand = 1; + +int +rand(void) +{ + _rand = _rand * 25364735 + 34563; + + return ((unsigned int) (_rand << 16) & RAND_MAX); +} diff --git a/qemu/roms/SLOF/lib/libc/stdlib/realloc.c b/qemu/roms/SLOF/lib/libc/stdlib/realloc.c new file mode 100644 index 000000000..652e90077 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/realloc.c @@ -0,0 +1,40 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + + +#include "stdlib.h" +#include "string.h" +#include "malloc_defs.h" + +void * +realloc(void *ptr, size_t size) +{ + struct chunk *header; + char *newptr, *start; + + header = (struct chunk *) ptr; + header--; + + if (size <= header->length) + return ptr; + + newptr = (char *) malloc(size); + if (newptr == NULL) + return 0; + + start = newptr; + memcpy((void *) newptr, (const void *) ptr, header->length); + + header->inuse = 0; + + return start; +} diff --git a/qemu/roms/SLOF/lib/libc/stdlib/strtol.c b/qemu/roms/SLOF/lib/libc/stdlib/strtol.c new file mode 100644 index 000000000..474597a23 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/strtol.c @@ -0,0 +1,115 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdlib.h> + +long int strtol(const char *S, char **PTR,int BASE) +{ + long rval = 0; + short int negative = 0; + short int digit; + // *PTR is S, unless PTR is NULL, in which case i override it with my own ptr + char* ptr; + if (PTR == 0) + { + //override + PTR = &ptr; + } + // i use PTR to advance through the string + *PTR = (char *) S; + //check if BASE is ok + if ((BASE < 0) || BASE > 36) + { + return 0; + } + // ignore white space at beginning of S + while ((**PTR == ' ') + || (**PTR == '\t') + || (**PTR == '\n') + || (**PTR == '\r') + ) + { + (*PTR)++; + } + // check if S starts with "-" in which case the return value is negative + if (**PTR == '-') + { + negative = 1; + (*PTR)++; + } + // if BASE is 0... determine the base from the first chars... + if (BASE == 0) + { + // if S starts with "0x", BASE = 16, else 10 + if ((**PTR == '0') && (*((*PTR)+1) == 'x')) + { + BASE = 16; + (*PTR)++; + (*PTR)++; + } + else + { + BASE = 10; + } + } + if (BASE == 16) + { + // S may start with "0x" + if ((**PTR == '0') && (*((*PTR)+1) == 'x')) + { + (*PTR)++; + (*PTR)++; + } + } + //until end of string + while (**PTR) + { + if (((**PTR) >= '0') && ((**PTR) <= '9')) + { + //digit (0..9) + digit = **PTR - '0'; + } + else if (((**PTR) >= 'a') && ((**PTR) <='z')) + { + //alphanumeric digit lowercase(a (10) .. z (35) ) + digit = (**PTR - 'a') + 10; + } + else if (((**PTR) >= 'A') && ((**PTR) <='Z')) + { + //alphanumeric digit uppercase(a (10) .. z (35) ) + digit = (**PTR - 'A') + 10; + } + else + { + //end of parseable number reached... + break; + } + if (digit < BASE) + { + rval = (rval * BASE) + digit; + } + else + { + //digit found, but its too big for current base + //end of parseable number reached... + break; + } + //next... + (*PTR)++; + } + if (negative) + { + return rval * -1; + } + //else + return rval; +} diff --git a/qemu/roms/SLOF/lib/libc/stdlib/strtoul.c b/qemu/roms/SLOF/lib/libc/stdlib/strtoul.c new file mode 100644 index 000000000..754e7db4b --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/stdlib/strtoul.c @@ -0,0 +1,105 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdlib.h> + +unsigned long int strtoul(const char *S, char **PTR,int BASE) +{ + unsigned long rval = 0; + short int digit; + // *PTR is S, unless PTR is NULL, in which case i override it with my own ptr + char* ptr; + if (PTR == 0) + { + //override + PTR = &ptr; + } + // i use PTR to advance through the string + *PTR = (char *) S; + //check if BASE is ok + if ((BASE < 0) || BASE > 36) + { + return 0; + } + // ignore white space at beginning of S + while ((**PTR == ' ') + || (**PTR == '\t') + || (**PTR == '\n') + || (**PTR == '\r') + ) + { + (*PTR)++; + } + // if BASE is 0... determine the base from the first chars... + if (BASE == 0) + { + // if S starts with "0x", BASE = 16, else 10 + if ((**PTR == '0') && (*((*PTR)+1) == 'x')) + { + BASE = 16; + (*PTR)++; + (*PTR)++; + } + else + { + BASE = 10; + } + } + if (BASE == 16) + { + // S may start with "0x" + if ((**PTR == '0') && (*((*PTR)+1) == 'x')) + { + (*PTR)++; + (*PTR)++; + } + } + //until end of string + while (**PTR) + { + if (((**PTR) >= '0') && ((**PTR) <='9')) + { + //digit (0..9) + digit = **PTR - '0'; + } + else if (((**PTR) >= 'a') && ((**PTR) <='z')) + { + //alphanumeric digit lowercase(a (10) .. z (35) ) + digit = (**PTR - 'a') + 10; + } + else if (((**PTR) >= 'A') && ((**PTR) <='Z')) + { + //alphanumeric digit uppercase(a (10) .. z (35) ) + digit = (**PTR - 'A') + 10; + } + else + { + //end of parseable number reached... + break; + } + if (digit < BASE) + { + rval = (rval * BASE) + digit; + } + else + { + //digit found, but its too big for current base + //end of parseable number reached... + break; + } + //next... + (*PTR)++; + } + //done + return rval; +} + diff --git a/qemu/roms/SLOF/lib/libc/string/Makefile.inc b/qemu/roms/SLOF/lib/libc/string/Makefile.inc new file mode 100644 index 000000000..7ccf3c405 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/Makefile.inc @@ -0,0 +1,22 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + + +STRING_SRC_C = strcat.c strchr.c strcmp.c strcpy.c strlen.c strncmp.c \ + strncpy.c strstr.c memset.c memcpy.c memmove.c memchr.c \ + memcmp.c strcasecmp.c strncasecmp.c strtok.c +STRING_SRC_ASM = +STRING_SRCS = $(STRING_SRC_C:%=$(STRINGCMNDIR)/%) $(STRING_SRC_ASM:%=$(STRINGCMNDIR)/%) +STRING_OBJS = $(STRING_SRC_C:%.c=%.o) $(STRING_SRC_ASM:%.S=%.o) + +%.o : $(STRINGCMNDIR)/%.c + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ diff --git a/qemu/roms/SLOF/lib/libc/string/memchr.c b/qemu/roms/SLOF/lib/libc/string/memchr.c new file mode 100644 index 000000000..c3fe751c6 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/memchr.c @@ -0,0 +1,29 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "string.h" + + +void * +memchr(const void *ptr, int c, size_t n) +{ + unsigned char ch = (unsigned char)c; + const unsigned char *p = ptr; + + while (n-- > 0) { + if (*p == ch) + return (void *)p; + p += 1; + } + + return NULL; +} diff --git a/qemu/roms/SLOF/lib/libc/string/memcmp.c b/qemu/roms/SLOF/lib/libc/string/memcmp.c new file mode 100644 index 000000000..3b69cefb9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/memcmp.c @@ -0,0 +1,30 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "string.h" + + +int +memcmp(const void *ptr1, const void *ptr2, size_t n) +{ + const unsigned char *p1 = ptr1; + const unsigned char *p2 = ptr2; + + while (n-- > 0) { + if (*p1 != *p2) + return (*p1 - *p2); + p1 += 1; + p2 += 1; + } + + return 0; +} diff --git a/qemu/roms/SLOF/lib/libc/string/memcpy.c b/qemu/roms/SLOF/lib/libc/string/memcpy.c new file mode 100644 index 000000000..00f419b80 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/memcpy.c @@ -0,0 +1,27 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "string.h" + +void * +memcpy(void *dest, const void *src, size_t n) +{ + char *cdest; + const char *csrc = src; + + cdest = dest; + while (n-- > 0) { + *cdest++ = *csrc++; + } + + return dest; +} diff --git a/qemu/roms/SLOF/lib/libc/string/memmove.c b/qemu/roms/SLOF/lib/libc/string/memmove.c new file mode 100644 index 000000000..3acf1a973 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/memmove.c @@ -0,0 +1,42 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "string.h" + + +void * +memmove(void *dest, const void *src, size_t n) +{ + char *cdest; + const char *csrc; + int i; + + /* Do the buffers overlap in a bad way? */ + if (src < dest && src + n >= dest) { + /* Copy from end to start */ + cdest = dest + n - 1; + csrc = src + n - 1; + for (i = 0; i < n; i++) { + *cdest-- = *csrc--; + } + } + else { + /* Normal copy is possible */ + cdest = dest; + csrc = src; + for (i = 0; i < n; i++) { + *cdest++ = *csrc++; + } + } + + return dest; +} diff --git a/qemu/roms/SLOF/lib/libc/string/memset.c b/qemu/roms/SLOF/lib/libc/string/memset.c new file mode 100644 index 000000000..f8dfbf524 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/memset.c @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "string.h" + +void * +memset(void *dest, int c, size_t size) +{ + unsigned char *d = (unsigned char *)dest; + + while (size-- > 0) { + *d++ = (unsigned char)c; + } + + return dest; +} diff --git a/qemu/roms/SLOF/lib/libc/string/strcasecmp.c b/qemu/roms/SLOF/lib/libc/string/strcasecmp.c new file mode 100644 index 000000000..f75294fb9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/strcasecmp.c @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include <ctype.h> + +int +strcasecmp(const char *s1, const char *s2) +{ + while (*s1 != 0 && *s2 != 0) { + if (toupper(*s1) != toupper(*s2)) + break; + ++s1; + ++s2; + } + + return *s1 - *s2; +} + diff --git a/qemu/roms/SLOF/lib/libc/string/strcat.c b/qemu/roms/SLOF/lib/libc/string/strcat.c new file mode 100644 index 000000000..eb597a025 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/strcat.c @@ -0,0 +1,24 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> + +char * +strcat(char *dst, const char *src) +{ + int p; + + p = strlen(dst); + strcpy(&dst[p], src); + + return dst; +} diff --git a/qemu/roms/SLOF/lib/libc/string/strchr.c b/qemu/roms/SLOF/lib/libc/string/strchr.c new file mode 100644 index 000000000..528a319c9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/strchr.c @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> + +char * +strchr(const char *s, int c) +{ + char cb = c; + + while (*s != 0) { + if (*s == cb) { + return (char *)s; + } + s += 1; + } + + return NULL; +} diff --git a/qemu/roms/SLOF/lib/libc/string/strcmp.c b/qemu/roms/SLOF/lib/libc/string/strcmp.c new file mode 100644 index 000000000..48eaed246 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/strcmp.c @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> + + +int +strcmp(const char *s1, const char *s2) +{ + while (*s1 != 0 && *s2 != 0) { + if (*s1 != *s2) + break; + s1 += 1; + s2 += 1; + } + + return *s1 - *s2; +} + diff --git a/qemu/roms/SLOF/lib/libc/string/strcpy.c b/qemu/roms/SLOF/lib/libc/string/strcpy.c new file mode 100644 index 000000000..48eb62cb5 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/strcpy.c @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> + +char * +strcpy(char *dst, const char *src) +{ + char *ptr = dst; + + do { + *ptr++ = *src; + } while (*src++ != 0); + + return dst; +} diff --git a/qemu/roms/SLOF/lib/libc/string/strlen.c b/qemu/roms/SLOF/lib/libc/string/strlen.c new file mode 100644 index 000000000..37a1b7812 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/strlen.c @@ -0,0 +1,27 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> + +size_t +strlen(const char *s) +{ + int len = 0; + + while (*s != 0) { + len += 1; + s += 1; + } + + return len; +} + diff --git a/qemu/roms/SLOF/lib/libc/string/strncasecmp.c b/qemu/roms/SLOF/lib/libc/string/strncasecmp.c new file mode 100644 index 000000000..4140931e3 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/strncasecmp.c @@ -0,0 +1,32 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include <ctype.h> + + +int +strncasecmp(const char *s1, const char *s2, size_t n) +{ + if (n < 1) + return 0; + + while (*s1 != 0 && *s2 != 0 && --n > 0) { + if (toupper(*s1) != toupper(*s2)) + break; + ++s1; + ++s2; + } + + return toupper(*s1) - toupper(*s2); +} + diff --git a/qemu/roms/SLOF/lib/libc/string/strncmp.c b/qemu/roms/SLOF/lib/libc/string/strncmp.c new file mode 100644 index 000000000..a886736a9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/strncmp.c @@ -0,0 +1,31 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> + + +int +strncmp(const char *s1, const char *s2, size_t n) +{ + if (n < 1) + return 0; + + while (*s1 != 0 && *s2 != 0 && --n > 0) { + if (*s1 != *s2) + break; + s1 += 1; + s2 += 1; + } + + return *s1 - *s2; +} + diff --git a/qemu/roms/SLOF/lib/libc/string/strncpy.c b/qemu/roms/SLOF/lib/libc/string/strncpy.c new file mode 100644 index 000000000..0f41f93c9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/strncpy.c @@ -0,0 +1,33 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> + +char * +strncpy(char *dst, const char *src, size_t n) +{ + char *ret = dst; + + /* Copy string */ + while (*src != 0 && n > 0) { + *dst++ = *src++; + n -= 1; + } + + /* strncpy always clears the rest of destination string... */ + while (n > 0) { + *dst++ = 0; + n -= 1; + } + + return ret; +} diff --git a/qemu/roms/SLOF/lib/libc/string/strstr.c b/qemu/roms/SLOF/lib/libc/string/strstr.c new file mode 100644 index 000000000..3e090d2c5 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/strstr.c @@ -0,0 +1,37 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> + +char * +strstr(const char *hay, const char *needle) +{ + char *pos; + int hlen, nlen; + + if (hay == NULL || needle == NULL) + return NULL; + + hlen = strlen(hay); + nlen = strlen(needle); + if (nlen < 1) + return (char *)hay; + + for (pos = (char *)hay; pos < hay + hlen; pos++) { + if (strncmp(pos, needle, nlen) == 0) { + return pos; + } + } + + return NULL; +} + diff --git a/qemu/roms/SLOF/lib/libc/string/strtok.c b/qemu/roms/SLOF/lib/libc/string/strtok.c new file mode 100644 index 000000000..665c08db6 --- /dev/null +++ b/qemu/roms/SLOF/lib/libc/string/strtok.c @@ -0,0 +1,45 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> + +char * +strtok(char *src, const char *pattern) +{ + static char *nxtTok; + char *retVal = NULL; + + if (!src) + src = nxtTok; + + while (*src) { + const char *pp = pattern; + while (*pp) { + if (*pp == *src) { + break; + } + pp++; + } + if (!*pp) { + if (!retVal) + retVal = src; + else if (!src[-1]) + break; + } else + *src = '\0'; + src++; + } + + nxtTok = src; + + return retVal; +} diff --git a/qemu/roms/SLOF/lib/libe1k/Makefile b/qemu/roms/SLOF/lib/libe1k/Makefile new file mode 100644 index 000000000..0c3169f02 --- /dev/null +++ b/qemu/roms/SLOF/lib/libe1k/Makefile @@ -0,0 +1,51 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008, 2013 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \ + -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) + +LDFLAGS = -nostdlib + +TARGET = ../libe1k.a + + +all: $(TARGET) Makefile.dep + +SRCS = e1k.c + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep diff --git a/qemu/roms/SLOF/lib/libe1k/e1k.c b/qemu/roms/SLOF/lib/libe1k/e1k.c new file mode 100644 index 000000000..4dd7d2eb9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libe1k/e1k.c @@ -0,0 +1,1000 @@ +/****************************************************************************** + * Copyright (c) 2007, 2011, 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * e1000 Gigabit Ethernet Driver for SLOF + * + * Reference: + * PCI/PCI-X Family of Gigabit Ethernet Controllers + * Software Developer's Manual Rev. 3.3, Intel, December 2006 + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <byteorder.h> +#include <helpers.h> +#include <netdriver.h> +#include "e1k.h" + +/* + * local defines + ****************************************************************************** + */ +#define E1K_NUM_RX_DESC 128 // do not change +#define E1K_NUM_TX_DESC 128 // do not change +#define E1K_BUF_SIZE 2096 // do not change + +#define NUM_MAC_ADDR 16 // number of mac address register pairs +#define EEPROM_MAC_OFFS 0 // position of mac address in eeprom + +/* + * local types + ****************************************************************************** + */ +typedef struct { + uint32_t m_dev_u32; + uint64_t m_devmsk_u64; + char *m_name; +} e1k_dev_t; + +/* + * e1k common data structures + */ + +/* + * transmit buffer descriptor + */ +typedef struct { + uint64_t m_buffer_u64; + uint16_t m_len_u16; + uint8_t m_cso_u08; + uint8_t m_cmd_u08; + uint8_t m_sta_u08; + uint8_t m_css_u08; + uint16_t m_spe_u16; +} __attribute__ ((packed)) e1k_tx_desc_st; + + +/* + * receive buffer descriptor + */ +typedef struct { + uint64_t m_buffer_u64; + uint16_t m_len_u16; + uint16_t m_csm_u16; + uint8_t m_sta_u08; + uint8_t m_err_u08; + uint16_t m_spe_u16; +} __attribute__ ((packed)) e1k_rx_desc_st; + +/* + * e1k device structure + */ +typedef struct { + /* + * device identification mask + */ + uint64_t m_device_u64; + + /* + * memory mapped base address of NIC + */ + uint64_t m_baseaddr_u64; + + /* + * transmit & receive rings + * must be 16 byte aligned + */ + e1k_tx_desc_st m_tx_ring_pst[E1K_NUM_TX_DESC]; + e1k_rx_desc_st m_rx_ring_pst[E1K_NUM_RX_DESC]; + + /* + * transmit & receive buffers + * must be 16 byte aligned + */ + uint8_t m_tx_buffer_pu08[E1K_NUM_TX_DESC][E1K_BUF_SIZE]; + uint8_t m_rx_buffer_pu08[E1K_NUM_RX_DESC][E1K_BUF_SIZE]; + + /* + * next receive descriptor index + */ + uint32_t m_rx_next_u32; + + /* + * command register storage + */ + uint16_t m_com_r_u16; + + /* + * padding to make the size of the structure a multiple of 16 byte + */ + uint16_t m_pad16_u16; + uint64_t m_pad64_u32; + +} __attribute__ ((packed)) e1k_st; + +/* + * local constants + ****************************************************************************** + */ +#define E1K_82540 ((uint64_t) 0x1) +#define E1K_82541 ((uint64_t) 0x2) +#define E1K_82544 ((uint64_t) 0x4) +#define E1K_82545 ((uint64_t) 0x8) +#define E1K_82546 ((uint64_t) 0x10) +#define E1K_82547 ((uint64_t) 0x20) + +#define IS_82541 ((m_e1k.m_device_u64 & E1K_82541) != 0) +#define IS_82546 ((m_e1k.m_device_u64 & E1K_82546) != 0) +#define IS_82547 ((m_e1k.m_device_u64 & E1K_82547) != 0) + +static const e1k_dev_t e1k_dev[] = { + { 0x1019, E1K_82547, "82547EI/GI Copper" }, + { 0x101A, E1K_82547, "82547EI Mobile" }, + { 0x1010, E1K_82546, "52546EB Copper, Dual Port" }, + { 0x1012, E1K_82546, "82546EB Fiber, Dual Port" }, +/* { 0x101D, E1K_82546, "82546EB Copper, Quad Port" }, */ + { 0x1079, E1K_82546, "82546GB Copper, Dual Port" }, + { 0x107A, E1K_82546, "82546GB Fiber, Dual Port" }, + { 0x107B, E1K_82546, "82546GB SerDes, Dual Port" }, + { 0x100F, E1K_82545, "82545EM Copper" }, + { 0x1011, E1K_82545, "82545EM Fiber" }, + { 0x1026, E1K_82545, "82545GM Copper" }, + { 0x1027, E1K_82545, "82545GM Fiber" }, + { 0x1028, E1K_82545, "82545GM SerDes" }, + { 0x1107, E1K_82544, "82544EI Copper" }, + { 0x1112, E1K_82544, "82544GC Copper" }, + { 0x1013, E1K_82541, "82541EI Copper" }, + { 0x1018, E1K_82541, "82541EI Mobile" }, + { 0x1076, E1K_82541, "82541GI Copper" }, + { 0x1077, E1K_82541, "82541GI Mobile" }, + { 0x1078, E1K_82541, "82541ER Copper" }, + { 0x107C, E1K_82541, "82541PI" }, + { 0x1015, E1K_82540, "82540EM Mobile" }, + { 0x1016, E1K_82540, "82540EP Mobile" }, + { 0x1017, E1K_82540, "82540EP Desktop" }, + { 0x100E, E1K_82540, "82540EM Desktop" }, + { 0 , 0 } +}; + +/* + * local variables + ****************************************************************************** + */ +static e1k_st m_e1k __attribute__ ((aligned(16))); +static long dma_offset; + +/* + * global functions + ****************************************************************************** + */ +int +check_driver(uint16_t vendor_id, uint16_t device_id); + +static int e1k_init(net_driver_t *driver); +static int e1k_term(void); +static int e1k_xmit(char *f_buffer_pc, int f_len_i); +static int e1k_receive(char *f_buffer_pc, int f_len_i); + +/** + * Translate virtual to "physical" address, ie. an address + * which can be used for DMA transfers. + */ +static uint64_t +virt2dma(void *addr) +{ + return (uint64_t)addr + dma_offset; +} + +static void * +dma2virt(uint64_t addr) +{ + return (void *)(addr - dma_offset); +} + +/* + * local inline functions for e1k register access + ****************************************************************************** + */ +static uint32_t +e1k_rd32(uint16_t f_offs_u16) +{ // caution: shall only be used after initialization! + return bswap_32(rd32(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16)); +} + +/* not used so far +static uint16_t +e1k_rd16(uint16_t f_offs_u16) +{ // caution: shall only be used after initialization! + return bswap_16(rd16(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16)); +}*/ + +/* not used so far +static uint8_t +e1k_rd08(uint16_t f_offs_u16) +{ // caution: shall only be used after initialization! + return rd08(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16); +}*/ + +static void +e1k_wr32(uint16_t f_offs_u16, uint32_t f_val_u32) +{ // caution: shall only be used after initialization! + wr32(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16, bswap_32(f_val_u32)); +} + +/* not used so far +static void +e1k_wr16(uint16_t f_offs_u16, uint16_t f_val_u16) +{ // caution: shall only be used after initialization! + wr16(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16, bswap_16(f_val_u16)); +}*/ + +/* not used so far +static void +e1k_wr08(uint16_t f_offs_u16, uint8_t f_val_u08) +{ // caution: shall only be used after initialization! + wr08(m_e1k.m_baseaddr_u64 + (uint64_t) f_offs_u16, f_val_u08); +}*/ + +static void +e1k_setb32(uint16_t f_offs_u16, uint32_t f_mask_u32) +{ + uint32_t v; + + v = e1k_rd32(f_offs_u16); + v |= f_mask_u32; + e1k_wr32(f_offs_u16, v); +} + +/* not used so far +static void +e1k_setb16(uint16_t f_offs_u16, uint16_t f_mask_u16) +{ + uint16_t v; + v = e1k_rd16(f_offs_u16); + v |= f_mask_u16; + e1k_wr16(f_offs_u16, v); +}*/ + +/* not used so far +static void +e1k_setb08(uint16_t f_offs_u16, uint8_t f_mask_u08) +{ + uint8_t v; + v = e1k_rd08(f_offs_u16); + v |= f_mask_u08; + e1k_wr08(f_offs_u16, v); +}*/ + +static void +e1k_clrb32(uint16_t f_offs_u16, uint32_t f_mask_u32) +{ + uint32_t v; + + v = e1k_rd32(f_offs_u16); + v &= ~f_mask_u32; + e1k_wr32(f_offs_u16, v); +} + +/* not used so far +static void +e1k_clrb16(uint16_t f_offs_u16, uint16_t f_mask_u16) +{ + uint16_t v; + + v = e1k_rd16(f_offs_u16); + v &= ~f_mask_u16; + e1k_wr16(f_offs_u16, v); +}*/ + +/* not used so far +static void +e1k_clrb08(uint16_t f_offs_u16, uint8_t f_mask_u08) +{ + uint8_t v; + v = e1k_rd08(f_offs_u16); + v &= ~f_mask_u08; + e1k_wr08(f_offs_u16, v); +}*/ + +static int32_t +e1k_eep_rd16(uint8_t f_offs_u08, uint16_t *f_data_pu16) +{ + uint32_t i; + uint32_t v; + int32_t done_shft; + int32_t addr_shft; + + if(IS_82541 || IS_82547) { + addr_shft = 2; + done_shft = 1; + } else { + addr_shft = 8; + done_shft = 4; + } + + /* + * initiate eeprom read + */ + e1k_wr32(EERD, ((uint32_t) f_offs_u08 << addr_shft) | // address + BIT32(0)); // start read + + /* + * wait for read done bit to be set + */ + i = 1000; + v = e1k_rd32(EERD); + while ((--i) && + ((v & BIT32(done_shft)) == 0)) { + SLOF_msleep(1); + v = e1k_rd32(EERD); + } + + /* + * return on error + */ + if ((v & BIT32(done_shft)) == 0) { + return -1; + } + + /* + * return data + */ + *f_data_pu16 = (uint16_t) ((v >> 16) & 0xffff); + + return 0; +} + +/* + * ring initialization + */ +static void +e1k_init_receiver(void) +{ + uint32_t i; + uint64_t addr; + + /* + * disable receiver for initialization + */ + e1k_wr32(RCTL, 0); + + /* + * clear receive desciptors and setup buffer pointers + */ + for (i = 0; i < E1K_NUM_RX_DESC; i++) { + memset((uint8_t *) &m_e1k.m_rx_ring_pst[i], 0, + sizeof(e1k_rx_desc_st)); + mb(); + + m_e1k.m_rx_ring_pst[i].m_buffer_u64 = + bswap_64(virt2dma(&m_e1k.m_rx_buffer_pu08[i][0])); + } + + /* + * initialize previously received index + */ + m_e1k.m_rx_next_u32 = 0; + + /* + * setup the base address and the length of the rx descriptor ring + */ + addr = virt2dma(&m_e1k.m_rx_ring_pst[0]); + e1k_wr32(RDBAH, (uint32_t) ((uint64_t) addr >> 32)); + e1k_wr32(RDBAL, (uint32_t) ((uint64_t) addr & 0xffffffff)); + e1k_wr32(RDLEN, E1K_NUM_RX_DESC * sizeof(e1k_rx_desc_st)); + + /* + * setup the rx head and tail descriptor indices + */ + e1k_wr32(RDH, 0); + e1k_wr32(RDT, E1K_NUM_RX_DESC - 1); + + /* + * setup the receive delay timer register + */ + e1k_wr32(RDTR, 0); + + /* + * setup the receive control register + */ + e1k_wr32(RCTL, BIT32( 1) | // enable receiver + BIT32( 4) | // enable multicast reception + BIT32(15)); // broadcast accept mode + // packet size 2048 + // no buffer extension +} + +static void +e1k_init_transmitter(void) +{ + uint32_t i; + uint64_t addr; + + /* + * clear transmit desciptors and setup buffer pointers + */ + for (i = 0; i < E1K_NUM_TX_DESC; i++) { + memset((uint8_t *) &m_e1k.m_tx_ring_pst[i], 0, + sizeof(e1k_tx_desc_st)); + mb(); + + m_e1k.m_tx_ring_pst[i].m_buffer_u64 = + bswap_64(virt2dma(&m_e1k.m_tx_buffer_pu08[i][0])); + } + + /* + * setup the base address and the length of the tx descriptor ring + */ + addr = virt2dma(&m_e1k.m_tx_ring_pst[0]); + e1k_wr32(TDBAH, (uint32_t) ((uint64_t) addr >> 32)); + e1k_wr32(TDBAL, (uint32_t) ((uint64_t) addr & 0xffffffff)); + e1k_wr32(TDLEN, E1K_NUM_TX_DESC * sizeof(e1k_tx_desc_st)); + + /* + * setup the rx head and tail descriptor indices + */ + e1k_wr32(TDH, 0); + e1k_wr32(TDT, 0); + + /* + * initialize the transmit control register + */ + e1k_wr32(TCTL, BIT32(1) | // enable transmitter + BIT32(3) | // pad short packets + ((uint32_t) 0x0f << 4) | // collision threshhold + ((uint32_t) 0x40 << 12)); // collision distance +} + +static int32_t +e1k_mac_init(uint8_t *f_mac_pu08) +{ + uint32_t l_ah_u32; + uint32_t l_al_u32; + uint32_t i; + uint32_t v; + + /* + * Use MAC address from device tree if possible + */ + for (i = 0, v = 0; i < 6; i++) { + v += (uint32_t) f_mac_pu08[i]; + } + + if (v != 0) { + /* + * use passed mac address for transmission to nic + */ + l_al_u32 = ((uint32_t) f_mac_pu08[3] << 24); + l_al_u32 |= ((uint32_t) f_mac_pu08[2] << 16); + l_al_u32 |= ((uint32_t) f_mac_pu08[1] << 8); + l_al_u32 |= ((uint32_t) f_mac_pu08[0] << 0); + l_ah_u32 = ((uint32_t) f_mac_pu08[5] << 8); + l_ah_u32 |= ((uint32_t) f_mac_pu08[4] << 0); + } else { + /* + * read mac address from eeprom + */ + uint16_t w[3]; // 3 16 bit words from eeprom + + for (i = 0; i < 3; i++) { + if (e1k_eep_rd16(EEPROM_MAC_OFFS + i, &w[i]) != 0) { + printf("Failed to read MAC address from EEPROM!\n"); + return -1; + } + } + + /* + * invert the least significant bit for 82546 dual port + * if the second device is in use (remember word is byteswapped) + */ + if ((IS_82546) && + ((e1k_rd32(STATUS) & BIT32(2)) != 0)) { + w[2] ^= (uint16_t) 0x100; + } + + /* + * store mac address for transmission to nic + */ + l_ah_u32 = ((uint32_t) w[2] << 0); + l_al_u32 = ((uint32_t) w[1] << 16); + l_al_u32 |= ((uint32_t) w[0] << 0); + + /* + * return mac address + * mac address in eeprom is stored byteswapped + */ + f_mac_pu08[1] = (uint8_t) ((w[0] >> 8) & 0xff); + f_mac_pu08[0] = (uint8_t) ((w[0] >> 0) & 0xff); + f_mac_pu08[3] = (uint8_t) ((w[1] >> 8) & 0xff); + f_mac_pu08[2] = (uint8_t) ((w[1] >> 0) & 0xff); + f_mac_pu08[5] = (uint8_t) ((w[2] >> 8) & 0xff); + f_mac_pu08[4] = (uint8_t) ((w[2] >> 0) & 0xff); + } + + /* + * insert mac address in receive address register + * and set AV bit + */ + e1k_wr32(RAL0, l_al_u32); + e1k_wr32(RAH0, l_ah_u32 | BIT32(31)); + + /* + * clear remaining receive address registers + */ + for (i = 1; i < NUM_MAC_ADDR; i++) { + e1k_wr32(RAL0 + i * sizeof(uint64_t), 0); + e1k_wr32(RAH0 + i * sizeof(uint64_t), 0); + } + + return 0; +} + + +/* + * interface + ****************************************************************************** + */ + +/* + * e1k_receive + */ +static int +e1k_receive(char *f_buffer_pc, int f_len_i) +{ + uint32_t l_rdh_u32 = e1k_rd32(RDH); // this includes needed dummy read + e1k_rx_desc_st *rx; + int l_ret_i; + + #ifdef E1K_DEBUG + #ifdef E1K_SHOW_RCV_DATA + int i; + #endif + #endif + + /* + * check whether new packets have arrived + */ + if (m_e1k.m_rx_next_u32 == l_rdh_u32) { + return 0; + } + + /* + * get a pointer to the next rx descriptor for ease of use + */ + rx = &m_e1k.m_rx_ring_pst[m_e1k.m_rx_next_u32]; + + /* + * check whether the descriptor done bit is set + */ + if ((rx->m_sta_u08 & 0x1) == 0) { + return 0; + } + + /* + * get the length of the packet, throw away checksum + */ + l_ret_i = (int) bswap_16(rx->m_len_u16) - (int) 4; + + /* + * copy the data + */ + memcpy((uint8_t *) f_buffer_pc, dma2virt(bswap_64(rx->m_buffer_u64)), + (size_t) l_ret_i); + + #ifdef E1K_DEBUG + #if defined(E1K_SHOW_RCV) || defined(E1K_SHOW_RCV_DATA) + printf("e1k: %d bytes received\n", l_ret_i); + #endif + + #ifdef E1K_SHOW_RCV_DATA + for (i = 0; i < l_ret_i; i++) { + + if ((i & 0x1f) == 0) { + printf("\n "); + } + + printf("%02X ", f_buffer_pc[i]); + } + + printf("\n\n"); + #endif + #endif + + /* + * clear descriptor for reusage, but leave buffer pointer untouched + */ + memset((uint8_t *) &rx->m_len_u16, 0, + sizeof(e1k_rx_desc_st) - sizeof(uint64_t)); + mb(); + + /* + * write new tail pointer + */ + e1k_wr32(RDT, m_e1k.m_rx_next_u32); + + /* + * update next receive index + */ + m_e1k.m_rx_next_u32 = (m_e1k.m_rx_next_u32 + 1) & (E1K_NUM_RX_DESC - 1); + + return l_ret_i; +} + +static int +e1k_xmit(char *f_buffer_pc, int f_len_i) +{ + uint32_t l_tdh_u32 = e1k_rd32(TDH); + uint32_t l_tdt_u32 = e1k_rd32(TDT); + uint32_t l_pre_u32 = (l_tdh_u32 + (E1K_NUM_TX_DESC - 1)) & + (E1K_NUM_TX_DESC - 1); + e1k_tx_desc_st *tx; + #if defined(E1K_DEBUG) && defined(E1K_SHOW_XMIT_DATA) + int i; + #endif + + /* + * check for available buffers + */ + if (l_pre_u32 == l_tdt_u32) { + return 0; + } + + /* + * get a pointer to the next tx descriptor for ease of use + */ + tx = &m_e1k.m_tx_ring_pst[l_tdt_u32]; + + /* + * copy the data + */ + memcpy(dma2virt(bswap_64(tx->m_buffer_u64)), (uint8_t *) f_buffer_pc, + (size_t) f_len_i); + + /* + * insert length & command flags + */ + tx->m_len_u16 = bswap_16((uint16_t) f_len_i); + tx->m_cmd_u08 = (BIT08(0) | // EOP + BIT08(1)); // IFCS + tx->m_sta_u08 = 0; + mb(); + + /* + * update tail index + */ + l_tdt_u32 = (l_tdt_u32 + 1) & (E1K_NUM_TX_DESC - 1); + e1k_wr32(TDT, l_tdt_u32); + + #ifdef E1K_DEBUG + #if defined(E1K_SHOW_XMIT) || defined(E1K_SHOW_XMIT_DATA) + printf("e1k: %d bytes transmitted\n", bswap_16(tx->m_len_u16)); + #endif + + #ifdef E1K_SHOW_XMIT_DATA + for (i = 0; i < bswap_16(tx->m_len_u16); i++) { + + if ((i & 0x1f) == 0) { + printf("\n "); + } + + f_buffer_pc = dma2virt(bswap_64(tx->m_buffer_u64)); + printf("%02X ", f_buffer_pc[i]); + } + + printf("\n\n"); + #endif + #endif + + return f_len_i; +} + +int +check_driver(uint16_t vendor_id, uint16_t device_id) +{ + uint64_t i; + + /* + * checks whether the driver is handling this device + * by verifying vendor & device id + * vendor id 0x8086 == Intel + */ + if (vendor_id != 0x8086) { + #ifdef E1K_DEBUG + printf("e1k: netdevice with vendor id %04X not supported\n", + vendor_id); + #endif + return -1; + } + + for (i = 0; e1k_dev[i].m_dev_u32 != 0; i++) { + if (e1k_dev[i].m_dev_u32 == (uint32_t) device_id) { + break; + } + } + + if (e1k_dev[i].m_dev_u32 == 0) { + #ifdef E1K_DEBUG + printf("e1k: netdevice with device id %04X not supported\n", + device_id); + #endif + return -1; + } + + /* + * initialize static variables + */ + m_e1k.m_device_u64 = e1k_dev[i].m_devmsk_u64; + m_e1k.m_baseaddr_u64 = 0; + + // success + #ifdef E1K_DEBUG + printf("e1k: found device %s\n", e1k_dev[i].m_name); + #endif + + return 0; +} + +static int +e1k_init(net_driver_t *driver) +{ + uint32_t i; + uint32_t v; + + if (!driver) + return -1; + + #ifdef E1K_DEBUG + printf("\ne1k: initializing\n"); + #endif + + dma_offset = SLOF_dma_map_in(&m_e1k, sizeof(m_e1k), 0); + #ifdef E1K_DEBUG + printf("e1k: dma offset: %lx - %lx = %lx\n", dma_offset, (long)&m_e1k, + dma_offset - (long)&m_e1k); + #endif + dma_offset = dma_offset - (long)&m_e1k; + + /* + * setup register & memory base addresses of NIC + */ + //m_e1k.m_baseaddr_u64 = baseaddr; + #ifdef E1K_DEBUG + printf("e1k: base address register = 0x%llx\n", m_e1k.m_baseaddr_u64); + #endif + + /* + * e1k hardware initialization + */ + + /* + * at first disable all interrupts + */ + e1k_wr32(IMC, (uint32_t) ~0); + + /* + * check for link up + */ + #ifdef E1K_DEBUG + printf("e1k: checking link status..\n"); + #endif + + i = 50; + v = e1k_rd32(STATUS); + while ((--i) && + ((v & BIT32(1)) == 0)) { + SLOF_msleep(100); + v = e1k_rd32(STATUS); + } + + if ((v & BIT32(1)) == 0) { + #ifdef E1K_DEBUG + printf("e1k: link is down.\n"); + printf(" terminating.\n"); + #endif + + return -1; + } + + #ifdef E1K_DEBUG + printf("e1k: link is up\n"); + + switch ((v >> 6) & 0x3) { + case 0: { + printf(" 10 Mb/s\n"); + } break; + case 1: { + printf(" 100 Mb/s\n"); + } break; + case 2: + case 3: { + printf(" 1000 Mb/s\n"); + } break; + } + + if ((v & BIT32(0)) == 0) { + printf(" half-duplex\n"); + } else { + printf(" full-duplex\n"); + } + #endif + + /* + * initialize mac address + */ + #ifdef E1K_DEBUG + printf("e1k: initializing mac address.. "); + #endif + if (e1k_mac_init((uint8_t *)driver->mac_addr) != 0) { + #ifdef E1K_DEBUG + printf("failed.\n"); + printf(" terminating.\n"); + #endif + + return -1; + } + + #ifdef E1K_DEBUG + printf("done.\n"); + printf(" mac address = %02X:%02X:%02X:%02X:%02X:%02X\n", + driver->mac_addr[0], driver->mac_addr[1], driver->mac_addr[2], + driver->mac_addr[3], driver->mac_addr[4], driver->mac_addr[5]); + #endif + + /* + * initialize transmitter + */ + #ifdef E1K_DEBUG + printf("e1k: initializing transmitter.. "); + #endif + e1k_init_transmitter(); + #ifdef E1K_DEBUG + printf("done.\n"); + #endif + + /* + * initialize receiver + */ + #ifdef E1K_DEBUG + printf("e1k: initializing receiver.. "); + #endif + e1k_init_receiver(); + #ifdef E1K_DEBUG + printf("done.\n"); + printf("e1k: initialization complete\n"); + #endif + + driver->running = 1; + + return 0; +} + +static int +e1k_reset(void) +{ + /* + * reset the PHY + */ + e1k_setb32(CTRL, BIT32(31)); + SLOF_msleep(10); + + /* + * reset the MAC + */ + e1k_setb32(CTRL, BIT32(26)); + SLOF_msleep(10); + + return 0; +} + +static int +e1k_term(void) +{ + #ifdef E1K_DEBUG + printf("e1k: shutdown.. "); + #endif + + /* + * disable receiver & transmitter + */ + e1k_wr32(RCTL, 0); + e1k_wr32(TCTL, 0); + SLOF_msleep(10); + + /* + * reset the ring indices + */ + e1k_wr32(RDH, 0); + e1k_wr32(RDT, 0); + e1k_wr32(TDH, 0); + e1k_wr32(TDT, 0); + + /* + * disable receive address + */ + e1k_clrb32(RAH0, BIT32(31)); + + /* + * reset the mac/phy + */ + e1k_reset(); + + /* + * Disable DMA translation + */ + SLOF_dma_map_out((long)virt2dma(&m_e1k), (void *)&m_e1k, (long)sizeof(m_e1k)); + + #ifdef E1K_DEBUG + printf("done.\n"); + #endif + + return 0; +} + +net_driver_t *e1k_open(uint64_t baseaddr) +{ + net_driver_t *driver; + + m_e1k.m_baseaddr_u64 = baseaddr; + driver = SLOF_alloc_mem(sizeof(*driver)); + if (!driver) { + printf("Unable to allocate virtio-net driver\n"); + return NULL; + } + memset(driver, 0, sizeof(*driver)); + + if (e1k_init(driver)) + goto FAIL; + + return driver; + +FAIL: SLOF_free_mem(driver, sizeof(*driver)); + return NULL; + + return 0; +} + +void e1k_close(net_driver_t *driver) +{ + if (driver->running == 0) + return; + + e1k_term(); + driver->running = 0; + SLOF_free_mem(driver, sizeof(*driver)); +} + +int e1k_read(char *buf, int len) +{ + if (buf) + return e1k_receive(buf, len); + return -1; +} + +int e1k_write(char *buf, int len) +{ + if (buf) + return e1k_xmit(buf, len); + return -1; +} + +int e1k_mac_setup(uint16_t vendor_id, uint16_t device_id, + uint64_t baseaddr, char *mac_addr) +{ + if (check_driver(vendor_id, device_id)) + return -1; + + m_e1k.m_baseaddr_u64 = baseaddr; + memset(mac_addr, 0, 6); + + return e1k_mac_init((uint8_t *)mac_addr); +} diff --git a/qemu/roms/SLOF/lib/libe1k/e1k.code b/qemu/roms/SLOF/lib/libe1k/e1k.code new file mode 100644 index 000000000..225ed4e83 --- /dev/null +++ b/qemu/roms/SLOF/lib/libe1k/e1k.code @@ -0,0 +1,75 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * libe1k Forth wrapper + */ + +#include <e1k.h> + +// : e1k-open ( baseaddr -- false | [ driver true ] ) +PRIM(E1K_X2d_OPEN) +{ + uint64_t baseaddr = TOS.u; POP; + net_driver_t *net_driver = e1k_open(baseaddr); + if (net_driver) { + PUSH; + TOS.u = (unsigned long)net_driver; PUSH; + TOS.n = -1; + } else { + PUSH; + TOS.n = 0; + } +} +MIRP + +// : e1k-close ( driver -- ) +PRIM(E1K_X2d_CLOSE) +{ + net_driver_t *driver = TOS.a; POP; + e1k_close(driver); +} +MIRP + + +// : e1k-read ( addr len -- actual ) +PRIM(E1K_X2d_READ) +{ + int len = TOS.u; POP; + TOS.n = e1k_read(TOS.a, len); +} +MIRP + +// : e1k-write ( addr len -- actual ) +PRIM(E1K_X2d_WRITE) +{ + int len = TOS.u; POP; + TOS.n = e1k_write(TOS.a, len); +} +MIRP + +// : e1k-mac-setup ( vendor-id device-id baseaddr addr -- false | [ mac-addr len true ] ) +PRIM(E1K_X2d_MAC_X2d_SETUP) +{ + char *mac_addr = TOS.a; POP; + uint64_t baseaddr = TOS.u; POP; + unsigned int device_id = TOS.u; POP; + + int ret = e1k_mac_setup(TOS.u, device_id, baseaddr, mac_addr); + if (!ret) { + TOS.a = mac_addr; PUSH; + TOS.n = 6; PUSH; + TOS.n = -1; + } else + TOS.n = 0; +} +MIRP diff --git a/qemu/roms/SLOF/lib/libe1k/e1k.h b/qemu/roms/SLOF/lib/libe1k/e1k.h new file mode 100644 index 000000000..c88b3e561 --- /dev/null +++ b/qemu/roms/SLOF/lib/libe1k/e1k.h @@ -0,0 +1,108 @@ +/****************************************************************************** + * Copyright (c) 2007, 2011, 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * Definitions for the e1000 Gigabit Ethernet Driver for SLOF + */ + +#include <stdint.h> +#include <cache.h> + +// compiler switches + +// Debug switches +//#define E1K_DEBUG // main debug switch, w/o it the other ones don't work +//#define E1K_SHOW_RCV +//#define E1K_SHOW_RCV_DATA +//#define E1K_SHOW_XMIT +//#define E1K_SHOW_XMIT_DATA + +/* + * pci register offsets + */ +// PCI command register +#define PCI_COM_R ((uint16_t) 0x0004) +// PCI Cache Line Size register +#define PCI_CACHELS_R ((uint16_t) 0x000c) +// PCI bar1 register +#define PCI_BAR1_R ((uint16_t) 0x0010) +// PCI bar2 register +#define PCI_BAR2_R ((uint16_t) 0x0014) +// PCI bar1 register +#define PCI_SUBID_R ((uint16_t) 0x002e) + +/* + * e1000 register offsets + */ +// Device Control register +#define CTRL ((uint16_t) 0x0000) +// Device Status register +#define STATUS ((uint16_t) 0x0008) +// Eeprom Read register +#define EERD ((uint16_t) 0x0014) +// Interrupt Mask Clear register +#define IMC ((uint16_t) 0x00d8) +// Receive Control register +#define RCTL ((uint16_t) 0x0100) +// Receive Descriptor Base Address Low register +#define RDBAL ((uint16_t) 0x2800) +// Receive Descriptor Base Address High register +#define RDBAH ((uint16_t) 0x2804) +// Receive Descriptor Length register +#define RDLEN ((uint16_t) 0x2808) +// Receive Descriptor Head register +#define RDH ((uint16_t) 0x2810) +// Receive Descriptor Tail register +#define RDT ((uint16_t) 0x2818) +// Receive Delay Timer register +#define RDTR ((uint16_t) 0x2820) +// Transmit Control register +#define TCTL ((uint16_t) 0x0400) +// Transmit Descriptor Base Address Low register +#define TDBAL ((uint16_t) 0x3800) +// Transmit Descriptor Base Address High register +#define TDBAH ((uint16_t) 0x3804) +// Transmit Descriptor Length register +#define TDLEN ((uint16_t) 0x3808) +// Transmit Descriptor Head register +#define TDH ((uint16_t) 0x3810) +// Transmit Descriptor Tail register +#define TDT ((uint16_t) 0x3818) +// Receive Address Low register +#define RAL0 ((uint16_t) 0x5400) +// Receive Address High register +#define RAH0 ((uint16_t) 0x5404) + + +/* + * useful def's + */ +#define rd08(a) ci_read_8((uint32_t *)(a)) +#define rd16(a) ci_read_16((uint32_t *)(a)) +#define rd32(a) ci_read_32((uint32_t *)(a)) +#define wr08(a,v) ci_write_8((uint32_t *)(a), (v)) +#define wr16(a,v) ci_write_16((uint32_t *)(a), (v)) +#define wr32(a,v) ci_write_32((uint32_t *)(a), (v)) +//#define printk snk_kernel_interface->print +//#define ms_delay snk_kernel_interface->ms_delay + +#define BIT08(bit) ((uint8_t) 0x1 << (bit)) +#define BIT16(bit) ((uint16_t) 0x1 << (bit)) +#define BIT32(bit) ((uint32_t) 0x1 << (bit)) + +//#define mb() asm volatile("sync" ::: "memory"); + +extern net_driver_t *e1k_open(uint64_t baseaddr); +extern void e1k_close(net_driver_t *driver); +extern int e1k_read(char *buf, int len); +extern int e1k_write(char *buf, int len); +extern int e1k_mac_setup(uint16_t vendor_id, uint16_t device_id, + uint64_t baseaddr, char *mac_addr); diff --git a/qemu/roms/SLOF/lib/libe1k/e1k.in b/qemu/roms/SLOF/lib/libe1k/e1k.in new file mode 100644 index 000000000..a9cb13f83 --- /dev/null +++ b/qemu/roms/SLOF/lib/libe1k/e1k.in @@ -0,0 +1,21 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * libe1k bindings for Forth - definitions + */ + +cod(E1K-OPEN) +cod(E1K-CLOSE) +cod(E1K-READ) +cod(E1K-WRITE) +cod(E1K-MAC-SETUP) diff --git a/qemu/roms/SLOF/lib/libelf/Makefile b/qemu/roms/SLOF/lib/libelf/Makefile new file mode 100644 index 000000000..34a8f20b6 --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/Makefile @@ -0,0 +1,47 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2011 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) +LDFLAGS= -nostdlib + +TARGET = ../libelf.a + +all: $(TARGET) + +SRCS = elf.c elf32.c elf64.c elf_claim.c + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -MM $(CPPFLAGS) $(CFLAGS) $(SRCS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep diff --git a/qemu/roms/SLOF/lib/libelf/elf.c b/qemu/roms/SLOF/lib/libelf/elf.c new file mode 100644 index 000000000..db2d2abc9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/elf.c @@ -0,0 +1,190 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * ELF loader + */ + +#include <string.h> +#include <cache.h> +#include <libelf.h> +#include <byteorder.h> + +/** + * elf_check_file tests if the file at file_addr is + * a correct endian, ELF PPC executable + * @param file_addr pointer to the start of the ELF file + * @return the class (1 for 32 bit, 2 for 64 bit) + * -1 if it is not an ELF file + * -2 if it has the wrong endianness + * -3 if it is not an ELF executable + * -4 if it is not for PPC + */ +static int +elf_check_file(unsigned long *file_addr) +{ + struct ehdr *ehdr = (struct ehdr *) file_addr; + uint8_t native_endian; + + /* check if it is an ELF image at all */ + if (cpu_to_be32(ehdr->ei_ident) != 0x7f454c46) + return -1; + +#ifdef __BIG_ENDIAN__ + native_endian = ELFDATA2MSB; +#else + native_endian = ELFDATA2LSB; +#endif + + if (native_endian != ehdr->ei_data) { + switch (ehdr->ei_class) { + case 1: + elf_byteswap_header32(file_addr); + break; + case 2: + elf_byteswap_header64(file_addr); + break; + } + } + + /* check if it is an ELF executable ... and also + * allow DYN files, since this is specified by ePAPR */ + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) + return -3; + + /* check if it is a PPC ELF executable */ + if (ehdr->e_machine != 0x14 && ehdr->e_machine != 0x15) + return -4; + + return ehdr->ei_class; +} + +/** + * load_elf_file tries to load the ELF file specified in file_addr + * + * it first checks if the file is a PPC ELF executable and then loads + * the segments depending if it is a 64bit or 32 bit ELF file + * + * @param file_addr pointer to the start of the elf file + * @param entry pointer where the ELF loader will store + * the entry point + * @param pre_load handler that is called before copying a segment + * @param post_load handler that is called after copying a segment + * @return 1 for a 32 bit file + * 2 for a 64 bit BE file + * 3 for a 64 bit LE ABIv1 file + * 4 for a 64 bit LE ABIv2 file + * 5 for a 32 bit LE ABIv1 file + * anything else means an error during load + */ +int +elf_load_file(void *file_addr, unsigned long *entry, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + int type = elf_check_file(file_addr); + struct ehdr *ehdr = (struct ehdr *) file_addr; + + switch (type) { + case 1: + *entry = elf_load_segments32(file_addr, 0, pre_load, post_load); + if (ehdr->ei_data != ELFDATA2MSB) { + type = 5; /* LE32 ABIv1 */ + } + break; + case 2: + *entry = elf_load_segments64(file_addr, 0, pre_load, post_load); + if (ehdr->ei_data != ELFDATA2MSB) { + uint32_t flags = elf_get_eflags_64(file_addr); + if ((flags & 0x3) == 2) + type = 4; /* LE64 ABIv2 */ + else + type = 3; /* LE64 ABIv1 */ + } + break; + } + if (*entry == 0) + type = 0; + + return type; +} + + +/** + * load_elf_file_to_addr loads an ELF file to given address. + * This is useful for 64-bit vmlinux images that use the virtual entry + * point address in their headers, and thereby need a special treatment. + * + * @param file_addr pointer to the start of the elf file + * @param entry pointer where the ELF loader will store + * the entry point + * @param pre_load handler that is called before copying a segment + * @param post_load handler that is called after copying a segment + * @return 1 for a 32 bit file + * 2 for a 64 bit file + * anything else means an error during load + */ +int +elf_load_file_to_addr(void *file_addr, void *addr, unsigned long *entry, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + int type; + long offset; + + type = elf_check_file(file_addr); + + switch (type) { + case 1: + /* Parse 32-bit image */ + offset = (long)addr - elf_get_base_addr32(file_addr); + *entry = elf_load_segments32(file_addr, offset, pre_load, + post_load) + offset; + // TODO: elf_relocate32(...) + break; + case 2: + /* Parse 64-bit image */ + offset = (long)addr - elf_get_base_addr64(file_addr); + *entry = elf_load_segments64(file_addr, offset, pre_load, + post_load) + offset; + elf_relocate64(file_addr, offset); + break; + } + + return type; +} + + +/** + * Get the base load address of the ELF image + * @return The base address or -1 for error + */ +long +elf_get_base_addr(void *file_addr) +{ + int type; + + type = elf_check_file(file_addr); + + switch (type) { + case 1: + /* Return 32-bit image base address */ + return elf_get_base_addr32(file_addr); + break; + case 2: + /* Return 64-bit image base address */ + return elf_get_base_addr64(file_addr); + break; + } + + return -1; +} diff --git a/qemu/roms/SLOF/lib/libelf/elf32.c b/qemu/roms/SLOF/lib/libelf/elf32.c new file mode 100644 index 000000000..fea5cf420 --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/elf32.c @@ -0,0 +1,193 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * 32-bit ELF loader + */ +#include <stdio.h> +#include <string.h> +#include <libelf.h> +#include <byteorder.h> + +struct ehdr32 { + uint32_t ei_ident; + uint8_t ei_class; + uint8_t ei_data; + uint8_t ei_version; + uint8_t ei_pad[9]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint32_t e_entry; + uint32_t e_phoff; + uint32_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +}; + +struct phdr32 { + uint32_t p_type; + uint32_t p_offset; + uint32_t p_vaddr; + uint32_t p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; +}; + + +static struct phdr32* +get_phdr32(void *file_addr) +{ + return (struct phdr32 *) (((unsigned char *)file_addr) + + ((struct ehdr32 *)file_addr)->e_phoff); +} + +static void +load_segment(void *file_addr, struct phdr32 *phdr, signed long offset, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + unsigned long src = phdr->p_offset + (unsigned long) file_addr; + unsigned long destaddr; + + destaddr = (unsigned long)phdr->p_paddr; + destaddr = destaddr + offset; + + /* check if we're allowed to copy */ + if (pre_load != NULL) { + if (pre_load((void*)destaddr, phdr->p_memsz) != 0) + return; + } + + /* copy into storage */ + memmove((void *)destaddr, (void *)src, phdr->p_filesz); + + /* clear bss */ + memset((void *)(destaddr + phdr->p_filesz), 0, + phdr->p_memsz - phdr->p_filesz); + + if (phdr->p_memsz && post_load) { + post_load((void*)destaddr, phdr->p_memsz); + } +} + +unsigned int +elf_load_segments32(void *file_addr, signed long offset, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + struct ehdr32 *ehdr = (struct ehdr32 *) file_addr; + /* Calculate program header address */ + struct phdr32 *phdr = get_phdr32(file_addr); + int i; + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + /* PT_LOAD ? */ + if (phdr->p_type == 1) { + if (phdr->p_paddr != phdr->p_vaddr) { + printf("ELF32: VirtAddr(%lx) != PhysAddr(%lx) not supported, aborting\n", + (long)phdr->p_vaddr, (long)phdr->p_paddr); + return 0; + } + + /* copy segment */ + load_segment(file_addr, phdr, offset, pre_load, + post_load); + } + /* step to next header */ + phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } + + /* Entry point is always a virtual address, so translate it + * to physical before returning it */ + return ehdr->e_entry; +} + +/** + * Return the base address for loading (i.e. the address of the first PT_LOAD + * segment) + * @param file_addr pointer to the ELF file in memory + * @return the base address + */ +long +elf_get_base_addr32(void *file_addr) +{ + struct ehdr32 *ehdr = (struct ehdr32 *) file_addr; + struct phdr32 *phdr = get_phdr32(file_addr); + int i; + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + /* PT_LOAD ? */ + if (phdr->p_type == 1) { + return phdr->p_paddr; + } + /* step to next header */ + phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } + + return 0; +} + +uint32_t elf_get_eflags_32(void *file_addr) +{ + struct ehdr32 *ehdr = (struct ehdr32 *) file_addr; + + return ehdr->e_flags; +} + +void +elf_byteswap_header32(void *file_addr) +{ + struct ehdr32 *ehdr = (struct ehdr32 *) file_addr; + struct phdr32 *phdr; + int i; + + bswap_16p(&ehdr->e_type); + bswap_16p(&ehdr->e_machine); + bswap_32p(&ehdr->e_version); + bswap_32p(&ehdr->e_entry); + bswap_32p(&ehdr->e_phoff); + bswap_32p(&ehdr->e_shoff); + bswap_32p(&ehdr->e_flags); + bswap_16p(&ehdr->e_ehsize); + bswap_16p(&ehdr->e_phentsize); + bswap_16p(&ehdr->e_phnum); + bswap_16p(&ehdr->e_shentsize); + bswap_16p(&ehdr->e_shnum); + bswap_16p(&ehdr->e_shstrndx); + + phdr = get_phdr32(file_addr); + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + bswap_32p(&phdr->p_type); + bswap_32p(&phdr->p_offset); + bswap_32p(&phdr->p_vaddr); + bswap_32p(&phdr->p_paddr); + bswap_32p(&phdr->p_filesz); + bswap_32p(&phdr->p_memsz); + bswap_32p(&phdr->p_flags); + bswap_32p(&phdr->p_align); + + /* step to next header */ + phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } +} diff --git a/qemu/roms/SLOF/lib/libelf/elf64.c b/qemu/roms/SLOF/lib/libelf/elf64.c new file mode 100644 index 000000000..37e9c10a9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/elf64.c @@ -0,0 +1,473 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * 64-bit ELF loader for PowerPC. + * See the "64-bit PowerPC ELF Application Binary Interface Supplement" and + * the "ELF-64 Object File Format" documentation for details. + */ + +#include <string.h> +#include <stdio.h> +#include <libelf.h> +#include <byteorder.h> + +struct ehdr64 +{ + uint32_t ei_ident; + uint8_t ei_class; + uint8_t ei_data; + uint8_t ei_version; + uint8_t ei_pad[9]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint64_t e_entry; + uint64_t e_phoff; + uint64_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +}; + +struct phdr64 +{ + uint32_t p_type; + uint32_t p_flags; + uint64_t p_offset; + uint64_t p_vaddr; + uint64_t p_paddr; + uint64_t p_filesz; + uint64_t p_memsz; + uint64_t p_align; +}; + +struct shdr64 +{ + uint32_t sh_name; /* Section name */ + uint32_t sh_type; /* Section type */ + uint64_t sh_flags; /* Section attributes */ + uint64_t sh_addr; /* Virtual address in memory */ + uint64_t sh_offset; /* Offset in file */ + uint64_t sh_size; /* Size of section */ + uint32_t sh_link; /* Link to other section */ + uint32_t sh_info; /* Miscellaneous information */ + uint64_t sh_addralign; /* Address alignment boundary */ + uint64_t sh_entsize; /* Size of entries, if section has table */ +}; + +struct rela /* RelA relocation table entry */ +{ + uint64_t r_offset; /* Address of reference */ + uint64_t r_info; /* Symbol index and type of relocation */ + int64_t r_addend; /* Constant part of expression */ +}; + +struct sym64 +{ + uint32_t st_name; /* Symbol name */ + uint8_t st_info; /* Type and Binding attributes */ + uint8_t st_other; /* Reserved */ + uint16_t st_shndx; /* Section table index */ + uint64_t st_value; /* Symbol value */ + uint64_t st_size; /* Size of object (e.g., common) */ +}; + + +/* For relocations */ +#define ELF_R_SYM(i) ((i)>>32) +#define ELF_R_TYPE(i) ((uint32_t)(i) & 0xFFFFFFFF) +#define ELF_R_INFO(s,t) ((((uint64_t) (s)) << 32) + (t)) + +/* + * Relocation types for PowerPC64. + */ +#define R_PPC64_NONE 0 +#define R_PPC64_ADDR32 1 +#define R_PPC64_ADDR24 2 +#define R_PPC64_ADDR16 3 +#define R_PPC64_ADDR16_LO 4 +#define R_PPC64_ADDR16_HI 5 +#define R_PPC64_ADDR16_HA 6 +#define R_PPC64_ADDR14 7 +#define R_PPC64_ADDR14_BRTAKEN 8 +#define R_PPC64_ADDR14_BRNTAKEN 9 +#define R_PPC64_REL24 10 +#define R_PPC64_REL14 11 +#define R_PPC64_REL14_BRTAKEN 12 +#define R_PPC64_REL14_BRNTAKEN 13 +#define R_PPC64_GOT16 14 +#define R_PPC64_GOT16_LO 15 +#define R_PPC64_GOT16_HI 16 +#define R_PPC64_GOT16_HA 17 +#define R_PPC64_COPY 19 +#define R_PPC64_GLOB_DAT 20 +#define R_PPC64_JMP_SLOT 21 +#define R_PPC64_RELATIVE 22 +#define R_PPC64_UADDR32 24 +#define R_PPC64_UADDR16 25 +#define R_PPC64_REL32 26 +#define R_PPC64_PLT32 27 +#define R_PPC64_PLTREL32 28 +#define R_PPC64_PLT16_LO 29 +#define R_PPC64_PLT16_HI 30 +#define R_PPC64_PLT16_HA 31 +#define R_PPC64_SECTOFF 33 +#define R_PPC64_SECTOFF_LO 34 +#define R_PPC64_SECTOFF_HI 35 +#define R_PPC64_SECTOFF_HA 36 +#define R_PPC64_ADDR30 37 +#define R_PPC64_ADDR64 38 +#define R_PPC64_ADDR16_HIGHER 39 +#define R_PPC64_ADDR16_HIGHERA 40 +#define R_PPC64_ADDR16_HIGHEST 41 +#define R_PPC64_ADDR16_HIGHESTA 42 +#define R_PPC64_UADDR64 43 +#define R_PPC64_REL64 44 +#define R_PPC64_PLT64 45 +#define R_PPC64_PLTREL64 46 +#define R_PPC64_TOC16 47 +#define R_PPC64_TOC16_LO 48 +#define R_PPC64_TOC16_HI 49 +#define R_PPC64_TOC16_HA 50 +#define R_PPC64_TOC 51 +#define R_PPC64_PLTGOT16 52 +#define R_PPC64_PLTGOT16_LO 53 +#define R_PPC64_PLTGOT16_HI 54 +#define R_PPC64_PLTGOT16_HA 55 +#define R_PPC64_ADDR16_DS 56 +#define R_PPC64_ADDR16_LO_DS 57 +#define R_PPC64_GOT16_DS 58 +#define R_PPC64_GOT16_LO_DS 59 +#define R_PPC64_PLT16_LO_DS 60 +#define R_PPC64_SECTOFF_DS 61 +#define R_PPC64_SECTOFF_LO_DS 62 +#define R_PPC64_TOC16_DS 63 +#define R_PPC64_TOC16_LO_DS 64 +#define R_PPC64_PLTGOT16_DS 65 +#define R_PPC64_PLTGOT16_LO_DS 66 +#define R_PPC64_TLS 67 +#define R_PPC64_DTPMOD64 68 +#define R_PPC64_TPREL16 69 +#define R_PPC64_TPREL16_LO 60 +#define R_PPC64_TPREL16_HI 71 +#define R_PPC64_TPREL16_HA 72 +#define R_PPC64_TPREL64 73 +#define R_PPC64_DTPREL16 74 +#define R_PPC64_DTPREL16_LO 75 +#define R_PPC64_DTPREL16_HI 76 +#define R_PPC64_DTPREL16_HA 77 +#define R_PPC64_DTPREL64 78 +#define R_PPC64_GOT_TLSGD16 79 +#define R_PPC64_GOT_TLSGD16_LO 80 +#define R_PPC64_GOT_TLSGD16_HI 81 +#define R_PPC64_GOT_TLSGD16_HA 82 +#define R_PPC64_GOT_TLSLD16 83 +#define R_PPC64_GOT_TLSLD16_LO 84 +#define R_PPC64_GOT_TLSLD16_HI 85 +#define R_PPC64_GOT_TLSLD16_HA 86 +#define R_PPC64_GOT_TPREL16_DS 87 +#define R_PPC64_GOT_TPREL16_LO_ DS 88 +#define R_PPC64_GOT_TPREL16_HI 89 +#define R_PPC64_GOT_TPREL16_HA 90 +#define R_PPC64_GOT_DTPREL16_DS 91 +#define R_PPC64_GOT_DTPREL16_LO_DS 92 +#define R_PPC64_GOT_DTPREL16_HI 93 +#define R_PPC64_GOT_DTPREL16_HA 94 +#define R_PPC64_TPREL16_DS 95 +#define R_PPC64_TPREL16_LO_DS 96 +#define R_PPC64_TPREL16_HIGHER 97 +#define R_PPC64_TPREL16_HIGHERA 98 +#define R_PPC64_TPREL16_HIGHEST 99 +#define R_PPC64_TPREL16_HIGHESTA 100 +#define R_PPC64_DTPREL16_DS 101 +#define R_PPC64_DTPREL16_LO_DS 102 +#define R_PPC64_DTPREL16_HIGHER 103 +#define R_PPC64_DTPREL16_HIGHERA 104 +#define R_PPC64_DTPREL16_HIGHEST 105 +#define R_PPC64_DTPREL16_HIGHESTA 106 + + +static struct phdr64* +get_phdr64(unsigned long *file_addr) +{ + return (struct phdr64 *) (((unsigned char *) file_addr) + + ((struct ehdr64 *)file_addr)->e_phoff); +} + +static void +load_segment64(unsigned long *file_addr, struct phdr64 *phdr, signed long offset, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + unsigned long src = phdr->p_offset + (unsigned long) file_addr; + unsigned long destaddr; + + destaddr = phdr->p_paddr + offset; + + /* check if we're allowed to copy */ + if (pre_load != NULL) { + if (pre_load((void*)destaddr, phdr->p_memsz) != 0) + return; + } + + /* copy into storage */ + memmove((void*)destaddr, (void*)src, phdr->p_filesz); + + /* clear bss */ + memset((void*)(destaddr + phdr->p_filesz), 0, + phdr->p_memsz - phdr->p_filesz); + + if (phdr->p_memsz && post_load != NULL) { + post_load((void*)destaddr, phdr->p_memsz); + } +} + +unsigned long +elf_load_segments64(void *file_addr, signed long offset, + int (*pre_load)(void*, long), + void (*post_load)(void*, long)) +{ + struct ehdr64 *ehdr = (struct ehdr64 *) file_addr; + /* Calculate program header address */ + struct phdr64 *phdr = get_phdr64(file_addr); + int i; + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + /* PT_LOAD ? */ + if (phdr->p_type == PT_LOAD) { + if (phdr->p_paddr != phdr->p_vaddr) { + printf("ELF64: VirtAddr(%lx) != PhysAddr(%lx) not supported, aborting\n", + (long)phdr->p_vaddr, (long)phdr->p_paddr); + return 0; + } + + /* copy segment */ + load_segment64(file_addr, phdr, offset, pre_load, post_load); + } + /* step to next header */ + phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } + + /* Entry point is always a virtual address, so translate it + * to physical before returning it */ + return ehdr->e_entry; +} + +/** + * Return the base address for loading (i.e. the address of the first PT_LOAD + * segment) + * @param file_addr pointer to the ELF file in memory + * @return the base address + */ +long +elf_get_base_addr64(void *file_addr) +{ + struct ehdr64 *ehdr = (struct ehdr64 *) file_addr; + /* Calculate program header address */ + struct phdr64 *phdr = get_phdr64(file_addr); + int i; + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + /* PT_LOAD ? */ + if (phdr->p_type == PT_LOAD) { + /* Return base address */ + return phdr->p_paddr; + } + /* step to next header */ + phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } + + return 0; +} + + +/** + * Apply one relocation entry. + */ +static void +elf_apply_rela64(void *file_addr, signed long offset, struct rela *relaentry, + struct sym64 *symtabentry) +{ + void *addr; + unsigned long s_a; + unsigned long base_addr; + + base_addr = elf_get_base_addr64(file_addr); + + /* Sanity check */ + if (relaentry->r_offset < base_addr) { + printf("\nELF relocation out of bounds!\n"); + return; + } + + base_addr += offset; + + /* Actual address where the relocation will be applied at. */ + addr = (void*)(relaentry->r_offset + offset); + + /* Symbol value (S) + Addend (A) */ + s_a = symtabentry->st_value + offset + relaentry->r_addend; + + switch (ELF_R_TYPE(relaentry->r_info)) { + case R_PPC64_ADDR32: /* S + A */ + *(uint32_t *)addr = (uint32_t) s_a; + break; + case R_PPC64_ADDR64: /* S + A */ + *(uint64_t *)addr = (uint64_t) s_a; + break; + case R_PPC64_TOC: /* .TOC */ + *(uint64_t *)addr += offset; + break; + case R_PPC64_ADDR16_HIGHEST: /* #highest(S + A) */ + *(uint16_t *)addr = ((s_a >> 48) & 0xffff); + break; + case R_PPC64_ADDR16_HIGHER: /* #higher(S + A) */ + *(uint16_t *)addr = ((s_a >> 32) & 0xffff); + break; + case R_PPC64_ADDR16_HI: /* #hi(S + A) */ + *(uint16_t *)addr = ((s_a >> 16) & 0xffff); + break; + case R_PPC64_ADDR16_LO: /* #lo(S + A) */ + *(uint16_t *)addr = s_a & 0xffff; + break; + case R_PPC64_ADDR16_LO_DS: + *(uint16_t *)addr = (s_a & 0xfffc); + break; + case R_PPC64_ADDR16_HA: /* #ha(S + A) */ + *(uint16_t *)addr = (((s_a >> 16) + ((s_a & 0x8000) ? 1 : 0)) + & 0xffff); + break; + + case R_PPC64_TOC16: /* half16* S + A - .TOC. */ + case R_PPC64_TOC16_LO_DS: + case R_PPC64_TOC16_LO: /* #lo(S + A - .TOC.) */ + case R_PPC64_TOC16_HI: /* #hi(S + A - .TOC.) */ + case R_PPC64_TOC16_HA: + case R_PPC64_TOC16_DS: /* (S + A - .TOC) >> 2 */ + case R_PPC64_REL14: + case R_PPC64_REL24: /* (S + A - P) >> 2 */ + case R_PPC64_REL64: /* S + A - P */ + case R_PPC64_GOT16_DS: + case R_PPC64_GOT16_LO_DS: + // printf("\t\tignoring relocation type %i\n", + // ELF_R_TYPE(relaentry->r_info)); + break; + default: + printf("ERROR: Unhandled relocation (A) type %i\n", + ELF_R_TYPE(relaentry->r_info)); + } +} + + +/** + * Step through all relocation entries and apply them one by one. + */ +static void +elf_apply_all_rela64(void *file_addr, signed long offset, struct shdr64 *shdrs, int idx) +{ + struct shdr64 *rela_shdr = &shdrs[idx]; + struct shdr64 *dst_shdr = &shdrs[rela_shdr->sh_info]; + struct shdr64 *sym_shdr = &shdrs[rela_shdr->sh_link]; + struct rela *relaentry; + struct sym64 *symtabentry; + uint32_t symbolidx; + int i; + + /* If the referenced section has not been allocated, then it has + * not been loaded and thus does not need to be relocated. */ + if ((dst_shdr->sh_flags & SHF_ALLOC) != SHF_ALLOC) + return; + + for (i = 0; i < rela_shdr->sh_size; i += rela_shdr->sh_entsize) { + relaentry = (struct rela *)(file_addr + rela_shdr->sh_offset + i); + + symbolidx = ELF_R_SYM(relaentry->r_info); + symtabentry = (struct sym64*)(file_addr + sym_shdr->sh_offset) + symbolidx; + + elf_apply_rela64(file_addr, offset, relaentry, symtabentry); + } +} + + +/** + * Apply ELF relocations + */ +void +elf_relocate64(void *file_addr, signed long offset) +{ + struct ehdr64 *ehdr = (struct ehdr64 *) file_addr; + /* Calculate section header address */ + struct shdr64 *shdrs = (struct shdr64 *) + (((unsigned char *) file_addr) + ehdr->e_shoff); + int i; + + /* loop over all segments */ + for (i = 0; i <= ehdr->e_shnum; i++) { + /* Skip if it is not a relocation segment */ + if (shdrs[i].sh_type == SHT_RELA) { + elf_apply_all_rela64(file_addr, offset, shdrs, i); + } + } +} + +void +elf_byteswap_header64(void *file_addr) +{ + struct ehdr64 *ehdr = (struct ehdr64 *) file_addr; + struct phdr64 *phdr; + int i; + + bswap_16p(&ehdr->e_type); + bswap_16p(&ehdr->e_machine); + bswap_32p(&ehdr->e_version); + bswap_64p(&ehdr->e_entry); + bswap_64p(&ehdr->e_phoff); + bswap_64p(&ehdr->e_shoff); + bswap_32p(&ehdr->e_flags); + bswap_16p(&ehdr->e_ehsize); + bswap_16p(&ehdr->e_phentsize); + bswap_16p(&ehdr->e_phnum); + bswap_16p(&ehdr->e_shentsize); + bswap_16p(&ehdr->e_shnum); + bswap_16p(&ehdr->e_shstrndx); + + phdr = get_phdr64(file_addr); + + /* loop e_phnum times */ + for (i = 0; i <= ehdr->e_phnum; i++) { + bswap_32p(&phdr->p_type); + bswap_32p(&phdr->p_flags); + bswap_64p(&phdr->p_offset); + bswap_64p(&phdr->p_vaddr); + bswap_64p(&phdr->p_paddr); + bswap_64p(&phdr->p_filesz); + bswap_64p(&phdr->p_memsz); + bswap_64p(&phdr->p_align); + + /* step to next header */ + phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize); + } +} + +uint32_t elf_get_eflags_64(void *file_addr) +{ + struct ehdr64 *ehdr = (struct ehdr64 *) file_addr; + + return ehdr->e_flags; +} diff --git a/qemu/roms/SLOF/lib/libelf/elf_claim.c b/qemu/roms/SLOF/lib/libelf/elf_claim.c new file mode 100644 index 000000000..43540f9b6 --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/elf_claim.c @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) 2009, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include <libelf.h> +#include "../../slof/paflof.h" + + +/** + * Call Forth code to try to claim the memory region + */ +int +elf_forth_claim(void *addr, long size) +{ + forth_push((long)addr); + forth_push(size); + forth_eval("elf-claim-segment"); + return forth_pop(); +} diff --git a/qemu/roms/SLOF/lib/libelf/libelf.code b/qemu/roms/SLOF/lib/libelf/libelf.code new file mode 100644 index 000000000..551468bdd --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/libelf.code @@ -0,0 +1,43 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * libelf Forth wrapper + */ + +#include <libelf.h> + +// : elf-load-file ( fileaddr -- entry type ) +PRIM(ELF_X2d_LOAD_X2d_FILE) +{ + void *file_addr = TOS.a; + int type; + unsigned long entry; + type = elf_load_file(file_addr, &entry, elf_forth_claim, flush_cache); + TOS.u = entry; + PUSH; TOS.n = type; +} +MIRP + +// : elf-load-file-to-addr ( fileaddr destaddr -- entry type ) +PRIM(ELF_X2d_LOAD_X2d_FILE_X2d_TO_X2d_ADDR) +{ + void *dest_addr = TOS.a; POP; + void *file_addr = TOS.a; + int type; + unsigned long entry; + type = elf_load_file_to_addr(file_addr, dest_addr, &entry, + elf_forth_claim, flush_cache); + TOS.u = entry; + PUSH; TOS.n = type; +} +MIRP diff --git a/qemu/roms/SLOF/lib/libelf/libelf.in b/qemu/roms/SLOF/lib/libelf/libelf.in new file mode 100644 index 000000000..9c5f019ce --- /dev/null +++ b/qemu/roms/SLOF/lib/libelf/libelf.in @@ -0,0 +1,18 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * libelf bindings for Forth - definitions + */ + +cod(ELF-LOAD-FILE) +cod(ELF-LOAD-FILE-TO-ADDR) diff --git a/qemu/roms/SLOF/lib/libhvcall/Makefile b/qemu/roms/SLOF/lib/libhvcall/Makefile new file mode 100644 index 000000000..2a9b2d7d1 --- /dev/null +++ b/qemu/roms/SLOF/lib/libhvcall/Makefile @@ -0,0 +1,57 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \ + -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) +LDFLAGS = -nostdlib + +TARGET = ../libhvcall.a + + +all: $(TARGET) + +SRCS = brokensc1.c +SRCSS = hvcall.S + + +OBJS = $(SRCS:%.c=%.o) $(SRCSS:%.S=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +%.o: %.S + $(CC) $(CPPFLAGS) $(ASFLAGS) -c $< -o $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep + diff --git a/qemu/roms/SLOF/lib/libhvcall/brokensc1.c b/qemu/roms/SLOF/lib/libhvcall/brokensc1.c new file mode 100644 index 000000000..e6387e0ab --- /dev/null +++ b/qemu/roms/SLOF/lib/libhvcall/brokensc1.c @@ -0,0 +1,162 @@ +#include <stdint.h> +#include <stddef.h> +#include <cpu.h> +#include "libhvcall.h" +#include "byteorder.h" + +// #define DEBUG_PATCHERY + +#define H_SET_DABR 0x28 +#define INS_SC1 0x44000022 +#define INS_SC1_REPLACE 0x7c000268 + +extern volatile uint32_t sc1ins; + +static unsigned long hcall(uint32_t inst, unsigned long arg0, unsigned long arg1) +{ + register unsigned long r3 asm("r3") = arg0; + register unsigned long r4 asm("r4") = arg1; + register unsigned long r5 asm("r5") = inst; + asm volatile("bl 1f \n" + "1: \n" + "li 11, 2f - 1b \n" + "mflr 12 \n" + "add 11, 11, 12 \n" + "stw 5, 0(11) \n" + "dcbst 0, 11 \n" + "sync \n" + "icbi 0, 11 \n" + "isync \n" + "2: \n" + ".long 0 \n" + : "=r" (r3) + : "r" (r3), "r" (r4), "r" (r5) + : "ctr", "r0", "r6", "r7", "r8", "r9", "r10", "r11", + "r12", "r13", "r31", "lr", "cc"); + return r3; +} + +static int check_broken_sc1(void) +{ + long r; + + /* + * Check if we can do a simple hcall. If it works, we are running in + * a sane environment and everything's fine. If it doesn't, we need + * to patch the hypercall instruction to something that traps into + * supervisor mode. + */ + r = hcall(INS_SC1, H_SET_DABR, 0); + if (r == H_SUCCESS || r == H_HARDWARE) { + /* All is fine */ + return 0; + } + + /* We found a broken sc1 host! */ + return 1; +} + +int patch_broken_sc1(void *start, void *end, uint32_t *test_ins) +{ + uint32_t *p; + /* The sc 1 instruction */ + uint32_t sc1 = INS_SC1; + /* An illegal instruction that KVM interprets as sc 1 */ + uint32_t sc1_replacement = INS_SC1_REPLACE; + int is_le = (test_ins && *test_ins == 0x48000008); +#ifdef DEBUG_PATCHERY + int cnt = 0; +#endif + + /* The host is sane, get out of here */ + if (!check_broken_sc1()) + return 0; + + /* We only get here with a broken sc1 implementation */ + + /* Trim the range we scan to not cover the data section */ + if (test_ins) { + /* This is the cpu table matcher for 970FX */ + uint32_t end_bytes[] = { 0xffff0000, 0x3c0000 }; + /* + * The .__start symbol contains a trap instruction followed + * by lots of zeros. + */ + uint32_t start_bytes[] = { 0x7fe00008, 0, 0, 0, 0 }; + + if (is_le) { + end_bytes[0] = bswap_32(end_bytes[0]); + end_bytes[1] = bswap_32(end_bytes[1]); + start_bytes[1] = bswap_32(start_bytes[1]); + } + + /* Find the start of the text section */ + for (p = test_ins; (long)p > (long)start; p--) { + if (p[0] == start_bytes[0] && + p[1] == start_bytes[1] && + p[2] == start_bytes[2] && + p[3] == start_bytes[3] && + p[4] == start_bytes[4]) { + /* + * We found a match of the instruction sequence + * trap + * .long 0 + * .long 0 + * .long 0 + * .long 0 + * which marks the beginning of the .text + * section on all Linux kernels I've checked. + */ +#ifdef DEBUG_PATCHERY + printf("Shortened start from %p to %p\n", end, p); +#endif + start = p; + break; + } + } + + /* Find the end of the text section */ + for (p = start; (long)p < (long)end; p++) { + if (p[0] == end_bytes[0] && p[1] == end_bytes[1]) { + /* + * We found a match of the PPC970FX entry in the + * guest kernel's CPU table. That table is + * usually found early in the .data section and + * thus marks the end of the .text section for + * us which we need to patch. + */ +#ifdef DEBUG_PATCHERY + printf("Shortened end from %p to %p\n", end, p); +#endif + end = p; + break; + } + } + } + + if (is_le) { + /* + * The kernel was built for LE mode, so our sc1 and replacement + * opcodes are in the wrong byte order. Reverse them. + */ + sc1 = bswap_32(sc1); + sc1_replacement = bswap_32(sc1_replacement); + } + + /* Patch all sc 1 instructions to reserved instruction 31/308 */ + for (p = start; (long)p < (long)end; p++) { + if (*p == sc1) { + *p = sc1_replacement; + flush_cache(p, sizeof(*p)); +#ifdef DEBUG_PATCHERY + cnt++; +#endif + } + } + +#ifdef DEBUG_PATCHERY + printf("Patched %d instructions (%p - %p)\n", cnt, start, end); +#endif + + return 1; +} diff --git a/qemu/roms/SLOF/lib/libhvcall/hvcall.S b/qemu/roms/SLOF/lib/libhvcall/hvcall.S new file mode 100644 index 000000000..92cf22e4c --- /dev/null +++ b/qemu/roms/SLOF/lib/libhvcall/hvcall.S @@ -0,0 +1,134 @@ +#define _ASM +#define __ASSEMBLY__ +#include "macros.h" +#include "libhvcall.h" +#include <termctrl.h> +#include <product.h> + +#define HVCALL .long 0x44000022 + .text + .align 3 + +ENTRY(get_print_banner) + LOAD32(r4, print_version) + LOAD32(r5, print_version_end) + std r4,0(r3) + std r5,8(r3) + blr + +ENTRY(hv_generic) + HVCALL + blr + +/* r3 = char, r4 = hvtermno */ +ENTRY(hv_putchar) + sldi r6,r3,(24+32) + li r3,H_PUT_TERM_CHAR + li r5,1 + HVCALL + blr + +/* r3 = hvtermno */ +ENTRY(hv_getchar) + mflr r10 + bl .hv_haschar + mtlr r10 + cmpwi cr0,r3,0 + beqlr + lis r9,inbuf@h + ori r9,r9,inbuf@l + lwz r4,20(r9) + lbzx r3,r4,r9 + addi r4,r4,1 + stw r4,20(r9) + blr + +/* r3 = hvtermno */ +ENTRY(hv_haschar) + mr r4,r3 + li r3,-1 + lis r9,inbuf@h + ori r9,r9,inbuf@l + lwz r5,16(r9) + lwz r6,20(r9) + cmplw cr0,r5,r6 + bnelr + li r3,H_GET_TERM_CHAR + HVCALL + lis r9,inbuf@h + ori r9,r9,inbuf@l + stw r4,16(r9) + li r3,0 + stw r3,20(r9) + cmplwi cr0,r4,0 + beqlr + li r3,-1 + std r5,0(r9) + std r6,8(r9) + blr + +ENTRY(hv_send_crq) + ld r5,0(r4) + ld r6,8(r4) + mr r4,r3 + li r3,H_SEND_CRQ + HVCALL + blr + +ENTRY(hv_send_logical_lan) + li r11,0 /* no continue token for now */ + mr r10,r9 + mr r9,r8 + mr r8,r7 + mr r7,r6 + mr r6,r5 + mr r5,r4 + mr r4,r3 + li r3,H_SEND_LOGICAL_LAN + HVCALL + blr + +ENTRY(hv_logical_ci_load) + mr r5,r4 + mr r4,r3 + li r3,H_LOGICAL_CI_LOAD + HVCALL + cmpdi cr0,r3,0 + mr r3,r4 + beqlr + li r3,-1 + blr + +ENTRY(hv_logical_ci_store) + mr r6,r5 + mr r5,r4 + mr r4,r3 + li r3,H_LOGICAL_CI_STORE + HVCALL + blr + +ENTRY(hv_logical_memop) + mr r8,r7 + mr r7,r6 + mr r6,r5 + mr r5,r4 + mr r4,r3 + lis r3,KVMPPC_H_LOGICAL_MEMOP@h + ori r3,r3,KVMPPC_H_LOGICAL_MEMOP@l + HVCALL + blr + +ENTRY(hv_cas) + mr r6,r5 + mr r5,r4 + mr r4,r3 + lis r3,KVMPPC_H_CAS@h + ori r3,r3,KVMPPC_H_CAS@l + HVCALL + blr + + .section ".bss" +inbuf: .space 16 +inlen: .space 4 +inpos: .space 4 + .text diff --git a/qemu/roms/SLOF/lib/libhvcall/hvcall.code b/qemu/roms/SLOF/lib/libhvcall/hvcall.code new file mode 100644 index 000000000..0ff50f27e --- /dev/null +++ b/qemu/roms/SLOF/lib/libhvcall/hvcall.code @@ -0,0 +1,131 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <libhvcall.h> + +// : hv-putchar ( hvtermno char -- ) +PRIM(hv_X2d_putchar) + char c = TOS.n; POP; + int hvtermno = TOS.n; POP; + hv_putchar(c, hvtermno); +MIRP + +// : hv-getchar ( hvtermno -- char ) +PRIM(hv_X2d_getchar) + TOS.n = hv_getchar(TOS.n); +MIRP + +// : hv-haschar ( hvtermno -- res ) +PRIM(hv_X2d_haschar) + TOS.n = hv_haschar(TOS.n); +MIRP + +// : hv-reg-crq ( unit qaddr qsize -- res ) +PRIM(hv_X2d_reg_X2d_crq) + unsigned long qsize = TOS.u; POP; + unsigned long qaddr = TOS.u; POP; + unsigned int unit = TOS.u; + TOS.n = hv_reg_crq(unit, qaddr, qsize); +MIRP + +// : hv-free-crq ( unit -- ) +PRIM(hv_X2d_free_X2d_crq) + unsigned int unit = TOS.u; POP; + hv_free_crq(unit); +MIRP + +// : hv-send-crq ( unit msgaddr -- rc ) +PRIM(hv_X2d_send_X2d_crq) + uint64_t *msgaddr = (uint64_t *)TOS.u; POP; + unsigned int unit = TOS.u; + TOS.n = hv_send_crq(unit, msgaddr); +MIRP + +// : hv-put-tce ( liobn ioba tce -- rc ) +PRIM(hv_X2d_put_X2d_tce) + uint64_t tce = TOS.u; POP; + uint64_t ioba = TOS.u; POP; + uint32_t liobn = TOS.u; + TOS.u = hv_generic(H_PUT_TCE, liobn, ioba, tce); +MIRP + +PRIM(RB_X40) + unsigned long qaddr = TOS.u; + TOS.u = hv_logical_ci_load(1, qaddr); +MIRP +PRIM(RB_X21) + unsigned long qaddr = TOS.u; POP; + unsigned char val = TOS.u; POP; + hv_logical_ci_store(1, qaddr, val); +MIRP +PRIM(RW_X40) + unsigned long qaddr = TOS.u; + TOS.u = hv_logical_ci_load(2, qaddr); +MIRP +PRIM(RW_X21) + unsigned long qaddr = TOS.u; POP; + unsigned short val = TOS.u; POP; + hv_logical_ci_store(2, qaddr, val); +MIRP +PRIM(RL_X40) + unsigned long qaddr = TOS.u; + TOS.u = hv_logical_ci_load(4, qaddr); +MIRP +PRIM(RL_X21) + unsigned long qaddr = TOS.u; POP; + unsigned int val = TOS.u; POP; + hv_logical_ci_store(4, qaddr, val); +MIRP +PRIM(RX_X40) + unsigned long qaddr = TOS.u; + TOS.u = hv_logical_ci_load(8, qaddr); +MIRP +PRIM(RX_X21) + unsigned long qaddr = TOS.u; POP; + unsigned long val = TOS.u; POP; + hv_logical_ci_store(8, qaddr, val); +MIRP + +PRIM(hv_X2d_logical_X2d_memop) + unsigned long op = TOS.u; POP; + unsigned long count = TOS.u; POP; + unsigned long esize = TOS.u; POP; + unsigned long src = TOS.u; POP; + unsigned long dst = TOS.u; + TOS.u = hv_logical_memop(dst, src, esize, count, op); +MIRP + +PRIM(hv_X2d_cas) + unsigned long size = TOS.u; POP; + unsigned long buf = TOS.u; POP; + unsigned long vec = TOS.u; + TOS.u = hv_cas(vec, buf, size); +MIRP + +PRIM(hv_X2d_rtas_X2d_update) + unsigned long rtas_entry = TOS.u; POP; + unsigned long rtas_base = TOS.u; + TOS.u = hv_generic(KVMPPC_H_RTAS_UPDATE, rtas_base, rtas_entry); +MIRP + +PRIM(get_X2d_print_X2d_version) + unsigned long addr = TOS.u; POP; + get_print_banner(addr); +MIRP + +PRIM(check_X2d_and_X2d_patch_X2d_sc1) + unsigned long end = TOS.u; POP; + unsigned long start = TOS.u; POP; + unsigned long patch_ins = TOS.u; POP; + + patch_broken_sc1((void*)start, (void*)end, (void*)patch_ins); +MIRP diff --git a/qemu/roms/SLOF/lib/libhvcall/hvcall.in b/qemu/roms/SLOF/lib/libhvcall/hvcall.in new file mode 100644 index 000000000..4437b77f0 --- /dev/null +++ b/qemu/roms/SLOF/lib/libhvcall/hvcall.in @@ -0,0 +1,34 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +cod(hv-putchar) +cod(hv-getchar) +cod(hv-haschar) +cod(hv-reg-crq) +cod(hv-free-crq) +cod(hv-send-crq) +cod(hv-put-tce) +cod(check-and-patch-sc1) + +cod(RB@) +cod(RB!) +cod(RW@) +cod(RW!) +cod(RL@) +cod(RL!) +cod(RX@) +cod(RX!) + +cod(hv-logical-memop) +cod(hv-cas) +cod(hv-rtas-update) +cod(get-print-version) diff --git a/qemu/roms/SLOF/lib/libhvcall/libhvcall.h b/qemu/roms/SLOF/lib/libhvcall/libhvcall.h new file mode 100644 index 000000000..193b7383d --- /dev/null +++ b/qemu/roms/SLOF/lib/libhvcall/libhvcall.h @@ -0,0 +1,107 @@ +#ifndef __LIBHVCALL_H__ +#define __LIBHVCALL_H__ + +#define H_SUCCESS 0 +#define H_HARDWARE -1 + +#define H_GET_TCE 0x1C +#define H_PUT_TCE 0x20 +#define H_LOGICAL_CI_LOAD 0x3c +#define H_LOGICAL_CI_STORE 0x40 +#define H_GET_TERM_CHAR 0x54 +#define H_PUT_TERM_CHAR 0x58 +#define H_REG_CRQ 0xFC +#define H_FREE_CRQ 0x100 +#define H_SEND_CRQ 0x108 +#define H_REGISTER_LOGICAL_LAN 0x114 +#define H_FREE_LOGICAL_LAN 0x118 +#define H_ADD_LOGICAL_LAN_BUFFER 0x11C +#define H_SEND_LOGICAL_LAN 0x120 + +/* KVM specific ones */ +#define KVMPPC_HCALL_BASE 0xf000 +#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0) +#define KVMPPC_H_LOGICAL_MEMOP (KVMPPC_HCALL_BASE + 0x1) +/* Client Architecture support */ +#define KVMPPC_H_CAS (KVMPPC_HCALL_BASE + 0x2) +#define KVMPPC_H_RTAS_UPDATE (KVMPPC_HCALL_BASE + 0x3) +#define KVMPPC_H_REPORT_MC_ERR (KVMPPC_HCALL_BASE + 0x4) +#define KVMPPC_HCALL_MAX KVMPPC_H_NMI_MCE + +#ifndef __ASSEMBLY__ + +extern long hv_generic(unsigned long opcode, ...); + +extern void hv_putchar(char c, int hvtermno); +extern char hv_getchar(int hvtermno); +extern char hv_haschar(int hvtermno); +extern void get_print_banner(unsigned long addr); + +extern int hv_send_crq(unsigned int unit, uint64_t *msgaddr); + +static inline long hv_reg_crq(unsigned int unit, unsigned long qaddr, + unsigned long qsize) +{ + return hv_generic(H_REG_CRQ, unit, qaddr, qsize); +} + +static inline void hv_free_crq(unsigned int unit) +{ + hv_generic(H_FREE_CRQ, unit); +} + +extern long hv_send_logical_lan(unsigned long unit_address, + unsigned long desc1, unsigned long desc2, + unsigned long desc3, unsigned long desc4, + unsigned long desc5, unsigned long desc6); + +static inline long h_register_logical_lan(unsigned long unit_address, + unsigned long buf_list, + unsigned long rec_q, + unsigned long filter_list, + unsigned long mac_address) +{ + return hv_generic(H_REGISTER_LOGICAL_LAN, unit_address, + buf_list, rec_q, filter_list, mac_address); +} + +static inline long h_free_logical_lan(unsigned long unit_address) +{ + return hv_generic(H_FREE_LOGICAL_LAN, unit_address); +} + +static inline long h_add_logical_lan_buffer(unsigned long unit_address, + unsigned long buffer) +{ + return hv_generic(H_ADD_LOGICAL_LAN_BUFFER, unit_address, buffer); +} + +#define HV_RTAS_MAX_ARGRET 5 + +struct hv_rtas_call { + uint32_t token; + uint32_t nargs; + uint32_t nrets; + uint32_t argret[HV_RTAS_MAX_ARGRET]; +}; + +static inline unsigned long h_rtas(struct hv_rtas_call *rtas_buf) +{ + return hv_generic(KVMPPC_H_RTAS, (unsigned long)rtas_buf); +} + +extern unsigned long hv_logical_ci_load(unsigned long size, unsigned long addr); +extern unsigned long hv_logical_ci_store(unsigned long size, unsigned long addr, + unsigned long value); + +extern unsigned long hv_logical_memop(unsigned long dst, unsigned long src, + unsigned long esize, unsigned long count, + unsigned long op); +extern int patch_broken_sc1(void *start, void *end, uint32_t *test_ins); + +extern unsigned long hv_cas(unsigned long vec, unsigned long buf, + unsigned long size); + +#endif /* __ASSEMBLY__ */ + +#endif /* __LIBHVCALL_H__ */ diff --git a/qemu/roms/SLOF/lib/libipmi/Makefile b/qemu/roms/SLOF/lib/libipmi/Makefile new file mode 100644 index 000000000..4777f9edd --- /dev/null +++ b/qemu/roms/SLOF/lib/libipmi/Makefile @@ -0,0 +1,28 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2007 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +LIBIPMICMNDIR = $(shell pwd) + +include $(TOPCMNDIR)/make.rules + +TARGET = ../libipmi.a + +all: $(TARGET) + +$(TARGET): + cp libipmi.oco $@ + +clean: + +distclean: diff --git a/qemu/roms/SLOF/lib/libipmi/libipmi.code b/qemu/roms/SLOF/lib/libipmi/libipmi.code new file mode 100644 index 000000000..59c124418 --- /dev/null +++ b/qemu/roms/SLOF/lib/libipmi/libipmi.code @@ -0,0 +1,120 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <libipmi.h> + +// : ipmi-kcs-cmd ( in-buf in-len out-buf out-maxlen -- out-len errorcode ) +PRIM(IPMI_X2d_KCS_X2d_CMD) + cell maxlen = TOS; POP; + cell outbuf = TOS; POP; + int len = TOS.n; POP; + cell inbuf = TOS; + int retval; + retval = ipmi_kcs_cmd(inbuf.a, outbuf.a, maxlen.n, (uint32_t *) &len); + TOS.n = len; + PUSH; TOS.n = retval; +MIRP + + +PRIM(IPMI_X2d_SYSTEM_X2d_REBOOT) + ipmi_system_reboot(); +MIRP + + +PRIM(IPMI_X2d_POWER_X2d_OFF) + ipmi_power_off(); +MIRP + + +// : ipmi-oem-stop-bootwatchdog ( -- errorcode ) +PRIM(IPMI_X2d_OEM_X2d_STOP_X2d_BOOTWATCHDOG) + PUSH; + TOS.n = ipmi_oem_stop_bootwatchdog(); +MIRP + + +// : ipmi-oem-set-bootwatchdog ( seconds -- errorcode ) +PRIM(IPMI_X2d_OEM_X2d_SET_X2d_BOOTWATCHDOG) + int sec = TOS.n; + TOS.n = ipmi_oem_set_bootwatchdog(sec); +MIRP + + +// : ipmi-oem-reset-bootwatchdog ( -- errorcode ) +PRIM(IPMI_X2d_OEM_X2d_RESET_X2d_BOOTWATCHDOG) + PUSH; + TOS.n = ipmi_oem_reset_bootwatchdog(); +MIRP + + +// : ipmi-oem-led-set ( type instance state -- errorcode ) +PRIM(IPMI_X2d_OEM_X2d_LED_X2d_SET) + int state = TOS.n; POP; + int instance = TOS.n; POP; + int type = TOS.n; + TOS.n = ipmi_oem_led_set(type, instance, state); +MIRP + + +// : ipmi-oem-read-vpd ( offset length dst -- status ) +PRIM(IPMI_X2d_OEM_X2d_READ_X2d_VPD) + cell dest = TOS; POP; + int len = TOS.n; POP; + int offset = TOS.n; + TOS.n = ipmi_oem_read_vpd(dest.a, len, offset); +MIRP + +// : ipmi-oem-write-vpd ( offset length src -- status ) +PRIM(IPMI_X2d_OEM_X2d_WRITE_X2d_VPD) + cell src = TOS; POP; + int len = TOS.n; POP; + int offset = TOS.n; + TOS.n = ipmi_oem_write_vpd(src.a, len, offset); +MIRP + + +// : ipmi-oem-get-blade-descr ( buf maxlen -- len status ) +PRIM(IPMI_X2d_OEM_X2d_GET_X2d_BLADE_X2d_DESCR) + int maxlen = TOS.n; POP; + cell buf = TOS; + int len = 0; + int retval; + retval = ipmi_oem_get_blade_descr(buf.a, maxlen, (uint32_t *) &len); + TOS.n = len; + PUSH; TOS.n = retval; +MIRP + + +// : ipmi-oem-bios2sp ( str-ptr str-len swid type -- errorcode ) +PRIM(IPMI_X2d_OEM_X2d_BIOS2SP) + int type = TOS.n; POP; + int swid = TOS.n; POP; + int len = TOS.n; POP; + void* addr = TOS.a; + TOS.n = ipmi_oem_bios2sp(swid, type, addr, len); +MIRP + +// : ipmi-set-sensor ( param-1 ... param-n n command sensor - errorcode ) +PRIM(IPMI_X2d_SET_X2d_SENSOR) + int sensor = TOS.n; POP; + int cmd = TOS.n; POP; + int n = TOS.n; + int i = n; + uint8_t param[10]; + while (i>0) { + i--; + POP; param[i]=TOS.n; + }; + TOS.n = ipmi_set_sensor((cmd<<8)+sensor,n, + param[0],param[1],param[2],param[3],param[4], + param[5],param[6],param[7],param[8],param[9]); +MIRP diff --git a/qemu/roms/SLOF/lib/libipmi/libipmi.h b/qemu/roms/SLOF/lib/libipmi/libipmi.h new file mode 100644 index 000000000..9ac83087e --- /dev/null +++ b/qemu/roms/SLOF/lib/libipmi/libipmi.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef __LIBIPMI_H +#define __LIBIPMI_H + +#include <stdint.h> + +extern int ipmi_kcs_cmd(uint8_t *, uint8_t *, uint32_t, uint32_t *); + +extern void ipmi_system_reboot(void); +extern void ipmi_power_off(void); +extern int ipmi_set_sensor(const int sensor, int number_of_args, ...); + +extern int ipmi_oem_stop_bootwatchdog(void); +extern int ipmi_oem_set_bootwatchdog(uint16_t seconds); +extern int ipmi_oem_reset_bootwatchdog(void); +extern int ipmi_oem_led_set(int type, int instance, int state); +extern uint32_t ipmi_oem_read_vpd(uint8_t *dst, uint32_t len, uint32_t offset); +extern uint32_t ipmi_oem_write_vpd(uint8_t *src, uint32_t len, uint32_t offset); +extern uint32_t ipmi_oem_get_blade_descr(uint8_t *dst, uint32_t maxlen, uint32_t *len); +extern int ipmi_oem_bios2sp(int swid, int type, char *data, int len); + +#endif diff --git a/qemu/roms/SLOF/lib/libipmi/libipmi.in b/qemu/roms/SLOF/lib/libipmi/libipmi.in new file mode 100644 index 000000000..5b0e0ec5e --- /dev/null +++ b/qemu/roms/SLOF/lib/libipmi/libipmi.in @@ -0,0 +1,24 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +cod(IPMI-KCS-CMD) +cod(IPMI-SYSTEM-REBOOT) +cod(IPMI-POWER-OFF) +cod(IPMI-OEM-STOP-BOOTWATCHDOG) +cod(IPMI-OEM-SET-BOOTWATCHDOG) +cod(IPMI-OEM-RESET-BOOTWATCHDOG) +cod(IPMI-OEM-LED-SET) +cod(IPMI-OEM-READ-VPD) +cod(IPMI-OEM-WRITE-VPD) +cod(IPMI-OEM-GET-BLADE-DESCR) +cod(IPMI-OEM-BIOS2SP) +cod(IPMI-SET-SENSOR) diff --git a/qemu/roms/SLOF/lib/libipmi/libipmi.oco b/qemu/roms/SLOF/lib/libipmi/libipmi.oco Binary files differnew file mode 100644 index 000000000..74af7249c --- /dev/null +++ b/qemu/roms/SLOF/lib/libipmi/libipmi.oco diff --git a/qemu/roms/SLOF/lib/libnativeio/nativeio.code b/qemu/roms/SLOF/lib/libnativeio/nativeio.code new file mode 100644 index 000000000..4887b115a --- /dev/null +++ b/qemu/roms/SLOF/lib/libnativeio/nativeio.code @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* There are 970 implementations. If we are ever to run native IOs + * on Power7 in hypervisor mode, these will have to change to use + * the new CI-load/store instructions + */ +PRIM(RB_X40) GET_CHAR1; SET_CI; GET_CHAR2; CLR_CI; GET_CHAR3; MIRP +PRIM(RB_X21) PUT_CHAR1; SET_CI; PUT_CHAR2; CLR_CI; MIRP +PRIM(RW_X40) GET_WORD1; SET_CI; GET_WORD2; CLR_CI; GET_WORD3; MIRP +PRIM(RW_X21) PUT_WORD1; SET_CI; PUT_WORD2; CLR_CI; MIRP +PRIM(RL_X40) GET_LONG1; SET_CI; GET_LONG2; CLR_CI; GET_LONG3; MIRP +PRIM(RL_X21) PUT_LONG1; SET_CI; PUT_LONG2; CLR_CI; MIRP +PRIM(RX_X40) GET_XONG1; SET_CI; GET_XONG2; CLR_CI; GET_XONG3; MIRP +PRIM(RX_X21) PUT_XONG1; SET_CI; PUT_XONG2; CLR_CI; MIRP + diff --git a/qemu/roms/SLOF/lib/libnativeio/nativeio.in b/qemu/roms/SLOF/lib/libnativeio/nativeio.in new file mode 100644 index 000000000..f5fb9d977 --- /dev/null +++ b/qemu/roms/SLOF/lib/libnativeio/nativeio.in @@ -0,0 +1,22 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +// I/O accesses. +cod(RB@) +cod(RB!) +cod(RW@) +cod(RW!) +cod(RL@) +cod(RL!) +cod(RX@) +cod(RX!) + diff --git a/qemu/roms/SLOF/lib/libnvram/Makefile b/qemu/roms/SLOF/lib/libnvram/Makefile new file mode 100644 index 000000000..d4e9a617e --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/Makefile @@ -0,0 +1,53 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +SRCS = nvram.c envvar.c + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames +CPPFLAGS = -I../libc/include $(CPUARCHDEF) $(FLAG) \ + -I$(INCLBRDDIR) -I$(INCLCMNDIR)/$(CPUARCH) -I. -I../../include +LDFLAGS = -nostdlib + +TARGET = ../libnvram.a + +all: $(TARGET) + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -MM $(CPPFLAGS) $(CFLAGS) $(SRCS) > Makefile.dep + + +# Include dependency file if available: +-include Makefile.dep + diff --git a/qemu/roms/SLOF/lib/libnvram/envvar.c b/qemu/roms/SLOF/lib/libnvram/envvar.c new file mode 100644 index 000000000..87aaf27a0 --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/envvar.c @@ -0,0 +1,243 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> +#include "../libc/include/stdio.h" +#include "../libc/include/string.h" +#include "../libc/include/stdlib.h" +#include "nvram.h" + +/* returns the offset of the first byte after the searched envvar */ +static int get_past_env_pos(partition_t part, char *envvar) +{ + int offset, len; + static char temp[256]; + uint8_t data; + + offset=part.addr; + + memset(temp, 0, 256); + + do { + len=0; + while((data=nvram_read_byte(offset++)) && len < 256) { + temp[len++]=data; + } + if (!strncmp(envvar, temp, strlen(envvar))) { + return offset; + } + } while (len); + + return -1; +} + +/** + * @param partition name of the envvar partition + * @param envvar name of the environment variable + * @return pointer to temporary string containing the value of envvar + */ + +char *get_env(partition_t part, char *envvar) +{ + static char temp[256+1]; + int len, offset; + uint8_t data; + + DEBUG("get_env %s... ", envvar); + if(!part.addr) { + /* ERROR: No environment variable partition */ + DEBUG("invalid partition.\n"); + return NULL; + } + + offset=part.addr; + + do { + len=0; + while((data=nvram_read_byte(offset++)) && len < 256) { + temp[len++]=data; + } + temp[len]=0; + + if (!strncmp(envvar, temp, strlen(envvar))) { + int pos=0; + while (temp[pos]!='=' && pos < len) pos++; + // DEBUG("value='%s'\n", temp+pos+1); + return temp+pos+1; + } + } while (len); + + DEBUG("not found\n"); + return NULL; +} + +static int find_last_envvar(partition_t part) +{ + uint8_t last, current; + int offset; + + offset=part.addr; + + last=nvram_read_byte(part.addr); + + for (offset=part.addr; offset<(int)(part.addr+part.len); offset++) { + current=nvram_read_byte(offset); + if(!last && !current) + return offset; + + last=current; + } + + return -1; +} + +int add_env(partition_t part, char *envvar, char *value) +{ + int freespace, last, len, offset; + unsigned int i; + + /* Find offset where we can write */ + last = find_last_envvar(part); + + /* How much space do we have left? */ + freespace = part.addr+part.len-last; + + /* how long is the entry we want to write? */ + len = strlen(envvar) + strlen(value) + 2; + + if(freespace<len) { + // TODO try to increase partition size + return -1; + } + + offset=last; + + for(i=0; i<strlen(envvar); i++) + nvram_write_byte(offset++, envvar[i]); + + nvram_write_byte(offset++, '='); + + for(i=0; i<strlen(value); i++) + nvram_write_byte(offset++, value[i]); + + return 0; +} + +int del_env(partition_t part, char *envvar) +{ + int last, current, pos, i; + char *buffer; + + if(!part.addr) + return -1; + + last=find_last_envvar(part); + current = pos = get_past_env_pos(part, envvar); + + // TODO is this really required? + /* go back to non-0 value */ + current--; + + while (nvram_read_byte(current)) + current--; + + // TODO is this required? + current++; + + buffer=get_nvram_buffer(last-pos); + + for (i=0; i<last-pos; i++) + buffer[i]=nvram_read_byte(i+pos); + + for (i=0; i<last-pos; i++) + nvram_write_byte(i+current, buffer[i]); + + free_nvram_buffer(buffer); + + erase_nvram(last, current+last-pos); + + return 0; +} + +int set_env(partition_t part, char *envvar, char *value) +{ + char *oldvalue, *buffer; + int last, current, buffersize, i; + + DEBUG("set_env %lx[%lx]: %s=%s\n", part.addr, part.len, envvar, value); + + if(!part.addr) + return -1; + + /* Check whether the environment variable exists already */ + oldvalue = get_env(part, envvar); + + if(oldvalue==NULL) + return add_env(part, envvar, value); + + + /* The value did not change. So we succeeded! */ + if(!strncmp(oldvalue, value, strlen(value)+1)) + return 0; + + /* we need to overwrite environment variables, back them up first */ + + // DEBUG("overwriting existing environment variable\n"); + + /* allocate a buffer */ + last=find_last_envvar(part); + current=get_past_env_pos(part, envvar); + buffersize = last - current; + buffer=get_nvram_buffer(buffersize); + if(!buffer) + return -1; + + for (i=0; i<buffersize; i++) { + buffer[i] = nvram_read_byte(current+i); + } + + /* walk back until the = */ + while (nvram_read_byte(current)!='=') { + current--; + } + + /* Start at envvar= */ + current++; + + /* Write the new value */ + for(i=0; i<(int)strlen(value); i++) { + nvram_write_byte(current++, value[i]); + } + + /* Write end of string marker */ + nvram_write_byte(current++, 0); + + /* Copy back the buffer */ + for (i=0; i<buffersize; i++) { + nvram_write_byte(current++, buffer[i]); + } + + free_nvram_buffer(buffer); + + /* If the new environment variable content is shorter than the old one, + * we need to erase the rest of the bytes + */ + + if (current<last) { + for(i=current; i<last; i++) { + nvram_write_byte(i, 0); + } + } + + return 0; /* success */ +} + diff --git a/qemu/roms/SLOF/lib/libnvram/libnvram.code b/qemu/roms/SLOF/lib/libnvram/libnvram.code new file mode 100644 index 000000000..723941d3e --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/libnvram.code @@ -0,0 +1,287 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +#include <nvram.h> + +#define STRING_INIT(str) \ + char str[255]; \ + char * str##_address; \ + int str##_length; + +#define STRING_FROM_STACK(str) \ + str##_length = TOS.u; POP; \ + str##_address = TOS.a; POP; \ + memcpy(str, str##_address, str##_length); \ + memset(str + str##_length, 0, 255 - str##_length); + +PRIM(nvram_X2d_c_X40) + unsigned int offset = TOS.u; + TOS.u=nvram_read_byte(offset); +MIRP + +PRIM(nvram_X2d_w_X40) + unsigned int offset = TOS.u; + TOS.u=nvram_read_word(offset); +MIRP + +PRIM(nvram_X2d_l_X40) + unsigned int offset = TOS.u; + TOS.u=nvram_read_dword(offset); +MIRP + +PRIM(nvram_X2d_x_X40) + unsigned int offset = TOS.u; + TOS.u=nvram_read_qword(offset); +MIRP + +PRIM(nvram_X2d_c_X21) + nvram_write_byte(TOS.u, NOS.u); + POP; POP; +MIRP + +PRIM(nvram_X2d_w_X21) + nvram_write_word(TOS.u, NOS.u); + POP; POP; +MIRP + +PRIM(nvram_X2d_l_X21) + nvram_write_dword(TOS.u, NOS.u); + POP; POP; +MIRP + +PRIM(nvram_X2d_x_X21) + nvram_write_qword(TOS.u, NOS.u); + POP; POP; +MIRP + +/* get-nvram-partition ( type -- addr len FAILED? ) */ +PRIM(get_X2d_nvram_X2d_partition) + partition_t partition; + unsigned int ptype = TOS.u; + partition = get_partition(ptype, NULL); + if(partition.len && partition.len != -1) { + TOS.u = partition.addr; + PUSH; + TOS.u = partition.len; + PUSH; + TOS.u = 0; // FALSE + } else { + TOS.u = -1; // TRUE + } +MIRP + +/* get-named-nvram-partition ( name.addr name.len -- addr len FAILED? ) */ +PRIM(get_X2d_named_X2d_nvram_X2d_partition) + STRING_INIT(name) + partition_t partition; + + STRING_FROM_STACK(name) + partition = get_partition(-1, name); + + if(partition.len && partition.len != -1) { + PUSH; + TOS.u = partition.addr; + PUSH; + TOS.u = partition.len; + PUSH; + TOS.u = 0; // FALSE + } else { + PUSH; + TOS.u = -1; // TRUE + } +MIRP + + + +/* new-nvram-partition ( type name.addr name.len len -- part.offs part.len FALSE | TRUE) */ +PRIM(new_X2d_nvram_X2d_partition) + int type, len, i, slen; + char name[12], *addr; + partition_t partition; + + len = TOS.u; POP; + slen = TOS.u; POP; + addr = (char *)TOS.u; POP; + type = TOS.u; POP; + + for (i=0; i<12; i++) { + if(slen>i) + name[i]=addr[i]; + else + name[i]=0; + } + + partition=new_nvram_partition(type, name, len); + + if(!partition.len) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = partition.addr; + PUSH; TOS.u = partition.len; + PUSH; TOS.u = 0; // FALSE + } +MIRP + +/* inrease-nvram-partition ( part.offs part.len new-len -- FALSE | TRUE ) */ +PRIM(increase_X2d_nvram_X2d_partition) + int len, ret; + partition_t partition; + + // FIXME + partition.addr = TOS.u; POP; + partition.len = TOS.u; POP; + len = TOS.u; POP; + + ret=increase_nvram_partition_size(partition, len); + + PUSH; + + if(!ret) + TOS.u=-1; // TRUE + else + TOS.u=0; // FALSE + +MIRP + +PRIM(internal_X2d_reset_X2d_nvram) + reset_nvram(); +MIRP + +PRIM(wipe_X2d_nvram) + wipe_nvram(); +MIRP + +PRIM(nvram_X2d_debug) + nvram_debug(); +MIRP + +// ( part.start part.len name.addr name.len -- var.addr var.len TRUE | false ) +PRIM(internal_X2d_get_X2d_env) + STRING_INIT(name) + partition_t part; + char *val; + + STRING_FROM_STACK(name) + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + val=get_env(part, name); + if(val) { + PUSH; TOS.a = val; + PUSH; TOS.u = strlen(val); + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } +MIRP + +// ( part.start part.len name.addr name.len val.addr val.len -- FALSE|TRUE) +PRIM(internal_X2d_add_X2d_env) + STRING_INIT(name) + STRING_INIT(value) + partition_t part; + int ret; + + STRING_FROM_STACK(value) + STRING_FROM_STACK(name) + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + ret=add_env(part, name, value); + if(ret) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } +MIRP + +// ( part.addr part.len name.addr name.len -- FALSE|TRUE) +PRIM(internal_X2d_del_X2d_env) + STRING_INIT(name) + partition_t part; + int ret; + + STRING_FROM_STACK(name); + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + ret=del_env(part, name); + if(ret) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } + +MIRP + +// internal-set-env ( part.addr part.len name.addr name.len val.addr val.len -- FALSE|TRUE) +PRIM(internal_X2d_set_X2d_env) + STRING_INIT(name) + STRING_INIT(value) + partition_t part; + int ret; + + STRING_FROM_STACK(value) + STRING_FROM_STACK(name) + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + ret=set_env(part, name, value); + if(ret) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } +MIRP + +// ( part.addr part.len -- FALSE|TRUE) +PRIM(erase_X2d_nvram_X2d_partition) + partition_t part; + int ret; + + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + ret=clear_nvram_partition(part); + if(ret) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } + +MIRP + +// ( part.addr part.len -- FALSE|TRUE) +PRIM(delete_X2d_nvram_X2d_partition) + partition_t part; + int ret; + + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + ret=delete_nvram_partition(part); + if(ret) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } + +MIRP + +// ( fetch_token store_token size nvram-addr -- ) +PRIM(internal_X2d_nvram_X2d_init) + void *nvram_addr = TOS.a; POP; + uint32_t nvram_size = TOS.u; POP; + uint32_t store_token = TOS.u; POP; + long fetch_token = TOS.u; POP; + + nvram_init(fetch_token, store_token, nvram_size, nvram_addr); +MIRP diff --git a/qemu/roms/SLOF/lib/libnvram/libnvram.in b/qemu/roms/SLOF/lib/libnvram/libnvram.in new file mode 100644 index 000000000..bbb20a889 --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/libnvram.in @@ -0,0 +1,42 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* NVRAM access primitives */ +cod(nvram-c@) +cod(nvram-c!) +cod(nvram-w@) +cod(nvram-w!) +cod(nvram-l@) +cod(nvram-l!) +cod(nvram-x@) +cod(nvram-x!) + +/* Generic NVRAM helpers */ +cod(internal-reset-nvram) +cod(nvram-debug) +cod(wipe-nvram) + +/* NVRAM Partition Handling */ +cod(get-nvram-partition) +cod(get-named-nvram-partition) +cod(new-nvram-partition) +cod(increase-nvram-partition) +cod(erase-nvram-partition) +cod(delete-nvram-partition) + +/* NVRAM environment access words */ +cod(internal-get-env) +cod(internal-add-env) +cod(internal-del-env) +cod(internal-set-env) + +cod(internal-nvram-init) diff --git a/qemu/roms/SLOF/lib/libnvram/nvram.c b/qemu/roms/SLOF/lib/libnvram/nvram.c new file mode 100644 index 000000000..5c1137669 --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/nvram.c @@ -0,0 +1,623 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "cache.h" +#include "nvram.h" +#include "../libhvcall/libhvcall.h" + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <southbridge.h> +#include <nvramlog.h> +#include <byteorder.h> + +#ifdef RTAS_NVRAM +static uint32_t fetch_token; +static uint32_t store_token; +static uint32_t NVRAM_LENGTH; +static char *nvram_buffer; /* use buffer allocated by SLOF code */ +#else +#ifndef NVRAM_LENGTH +#define NVRAM_LENGTH 0x10000 +#endif +/* + * This is extremely ugly, but still better than implementing + * another sbrk() around it. + */ +static char nvram_buffer[NVRAM_LENGTH]; +#endif + +static uint8_t nvram_buffer_locked=0x00; + +void nvram_init(uint32_t _fetch_token, uint32_t _store_token, + long _nvram_length, void* nvram_addr) +{ +#ifdef RTAS_NVRAM + fetch_token = _fetch_token; + store_token = _store_token; + NVRAM_LENGTH = _nvram_length; + nvram_buffer = nvram_addr; + + DEBUG("\nNVRAM: size=%d, fetch=%x, store=%x\n", + NVRAM_LENGTH, fetch_token, store_token); +#endif +} + + +void asm_cout(long Character,long UART,long NVRAM); + +#if defined(DISABLE_NVRAM) + +static volatile uint8_t nvram[NVRAM_LENGTH]; /* FAKE */ + +#define nvram_access(type,size,name) \ + type nvram_read_##name(unsigned int offset) \ + { \ + type *pos; \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return 0; \ + pos = (type *)(nvram+offset); \ + return *pos; \ + } \ + void nvram_write_##name(unsigned int offset, type data) \ + { \ + type *pos; \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return; \ + pos = (type *)(nvram+offset); \ + *pos = data; \ + } + +#elif defined(RTAS_NVRAM) + +static inline void nvram_fetch(unsigned int offset, void *buf, unsigned int len) +{ + struct hv_rtas_call rtas = { + .token = fetch_token, + .nargs = 3, + .nrets = 2, + .argret = { offset, (uint32_t)(unsigned long)buf, len }, + }; + h_rtas(&rtas); +} + +static inline void nvram_store(unsigned int offset, void *buf, unsigned int len) +{ + struct hv_rtas_call rtas = { + .token = store_token, + .nargs = 3, + .nrets = 2, + .argret = { offset, (uint32_t)(unsigned long)buf, len }, + }; + h_rtas(&rtas); +} + +#define nvram_access(type,size,name) \ + type nvram_read_##name(unsigned int offset) \ + { \ + type val; \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return 0; \ + nvram_fetch(offset, &val, size / 8); \ + return val; \ + } \ + void nvram_write_##name(unsigned int offset, type data) \ + { \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return; \ + nvram_store(offset, &data, size / 8); \ + } + +#else /* DISABLE_NVRAM */ + +static volatile uint8_t *nvram = (volatile uint8_t *)SB_NVRAM_adr; + +#define nvram_access(type,size,name) \ + type nvram_read_##name(unsigned int offset) \ + { \ + type *pos; \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return 0; \ + pos = (type *)(nvram+offset); \ + return ci_read_##size(pos); \ + } \ + void nvram_write_##name(unsigned int offset, type data) \ + { \ + type *pos; \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return; \ + pos = (type *)(nvram+offset); \ + ci_write_##size(pos, data); \ + } + +#endif + +/* + * producer for nvram access functions. Since these functions are + * basically all the same except for the used data types, produce + * them via the nvram_access macro to keep the code from bloating. + */ + +nvram_access(uint8_t, 8, byte) +nvram_access(uint16_t, 16, word) +nvram_access(uint32_t, 32, dword) +nvram_access(uint64_t, 64, qword) + + + +/** + * This function is a minimal abstraction for our temporary + * buffer. It should have been malloced, but since there is no + * usable malloc, we go this route. + * + * @return pointer to temporary buffer + */ + +char *get_nvram_buffer(int len) +{ + if(len>NVRAM_LENGTH) + return NULL; + + if(nvram_buffer_locked) + return NULL; + + nvram_buffer_locked = 0xff; + + return nvram_buffer; +} + +/** + * @param buffer pointer to the allocated buffer. This + * is unused, but nice in case we ever get a real malloc + */ + +void free_nvram_buffer(char *buffer __attribute__((unused))) +{ + nvram_buffer_locked = 0x00; +} + +/** + * @param fmt format string, like in printf + * @param ... variable number of arguments + */ + +int nvramlog_printf(const char* fmt, ...) +{ + char buff[256]; + int count, i; + va_list ap; + + va_start(ap, fmt); + count = vsprintf(buff, fmt, ap); + va_end(ap); + + for (i=0; i<count; i++) + asm_cout(buff[i], 0, 1); + + return count; +} + +/** + * @param offset start offset of the partition header + */ + +static uint8_t get_partition_type(int offset) +{ + return nvram_read_byte(offset); +} + +/** + * @param offset start offset of the partition header + */ + +static uint8_t get_partition_header_checksum(int offset) +{ + return nvram_read_byte(offset+1); +} + +/** + * @param offset start offset of the partition header + */ + +static uint16_t get_partition_len(int offset) +{ + return nvram_read_word(offset+2); +} + +/** + * @param offset start offset of the partition header + * @return static char array containing the partition name + * + * NOTE: If the partition name needs to be non-temporary, strdup + * and use the copy instead. + */ + +static char * get_partition_name(int offset) +{ + static char name[12]; + int i; + for (i=0; i<12; i++) + name[i]=nvram_read_byte(offset+4+i); + + DEBUG("name: \"%s\"\n", name); + return name; +} + +static uint8_t calc_partition_header_checksum(int offset) +{ + uint16_t plainsum; + uint8_t checksum; + int i; + + plainsum = nvram_read_byte(offset); + + for (i=2; i<PARTITION_HEADER_SIZE; i++) + plainsum+=nvram_read_byte(offset+i); + + checksum=(plainsum>>8)+(plainsum&0xff); + + return checksum; +} + +static int calc_used_nvram_space(void) +{ + int walk, len; + + for (walk=0; walk<NVRAM_LENGTH;) { + if(nvram_read_byte(walk) == 0 + || get_partition_header_checksum(walk) != + calc_partition_header_checksum(walk)) { + /* If there's no valid entry, bail out */ + break; + } + + len=get_partition_len(walk); + DEBUG("... part len=%x, %x\n", len, len*16); + + if(!len) { + /* If there's a partition type but no len, bail out. + * Don't bail out if type is 0. This can be used to + * find the offset of the first free byte. + */ + break; + } + + walk += len * 16; + } + DEBUG("used nvram space: %d\n", walk); + + return walk; +} + +/** + * + * @param type partition type. Set this to the partition type you are looking + * for. If there are several partitions with the same type, only + * the first partition with that type will be found. + * Set to -1 to ignore. Set to 0 to find free unpartitioned space. + * + * @param name partition name. Set this to the name of the partition you are + * looking for. If there are several partitions with the same name, + * only the first partition with that name will be found. + * Set to NULL to ignore. + * + * To disambiguate the partitions you should have a unique name if you plan to + * have several partitions of the same type. + * + */ + +partition_t get_partition(unsigned int type, char *name) +{ + partition_t ret={0,-1}; + int walk, len; + + DEBUG("get_partition(%i, '%s')\n", type, name); + + for (walk=0; walk<NVRAM_LENGTH;) { + // DEBUG("get_partition: walk=%x\n", walk); + if(get_partition_header_checksum(walk) != + calc_partition_header_checksum(walk)) { + /* If there's no valid entry, bail out */ + break; + } + + len=get_partition_len(walk); + if(type && !len) { + /* If there's a partition type but no len, bail out. + * Don't bail out if type is 0. This can be used to + * find the offset of the first free byte. + */ + break; + } + + /* Check if either type or name or both do not match. */ + if ( (type!=(unsigned int)-1 && type != get_partition_type(walk)) || + (name && strncmp(get_partition_name(walk), name, 12)) ) { + /* We hit another partition. Continue + * at the end of this partition + */ + walk += len*16; + continue; + } + + ret.addr=walk+PARTITION_HEADER_SIZE; + ret.len=(len*16)-PARTITION_HEADER_SIZE; + break; + } + + return ret; +} + +void erase_nvram(int offset, int len) +{ + int i; + + for (i=offset; i<offset+len; i++) + nvram_write_byte(i, 0); +} + +void wipe_nvram(void) +{ + erase_nvram(0, NVRAM_LENGTH); +} + +/** + * @param partition partition structure pointing to the partition to wipe. + * @param header_only if header_only is != 0 only the partition header is + * nulled out, not the whole partition. + */ + +int wipe_partition(partition_t partition, int header_only) +{ + int pstart, len; + + pstart=partition.addr-PARTITION_HEADER_SIZE; + + len=PARTITION_HEADER_SIZE; + + if(!header_only) + len += partition.len; + + erase_nvram(pstart, len); + + return 0; +} + + +static partition_t create_nvram_partition(int type, const char *name, int len) +{ + partition_t ret = { 0, 0 }; + int offset, plen; + unsigned int i; + + plen = ALIGN(len+PARTITION_HEADER_SIZE, 16); + + DEBUG("Creating partition type=%x, name=%s, len=%d plen=%d\n", + type, name, len, plen); + + offset = calc_used_nvram_space(); + + if (NVRAM_LENGTH-(calc_used_nvram_space())<plen) { + DEBUG("Not enough free space.\n"); + return ret; + } + + DEBUG("Writing header."); + + nvram_write_byte(offset, type); + nvram_write_word(offset+2, plen/16); + + for (i=0; i<strlen(name); i++) + nvram_write_byte(offset+4+i, name[i]); + + nvram_write_byte(offset+1, calc_partition_header_checksum(offset)); + + ret.addr = offset+PARTITION_HEADER_SIZE; + ret.len = len; + + DEBUG("partition created: addr=%lx len=%lx\n", ret.addr, ret.len); + + return ret; +} + +static int create_free_partition(void) +{ + int free_space; + partition_t free_part; + + free_space = NVRAM_LENGTH - calc_used_nvram_space() - PARTITION_HEADER_SIZE; + free_part = create_nvram_partition(0x7f, "free space", free_space); + + return (free_part.addr != 0); +} + +partition_t new_nvram_partition(int type, char *name, int len) +{ + partition_t free_part, new_part = { 0, 0 }; + + /* NOTE: Assume all free space is consumed by the "free space" + * partition. This means a partition can not be increased in the middle + * of reset_nvram, which is obviously not a big loss. + */ + + free_part=get_partition(0x7f, NULL); + if( free_part.len && free_part.len != -1) + wipe_partition(free_part, 1); + + new_part = create_nvram_partition(type, name, len); + + if(new_part.len != len) { + new_part.len = 0; + new_part.addr = 0; + } + + create_free_partition(); + + return new_part; +} + +/** + * @param partition partition structure pointing to the partition to wipe. + */ + +int delete_nvram_partition(partition_t partition) +{ + int i; + partition_t free_part; + + if(!partition.len || partition.len == -1) + return 0; + + for (i=partition.addr+partition.len; i< NVRAM_LENGTH; i++) + nvram_write_byte(i - partition.len - PARTITION_HEADER_SIZE, nvram_read_byte(i)); + + erase_nvram(NVRAM_LENGTH-partition.len-PARTITION_HEADER_SIZE, + partition.len-PARTITION_HEADER_SIZE); + + free_part=get_partition(0x7f, NULL); + wipe_partition(free_part, 0); + create_free_partition(); + + return 1; +} + +int clear_nvram_partition(partition_t part) +{ + if(!part.addr) + return 0; + + erase_nvram(part.addr, part.len); + + return 1; +} + + +int increase_nvram_partition_size(partition_t partition, int newsize) +{ + partition_t free_part; + int free_offset, end_offset, i; + + /* We don't support shrinking partitions (yet) */ + if (newsize < partition.len) { + return 0; + } + + /* NOTE: Assume all free space is consumed by the "free space" + * partition. This means a partition can not be increased in the middle + * of reset_nvram, which is obviously not a big loss. + */ + + free_part=get_partition(0x7f, NULL); + + // FIXME: It could be 16 byte more. Also handle empty "free" partition. + if (free_part.len == -1 || free_part.len < newsize - partition.len ) { + return 0; + } + + free_offset=free_part.addr - PARTITION_HEADER_SIZE; // first unused byte + end_offset=partition.addr + partition.len; // last used byte of partition + 1 + + if(free_offset > end_offset) { + int j, bufferlen; + char *overlap_buffer; + + bufferlen=free_offset - end_offset; + + overlap_buffer=get_nvram_buffer(bufferlen); + if(!overlap_buffer) { + return 0; + } + + for (i=end_offset, j=0; i<free_offset; i++, j++) + overlap_buffer[j]=nvram_read_byte(i); + + /* Only wipe the header. The free space partition is empty per + * definition + */ + + wipe_partition(free_part, 1); + + for (i=partition.addr+newsize, j=0; i<(int)(partition.addr+newsize+bufferlen); i++, j++) + nvram_write_byte(i, overlap_buffer[j]); + + free_nvram_buffer(overlap_buffer); + } else { + /* Only wipe the header. */ + wipe_partition(free_part, 1); + } + + /* Clear the new partition space */ + erase_nvram(partition.addr+partition.len, newsize-partition.len); + + nvram_write_word(partition.addr - 16 + 2, newsize); + + create_free_partition(); + + return 1; +} + +static void init_cpulog_partition(partition_t cpulog) +{ + unsigned int offset=cpulog.addr; + + /* see board-xxx/include/nvramlog.h for information */ + nvram_write_word(offset+0, 0x40); // offset + nvram_write_word(offset+2, 0x00); // flags + nvram_write_dword(offset+4, 0x01); // pointer + +} + +void reset_nvram(void) +{ + partition_t cpulog0, cpulog1; + struct { + uint32_t prefix; + uint64_t name; + } __attribute__((packed)) header; + + DEBUG("Erasing NVRAM\n"); + erase_nvram(0, NVRAM_LENGTH); + + DEBUG("Creating CPU log partitions\n"); + header.prefix = be32_to_cpu(LLFW_LOG_BE0_NAME_PREFIX); + header.name = be64_to_cpu(LLFW_LOG_BE0_NAME); + cpulog0=create_nvram_partition(LLFW_LOG_BE0_SIGNATURE, (char *)&header, + (LLFW_LOG_BE0_LENGTH*16)-PARTITION_HEADER_SIZE); + + header.prefix = be32_to_cpu(LLFW_LOG_BE1_NAME_PREFIX); + header.name = be64_to_cpu(LLFW_LOG_BE1_NAME); + cpulog1=create_nvram_partition(LLFW_LOG_BE1_SIGNATURE, (char *)&header, + (LLFW_LOG_BE1_LENGTH*16)-PARTITION_HEADER_SIZE); + + DEBUG("Initializing CPU log partitions\n"); + init_cpulog_partition(cpulog0); + init_cpulog_partition(cpulog1); + + nvramlog_printf("Creating common NVRAM partition\r\n"); + create_nvram_partition(0x70, "common", 0x01000-PARTITION_HEADER_SIZE); + + create_free_partition(); +} + +void nvram_debug(void) +{ +#ifndef RTAS_NVRAM + printf("\nNVRAM_BASE: %p\n", nvram); + printf("NVRAM_LEN: 0x%x\n", NVRAM_LENGTH); +#endif +} + +unsigned int get_nvram_size(void) +{ + return NVRAM_LENGTH; +} diff --git a/qemu/roms/SLOF/lib/libnvram/nvram.h b/qemu/roms/SLOF/lib/libnvram/nvram.h new file mode 100644 index 000000000..fa6bdd425 --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/nvram.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef __NVRAM_H +#define __NVRAM_H 1 + +/* data structures */ + +typedef struct { + unsigned long addr; + long len; +} partition_t; + +/* macros */ + +#define DEBUG(x...) +// #define DEBUG(x...) printf(x); + +#ifndef ALIGN +#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) +#endif + +#define NULL ((void *)0) + +#define PARTITION_HEADER_SIZE 16 + + +/* exported functions */ + +#define nvram_access_proto(type,name) \ + type nvram_read_##name(unsigned int offset); \ + void nvram_write_##name(unsigned int offset, type data); + +nvram_access_proto(uint8_t, byte) +nvram_access_proto(uint16_t, word) +nvram_access_proto(uint32_t, dword) +nvram_access_proto(uint64_t, qword) + +/* nvram.c */ + +char *get_nvram_buffer(int len); +void free_nvram_buffer(char *buffer); +int nvramlog_printf(const char* fmt, ...); +partition_t get_partition(unsigned int type, char *name); +void erase_nvram(int offset, int len); +int wipe_partition(partition_t partition, int header_only); +partition_t new_nvram_partition(int type, char *name, int len); +int increase_nvram_partition_size(partition_t partition, int newsize); +int clear_nvram_partition(partition_t part); +int delete_nvram_partition(partition_t part); +void reset_nvram(void); +void wipe_nvram(void); +void nvram_debug(void); +void nvram_init(uint32_t store_token, uint32_t fetch_token, + long nv_size, void* nvram_addr); +unsigned int get_nvram_size(void); + +/* envvar.c */ +char *get_env(partition_t part, char *envvar); +int add_env(partition_t part, char *envvar, char *value); +int del_env(partition_t part, char *envvar); +int set_env(partition_t part, char *envvar, char *value); + +#endif diff --git a/qemu/roms/SLOF/lib/libusb/Makefile b/qemu/roms/SLOF/lib/libusb/Makefile new file mode 100644 index 000000000..0780489ea --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/Makefile @@ -0,0 +1,52 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \ + -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) -I$(SLOFCMNDIR) +LDFLAGS = -nostdlib + +TARGET = ../libusb.a + + +all: $(TARGET) + +SRCS = usb-core.c usb-ohci.c usb-ehci.c usb-slof.c usb-key.c usb-hid.c \ + usb-hub.c usb-xhci.c + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep diff --git a/qemu/roms/SLOF/lib/libusb/tools.h b/qemu/roms/SLOF/lib/libusb/tools.h new file mode 100644 index 000000000..f531175c1 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/tools.h @@ -0,0 +1,77 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef __TOOLS_H +#define __TOOLS_H + +#include <stdint.h> +#include <byteorder.h> +#include <cache.h> + +#define PTR_U32(x) ((uint32_t) (uint64_t) (x)) + +static inline uint32_t read_reg32(uint32_t *reg) +{ + return bswap_32(ci_read_32(reg)); +} + +static inline void write_reg32(uint32_t *reg, uint32_t value) +{ + mb(); + ci_write_32(reg, bswap_32(value)); +} + +static inline uint8_t read_reg8(uint8_t *reg) +{ + return ci_read_8(reg); +} + +static inline void write_reg8(uint8_t *reg, uint8_t value) +{ + mb(); + ci_write_8(reg, value); +} + +static inline uint16_t read_reg16(uint16_t *reg) +{ + return bswap_16(ci_read_16(reg)); +} + +static inline void write_reg16(uint16_t *reg, uint16_t value) +{ + mb(); + ci_write_16(reg, bswap_16(value)); +} + +static inline uint64_t read_reg64(uint64_t *reg) +{ + return bswap_64(ci_read_64(reg)); +} + +static inline void write_reg64(uint64_t *reg, uint64_t value) +{ + mb(); + ci_write_64(reg, bswap_64(value)); +} + +static inline uint32_t ci_read_reg(uint32_t *reg) +{ + return bswap_32(ci_read_32(reg)); +} + +static inline void ci_write_reg(uint32_t *reg, uint32_t value) +{ + mb(); + ci_write_32(reg, bswap_32(value)); +} + +#endif diff --git a/qemu/roms/SLOF/lib/libusb/usb-core.c b/qemu/roms/SLOF/lib/libusb/usb-core.c new file mode 100644 index 000000000..6719c5726 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-core.c @@ -0,0 +1,590 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include "usb-core.h" + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +#define __unused __attribute__((unused)) + +struct usb_hcd_ops *head; +struct usb_dev *devpool; +#define USB_DEVPOOL_SIZE 4096 + +static struct usb_dev *usb_alloc_devpool(void) +{ + struct usb_dev *head, *curr, *prev; + unsigned int dev_count = 0, i; + + head = SLOF_alloc_mem(USB_DEVPOOL_SIZE); + if (!head) + return NULL; + + dev_count = USB_DEVPOOL_SIZE/sizeof(struct usb_dev); + dprintf("%s: %d number of devices\n", __func__, dev_count); + /* Although an array, link them*/ + for (i = 0, curr = head, prev = NULL; i < dev_count; i++, curr++) { + if (prev) + prev->next = curr; + curr->next = NULL; + prev = curr; + } + +#ifdef DEBUG + for (i = 0, curr = head; curr; curr = curr->next) + printf("%s: %d dev %p\n", __func__, i++, curr); +#endif + + return head; +} + +struct usb_dev *usb_devpool_get(void) +{ + struct usb_dev *new; + + if (!devpool) { + devpool = usb_alloc_devpool(); + if (!devpool) + return NULL; + } + + new = devpool; + devpool = devpool->next; + memset(new, 0, sizeof(*new)); + new->next = NULL; + return new; +} + +void usb_devpool_put(struct usb_dev *dev) +{ + struct usb_dev *curr; + if (!dev && !devpool) + return; + + curr = devpool; + while (curr->next) + curr = curr->next; + curr->next = dev; + dev->next = NULL; +} + +#ifndef DEBUG +#define validate_hcd_ops(dev) (dev && dev->hcidev && dev->hcidev->ops) +#else +int validate_hcd_ops(struct usb_dev *dev) +{ + int ret = true; + + if (!dev) { + printf("dev is NULL\n"); + ret = false; + } else if (!dev->hcidev) { + printf("hcidev is NULL\n"); + ret = false; + } else if (!dev->hcidev->ops) { + printf("ops is NULL\n"); + ret = false; + } + return ret; +} +#endif + +struct usb_pipe *usb_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + char *buf, size_t len) +{ + if (validate_hcd_ops(dev) && dev->hcidev->ops->get_pipe) + return dev->hcidev->ops->get_pipe(dev, ep, buf, len); + else { + printf("%s: Failed\n", __func__); + return NULL; + } +} + +void usb_put_pipe(struct usb_pipe *pipe) +{ + struct usb_dev *dev = NULL; + if (pipe && pipe->dev) { + dev = pipe->dev; + if (validate_hcd_ops(dev) && dev->hcidev->ops->put_pipe) + dev->hcidev->ops->put_pipe(pipe); + } +} + +int usb_poll_intr(struct usb_pipe *pipe, uint8_t *buf) +{ + struct usb_dev *dev = NULL; + if (pipe && pipe->dev) { + dev = pipe->dev; + if (validate_hcd_ops(dev) && dev->hcidev->ops->poll_intr) + return dev->hcidev->ops->poll_intr(pipe, buf); + } + return 0; +} + +void usb_hcd_register(struct usb_hcd_ops *ops) +{ + struct usb_hcd_ops *list; + + if (!ops) + printf("Error"); + dprintf("Registering %s %d\n", ops->name, ops->usb_type); + + if (head) { + list = head; + while (list->next) + list = list->next; + list->next = ops; + } else + head = ops; +} + +void usb_hcd_init(void *hcidev) +{ + struct usb_hcd_dev *dev = hcidev; + struct usb_hcd_ops *list = head; + + if (!dev) { + printf("Device Error"); + return; + } + + while (list) { + if (list->usb_type == dev->type) { + dprintf("usb_ops(%p) for the controller found\n", list); + dev->ops = list; + dev->ops->init(dev); + return; + } + list = list->next; + } + + dprintf("usb_ops for the controller not found\n"); +} + +void usb_hcd_exit(void *_hcidev) +{ + struct usb_hcd_dev *hcidev = _hcidev; + + dprintf("%s: enter \n", __func__); + if (!hcidev) { + printf("Device Error"); + return; + } + + if (hcidev->ops->exit) + hcidev->ops->exit(hcidev); +} + +int usb_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data) +{ + struct usb_dev *dev = NULL; + if (!pipe) + return false; + dev = pipe->dev; + if (validate_hcd_ops(dev) && dev->hcidev->ops->send_ctrl) + return dev->hcidev->ops->send_ctrl(pipe, req, data); + else { + printf("%s: Failed\n", __func__); + return false; + } +} + +int usb_transfer_ctrl(void *dev, void *req, void *data) +{ + struct usb_pipe *pipe = NULL; + struct usb_dev *usbdev; + + if (!dev) + return false; + usbdev = (struct usb_dev *)dev; + pipe = usbdev->control; + return usb_send_ctrl(pipe, req, data); +} + +int usb_transfer_bulk(void *dev, int dir, void *td, void *td_phys, void *data, int size) +{ + struct usb_pipe *pipe = NULL; + struct usb_dev *usbdev; + + if (!dev) + return false; + usbdev = (struct usb_dev *)dev; + pipe = (dir == USB_PIPE_OUT) ? usbdev->bulk_out : usbdev->bulk_in; + if (!pipe) + return false; + if (validate_hcd_ops(usbdev) && usbdev->hcidev->ops->transfer_bulk) + return usbdev->hcidev->ops->transfer_bulk(pipe, td, td_phys, data, size); + else { + printf("%s: Failed\n", __func__); + return false; + } +} + +/* + * USB Specification 1.1 + * 9.3 USB Device Requests + * 9.4 Standard Device Requests + */ +static int usb_set_address(struct usb_dev *dev, uint32_t port) +{ + struct usb_dev_req req; + struct usb_hcd_dev *hcidev; + + if (!dev) + return false; + + hcidev = dev->hcidev; + req.bmRequestType = 0; + req.bRequest = REQ_SET_ADDRESS; + req.wIndex = 0; + req.wLength = 0; + req.wValue = cpu_to_le16((uint16_t)(hcidev->nextaddr)); + if (usb_send_ctrl(dev->control, &req, NULL)) { + dev->addr = hcidev->nextaddr++; + return true; + } else + return false; +} + +static int usb_get_device_descr(struct usb_dev *dev, void *data, size_t size) +{ + struct usb_dev_req req; + + if (!dev) + return false; + + req.bmRequestType = 0x80; + req.bRequest = REQ_GET_DESCRIPTOR; + req.wIndex = 0; + req.wLength = cpu_to_le16((uint16_t) size); + req.wValue = cpu_to_le16(DESCR_TYPE_DEVICE << 8); + return usb_send_ctrl(dev->control, &req, data); +} + +static int usb_get_config_descr(struct usb_dev *dev, void *data, size_t size) +{ + struct usb_dev_req req; + + if (!dev) + return false; + + req.bmRequestType = 0x80; + req.bRequest = REQ_GET_DESCRIPTOR; + req.wIndex = 0; + req.wLength = cpu_to_le16((uint16_t) size); + req.wValue = cpu_to_le16(DESCR_TYPE_CONFIGURATION << 8); + return usb_send_ctrl(dev->control, &req, data); + +} + +static int usb_set_config(struct usb_dev *dev, uint8_t cfg_value) +{ + struct usb_dev_req req; + + if (!dev) + return false; + + req.bmRequestType = 0x00; + req.bRequest = REQ_SET_CONFIGURATION; + req.wIndex = 0; + req.wLength = 0; + req.wValue = cpu_to_le16(0x00FF & cfg_value); + return usb_send_ctrl(dev->control, &req, NULL); +} + +static int usb_clear_halt(struct usb_pipe *pipe) +{ + struct usb_dev_req req; + struct usb_dev *dev; + + if (pipe && pipe->dev) { + dev = pipe->dev; + dprintf("Clearing port %d dir %d type %d\n", + pipe->epno, pipe->dir, pipe->type); + req.bmRequestType = REQT_DIR_OUT | REQT_REC_EP; + req.bRequest = REQ_CLEAR_FEATURE; + req.wValue = FEATURE_ENDPOINT_HALT; + req.wIndex = cpu_to_le16(pipe->epno | pipe->dir); + req.wLength = 0; + return usb_send_ctrl(dev->control, &req, NULL); + } + return false; +} + +int usb_dev_populate_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + void *buf, size_t len) +{ + uint8_t dir, type; + + dir = (ep->bEndpointAddress & 0x80) >> 7; + type = ep->bmAttributes & USB_EP_TYPE_MASK; + + dprintf("EP: %s: %d size %d type %d\n", dir ? "IN " : "OUT", + ep->bEndpointAddress & 0xF, le16_to_cpu(ep->wMaxPacketSize), + type); + if (type == USB_EP_TYPE_BULK) { + if (dir) + dev->bulk_in = usb_get_pipe(dev, ep, buf, len); + else + dev->bulk_out = usb_get_pipe(dev, ep, buf, len); + } else if (type == USB_EP_TYPE_INTR) + dev->intr = usb_get_pipe(dev, ep, buf, len); + + return true; +} + +static void usb_dev_copy_epdesc(struct usb_dev *dev, struct usb_ep_descr *ep) +{ + uint32_t ep_cnt; + + ep_cnt = dev->ep_cnt; + if (ep_cnt < USB_DEV_EP_MAX) + memcpy((void *)&dev->ep[ep_cnt], ep, sizeof(*ep)); + else + dprintf("usb-core: only %d EPs supported\n", USB_DEV_EP_MAX); + dev->ep_cnt++; +} + +int usb_hid_init(void *vdev) +{ + struct usb_dev *dev; + dev = (struct usb_dev *) vdev; + if (!dev) + return false; + if (dev->class == DEV_HID_KEYB) + usb_hid_kbd_init(dev); + return true; +} + +int usb_hid_exit(void *vdev) +{ + struct usb_dev *dev; + dev = (struct usb_dev *) vdev; + if (!dev) + return false; + if (dev->class == DEV_HID_KEYB) + usb_hid_kbd_exit(dev); + return true; +} + +#define usb_get_intf_class(x) ((x & 0x00FF0000) >> 16) + +int usb_msc_init(void *vdev) +{ + struct usb_dev *dev; + int i; + + dev = (struct usb_dev *) vdev; + dprintf("%s: enter %x\n", __func__, dev->class); + if (!dev) + return false; + if (usb_get_intf_class(dev->class) == 8) { + for (i = 0; i < dev->ep_cnt; i++) { + if ((dev->ep[i].bmAttributes & USB_EP_TYPE_MASK) + == USB_EP_TYPE_BULK) + usb_dev_populate_pipe(dev, &dev->ep[i], NULL, 0); + } + } + return true; +} + +int usb_msc_exit(void *vdev) +{ + struct usb_dev *dev; + dev = (struct usb_dev *) vdev; + dprintf("%s: enter %x\n", __func__, dev->class); + if (!dev) + return false; + if (usb_get_intf_class(dev->class) == 8) { + if (dev->bulk_in) + usb_put_pipe(dev->bulk_in); + if (dev->bulk_out) + usb_put_pipe(dev->bulk_out); + } + return true; +} + +static int usb_msc_reset(struct usb_dev *dev) +{ + struct usb_dev_req req; + + if (!dev) + return false; + + req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT; + req.bRequest = 0xFF; + req.wLength = 0; + req.wValue = 0; + req.wIndex = cpu_to_le16(dev->intf_num); + return usb_send_ctrl(dev->control, &req, NULL); +} + +void usb_msc_resetrecovery(struct usb_dev *dev) +{ + // usb_msc_reset(dev); + usb_clear_halt(dev->bulk_in); + usb_clear_halt(dev->bulk_out); + SLOF_msleep(2); +} + +static int usb_handle_device(struct usb_dev *dev, struct usb_dev_config_descr *cfg, + uint8_t *ptr, uint16_t len) +{ + struct usb_dev_intf_descr *intf = NULL; + struct usb_ep_descr *ep = NULL; + struct usb_dev_hid_descr *hid __unused = NULL; + uint8_t desc_len, desc_type; + + len -= sizeof(struct usb_dev_config_descr); + ptr = (uint8_t *)(ptr + sizeof(struct usb_dev_config_descr)); + + while (len > 0) { + desc_len = *ptr; + desc_type = *(ptr + 1); + switch (desc_type) { + case DESCR_TYPE_INTERFACE: + intf = (struct usb_dev_intf_descr *)ptr; + dev->class = intf->bInterfaceClass << 16 | + intf->bInterfaceSubClass << 8 | + intf->bInterfaceProtocol; + break; + case DESCR_TYPE_ENDPOINT: + ep = (struct usb_ep_descr *)ptr; + dev->intf_num = intf->bInterfaceNumber; + usb_dev_copy_epdesc(dev, ep); + break; + case DESCR_TYPE_HID: + hid = (struct usb_dev_hid_descr *)ptr; + dprintf("hid-report %d size %d\n", + hid->bReportType, le16_to_cpu(hid->wReportLength)); + break; + case DESCR_TYPE_HUB: + break; + default: + printf("ptr %p desc_type %d\n", ptr, desc_type); + } + ptr += desc_len; + len -= desc_len; + } + return true; +} + +int setup_new_device(struct usb_dev *dev, unsigned int port) +{ + struct usb_dev_descr descr; + struct usb_dev_config_descr cfg; + struct usb_ep_descr ep; + uint16_t len; + void *data = NULL; + + dprintf("usb: %s - port %d\n", __func__, port); + + dev->addr = 0; + dev->port = port; + ep.bEndpointAddress = 0; + ep.bmAttributes = USB_EP_TYPE_CONTROL; + ep.wMaxPacketSize = cpu_to_le16(8); + dev->control = usb_get_pipe(dev, &ep, NULL, 0); + + if (!usb_get_device_descr(dev, &descr, 8)) + goto fail; + dev->control->mps = descr.bMaxPacketSize0; + + /* + * For USB3.0 ADDRESS-SLOT command takes care of setting + * address, skip this during generic device setup for USB3.0 + * devices + */ + if (dev->speed != USB_SUPER_SPEED) { + /* + * Qemu starts the port number from 1 which was + * revealed in bootindex and resulted in mismatch for + * storage devices names. Adjusting this here for + * compatibility. + */ + dev->port = port + 1; + if(!usb_set_address(dev, dev->port)) + goto fail; + } + mb(); + SLOF_msleep(100); + + if (!usb_get_device_descr(dev, &descr, sizeof(struct usb_dev_descr))) + goto fail; + + if (!usb_get_config_descr(dev, &cfg, sizeof(struct usb_dev_config_descr))) + goto fail; + + len = le16_to_cpu(cfg.wTotalLength); + /* No device config descriptor present */ + if (len == sizeof(struct usb_dev_config_descr)) + goto fail; + + data = SLOF_dma_alloc(len); + if (!data) { + printf("%s: alloc failed %d\n", __func__, port); + goto fail; + } + + if (!usb_get_config_descr(dev, data, len)) + goto fail_mem_free; + if (!usb_set_config(dev, cfg.bConfigurationValue)) + goto fail_mem_free; + mb(); + SLOF_msleep(100); + + if (!usb_handle_device(dev, &cfg, data, len)) + goto fail_mem_free; + + switch (usb_get_intf_class(dev->class)) { + case 3: + dprintf("HID found %06X\n", dev->class); + slof_usb_handle(dev); + break; + case 8: + dprintf("MASS STORAGE found %d %06X\n", dev->intf_num, + dev->class); + if ((dev->class & 0x50) != 0x50) { /* Bulk-only supported */ + printf("Device not supported %06X\n", dev->class); + goto fail_mem_free; + } + + if (!usb_msc_reset(dev)) { + printf("%s: bulk reset failed\n", __func__); + goto fail_mem_free; + } + SLOF_msleep(100); + slof_usb_handle(dev); + break; + case 9: + dprintf("HUB found\n"); + slof_usb_handle(dev); + break; + default: + printf("USB Interface class -%x- Not supported\n", dev->class); + break; + } + + SLOF_dma_free(data, len); + return true; +fail_mem_free: + SLOF_dma_free(data, len); +fail: + return false; +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-core.h b/qemu/roms/SLOF/lib/libusb/usb-core.h new file mode 100644 index 000000000..7441979e9 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-core.h @@ -0,0 +1,279 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef __USB_CORE_H +#define __USB_CORE_H + +#include <stdio.h> +#include <stdbool.h> +#include "helpers.h" +#include "usb.h" +#include "tools.h" + +enum usb_hcd_type { + USB_OHCI = 1, + USB_EHCI = 2, + USB_XHCI = 3, +}; + +struct usb_hcd_dev; + +struct usb_hcd_dev { + void *base; + long type; + long num; + struct usb_hcd_ops *ops; + void *priv; /* hcd owned structure */ + long nextaddr; /* address for devices */ +}; + +struct usb_pipe; + +/*******************************************/ +/* Standard Endpoint Descriptor */ +/*******************************************/ +/* bmAttributes */ +#define USB_EP_TYPE_MASK 0x03 +#define USB_EP_TYPE_CONTROL 0 +#define USB_EP_TYPE_ISOC 1 +#define USB_EP_TYPE_BULK 2 +#define USB_EP_TYPE_INTR 3 + +struct usb_ep_descr { + uint8_t bLength; /* size of descriptor */ + uint8_t bDescriptorType; /* Type = 5 */ + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; +} __attribute__((packed)); + +#define DEV_HID_KEYB 0x030101 /* class=HIB, protocol=Keyboard */ +#define DEV_HID_MOUSE 0x030102 /* class=HIB, protocol=Mouse */ +#define DEV_HUB 0x090000 /* class=HUB, subclass, protocol */ +#define DEV_MASS_RBC 0x080150 /* MassStorage, RBC, Bulk */ +#define DEV_CDROM_ATAPI 0x080250 /* MassStorage, SFF-8020i , Bulk */ +#define DEV_MASS_FLOPPY 0x080450 /* MassStorage, UFI, Bulk */ +#define DEV_MASS_ATAPI 0x080550 /* MassStorage, SFF-8070i , Bulk */ +#define DEV_MASS_SCSI 0x080650 /* MassStorage, SCSI, Bulk */ + +enum USB_SPEED_TYPE { + USB_LOW_SPEED = 0, + USB_FULL_SPEED = 1, + USB_HIGH_SPEED = 2, + USB_SUPER_SPEED = 3, +}; + +/* Max number of endpoints supported in a device */ +#define USB_DEV_EP_MAX 4 +#define USB_TIMEOUT 5000 /* 5 sec usb timeout */ + +struct usb_dev { + struct usb_dev *next; + struct usb_hcd_dev *hcidev; + struct usb_pipe *intr; + struct usb_pipe *control; + struct usb_pipe *bulk_in; + struct usb_pipe *bulk_out; + struct usb_ep_descr ep[USB_DEV_EP_MAX]; + void *priv; + uint32_t ep_cnt; + uint32_t class; + uint32_t speed; + uint32_t addr; + uint32_t mps0; + uint32_t port; + uint16_t intf_num; +}; + +#define DEVICE_KEYBOARD 1 +#define DEVICE_MOUSE 2 +#define DEVICE_DISK 3 +#define DEVICE_HUB 4 + +/* Structure in sync with FORTH code */ +struct slof_usb_dev { + void *udev; + uint32_t port; + uint32_t addr; + uint32_t hcitype; + uint32_t num; + uint32_t devtype; +} __attribute__((packed)); + +enum USB_PIPE_DIR { + USB_PIPE_OUT = 0, + USB_PIPE_IN, +}; + +struct usb_pipe { + struct usb_dev *dev; + struct usb_pipe *next; + uint32_t type; + uint32_t speed; + uint32_t dir; + uint16_t epno; + uint16_t mps; +} __attribute__((packed)); + +#define REQ_GET_STATUS 0 /* see Table 9-4 */ +#define REQ_CLEAR_FEATURE 1 +#define REQ_GET_STATE 2 /* HUB specific */ +#define REQ_SET_FEATURE 3 +#define REQ_SET_ADDRESS 5 +#define REQ_GET_DESCRIPTOR 6 +#define REQ_SET_DESCRIPTOR 7 +#define REQ_GET_CONFIGURATION 8 +#define REQ_SET_CONFIGURATION 9 +#define REQ_GET_INTERFACE 10 +#define REQ_SET_INTERFACE 11 +#define REQ_SYNCH_FRAME 12 + +#define FEATURE_DEVICE_REMOTE_WAKEUP 1 +#define FEATURE_ENDPOINT_HALT 0 + +#define REQT_REC_DEVICE 0 +#define REQT_REC_INTERFACE 1 +#define REQT_REC_EP 2 +#define REQT_REC_OTHER 3 +#define REQT_TYPE_STANDARD (0 << 5) +#define REQT_TYPE_CLASS (1 << 5) +#define REQT_TYPE_VENDOR (2 << 5) +#define REQT_TYPE_RSRVD (3 << 5) +#define REQT_DIR_OUT (0 << 7) /* host -> device */ +#define REQT_DIR_IN (1 << 7) /* device -> host */ + +#define DESCR_TYPE_DEVICE 1 /* see Table 9-5 */ +#define DESCR_TYPE_CONFIGURATION 2 +#define DESCR_TYPE_STRING 3 +#define DESCR_TYPE_INTERFACE 4 +#define DESCR_TYPE_ENDPOINT 5 +#define DESCR_TYPE_HUB 0x29 /* Class Descriptor HUB */ +#define DESCR_TYPE_HID 0x21 /* Class Descriptor HID */ +#define DESCR_TYPE_REPORT 0x22 /* Class Descriptor HID */ +#define DESCR_TYPE_PHYSICAL 0x23 /* Class Descriptor HID */ + +struct usb_dev_req { + uint8_t bmRequestType; /* direction, recipient */ + uint8_t bRequest; /* see spec: Table 9-3 */ + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; /* number of bytes to transfer */ +} __attribute__((packed)); + +/* Standard Device Descriptor (18 Bytes) */ +/*******************************************/ +struct usb_dev_descr { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} __attribute__((packed)); + +/*******************************************/ +/* Standard Configuration Descriptor */ +/*******************************************/ +struct usb_dev_config_descr { + uint8_t bLength; /* size of descriptor */ + uint8_t bDescriptorType; /* Type = 2 */ + uint16_t wTotalLength; /* total returned data */ + uint8_t bNumInterfaces; /* interfaces supported by this config */ + uint8_t bConfigurationValue; /* Configuration-ID for SetConfiguration */ + uint8_t iConfiguration; /* index of string descriptor */ + uint8_t bmAttributes; /* configuration characteristics */ + uint8_t bMaxPower; /* in 2mA units */ +} __attribute__((packed)); + +/*******************************************/ +/* Standard Interface Descriptor */ +/*******************************************/ +struct usb_dev_intf_descr { + uint8_t bLength; /* size of descriptor */ + uint8_t bDescriptorType; /* Type = 4 */ + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; /* protocol code */ + uint8_t iInterface; /* index to string descriptor */ +} __attribute__((packed)); + +/*******************************************/ +/* HUB-Class Descriptor */ +/*******************************************/ +struct usb_dev_hub_descr { + uint8_t bLength; /* size of complete descriptor */ + uint8_t bDescriptorType; /* type = 0x29 for HUB */ + uint8_t bNbrPorts; /* number of downstream ports */ + uint8_t wHubCharacteristics; /* mode bits 7..0 */ + uint8_t reserved; /* mode bits 15..8 */ + uint8_t bPwrOn2PwrGood; /* in 2ms units */ + uint8_t bHubContrCurrent; /* current requirement in mA */ + uint8_t DeviceTable; /* length depends on number of ports */ +} __attribute__((packed)); + +/*******************************************/ +/* HID-Class Descriptor */ +/*******************************************/ +struct usb_dev_hid_descr { + uint8_t bLength; /* size of this descriptor */ + uint8_t bDescriptorType; /* type = 0x21 for HID */ + uint16_t bcdHID; /* Sample: 0x0102 for 2.01 */ + uint8_t bCountryCode; /* Hardware target country */ + uint8_t bNumDescriptors; /* Number of HID class descr. */ + uint8_t bReportType; /* Report Descriptor Type */ + uint16_t wReportLength; /* Total Length of Report Descr. */ +} __attribute__((packed)); + +struct usb_hcd_ops { + const char *name; + void (*init)(struct usb_hcd_dev *); + void (*exit)(struct usb_hcd_dev *); + void (*detect)(void); + void (*disconnect)(void); + int (*send_ctrl)(struct usb_pipe *pipe, struct usb_dev_req *req, void *data); + struct usb_pipe* (*get_pipe)(struct usb_dev *dev, struct usb_ep_descr *ep, + char *buf, size_t len); + int (*transfer_bulk)(struct usb_pipe *pipe, void *td, void *td_phys, void *data, int size); + void (*put_pipe)(struct usb_pipe *); + int (*poll_intr)(struct usb_pipe *, uint8_t *); + struct usb_hcd_ops *next; + unsigned int usb_type; +}; + +extern void usb_hcd_register(struct usb_hcd_ops *ops); +extern struct usb_pipe *usb_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + char *buf, size_t len); +extern void usb_put_pipe(struct usb_pipe *pipe); +extern int usb_poll_intr(struct usb_pipe *pipe, uint8_t *buf); +extern int usb_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data); +extern struct usb_dev *usb_devpool_get(void); +extern void usb_devpool_put(struct usb_dev *); +extern int setup_new_device(struct usb_dev *dev, unsigned int port); +extern int slof_usb_handle(struct usb_dev *dev); +extern int usb_dev_populate_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + void *buf, size_t len); +extern int usb_hid_kbd_init(struct usb_dev *dev); +extern int usb_hid_kbd_exit(struct usb_dev *dev); +extern void usb_msc_resetrecovery(struct usb_dev *dev); +#endif diff --git a/qemu/roms/SLOF/lib/libusb/usb-ehci.c b/qemu/roms/SLOF/lib/libusb/usb-ehci.c new file mode 100644 index 000000000..4cca0da15 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-ehci.c @@ -0,0 +1,609 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include "usb.h" +#include "usb-core.h" +#include "usb-ehci.h" +#include "tools.h" +#include "paflof.h" + +#undef EHCI_DEBUG +//#define EHCI_DEBUG +#ifdef EHCI_DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +#ifdef EHCI_DEBUG +static void dump_ehci_regs(struct ehci_hcd *ehcd) +{ + struct ehci_cap_regs *cap_regs; + struct ehci_op_regs *op_regs; + + cap_regs = ehcd->cap_regs; + op_regs = ehcd->op_regs; + + dprintf("\n - CAPLENGTH %02X", read_reg8(&cap_regs->caplength)); + dprintf("\n - HCIVERSION %04X", read_reg16(&cap_regs->hciversion)); + dprintf("\n - HCSPARAMS %08X", read_reg32(&cap_regs->hcsparams)); + dprintf("\n - HCCPARAMS %08X", read_reg32(&cap_regs->hccparams)); + dprintf("\n - HCSP_PORTROUTE %016llX", read_reg64(&cap_regs->portroute)); + dprintf("\n"); + + dprintf("\n - USBCMD %08X", read_reg32(&op_regs->usbcmd)); + dprintf("\n - USBSTS %08X", read_reg32(&op_regs->usbsts)); + dprintf("\n - USBINTR %08X", read_reg32(&op_regs->usbintr)); + dprintf("\n - FRINDEX %08X", read_reg32(&op_regs->frindex)); + dprintf("\n - CTRLDSSEGMENT %08X", read_reg32(&op_regs->ctrldssegment)); + dprintf("\n - PERIODICLISTBASE %08X", read_reg32(&op_regs->periodiclistbase)); + dprintf("\n - ASYNCLISTADDR %08X", read_reg32(&op_regs->asynclistaddr)); + dprintf("\n - CONFIGFLAG %08X", read_reg32(&op_regs->configflag)); + dprintf("\n - PORTSC %08X", read_reg32(&op_regs->portsc[0])); + dprintf("\n"); +} +#endif + +static int ehci_hub_check_ports(struct ehci_hcd *ehcd) +{ + uint32_t num_ports, portsc, i; + struct usb_dev *dev; + + dprintf("%s: enter\n", __func__); + num_ports = read_reg32(&ehcd->cap_regs->hcsparams) & HCS_NPORTS_MASK; + for (i = 0; i < num_ports; i++) { + dprintf("%s: device %d\n", __func__, i); + portsc = read_reg32(&ehcd->op_regs->portsc[i]); + if (portsc & PORT_CONNECT) { /* Device present */ + dprintf("usb-ehci: Device present on port %d\n", i); + /* Reset the port */ + portsc = read_reg32(&ehcd->op_regs->portsc[i]); + portsc = (portsc & ~PORT_PE) | PORT_RESET; + write_reg32(&ehcd->op_regs->portsc[i], portsc); + SLOF_msleep(20); + portsc = read_reg32(&ehcd->op_regs->portsc[i]); + portsc &= ~PORT_RESET; + write_reg32(&ehcd->op_regs->portsc[i], portsc); + SLOF_msleep(20); + dev = usb_devpool_get(); + dprintf("usb-ehci: allocated device %p\n", dev); + dev->hcidev = ehcd->hcidev; + dev->speed = USB_HIGH_SPEED; /* TODO: Check for Low/Full speed device */ + if (!setup_new_device(dev, i)) + printf("usb-ehci: unable to setup device on port %d\n", i); + } + } + dprintf("%s: exit\n", __func__); + return 0; +} + +static int ehci_hcd_init(struct ehci_hcd *ehcd) +{ + uint32_t usbcmd; + uint32_t time; + struct ehci_framelist *fl; + struct ehci_qh *qh_intr, *qh_async; + int i; + long fl_phys = 0, qh_intr_phys = 0, qh_async_phys; + + /* Reset the host controller */ + time = SLOF_GetTimer() + 250; + usbcmd = read_reg32(&ehcd->op_regs->usbcmd); + write_reg32(&ehcd->op_regs->usbcmd, (usbcmd & ~(CMD_PSE | CMD_ASE)) | CMD_HCRESET); + while (time > SLOF_GetTimer()) + cpu_relax(); + usbcmd = read_reg32(&ehcd->op_regs->usbcmd); + if (usbcmd & CMD_HCRESET) { + printf("usb-ehci: reset failed\n"); + return -1; + } + + /* Initialize periodic list */ + fl = SLOF_dma_alloc(sizeof(*fl)); + if (!fl) { + printf("usb-ehci: Unable to allocate frame list\n"); + goto fail; + } + fl_phys = SLOF_dma_map_in(fl, sizeof(*fl), true); + dprintf("fl %p, fl_phys %lx\n", fl, fl_phys); + + /* TODO: allocate qh pool */ + qh_intr = SLOF_dma_alloc(sizeof(*qh_intr)); + if (!qh_intr) { + printf("usb-ehci: Unable to allocate interrupt queue head\n"); + goto fail_qh_intr; + } + qh_intr_phys = SLOF_dma_map_in(qh_intr, sizeof(*qh_intr), true); + dprintf("qh_intr %p, qh_intr_phys %lx\n", qh_intr, qh_intr_phys); + + memset(qh_intr, 0, sizeof(*qh_intr)); + qh_intr->qh_ptr = QH_PTR_TERM; + qh_intr->ep_cap2 = cpu_to_le32(0x01 << QH_SMASK_SHIFT); + qh_intr->next_qtd = qh_intr->alt_next_qtd = QH_PTR_TERM; + qh_intr->token = cpu_to_le32(QH_STS_HALTED); + for (i = 0; i < FL_SIZE; i++) + fl->fl_ptr[i] = cpu_to_le32(qh_intr_phys | EHCI_TYP_QH); + write_reg32(&ehcd->op_regs->periodiclistbase, fl_phys); + + /* Initialize async list */ + qh_async = SLOF_dma_alloc(sizeof(*qh_async)); + if (!qh_async) { + printf("usb-ehci: Unable to allocate async queue head\n"); + goto fail_qh_async; + } + qh_async_phys = SLOF_dma_map_in(qh_async, sizeof(*qh_async), true); + dprintf("qh_async %p, qh_async_phys %lx\n", qh_async, qh_async_phys); + + memset(qh_async, 0, sizeof(*qh_async)); + qh_async->qh_ptr = cpu_to_le32(qh_async_phys | EHCI_TYP_QH); + qh_async->ep_cap1 = cpu_to_le32(QH_CAP_H); + qh_async->next_qtd = qh_async->alt_next_qtd = QH_PTR_TERM; + qh_async->token = cpu_to_le32(QH_STS_HALTED); + write_reg32(&ehcd->op_regs->asynclistaddr, qh_async_phys); + ehcd->qh_async = qh_async; + ehcd->qh_async_phys = qh_async_phys; + ehcd->qh_intr = qh_intr; + ehcd->qh_intr_phys = qh_intr_phys; + ehcd->fl = fl; + ehcd->fl_phys = fl_phys; + + write_reg32(&ehcd->op_regs->usbcmd, usbcmd | CMD_ASE | CMD_RUN); + write_reg32(&ehcd->op_regs->configflag, 1); + + return 0; + +fail_qh_async: + SLOF_dma_map_out(qh_intr_phys, qh_intr, sizeof(*qh_intr)); + SLOF_dma_free(qh_intr, sizeof(*qh_intr)); +fail_qh_intr: + SLOF_dma_map_out(fl_phys, fl, sizeof(*fl)); + SLOF_dma_free(fl, sizeof(*fl)); +fail: + return -1; +} + +static int ehci_hcd_exit(struct ehci_hcd *ehcd) +{ + uint32_t usbcmd; + + if (!ehcd) { + dprintf("NULL pointer\n"); + return false; + } + + usbcmd = read_reg32(&ehcd->op_regs->usbcmd); + write_reg32(&ehcd->op_regs->usbcmd, usbcmd | ~CMD_RUN); + write_reg32(&ehcd->op_regs->periodiclistbase, 0); + + if (ehcd->pool) { + SLOF_dma_map_out(ehcd->pool_phys, ehcd->pool, EHCI_PIPE_POOL_SIZE); + SLOF_dma_free(ehcd->pool, EHCI_PIPE_POOL_SIZE); + } + if (ehcd->qh_intr) { + SLOF_dma_map_out(ehcd->qh_intr_phys, ehcd->qh_intr, sizeof(struct ehci_qh)); + SLOF_dma_free(ehcd->qh_intr, sizeof(struct ehci_qh)); + } + if (ehcd->qh_async) { + SLOF_dma_map_out(ehcd->qh_async_phys, ehcd->qh_async, sizeof(struct ehci_qh)); + SLOF_dma_free(ehcd->qh_async, sizeof(struct ehci_qh)); + } + if (ehcd->fl) { + SLOF_dma_map_out(ehcd->fl_phys, ehcd->fl, sizeof(struct ehci_framelist)); + SLOF_dma_free(ehcd->fl, sizeof(struct ehci_framelist)); + } + return true; +} + +static int ehci_alloc_pipe_pool(struct ehci_hcd *ehcd) +{ + struct ehci_pipe *epipe, *curr, *prev; + unsigned int i, count; + long epipe_phys = 0; + + count = EHCI_PIPE_POOL_SIZE/sizeof(*epipe); + ehcd->pool = epipe = SLOF_dma_alloc(EHCI_PIPE_POOL_SIZE); + if (!epipe) + return -1; + ehcd->pool_phys = epipe_phys = SLOF_dma_map_in(epipe, EHCI_PIPE_POOL_SIZE, true); + dprintf("%s: epipe %p, epipe_phys %lx\n", __func__, epipe, epipe_phys); + + /* Although an array, link them */ + for (i = 0, curr = epipe, prev = NULL; i < count; i++, curr++) { + if (prev) + prev->pipe.next = &curr->pipe; + curr->pipe.next = NULL; + prev = curr; + curr->qh_phys = epipe_phys + (curr - epipe) * sizeof(*curr) + + offset_of(struct ehci_pipe, qh); + dprintf("%s - %d: qh %p, qh_phys %lx\n", __func__, + i, &curr->qh, curr->qh_phys); + } + + if (!ehcd->freelist) + ehcd->freelist = &epipe->pipe; + else + ehcd->end->next = &epipe->pipe; + ehcd->end = &prev->pipe; + + return 0; +} + +static void ehci_init(struct usb_hcd_dev *hcidev) +{ + struct ehci_hcd *ehcd; + + printf(" EHCI: Initializing\n"); + dprintf("%s: device base address %p\n", __func__, hcidev->base); + + ehcd = SLOF_alloc_mem(sizeof(*ehcd)); + if (!ehcd) { + printf("usb-ehci: Unable to allocate memory\n"); + return; + } + memset(ehcd, 0, sizeof(*ehcd)); + + hcidev->nextaddr = 1; + hcidev->priv = ehcd; + ehcd->hcidev = hcidev; + ehcd->cap_regs = (struct ehci_cap_regs *)(hcidev->base); + ehcd->op_regs = (struct ehci_op_regs *)(hcidev->base + + read_reg8(&ehcd->cap_regs->caplength)); +#ifdef EHCI_DEBUG + dump_ehci_regs(ehcd); +#endif + ehci_hcd_init(ehcd); + ehci_hub_check_ports(ehcd); +} + +static void ehci_exit(struct usb_hcd_dev *hcidev) +{ + struct ehci_hcd *ehcd; + static int count = 0; + + dprintf("%s: enter \n", __func__); + + if (!hcidev && !hcidev->priv) { + return; + } + count++; + if (count > 1) { + printf("%s: already called once \n", __func__); + return; + } + ehcd = hcidev->priv; + ehci_hcd_exit(ehcd); + SLOF_free_mem(ehcd, sizeof(*ehcd)); + hcidev->priv = NULL; +} + +static void ehci_detect(void) +{ + +} + +static void ehci_disconnect(void) +{ + +} + +static int ehci_handshake(struct ehci_hcd *ehcd, uint32_t timeout) +{ + uint32_t usbsts = 0, time; + uint32_t usbcmd; + mb(); + usbcmd = read_reg32(&ehcd->op_regs->usbcmd); + /* Ring a doorbell */ + write_reg32(&ehcd->op_regs->usbcmd, usbcmd | CMD_IAAD); + mb(); + time = SLOF_GetTimer() + timeout; + while ((time > SLOF_GetTimer())) { + /* Wait for controller to confirm */ + usbsts = read_reg32(&ehcd->op_regs->usbsts); + if (usbsts & STS_IAA) { + /* Acknowledge it, for next doorbell to work */ + write_reg32(&ehcd->op_regs->usbsts, STS_IAA); + return true; + } + cpu_relax(); + } + return false; +} + +static int fill_qtd_buff(struct ehci_qtd *qtd, long data, uint32_t size) +{ + long i, rem; + long pos = (data + 0x1000) & ~0xfff; + + qtd->buffer[0] = cpu_to_le32(PTR_U32(data)); + for (i = 1; i < 5; i++) { + if ((data + size - 1) >= pos) { + //dprintf("data spans page boundary: %d, %p\n", i, pos); + qtd->buffer[i] = cpu_to_le32(pos); + pos += 0x1000; + } else + break; + } + if ((data + size) > pos) + rem = data + size - pos; + else + rem = 0; + return rem; +} + +static int ehci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data) +{ + struct ehci_hcd *ehcd; + struct ehci_qtd *qtd, *qtds, *qtds_phys; + struct ehci_pipe *epipe; + uint32_t transfer_size = sizeof(*req); + uint32_t datalen, pid; + uint32_t time; + long req_phys = 0, data_phys = 0; + int ret = true; + + if (pipe->type != USB_EP_TYPE_CONTROL) { + printf("usb-ehci: Not a control pipe.\n"); + return false; + } + + ehcd = pipe->dev->hcidev->priv; + qtds = qtd = SLOF_dma_alloc(sizeof(*qtds) * 3); + if (!qtds) { + printf("Error allocating qTDs.\n"); + return false; + } + qtds_phys = (struct ehci_qtd *)SLOF_dma_map_in(qtds, sizeof(*qtds) * 3, true); + memset(qtds, 0, sizeof(*qtds) * 3); + req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true); + qtd->next_qtd = cpu_to_le32(PTR_U32(&qtds_phys[1])); + qtd->alt_next_qtd = QH_PTR_TERM; + qtd->token = cpu_to_le32((transfer_size << TOKEN_TBTT_SHIFT) | + (3 << TOKEN_CERR_SHIFT) | + (PID_SETUP << TOKEN_PID_SHIFT) | + (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); + fill_qtd_buff(qtd, req_phys, sizeof(*req)); + + qtd++; + datalen = cpu_to_le16(req->wLength); + pid = (req->bmRequestType & REQT_DIR_IN) ? PID_IN : PID_OUT; + if (datalen) { + data_phys = SLOF_dma_map_in(data, datalen, true); + qtd->next_qtd = cpu_to_le32(PTR_U32(&qtds_phys[2])); + qtd->alt_next_qtd = QH_PTR_TERM; + qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) | + (datalen << TOKEN_TBTT_SHIFT) | + (3 << TOKEN_CERR_SHIFT) | + (pid << TOKEN_PID_SHIFT) | + (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); + fill_qtd_buff(qtd, data_phys, datalen); + qtd++; + } + + if (pid == PID_IN) + pid = PID_OUT; + else + pid = PID_IN; + qtd->next_qtd = QH_PTR_TERM; + qtd->alt_next_qtd = QH_PTR_TERM; + qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) | + (3 << TOKEN_CERR_SHIFT) | + (pid << TOKEN_PID_SHIFT) | + (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); + + /* link qtd to qh and attach to ehcd */ + mb(); + epipe = container_of(pipe, struct ehci_pipe, pipe); + epipe->qh.next_qtd = cpu_to_le32(PTR_U32(qtds_phys)); + epipe->qh.qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); + epipe->qh.ep_cap1 = cpu_to_le32((pipe->mps << QH_MPS_SHIFT) | + (pipe->speed << QH_EPS_SHIFT) | + (pipe->epno << QH_EP_SHIFT) | + (pipe->dev->addr << QH_DEV_ADDR_SHIFT)); + mb(); + + ehcd->qh_async->qh_ptr = cpu_to_le32(epipe->qh_phys | EHCI_TYP_QH); + + /* transfer data */ + mb(); + qtd = &qtds[0]; + time = SLOF_GetTimer() + USB_TIMEOUT; + do { + if (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)) + mb(); + else + qtd++; + + if (time < SLOF_GetTimer()) { /* timed out */ + printf("usb-ehci: control transfer timed out_\n"); + ret = false; + break; + } + } while (qtd->next_qtd != QH_PTR_TERM); + + ehcd->qh_async->qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); + mb(); + if (!ehci_handshake(ehcd, USB_TIMEOUT)) { + printf("%s: handshake failed\n", __func__); + ret = false; + } + + SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req)); + SLOF_dma_map_out(data_phys, data, datalen); + SLOF_dma_map_out(PTR_U32(qtds_phys), qtds, sizeof(*qtds) * 3); + SLOF_dma_free(qtds, sizeof(*qtds) * 3); + + return ret; +} + +static int ehci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, + void *data_phys, int size) +{ + struct ehci_hcd *ehcd; + struct ehci_qtd *qtd, *qtd_phys; + struct ehci_pipe *epipe; + uint32_t pid; + int i, rem, ret = true; + uint32_t time; + long ptr; + + dprintf("usb-ehci: bulk transfer: data %p, size %d, td %p, td_phys %p\n", + data_phys, size, td, td_phys); + + if (pipe->type != USB_EP_TYPE_BULK) { + printf("usb-ehci: Not a bulk pipe.\n"); + return false; + } + + if (size > QTD_MAX_TRANSFER_LEN) { + printf("usb-ehci: bulk transfer size too big\n"); + return false; + } + + ehcd = pipe->dev->hcidev->priv; + pid = (pipe->dir == USB_PIPE_OUT) ? PID_OUT : PID_IN; + qtd = (struct ehci_qtd *)td; + qtd_phys = (struct ehci_qtd *)td_phys; + ptr = (long)data_phys; + for (i = 0; i < NUM_BULK_QTDS; i++) { + memset(qtd, 0, sizeof(*qtd)); + rem = fill_qtd_buff(qtd, ptr, size); + qtd->token = cpu_to_le32((1 << TOKEN_DT_SHIFT) | + ((size - rem) << TOKEN_TBTT_SHIFT) | + (3 << TOKEN_CERR_SHIFT) | + (pid << TOKEN_PID_SHIFT) | + (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)); + if (rem) { + qtd->next_qtd = cpu_to_le32(PTR_U32(&qtd_phys[i+1])); + qtd->alt_next_qtd = QH_PTR_TERM; + ptr += size - rem; + size = rem; + qtd++; + } else { + qtd->next_qtd = qtd->alt_next_qtd = QH_PTR_TERM; + break; /* no more data */ + } + } + + /* link qtd to qh and attach to ehcd */ + mb(); + epipe = container_of(pipe, struct ehci_pipe, pipe); + epipe->qh.next_qtd = cpu_to_le32(PTR_U32(qtd_phys)); + epipe->qh.qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); + epipe->qh.ep_cap1 = cpu_to_le32((pipe->mps << QH_MPS_SHIFT) | + (pipe->speed << QH_EPS_SHIFT) | + (pipe->epno << QH_EP_SHIFT) | + (pipe->dev->addr << QH_DEV_ADDR_SHIFT)); + mb(); + + ehcd->qh_async->qh_ptr = cpu_to_le32(epipe->qh_phys | EHCI_TYP_QH); + + /* transfer data */ + mb(); + qtd = (struct ehci_qtd *)td; + for (i = 0; i < NUM_BULK_QTDS; i++) { + time = SLOF_GetTimer() + USB_TIMEOUT; + while ((time > SLOF_GetTimer()) && + (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT))) + cpu_relax(); + mb(); + if (qtd->next_qtd == QH_PTR_TERM) + break; + + if (le32_to_cpu(qtd->token) & (QH_STS_ACTIVE << TOKEN_STATUS_SHIFT)) { + printf("usb-ehci: bulk transfer timed out_\n"); + ret = false; + break; + } + qtd++; + } + + ehcd->qh_async->qh_ptr = cpu_to_le32(ehcd->qh_async_phys | EHCI_TYP_QH); + mb(); + if (!ehci_handshake(ehcd, USB_TIMEOUT)) { + printf("%s: handshake failed\n", __func__); + ret = false; + } + return ret; +} + +static struct usb_pipe *ehci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + char *buf, size_t len) +{ + struct ehci_hcd *ehcd; + struct usb_pipe *new = NULL; + + if (!dev) + return NULL; + + ehcd = (struct ehci_hcd *)dev->hcidev->priv; + if (!ehcd->freelist) { + dprintf("usb-ehci: %s allocating pool\n", __func__); + if (ehci_alloc_pipe_pool(ehcd)) + return NULL; + } + + new = ehcd->freelist; + ehcd->freelist = ehcd->freelist->next; + if (!ehcd->freelist) + ehcd->end = NULL; + + memset(new, 0, sizeof(*new)); + new->dev = dev; + new->next = NULL; + new->type = ep->bmAttributes & USB_EP_TYPE_MASK; + new->speed = dev->speed; + new->mps = ep->wMaxPacketSize; + new->dir = (ep->bEndpointAddress & 0x80) >> 7; + new->epno = ep->bEndpointAddress & 0x0f; + + return new; +} + +static void ehci_put_pipe(struct usb_pipe *pipe) +{ + struct ehci_hcd *ehcd; + + dprintf("usb-ehci: %s enter - %p\n", __func__, pipe); + if (!pipe || !pipe->dev) + return; + ehcd = pipe->dev->hcidev->priv; + if (ehcd->end) + ehcd->end->next = pipe; + else + ehcd->freelist = pipe; + + ehcd->end = pipe; + pipe->next = NULL; + pipe->dev = NULL; + memset(pipe, 0, sizeof(*pipe)); + dprintf("usb-ehci: %s exit\n", __func__); +} + +struct usb_hcd_ops ehci_ops = { + .name = "ehci-hcd", + .init = ehci_init, + .exit = ehci_exit, + .detect = ehci_detect, + .disconnect = ehci_disconnect, + .get_pipe = ehci_get_pipe, + .put_pipe = ehci_put_pipe, + .send_ctrl = ehci_send_ctrl, + .transfer_bulk = ehci_transfer_bulk, + .usb_type = USB_EHCI, + .next = NULL, +}; + +void usb_ehci_register(void) +{ + usb_hcd_register(&ehci_ops); +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-ehci.h b/qemu/roms/SLOF/lib/libusb/usb-ehci.h new file mode 100644 index 000000000..2955a9cb8 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-ehci.h @@ -0,0 +1,155 @@ +/****************************************************************************** + * Copyright (c) 2007, 2012, 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * Definitions for EHCI Controller + * + */ + +#ifndef USB_EHCI_H +#define USB_EHCI_H + +#include <stdint.h> +#include "usb-core.h" + +#define FL_SIZE 1024 + +struct ehci_cap_regs { + uint8_t caplength; + uint8_t reserved; + uint16_t hciversion; + uint32_t hcsparams; + uint32_t hccparams; + uint64_t portroute; +} __attribute__ ((packed)); + +struct ehci_op_regs { + uint32_t usbcmd; + uint32_t usbsts; + uint32_t usbintr; + uint32_t frindex; + uint32_t ctrldssegment; + uint32_t periodiclistbase; + uint32_t asynclistaddr; + uint32_t reserved[9]; + uint32_t configflag; + uint32_t portsc[0]; +} __attribute__ ((packed)); + +struct ehci_framelist { + uint32_t fl_ptr[FL_SIZE]; +} __attribute__ ((packed)); + +struct ehci_hcd { + struct ehci_cap_regs *cap_regs; + struct ehci_op_regs *op_regs; + struct usb_hcd_dev *hcidev; + struct ehci_qh *qh_async; + struct ehci_qh *qh_intr; + struct usb_pipe *freelist; + struct usb_pipe *end; + struct ehci_framelist *fl; + long qh_async_phys; + long qh_intr_phys; + long fl_phys; + void *pool; + long pool_phys; +}; + +struct ehci_qtd { + uint32_t next_qtd; + uint32_t alt_next_qtd; + uint32_t token; + uint32_t buffer[5]; +} __attribute__ ((packed)); + +struct ehci_qh { + uint32_t qh_ptr; + uint32_t ep_cap1; + uint32_t ep_cap2; + uint32_t curr_qtd; + uint32_t next_qtd; + uint32_t alt_next_qtd; + uint32_t token; + uint32_t buffer[5]; +} __attribute__ ((packed)) __attribute__((aligned(32))); + +struct ehci_pipe { + struct ehci_qh qh; + struct usb_pipe pipe; + long qh_phys; +}; + +#define EHCI_PIPE_POOL_SIZE 4096 + +#define EHCI_TYP_ITD 0x00 +#define EHCI_TYP_QH 0x02 +#define EHCI_TYP_SITD 0x04 +#define EHCI_TYP_FSTN 0x06 + +#define PID_OUT 0x00 +#define PID_IN 0x01 +#define PID_SETUP 0x02 + +#define HCS_NPORTS_MASK 0x000f + +#define CMD_IAAD (1 << 6) +#define CMD_ASE (1 << 5) +#define CMD_PSE (1 << 4) +#define CMD_FLS_MASK (3 << 2) +#define CMD_HCRESET (1 << 1) +#define CMD_RUN (1 << 0) + +#define STS_IAA (1 << 5) + +#define PORT_RESET (1 << 8) +#define PORT_PE (1 << 2) +#define PORT_CSC (1 << 1) +#define PORT_CONNECT (1 << 0) + +#define QH_LOW_SPEED 0 +#define QH_FULL_SPEED 1 +#define QH_HIGH_SPEED 2 + +#define QH_RL_SHIFT 28 +#define QH_CAP_C (1 << 27) +#define QH_MPS_SHIFT 16 +#define QH_CAP_H (1 << 15) +#define QH_CAP_DTC (1 << 14) +#define QH_EPS_SHIFT 12 +#define QH_EP_SHIFT 8 +#define QH_CAP_I (1 << 7) +#define QH_DEV_ADDR_SHIFT 0 + +#define QH_PTR_TERM __builtin_bswap32(1) +#define QH_SMASK_SHIFT 0 +#define QH_STS_ACTIVE (1 << 7) +#define QH_STS_HALTED (1 << 6) +#define QH_STS_DBE (1 << 5) +#define QH_STS_BABBLE (1 << 4) +#define QH_STS_XACTERR (1 << 3) +#define QH_STS_MMF (1 << 2) +#define QH_STS_SXS (1 << 1) +#define QH_STS_PING (1 << 0) + +#define NUM_BULK_QTDS 4 +#define MAX_XFER_PER_QTD (20 * 1024) +#define QTD_MAX_TRANSFER_LEN (NUM_BULK_QTDS * MAX_XFER_PER_QTD) + +#define TOKEN_DT_SHIFT 31 +#define TOKEN_TBTT_SHIFT 16 +#define TOKEN_IOC_SHIFT 15 +#define TOKEN_CPAGE_SHIFT 12 +#define TOKEN_CERR_SHIFT 10 +#define TOKEN_PID_SHIFT 8 +#define TOKEN_STATUS_SHIFT 0 + +#endif /* USB_EHCI_H */ diff --git a/qemu/roms/SLOF/lib/libusb/usb-hid.c b/qemu/roms/SLOF/lib/libusb/usb-hid.c new file mode 100644 index 000000000..f0cab8a69 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-hid.c @@ -0,0 +1,453 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <termctrl.h> + +#include "usb-core.h" +#include "usb-key.h" + +/* + * HID Spec Version 1.11 + */ + +#define HID_REQ_GET_REPORT 0x01 +#define HID_REQ_GET_IDLE 0x02 +#define HID_REQ_GET_PROTOCOL 0x03 +#define HID_REQ_SET_REPORT 0x09 +#define HID_REQ_SET_IDLE 0x0A +#define HID_REQ_SET_PROTOCOL 0x0B + +//#define KEY_DEBUG + +/* HID SPEC - 7.2.6 Set_Protocol Request */ +static int usb_hid_set_protocol(struct usb_dev *dev, uint16_t value) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT; + req.bRequest = HID_REQ_SET_PROTOCOL; + req.wValue = cpu_to_le16(value); + req.wIndex = cpu_to_le16(dev->intf_num); + req.wLength = 0; + return usb_send_ctrl(dev->control, &req, NULL); +} + +/* HID SPEC - 7.2.4 Set_Idle Request */ +static int usb_hid_set_idle(struct usb_dev *dev, uint16_t ms_delay) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT; + req.bRequest = HID_REQ_SET_IDLE; + req.wValue = cpu_to_le16((ms_delay/4) << 8); + req.wIndex = cpu_to_le16(dev->intf_num); + req.wLength = 0; + return usb_send_ctrl(dev->control, &req, NULL); +} + +/* HID SPEC - 7.2.1 Get Report Request */ +static int usb_hid_get_report(struct usb_dev *dev, void *data, size_t size) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_IN; + req.bRequest = HID_REQ_GET_REPORT; + req.wIndex = cpu_to_le16(dev->intf_num); + + req.wLength = cpu_to_le16((uint16_t)size); + req.wValue = cpu_to_le16(1 << 8); + return usb_send_ctrl(dev->control, &req, data); +} + +/* ring buffer with RD/WR indices for key buffering */ +static uint8_t keybuf[256]; /* size fixed to byte range ! */ +uint8_t r_ptr = 0; /* RD-index for Keyboard-Buffer */ +uint8_t w_ptr = 0; /* WR-index for Keyboard-Buffer */ + +/* variables for LED status */ +uint8_t set_leds; +const uint8_t *key_std = NULL; +const uint8_t *key_std_shift = NULL; + +/** + * read character from Keyboard-Buffer + * + * @param - + * @return > 0 Keycode + * = 0 if no key available + */ +static int read_key(void) +{ + if (r_ptr != w_ptr) + return (int)keybuf[r_ptr++]; + else + return false; +} + +/** + * Store character into Keyboard-Buffer + * + * @param Key = detected ASCII-Key (> 0) + * @return - + */ +static void write_key(uint8_t key) +{ + if ((w_ptr + 1) != r_ptr) + keybuf[w_ptr++] = key; +} + +/** + * Convert keyboard usage-ID to ANSI-Code + * + * @param Ctrl=Modifier Byte + * Key =Usage ID from USB Keyboard + * @return - + */ +static void get_char(uint8_t ctrl, uint8_t keypos) +{ + uint8_t ch; + +#ifdef KEY_DEBUG + printf("pos %02X\n", keypos); +#endif + + if (set_leds & LED_CAPS_LOCK) /* is CAPS Lock set ? */ + ctrl |= MODIFIER_SHIFT; /* simulate shift */ + + if (ctrl == 0) { + ch = key_std[keypos]; + if (ch != 0) + write_key(ch); + return; + } + + if (ctrl & MODIFIER_SHIFT) { + ch = key_std_shift[keypos]; + if (ch != 0) + write_key(ch); + return; + } + + if (ctrl & MODIFIER_CTRL) { + ch = keycodes_ctrl[keypos]; + if (ch != 0) + write_key(ch); + return; + } + + if (ctrl == MODIFIER_ALT_GR) { + ch = keycodes_alt_GR[keypos]; + if (ch != 0) + write_key(ch); + return; + } +} + +static void check_key_code(uint8_t *buf) +{ + static uint8_t key_last[6]; /* list of processed keys */ + uint8_t i, j, key_pos; + + /* set translation table to defaults */ + if ((key_std == NULL) || (key_std_shift == NULL)) { + key_std = keycodes_std_US; + key_std_shift = keycodes_shift_US; + } + + if (buf[0] & MODIFIER_SHIFT) /* any shift key pressed ? */ + set_leds &= ~LED_CAPS_LOCK; /* CAPS-LOCK-LED always off */ + + i = 2; /* skip modifier byte and reserved byte */ + while (i < 8) { + key_pos = buf[i]; + if ((key_pos != 0) && (key_pos <= 100)) { /* support for 101 keys */ + j = 0; + /* search if already processed */ + while ((j < 6) && (key_pos != key_last[j])) + j++; + + if (j >= 6) { /* not found (= not processed) */ + switch (key_pos) { + case 0x39: /* caps-lock key ? */ + case 0x32: /* caps-lock key ? */ + set_leds ^= LED_CAPS_LOCK; + break; + + case 0x3a: /* F1 */ + write_key(0x1b); + write_key(0x5b); + write_key(0x31); + write_key(0x31); + write_key(0x7e); + break; + + case 0x3b: /* F2 */ + write_key(0x1b); + write_key(0x5b); + write_key(0x31); + write_key(0x32); + write_key(0x7e); + break; + + case 0x3c: + write_key(0x1b); /* F3 */ + write_key(0x5b); + write_key(0x31); + write_key(0x33); + write_key(0x7e); + break; + + case 0x3d: + write_key(0x1b); /* F4 */ + write_key(0x5b); + write_key(0x31); + write_key(0x34); + write_key(0x7e); + break; + + case 0x3e: + write_key(0x1b); /* F5 */ + write_key(0x5b); + write_key(0x31); + write_key(0x35); + write_key(0x7e); + break; + + case 0x3f: + write_key(0x1b); /* F6 */ + write_key(0x5b); + write_key(0x31); + write_key(0x37); + write_key(0x7e); + break; + + case 0x40: + write_key(0x1b); /* F7 */ + write_key(0x5b); + write_key(0x31); + write_key(0x38); + write_key(0x7e); + break; + + case 0x41: + write_key(0x1b); /* F8 */ + write_key(0x5b); + write_key(0x31); + write_key(0x39); + write_key(0x7e); + break; + + case 0x42: + write_key(0x1b); /* F9 */ + write_key(0x5b); + write_key(0x31); + write_key(0x30); + write_key(0x7e); + break; + + case 0x43: + write_key(0x1b); /* F10 */ + write_key(0x5b); + write_key(0x31); + write_key(0x31); + write_key(0x7e); + break; + + case 0x44: + write_key(0x1b); /* F11 */ + write_key(0x5b); + write_key(0x31); + write_key(0x33); + write_key(0x7e); + break; + + case 0x45: + write_key(0x1b); /* F12 */ + write_key(0x5b); + write_key(0x31); + write_key(0x34); + write_key(0x7e); + break; + + case 0x47: /* scroll-lock key ? */ + set_leds ^= LED_SCROLL_LOCK; + break; + + case 0x49: + write_key(0x1b); /* INS */ + write_key(0x5b); + write_key(0x31); + write_key(0x7e); + break; + + case 0x4a: + write_key(0x1b); /* HOME */ + write_key(0x5b); + write_key(0x32); + write_key(0x7e); + break; + + case 0x4b: + write_key(0x1b); /* PgUp */ + write_key(0x5b); + write_key(0x33); + write_key(0x7e); + break; + + case 0x4c: + write_key(0x1b); /* DEL */ + write_key(0x5b); + write_key(0x34); + write_key(0x7e); + break; + + case 0x4d: + write_key(0x1b); /* END */ + write_key(0x5b); + write_key(0x35); + write_key(0x7e); + break; + + case 0x4e: + write_key(0x1b); /* PgDn */ + write_key(0x5b); + write_key(0x36); + write_key(0x7e); + break; + + case 0x4f: + write_key(0x1b); /* R-Arrow */ + write_key(0x5b); + write_key(0x43); + break; + + case 0x50: + write_key(0x1b); /* L-Arrow */ + write_key(0x5b); + write_key(0x44); + break; + + case 0x51: + write_key(0x1b); /* D-Arrow */ + write_key(0x5b); + write_key(0x42); + break; + + case 0x52: + write_key(0x1b); /* U-Arrow */ + write_key(0x5b); + write_key(0x41); + break; + + case 0x53: /* num-lock key ? */ + set_leds ^= LED_NUM_LOCK; + break; + + default: + /* convert key position to ASCII code */ + get_char(buf[0], key_pos); + break; + } + } + } + i++; + } + /*****************************************/ + /* all keys are processed, create a copy */ + /* to flag them as processed */ + /*****************************************/ + for (i = 2, j = 0; j < 6; i++, j++) + key_last[j] = buf[i]; /* copy all actual keys to last */ +} + +#define USB_HID_SIZE 128 +uint32_t *kbd_buffer; + +int usb_hid_kbd_init(struct usb_dev *dev) +{ + int i; + uint8_t key[8]; + + usb_hid_set_protocol(dev, 0); + usb_hid_set_idle(dev, 500); + + memset(key, 0, 8); + if (usb_hid_get_report(dev, key, 8)) + check_key_code(key); + + kbd_buffer = SLOF_dma_alloc(USB_HID_SIZE); + if (!kbd_buffer) { + printf("%s: unable to allocate keyboard buffer\n", __func__); + return false; + } + +#ifdef KEY_DEBUG + printf("HID kbd init %d\n", dev->ep_cnt); +#endif + for (i = 0; i < dev->ep_cnt; i++) { + if ((dev->ep[i].bmAttributes & USB_EP_TYPE_MASK) + == USB_EP_TYPE_INTR) + usb_dev_populate_pipe(dev, &dev->ep[i], kbd_buffer, USB_HID_SIZE); + } + return true; +} + +int usb_hid_kbd_exit(struct usb_dev *dev) +{ + if (dev->intr) { + usb_put_pipe(dev->intr); + dev->intr = NULL; + } + SLOF_dma_free(kbd_buffer, USB_HID_SIZE); + return true; +} + +static int usb_poll_key(void *vdev) +{ + struct usb_dev *dev = vdev; + uint8_t key[8]; + int rc; + + memset(key, 0, 8); + rc = usb_poll_intr(dev->intr, key); + if (rc) + check_key_code(key); + return rc; +} + +unsigned char usb_key_available(void *dev) +{ + if (!dev) + return false; + + usb_poll_key(dev); + if (r_ptr != w_ptr) + return true; + else + return false; +} + +unsigned char usb_read_keyb(void *vdev) +{ + if (!vdev) + return false; + + while (usb_poll_key(vdev)) { + /* loop for all pending keys */ + } + return read_key(); +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-hub.c b/qemu/roms/SLOF/lib/libusb/usb-hub.c new file mode 100644 index 000000000..7059cd019 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-hub.c @@ -0,0 +1,183 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include "usb-core.h" + +#undef HUB_DEBUG +//#define HUB_DEBUG +#ifdef HUB_DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +/* + * USB Spec 1.1 + * 11.16.2 Class-specific Requests + */ +struct usb_hub_ps { + uint16_t wPortStatus; + uint16_t wPortChange; +} __attribute__((packed)); + +#define HUB_PS_CONNECTION (1 << 0) +#define HUB_PS_ENABLE (1 << 1) +#define HUB_PS_SUSPEND (1 << 2) +#define HUB_PS_OVER_CURRENT (1 << 3) +#define HUB_PS_RESET (1 << 4) +#define HUB_PS_POWER (1 << 8) +#define HUB_PS_LOW_SPEED (1 << 9) + +#define HUB_PF_CONNECTION 0 +#define HUB_PF_ENABLE 1 +#define HUB_PF_SUSPEND 2 +#define HUB_PF_OVER_CURRENT 3 +#define HUB_PF_RESET 4 +#define HUB_PF_POWER 8 +#define HUB_PF_LOWSPEED 9 +#define HUB_PF_C_CONNECTION 16 +#define HUB_PF_C_ENABLE 17 +#define HUB_PF_C_SUSPEND 18 +#define HUB_PF_C_OVER_CURRENT 19 +#define HUB_PF_C_RESET 20 + +static int usb_get_hub_desc(struct usb_dev *dev, void *data, size_t size) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_IN | REQT_TYPE_CLASS | REQT_REC_DEVICE; + req.bRequest = REQ_GET_DESCRIPTOR; + req.wIndex = 0; + req.wLength = cpu_to_le16((uint16_t) size); + req.wValue = cpu_to_le16(DESCR_TYPE_HUB << 8); + return usb_send_ctrl(dev->control, &req, data); +} + +static int hub_get_port_status(struct usb_dev *dev, int port, void *data, size_t size) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_IN | REQT_TYPE_CLASS | REQT_REC_OTHER; + req.bRequest = REQ_GET_STATUS; + req.wValue = 0; + req.wIndex = cpu_to_le16((uint16_t)(port + 1)); + req.wLength = cpu_to_le16((uint16_t)size); + return usb_send_ctrl(dev->control, &req, data); +} + +static int hub_set_port_feature(struct usb_dev *dev, int port, int feature) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_OUT | REQT_TYPE_CLASS | REQT_REC_OTHER; + req.bRequest = REQ_SET_FEATURE; + req.wLength = 0; + req.wValue = cpu_to_le16((uint16_t)feature); + req.wIndex = cpu_to_le16((uint16_t)(port + 1)); + return usb_send_ctrl(dev->control, &req, NULL); +} + +#if 0 +static int hub_clear_port_feature(struct usb_dev *dev, int port, int feature) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_OUT | REQT_TYPE_CLASS | REQT_REC_OTHER; + req.bRequest = REQ_CLEAR_FEATURE; + req.wLength = 0; + req.wValue = cpu_to_le16((uint16_t)feature); + req.wIndex = cpu_to_le16((uint16_t)(port + 1)); + return usb_send_ctrl(dev->control, &req, NULL); +} +#endif + +static int hub_check_port(struct usb_dev *dev, int port) +{ + struct usb_hub_ps ps; + uint32_t time; + + if (!hub_get_port_status(dev, port, &ps, sizeof(ps))) + return false; + dprintf("Port Status %04X Port Change %04X\n", + le16_to_cpu(ps.wPortStatus), + le16_to_cpu(ps.wPortChange)); + + if (!(le16_to_cpu(ps.wPortStatus) & HUB_PS_POWER)) { + hub_set_port_feature(dev, port, HUB_PF_POWER); + SLOF_msleep(100); + time = SLOF_GetTimer() + USB_TIMEOUT; + while (time > SLOF_GetTimer()) { + cpu_relax(); + hub_get_port_status(dev, port, &ps, sizeof(ps)); + if (le16_to_cpu(ps.wPortStatus) & HUB_PS_CONNECTION) { + dprintf("power on Port Status %04X Port Change %04X\n", + le16_to_cpu(ps.wPortStatus), + le16_to_cpu(ps.wPortChange)); + break; + } + } + } + + if (le16_to_cpu(ps.wPortStatus) & HUB_PS_CONNECTION) { + hub_set_port_feature(dev, port, HUB_PF_RESET); + SLOF_msleep(100); + time = SLOF_GetTimer() + USB_TIMEOUT; + while (time > SLOF_GetTimer()) { + cpu_relax(); + hub_get_port_status(dev, port, &ps, sizeof(ps)); + if (!(le16_to_cpu(ps.wPortStatus) & HUB_PS_RESET)) { + dprintf("reset Port Status %04X Port Change %04X\n", + le16_to_cpu(ps.wPortStatus), + le16_to_cpu(ps.wPortChange)); + return true; + } + } + } + return false; +} + +unsigned int usb_hub_init(void *hubdev) +{ + struct usb_dev *dev = hubdev; + struct usb_dev_hub_descr hub; + struct usb_dev *newdev; + int i; + + dprintf("%s: enter %p\n", __func__, dev); + if (!dev) { + printf("usb-hub: NULL\n"); + return false; + } + memset(&hub, 0, sizeof(hub)); + usb_get_hub_desc(dev, &hub, sizeof(hub)); + dprintf("usb-hub: ports connected %d\n", hub.bNbrPorts); + for (i = 0; i < hub.bNbrPorts; i++) { + dprintf("usb-hub: ports scanning %d\n", i); + if (hub_check_port(dev, i)) { + dprintf("***********************************************\n"); + dprintf("\t\tusb-hub: device found %d\n", i); + dprintf("***********************************************\n"); + newdev = usb_devpool_get(); + dprintf("usb-hub: allocated device %p\n", newdev); + newdev->hcidev = dev->hcidev; + if (!setup_new_device(newdev, i)) + printf("usb-hub: unable to setup device on port %d\n", i); + } + } + return true; +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-key.c b/qemu/roms/SLOF/lib/libusb/usb-key.c new file mode 100644 index 000000000..7fb45da2c --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-key.c @@ -0,0 +1,446 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> + +/***********************************/ +/* Keycodes for US Keyboard */ +/* - no control keys pressed - */ +/***********************************/ +const uint8_t keycodes_std_US[] = { + 0, /* 0 00 Reserved (no event indicated) */ + 0, /* 1 01 Keyboard ErrorRollOver */ + 0, /* 2 02 Keyboard POSTFail */ + 0, /* 3 03 Keyboard ErrorUndefined */ + 'a', /* 4 04 Keyboard a and A 31 */ + 'b', /* 5 05 Keyboard b and B 50 */ + 'c', /* 6 06 Keyboard c and C 48 */ + 'd', /* 7 07 Keyboard d and D 33 */ + 'e', /* 8 08 Keyboard e and E 19 */ + 'f', /* 9 09 Keyboard f and F 34 */ + 'g', /* 10 0A Keyboard g and G 35 */ + 'h', /* 11 0B Keyboard h and H 36 */ + 'i', /* 12 0C Keyboard i and I 24 */ + 'j', /* 13 0D Keyboard j and J 37 */ + 'k', /* 14 0E Keyboard k and K 38 */ + 'l', /* 15 0F Keyboard l and L 39 */ + 'm', /* 16 10 Keyboard m and M 52 */ + 'n', /* 17 11 Keyboard n and N 51 */ + 'o', /* 18 12 Keyboard o and O 25 */ + 'p', /* 19 13 Keyboard p and P 26 */ + 'q', /* 20 14 Keyboard q and Q 17 */ + 'r', /* 21 15 Keyboard r and R 20 */ + 's', /* 22 16 Keyboard s and S 32 */ + 't', /* 23 17 Keyboard t and T 21 */ + 'u', /* 24 18 Keyboard u and U 23 */ + 'v', /* 25 19 Keyboard v and V 49 */ + 'w', /* 26 1A Keyboard w and W 18 */ + 'x', /* 27 1B Keyboard x and X 47 */ + 'y', /* 28 1C Keyboard y and Y 22 */ + 'z', /* 29 1D Keyboard z and Z 46 */ + '1', /* 30 1E Keyboard 1 and ! 2 */ + '2', /* 31 1F Keyboard 2 and @ 3 */ + '3', /* 32 20 Keyboard 3 and # 4 */ + '4', /* 33 21 Keyboard 4 and $ 5 */ + '5', /* 34 22 Keyboard 5 and % 6 */ + '6', /* 35 23 Keyboard 6 and ^ 7 */ + '7', /* 36 24 Keyboard 7 and & 8 */ + '8', /* 37 25 Keyboard 8 and * 9 */ + '9', /* 38 26 Keyboard 9 and ( 10 */ + '0', /* 39 27 Keyboard 0 and ) 11 */ + 13, /* 40 28 Keyboard Return (ENTER) 43 */ + 27, /* 41 29 Keyboard ESCAPE 110 */ + 8, /* 42 2A Keyboard DELETE (BS) 15 */ + 9, /* 43 2B Keyboard Tab 16 */ + ' ', /* 44 2C Keyboard Spacebar 61 */ + '-', /* 45 2D Keyboard - and (underscore) 12 */ + '=', /* 46 2E Keyboard = and + 13 */ + '[', /* 47 2F Keyboard [ and { 27 */ + ']', /* 48 30 Keyboard ] and } 28 */ + '\\', /* 49 31 Keyboard \ and | 29 */ + '\\', /* 50 32 Keyboard \ and | 42 */ + ';', /* 51 33 Keyboard ; and : 40 */ + 39, /* 52 34 Keyboard ' and " 41 */ + 96, /* 53 35 Keyboard Grave Accent and Tilde 1 */ + ',', /* 54 36 Keyboard , and < 53 */ + '.', /* 55 37 Keyboard . and > 54 */ + '/', /* 56 38 Keyboard / and ? 55 */ + 0, /* 57 39 Keyboard Caps Lock 30 */ + 0, /* 58 3A Keyboard F1 112 */ + 0, /* 59 3B Keyboard F2 113 */ + 0, /* 60 3C Keyboard F3 114 */ + 0, /* 61 3D Keyboard F4 115 */ + 0, /* 62 3E Keyboard F5 116 */ + 0, /* 63 3F Keyboard F6 117 */ + 0, /* 64 40 Keyboard F7 118 */ + 0, /* 65 41 Keyboard F8 119 */ + 0, /* 66 42 Keyboard F9 120 */ + 0, /* 67 43 Keyboard F10 121 */ + 0, /* 68 44 Keyboard F11 122 */ + 0, /* 69 45 Keyboard F12 123 */ + 0, /* 70 46 Keyboard PrintScreen 124 */ + 0, /* 71 47 Keyboard Scroll Lock 125 */ + 0, /* 72 48 Keyboard Pause 126 */ + 0, /* 73 49 Keyboard Insert 75 */ + 0, /* 74 4A Keyboard Home 80 */ + 0, /* 75 4B Keyboard PageUp 85 */ + 0, /* 76 4C Keyboard Delete Forward 76 */ + 0, /* 77 4D Keyboard End 81 */ + 0, /* 78 4E Keyboard PageDown 86 */ + 0, /* 79 4F Keyboard RightArrow 89 */ + 0, /* 80 50 Keyboard LeftArrow 79 */ + 0, /* 81 51 Keyboard DownArrow 84 */ + 0, /* 82 52 Keyboard UpArrow 83 */ + 0, /* 83 53 Keypad Num Lock and Clear 90 */ + '/', /* 84 54 Keypad / 95 */ + '*', /* 85 55 Keypad * 100 */ + '-', /* 86 56 Keypad - 105 */ + '+', /* 87 57 Keypad + 106 */ + 13, /* 88 58 Keypad ENTER 108 */ + '1', /* 89 59 Keypad 1 and End 93 */ + '2', /* 90 5A Keypad 2 and Down Arrow 98 */ + '3', /* 91 5B Keypad 3 and PageDn 103 */ + '4', /* 92 5C Keypad 4 and Left Arrow 92 */ + '5', /* 93 5D Keypad 5 97 */ + '6', /* 94 5E Keypad 6 and Right Arrow 102 */ + '7', /* 95 5F Keypad 7 and Home 91 */ + '8', /* 96 60 Keypad 8 and Up Arrow 96 */ + '9', /* 97 61 Keypad 9 and PageUp 101 */ + '0', /* 98 62 Keypad 0 and Insert 99 */ + '.', /* 99 63 Keypad . and Delete 104 */ + '\\' /* 100 64 Keyboard Non-US \ and | 45 */ +}; + +/***********************************/ +/* Keycodes for US Keyboard */ +/* - SHIFT-KEY pressed - */ +/***********************************/ +const uint8_t keycodes_shift_US[] = { + 0, /* 0 00 Reserved (no event indicated) */ + 0, /* 1 01 Keyboard ErrorRollOver */ + 0, /* 2 02 Keyboard POSTFail */ + 0, /* 3 03 Keyboard ErrorUndefined */ + 'A', /* 4 04 Keyboard a and A 31 */ + 'B', /* 5 05 Keyboard b and B 50 */ + 'C', /* 6 06 Keyboard c and C 48 */ + 'D', /* 7 07 Keyboard d and D 33 */ + 'E', /* 8 08 Keyboard e and E 19 */ + 'F', /* 9 09 Keyboard f and F 34 */ + 'G', /* 10 0A Keyboard g and G 35 */ + 'H', /* 11 0B Keyboard h and H 36 */ + 'I', /* 12 0C Keyboard i and I 24 */ + 'J', /* 13 0D Keyboard j and J 37 */ + 'K', /* 14 0E Keyboard k and K 38 */ + 'L', /* 15 0F Keyboard l and L 39 */ + 'M', /* 16 10 Keyboard m and M 52 */ + 'N', /* 17 11 Keyboard n and N 51 */ + 'O', /* 18 12 Keyboard o and O 25 */ + 'P', /* 19 13 Keyboard p and P 26 */ + 'Q', /* 20 14 Keyboard q and Q 17 */ + 'R', /* 21 15 Keyboard r and R 20 */ + 'S', /* 22 16 Keyboard s and S 32 */ + 'T', /* 23 17 Keyboard t and T 21 */ + 'U', /* 24 18 Keyboard u and U 23 */ + 'V', /* 25 19 Keyboard v and V 49 */ + 'W', /* 26 1A Keyboard w and W 18 */ + 'X', /* 27 1B Keyboard x and X 47 */ + 'Y', /* 28 1C Keyboard y and Y 22 */ + 'Z', /* 29 1D Keyboard z and Z 46 */ + '!', /* 30 1E Keyboard 1 and ! 2 */ + '@', /* 31 1F Keyboard 2 and @ 3 */ + '#', /* 32 20 Keyboard 3 and # 4 */ + '$', /* 33 21 Keyboard 4 and $ 5 */ + '%', /* 34 22 Keyboard 5 and % 6 */ + '^', /* 35 23 Keyboard 6 and ^ 7 */ + '&', /* 36 24 Keyboard 7 and & 8 */ + '*', /* 37 25 Keyboard 8 and * 9 */ + '(', /* 38 26 Keyboard 9 and ( 10 */ + ')', /* 39 27 Keyboard 0 and ) 11 */ + 13, /* 40 28 Keyboard Return (ENTER) 43 */ + 27, /* 41 29 Keyboard ESCAPE 110 */ + 8, /* 42 2A Keyboard DELETE (BS) 15 */ + 9, /* 43 2B Keyboard Tab 16 */ + ' ', /* 44 2C Keyboard Spacebar 61 */ + '_', /* 45 2D Keyboard - and (underscore) 12 */ + '+', /* 46 2E Keyboard = and + 13 */ + '{', /* 47 2F Keyboard [ and { 27 */ + '}', /* 48 30 Keyboard ] and } 28 */ + '|', /* 49 31 Keyboard \ and | 29 */ + '|', /* 50 32 Keyboard \ and | 42 */ + ':', /* 51 33 Keyboard ; and : 40 */ + '"', /* 52 34 Keyboard ' and " 41 */ + '~', /* 53 35 Keyboard Grave Accent and Tilde 1 */ + '<', /* 54 36 Keyboard , and < 53 */ + '>', /* 55 37 Keyboard . and > 54 */ + '?', /* 56 38 Keyboard / and ? 55 */ + 0, /* 57 39 Keyboard Caps Lock 30 */ + 0, /* 58 3A Keyboard F1 112 */ + 0, /* 59 3B Keyboard F2 113 */ + 0, /* 60 3C Keyboard F3 114 */ + 0, /* 61 3D Keyboard F4 115 */ + 0, /* 62 3E Keyboard F5 116 */ + 0, /* 63 3F Keyboard F6 117 */ + 0, /* 64 40 Keyboard F7 118 */ + 0, /* 65 41 Keyboard F8 119 */ + 0, /* 66 42 Keyboard F9 120 */ + 0, /* 67 43 Keyboard F10 121 */ + 0, /* 68 44 Keyboard F11 122 */ + 0, /* 69 45 Keyboard F12 123 */ + 0, /* 70 46 Keyboard PrintScreen 124 */ + 0, /* 71 47 Keyboard Scroll Lock 125 */ + 0, /* 72 48 Keyboard Pause 126 */ + 48, /* 73 49 Keyboard Insert 75 */ + 55, /* 74 4A Keyboard Home 80 */ + 57, /* 75 4B Keyboard PageUp 85 */ + 46, /* 76 4C Keyboard Delete Forward 76 */ + 49, /* 77 4D Keyboard End 81 */ + 51, /* 78 4E Keyboard PageDown 86 */ + 54, /* 79 4F Keyboard RightArrow 89 */ + 52, /* 80 50 Keyboard LeftArrow 79 */ + 50, /* 81 51 Keyboard DownArrow 84 */ + 56, /* 82 52 Keyboard UpArrow 83 */ + 0, /* 83 53 Keypad Num Lock and Clear 90 */ + '/', /* 84 54 Keypad / 95 */ + '*', /* 85 55 Keypad * 100 */ + '-', /* 86 56 Keypad - 105 */ + '+', /* 87 57 Keypad + 106 */ + 13, /* 88 58 Keypad ENTER 108 */ + '1', /* 89 59 Keypad 1 and End 93 */ + '2', /* 90 5A Keypad 2 and Down Arrow 98 */ + '3', /* 91 5B Keypad 3 and PageDn 103 */ + '4', /* 92 5C Keypad 4 and Left Arrow 92 */ + '5', /* 93 5D Keypad 5 97 */ + '6', /* 94 5E Keypad 6 and Right Arrow 102 */ + '7', /* 95 5F Keypad 7 and Home 91 */ + '8', /* 96 60 Keypad 8 and Up Arrow 96 */ + '9', /* 97 61 Keypad 9 and PageUp 101 */ + '0', /* 98 62 Keypad 0 and Insert 99 */ + '.', /* 99 63 Keypad . and Delete 104 */ + '|' /* 100 64 Keyboard Non-US \ and | 45 */ +}; + +/***********************************/ +/* Keycodes for 1 byte translation */ +/* - CONTROL-KEY pressed - */ +/***********************************/ +const uint8_t keycodes_alt_GR[] = { + 0, /* 0 00 Reserved (no event indicated) */ + 0, /* 1 01 Keyboard ErrorRollOver */ + 0, /* 2 02 Keyboard POSTFail */ + 0, /* 3 03 Keyboard ErrorUndefined */ + 0, /* 4 04 Keyboard a and A 31 */ + 0, /* 5 05 Keyboard b and B 50 */ + 0, /* 6 06 Keyboard c and C 48 */ + 0, /* 7 07 Keyboard d and D 33 */ + 0, /* 8 08 Keyboard e and E 19 */ + 0, /* 9 09 Keyboard f and F 34 */ + 0, /* 10 0A Keyboard g and G 35 */ + 0, /* 11 0B Keyboard h and H 36 */ + 0, /* 12 0C Keyboard i and I 24 */ + 0, /* 13 0D Keyboard j and J 37 */ + 0, /* 14 0E Keyboard k and K 38 */ + 0, /* 15 0F Keyboard l and L 39 */ + 0, /* 16 10 Keyboard m and M 52 */ + 0, /* 17 11 Keyboard n and N 51 */ + 0, /* 18 12 Keyboard o and O 25 */ + 0, /* 19 13 Keyboard p and P 26 */ + '@', /* 20 14 Keyboard q and Q 17 */ + 0, /* 21 15 Keyboard r and R 20 */ + 0, /* 22 16 Keyboard s and S 32 */ + 0, /* 23 17 Keyboard t and T 21 */ + 0, /* 24 18 Keyboard u and U 23 */ + 0, /* 25 19 Keyboard v and V 49 */ + 0, /* 26 1A Keyboard w and W 18 */ + 0, /* 27 1B Keyboard x and X 47 */ + 0, /* 28 1C Keyboard y and Y 22 */ + 0, /* 29 1D Keyboard z and Z 46 */ + 0, /* 30 1E Keyboard 1 and ! 2 */ + 0, /* 31 1F Keyboard 2 and @ 3 */ + 0, /* 32 20 Keyboard 3 and # 4 */ + 0, /* 33 21 Keyboard 4 and $ 5 */ + 0, /* 34 22 Keyboard 5 and % 6 */ + 0, /* 35 23 Keyboard 6 and ^ 7 */ + '{', /* 36 24 Keyboard 7 and & 8 */ + '[', /* 37 25 Keyboard 8 and * 9 */ + ']', /* 38 26 Keyboard 9 and ( 10 */ + '}', /* 39 27 Keyboard 0 and ) 11 */ + 0, /* 40 28 Keyboard Return (ENTER) 43 */ + 0, /* 41 29 Keyboard ESCAPE 110 */ + 0, /* 42 2A Keyboard DELETE (BS) 15 */ + 0, /* 43 2B Keyboard Tab 16 */ + 0, /* 44 2C Keyboard Spacebar 61 */ + '\\', /* 45 2D Keyboard - and (underscore) 12 */ + 0, /* 46 2E Keyboard = and + 13 */ + 0, /* 47 2F Keyboard [ and { 27 */ + '~', /* 48 30 Keyboard ] and } 28 */ + 0, /* 49 31 Keyboard \ and | 29 */ + 0, /* 50 32 Keyboard Non-US # and ~ 42 */ + 0, /* 51 33 Keyboard ; and : 40 */ + 0, /* 52 34 Keyboard ' and " 41 */ + 0, /* 53 35 Keyboard Grave Accent and Tilde 1 */ + 0, /* 54 36 Keyboard , and < 53 */ + 0, /* 55 37 Keyboard . and > 54 */ + 0, /* 56 38 Keyboard / and ? 55 */ + 0, /* 57 39 Keyboard Caps Lock 30 */ + 0, /* 58 3A Keyboard F1 112 */ + 0, /* 59 3B Keyboard F2 113 */ + 0, /* 60 3C Keyboard F3 114 */ + 0, /* 61 3D Keyboard F4 115 */ + 0, /* 62 3E Keyboard F5 116 */ + 0, /* 63 3F Keyboard F6 117 */ + 0, /* 64 40 Keyboard F7 118 */ + 0, /* 65 41 Keyboard F8 119 */ + 0, /* 66 42 Keyboard F9 120 */ + 0, /* 67 43 Keyboard F10 121 */ + 0, /* 68 44 Keyboard F11 122 */ + 0, /* 69 45 Keyboard F12 123 */ + 0, /* 70 46 Keyboard PrintScreen 124 */ + 0, /* 71 47 Keyboard Scroll Lock 125 */ + 0, /* 72 48 Keyboard Pause 126 */ + 0, /* 73 49 Keyboard Insert 75 */ + 0, /* 74 4A Keyboard Home 80 */ + 0, /* 75 4B Keyboard PageUp 85 */ + 0, /* 76 4C Keyboard Delete Forward 76 */ + 0, /* 77 4D Keyboard End 81 */ + 0, /* 78 4E Keyboard PageDown 86 */ + 0, /* 79 4F Keyboard RightArrow 89 */ + 0, /* 80 50 Keyboard LeftArrow 79 */ + 0, /* 81 51 Keyboard DownArrow 84 */ + 0, /* 82 52 Keyboard UpArrow 83 */ + 0, /* 83 53 Keypad Num Lock and Clear 90 */ + 0, /* 84 54 Keypad / 95 */ + 0, /* 85 55 Keypad * 100 */ + 0, /* 86 56 Keypad - 105 */ + 0, /* 87 57 Keypad + 106 */ + 0, /* 88 58 Keypad ENTER 108 */ + 0, /* 89 59 Keypad 1 and End 93 */ + 0, /* 90 5A Keypad 2 and Down Arrow 98 */ + 0, /* 91 5B Keypad 3 and PageDn 103 */ + 0, /* 92 5C Keypad 4 and Left Arrow 92 */ + 0, /* 93 5D Keypad 5 97 */ + 0, /* 94 5E Keypad 6 and Right Arrow 102 */ + 0, /* 95 5F Keypad 7 and Home 91 */ + 0, /* 96 60 Keypad 8 and Up Arrow 96 */ + 0, /* 97 61 Keypad 9 and PageUp 101 */ + 0, /* 98 62 Keypad 0 and Insert 99 */ + 0, /* 99 63 Keypad . and Delete 104 */ + '|' /* 100 64 Keyboard Non-US \ and | 45 */ +}; + + +/***********************************/ +/* Keycodes for 1 byte translation */ +/* - CONTROL-KEY pressed - */ +/***********************************/ +const uint8_t keycodes_ctrl[] = { + 0, /* 0 00 Reserved (no event indicated) */ + 0, /* 1 01 Keyboard ErrorRollOver */ + 0, /* 2 02 Keyboard POSTFail */ + 0, /* 3 03 Keyboard ErrorUndefined */ + 1, /* 4 04 Keyboard a and A 31 */ + 2, /* 5 05 Keyboard b and B 50 */ + 3, /* 6 06 Keyboard c and C 48 */ + 4, /* 7 07 Keyboard d and D 33 */ + 5, /* 8 08 Keyboard e and E 19 */ + 6, /* 9 09 Keyboard f and F 34 */ + 7, /* 10 0A Keyboard g and G 35 */ + 8, /* 11 0B Keyboard h and H 36 */ + 9, /* 12 0C Keyboard i and I 24 */ + 10, /* 13 0D Keyboard j and J 37 */ + 11, /* 14 0E Keyboard k and K 38 */ + 12, /* 15 0F Keyboard l and L 39 */ + 13, /* 16 10 Keyboard m and M 52 */ + 14, /* 17 11 Keyboard n and N 51 */ + 15, /* 18 12 Keyboard o and O 25 */ + 16, /* 19 13 Keyboard p and P 26 */ + 17, /* 20 14 Keyboard q and Q 17 */ + 18, /* 21 15 Keyboard r and R 20 */ + 19, /* 22 16 Keyboard s and S 32 */ + 20, /* 23 17 Keyboard t and T 21 */ + 21, /* 24 18 Keyboard u and U 23 */ + 22, /* 25 19 Keyboard v and V 49 */ + 23, /* 26 1A Keyboard w and W 18 */ + 24, /* 27 1B Keyboard x and X 47 */ + 25, /* 28 1C Keyboard y and Y 22 */ + 26, /* 29 1D Keyboard z and Z 46 */ + 0, /* 30 1E Keyboard 1 and ! 2 */ + 0, /* 31 1F Keyboard 2 and @ 3 */ + 0, /* 32 20 Keyboard 3 and # 4 */ + 0, /* 33 21 Keyboard 4 and $ 5 */ + 0, /* 34 22 Keyboard 5 and % 6 */ + 0, /* 35 23 Keyboard 6 and ^ 7 */ + 0, /* 36 24 Keyboard 7 and & 8 */ + 0, /* 37 25 Keyboard 8 and * 9 */ + 0, /* 38 26 Keyboard 9 and ( 10 */ + 0, /* 39 27 Keyboard 0 and ) 11 */ + 0, /* 40 28 Keyboard Return (ENTER) 43 */ + 0, /* 41 29 Keyboard ESCAPE 110 */ + 0, /* 42 2A Keyboard DELETE (BS) 15 */ + 0, /* 43 2B Keyboard Tab 16 */ + 0, /* 44 2C Keyboard Spacebar 61 */ + 0, /* 45 2D Keyboard - and (underscore) 12 */ + 0, /* 46 2E Keyboard = and + 13 */ + 0, /* 47 2F Keyboard [ and { 27 */ + 0, /* 48 30 Keyboard ] and } 28 */ + 0, /* 49 31 Keyboard \ and | 29 */ + 0, /* 50 32 Keyboard Non-US # and ~ 42 */ + 0, /* 51 33 Keyboard ; and : 40 */ + 0, /* 52 34 Keyboard ' and " 41 */ + 0, /* 53 35 Keyboard Grave Accent and Tilde 1 */ + 0, /* 54 36 Keyboard , and < 53 */ + 0, /* 55 37 Keyboard . and > 54 */ + 0, /* 56 38 Keyboard / and ? 55 */ + 0, /* 57 39 Keyboard Caps Lock 30 */ + 0, /* 58 3A Keyboard F1 112 */ + 0, /* 59 3B Keyboard F2 113 */ + 0, /* 60 3C Keyboard F3 114 */ + 0, /* 61 3D Keyboard F4 115 */ + 0, /* 62 3E Keyboard F5 116 */ + 0, /* 63 3F Keyboard F6 117 */ + 0, /* 64 40 Keyboard F7 118 */ + 0, /* 65 41 Keyboard F8 119 */ + 0, /* 66 42 Keyboard F9 120 */ + 0, /* 67 43 Keyboard F10 121 */ + 0, /* 68 44 Keyboard F11 122 */ + 0, /* 69 45 Keyboard F12 123 */ + 0, /* 70 46 Keyboard PrintScreen 124 */ + 0, /* 71 47 Keyboard Scroll Lock 125 */ + 0, /* 72 48 Keyboard Pause 126 */ + 0, /* 73 49 Keyboard Insert 75 */ + 0, /* 74 4A Keyboard Home 80 */ + 0, /* 75 4B Keyboard PageUp 85 */ + 0, /* 76 4C Keyboard Delete Forward 76 */ + 0, /* 77 4D Keyboard End 81 */ + 0, /* 78 4E Keyboard PageDown 86 */ + 0, /* 79 4F Keyboard RightArrow 89 */ + 0, /* 80 50 Keyboard LeftArrow 79 */ + 0, /* 81 51 Keyboard DownArrow 84 */ + 0, /* 82 52 Keyboard UpArrow 83 */ + 0, /* 83 53 Keypad Num Lock and Clear 90 */ + 0, /* 84 54 Keypad / 95 */ + 0, /* 85 55 Keypad * 100 */ + 0, /* 86 56 Keypad - 105 */ + 0, /* 87 57 Keypad + 106 */ + 0, /* 88 58 Keypad ENTER 108 */ + 0, /* 89 59 Keypad 1 and End 93 */ + 0, /* 90 5A Keypad 2 and Down Arrow 98 */ + 0, /* 91 5B Keypad 3 and PageDn 103 */ + 0, /* 92 5C Keypad 4 and Left Arrow 92 */ + 0, /* 93 5D Keypad 5 97 */ + 0, /* 94 5E Keypad 6 and Right Arrow 102 */ + 0, /* 95 5F Keypad 7 and Home 91 */ + 0, /* 96 60 Keypad 8 and Up Arrow 96 */ + 0, /* 97 61 Keypad 9 and PageUp 101 */ + 0, /* 98 62 Keypad 0 and Insert 99 */ + 0, /* 99 63 Keypad . and Delete 104 */ + 0 /* 100 64 Keyboard Non-US \ and | 45 */ +}; diff --git a/qemu/roms/SLOF/lib/libusb/usb-key.h b/qemu/roms/SLOF/lib/libusb/usb-key.h new file mode 100644 index 000000000..1871a9956 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-key.h @@ -0,0 +1,42 @@ +#ifndef _USB_KEYB_H +#define _USB_KEYB_H + +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#define BIT_0 1 +#define BIT_1 (BIT_0 << 1) +#define BIT_2 (BIT_0 << 2) +#define BIT_3 (BIT_0 << 3) +#define BIT_4 (BIT_0 << 4) +#define BIT_5 (BIT_0 << 5) +#define BIT_6 (BIT_0 << 6) +#define BIT_7 (BIT_0 << 7) + +/* bits from modifier input */ +#define MODIFIER_CTRL (BIT_0 | BIT_4) +#define MODIFIER_SHIFT (BIT_1 | BIT_5) +#define MODIFIER_ALT (BIT_2 | BIT_6) +#define MODIFIER_GUI (BIT_3 | BIT_7) +#define MODIFIER_ALT_GR BIT_6 + +/* bits representing Keyboard-LEDs */ +#define LED_NUM_LOCK BIT_0 +#define LED_CAPS_LOCK BIT_1 +#define LED_SCROLL_LOCK BIT_2 + +extern const uint8_t keycodes_std_US[]; +extern const uint8_t keycodes_shift_US[]; +extern const uint8_t keycodes_alt_GR[]; +extern const uint8_t keycodes_ctrl[]; + +#endif diff --git a/qemu/roms/SLOF/lib/libusb/usb-ohci.c b/qemu/roms/SLOF/lib/libusb/usb-ohci.c new file mode 100644 index 000000000..0e8400481 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-ohci.c @@ -0,0 +1,1055 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include <byteorder.h> +#include "usb.h" +#include "usb-core.h" +#include "usb-ohci.h" + +#undef OHCI_DEBUG +//#define OHCI_DEBUG +#ifdef OHCI_DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +#undef OHCI_DEBUG_PACKET +//#define OHCI_DEBUG_PACKET +#ifdef OHCI_DEBUG_PACKET +#define dpprintf(_x ...) do { printf(_x); } while(0) +#else +#define dpprintf(_x ...) +#endif + + +/* + * Dump OHCI register + * + * @param - ohci_hcd + * @return - + */ +static void ohci_dump_regs(struct ohci_regs *regs) +{ + dprintf("\n - HcRevision %08X", read_reg32(®s->rev)); + dprintf(" - HcControl %08X", read_reg32(®s->control)); + dprintf("\n - HcCommandStatus %08X", read_reg32(®s->cmd_status)); + dprintf(" - HcInterruptStatus %08X", read_reg32(®s->intr_status)); + dprintf("\n - HcInterruptEnable %08X", read_reg32(®s->intr_enable)); + dprintf(" - HcInterruptDisable %08X", read_reg32(®s->intr_disable)); + dprintf("\n - HcHCCA %08X", read_reg32(®s->hcca)); + dprintf(" - HcPeriodCurrentED %08X", read_reg32(®s->period_curr_ed)); + dprintf("\n - HcControlHeadED %08X", read_reg32(®s->cntl_head_ed)); + dprintf(" - HcControlCurrentED %08X", read_reg32(®s->cntl_curr_ed)); + dprintf("\n - HcBulkHeadED %08X", read_reg32(®s->bulk_head_ed)); + dprintf(" - HcBulkCurrentED %08X", read_reg32(®s->bulk_curr_ed)); + dprintf("\n - HcDoneHead %08X", read_reg32(®s->done_head)); + dprintf(" - HcFmInterval %08X", read_reg32(®s->fm_interval)); + dprintf("\n - HcFmRemaining %08X", read_reg32(®s->fm_remaining)); + dprintf(" - HcFmNumber %08X", read_reg32(®s->fm_num)); + dprintf("\n - HcPeriodicStart %08X", read_reg32(®s->period_start)); + dprintf(" - HcLSThreshold %08X", read_reg32(®s->ls_threshold)); + dprintf("\n - HcRhDescriptorA %08X", read_reg32(®s->rh_desc_a)); + dprintf(" - HcRhDescriptorB %08X", read_reg32(®s->rh_desc_b)); + dprintf("\n - HcRhStatus %08X", read_reg32(®s->rh_status)); + dprintf("\n"); +} + +/* + * OHCI Spec 5.1.1 + * OHCI Spec 7.4 Root Hub Partition + */ +static int ohci_hcd_reset(struct ohci_regs *regs) +{ + uint32_t time; + + /* USBRESET - 1sec */ + write_reg32(®s->control, 0); + SLOF_msleep(100); + + write_reg32(®s->intr_disable, ~0); + write_reg32(®s->cmd_status, OHCI_CMD_STATUS_HCR); + mb(); + + time = 30; /* wait for not more than 30usec */ + while ((read_reg32(®s->cmd_status) & OHCI_CMD_STATUS_HCR) != 0) { + time--; + if (!time) { + printf(" ** HCD Reset failed..."); + return -1; + } + SLOF_usleep(1); + } + return 0; +} + +static int ohci_hcd_init(struct ohci_hcd *ohcd) +{ + struct ohci_regs *regs; + struct ohci_ed *ed; + long ed_phys = 0; + unsigned int i; + uint32_t oldrwc; + struct usb_dev *rhdev = NULL; + struct usb_ep_descr ep; + uint32_t reg; + + if (!ohcd) + return -1; + + regs = ohcd->regs; + rhdev = &ohcd->rhdev; + dprintf("%s: HCCA memory %p\n", __func__, ohcd->hcca); + dprintf("%s: OHCI Regs %p\n", __func__, regs); + + rhdev->hcidev = ohcd->hcidev; + ep.bmAttributes = USB_EP_TYPE_INTR; + ep.wMaxPacketSize = 8; + rhdev->intr = usb_get_pipe(rhdev, &ep, NULL, 0); + if (!rhdev->intr) { + printf("usb-ohci: oops could not allocate intr_pipe\n"); + return -1; + } + + /* + * OHCI Spec 4.4: Host Controller Communications Area + */ + ed = ohci_pipe_get_ed(rhdev->intr); + ed_phys = ohci_pipe_get_ed_phys(rhdev->intr); + memset(ohcd->hcca, 0, HCCA_SIZE); + memset(ed, 0, sizeof(struct ohci_ed)); + ed->attr = cpu_to_le32(EDA_SKIP); + for (i = 0; i < HCCA_INTR_NUM; i++) + ohcd->hcca->intr_table[i] = cpu_to_le32(ed_phys); + + write_reg32(®s->hcca, ohcd->hcca_phys); + write_reg32(®s->cntl_head_ed, 0); + write_reg32(®s->bulk_head_ed, 0); + + /* OHCI Spec 7.1.2 HcControl Register */ + oldrwc = read_reg32(®s->control) & OHCI_CTRL_RWC; + write_reg32(®s->control, (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | + OHCI_CTRL_BLE | OHCI_CTRL_PLE | + OHCI_USB_OPER | oldrwc)); + SLOF_msleep(100); + /* + * For JS20/21 need to rewrite it after setting it to + * operational state + */ + write_reg32(®s->fm_interval, FRAME_INTERVAL); + write_reg32(®s->period_start, PERIODIC_START); + reg = read_reg32(®s->rh_desc_a); + reg &= ~( RHDA_PSM_INDIVIDUAL | RHDA_OCPM_PERPORT ); + reg |= RHDA_NPS_ENABLE; + write_reg32(®s->rh_desc_a, reg); + write_reg32(®s->rh_desc_b, 0); + mb(); + SLOF_msleep(100); + ohci_dump_regs(regs); + return 0; +} + +/* + * OHCI Spec 7.4 Root Hub Partition + */ +static void ohci_hub_check_ports(struct ohci_hcd *ohcd) +{ + struct ohci_regs *regs; + struct usb_dev *dev; + unsigned int ports, i, port_status, port_clear = 0; + + regs = ohcd->regs; + ports = read_reg32(®s->rh_desc_a) & RHDA_NDP; + write_reg32(®s->rh_status, RH_STATUS_LPSC); + SLOF_msleep(100); + dprintf("usb-ohci: ports connected %d\n", ports); + for (i = 0; i < ports; i++) { + dprintf("usb-ohci: ports scanning %d\n", i); + port_status = read_reg32(®s->rh_ps[i]); + if (port_status & RH_PS_CSC) { + if (port_status & RH_PS_CCS) { + write_reg32(®s->rh_ps[i], RH_PS_PRS); + mb(); + port_clear |= RH_PS_CSC; + dprintf("Start enumerating device\n"); + SLOF_msleep(100); + } else + printf("Start removing device\n"); + } + port_status = read_reg32(®s->rh_ps[i]); + if (port_status & RH_PS_PRSC) { + port_clear |= RH_PS_PRSC; + dev = usb_devpool_get(); + dprintf("usb-ohci: Device reset, setting up %p\n", dev); + dev->hcidev = ohcd->hcidev; + if (!setup_new_device(dev, i)) + printf("usb-ohci: unable to setup device on port %d\n", i); + } + if (port_status & RH_PS_PESC) { + port_clear |= RH_PS_PESC; + if (port_status & RH_PS_PES) + dprintf("enabled\n"); + else + dprintf("disabled\n"); + } + if (port_status & RH_PS_PSSC) { + port_clear |= RH_PS_PESC; + dprintf("suspended\n"); + } + port_clear &= 0xFFFF0000; + if (port_clear) + write_reg32(®s->rh_ps[i], port_clear); + } +} + +static inline struct ohci_ed *ohci_pipe_get_ed(struct usb_pipe *pipe) +{ + struct ohci_pipe *opipe; + opipe = container_of(pipe, struct ohci_pipe, pipe); + dpprintf("%s: ed is %p\n", __func__, &opipe->ed); + return &opipe->ed; +} + +static inline long ohci_pipe_get_ed_phys(struct usb_pipe *pipe) +{ + struct ohci_pipe *opipe; + opipe = container_of(pipe, struct ohci_pipe, pipe); + dpprintf("%s: ed_phys is %x\n", __func__, opipe->ed_phys); + return opipe->ed_phys; +} + +static inline struct ohci_pipe *ohci_pipe_get_opipe(struct usb_pipe *pipe) +{ + struct ohci_pipe *opipe; + opipe = container_of(pipe, struct ohci_pipe, pipe); + dpprintf("%s: opipe is %p\n", __func__, opipe); + return opipe; +} + +static int ohci_alloc_pipe_pool(struct ohci_hcd *ohcd) +{ + struct ohci_pipe *opipe, *curr, *prev; + long opipe_phys = 0; + unsigned int i, count; +#ifdef OHCI_DEBUG_PACKET + struct usb_pipe *pipe; +#endif + + dprintf("usb-ohci: %s enter\n", __func__); + count = OHCI_PIPE_POOL_SIZE/sizeof(*opipe); + ohcd->pool = opipe = SLOF_dma_alloc(OHCI_PIPE_POOL_SIZE); + if (!opipe) + return false; + + ohcd->pool_phys = opipe_phys = SLOF_dma_map_in(opipe, OHCI_PIPE_POOL_SIZE, true); + dprintf("usb-ohci: %s opipe %x, opipe_phys %x size %d count %d\n", + __func__, opipe, opipe_phys, sizeof(*opipe), count); + /* Although an array, link them*/ + for (i = 0, curr = opipe, prev = NULL; i < count; i++, curr++) { + if (prev) + prev->pipe.next = &curr->pipe; + curr->pipe.next = NULL; + prev = curr; + + if (((uint64_t)&curr->ed) % 16) + printf("usb-ohci: Warning ED not aligned to 16byte boundary"); + curr->ed_phys = opipe_phys + (curr - opipe) * sizeof(*curr) + + offset_of(struct ohci_pipe, ed); + } + + if (!ohcd->freelist) + ohcd->freelist = &opipe->pipe; + else + ohcd->end->next = &opipe->pipe; + ohcd->end = &prev->pipe; + +#ifdef OHCI_DEBUG_PACKET + for (i = 0, pipe = ohcd->freelist; pipe; pipe = pipe->next) + dprintf("usb-ohci: %d: pipe cur %p ed %p ed_phys %x\n", + i++, pipe, ohci_pipe_get_ed(pipe), + ohci_pipe_get_ed_phys(pipe)); +#endif + + dprintf("usb-ohci: %s exit\n", __func__); + return true; +} + +static void ohci_init(struct usb_hcd_dev *hcidev) +{ + struct ohci_hcd *ohcd; + + printf(" OHCI: initializing\n"); + dprintf("%s: device base address %p\n", __func__, hcidev->base); + + ohcd = SLOF_alloc_mem(sizeof(struct ohci_hcd)); + if (!ohcd) { + printf("usb-ohci: Unable to allocate memory\n"); + goto out; + } + + hcidev->nextaddr = 1; + hcidev->priv = ohcd; + memset(ohcd, 0, sizeof(*ohcd)); + ohcd->hcidev = hcidev; + ohcd->freelist = NULL; + ohcd->end = NULL; + ohcd->regs = (struct ohci_regs *)(hcidev->base); + ohcd->hcca = SLOF_dma_alloc(sizeof(struct ohci_hcca)); + if (!ohcd->hcca || PTR_U32(ohcd->hcca) & HCCA_ALIGN) { + printf("usb-ohci: Unable to allocate/unaligned HCCA memory %p\n", + ohcd->hcca); + goto out_free_hcd; + } + ohcd->hcca_phys = SLOF_dma_map_in(ohcd->hcca, + sizeof(struct ohci_hcca), true); + dprintf("usb-ohci: HCCA memory %p HCCA-dev memory %08lx\n", + ohcd->hcca, ohcd->hcca_phys); + + ohci_hcd_reset(ohcd->regs); + ohci_hcd_init(ohcd); + ohci_hub_check_ports(ohcd); + return; + +out_free_hcd: + SLOF_dma_free(ohcd->hcca, sizeof(struct ohci_hcca)); + SLOF_free_mem(ohcd, sizeof(struct ohci_hcd)); +out: + return; +} + +static void ohci_exit(struct usb_hcd_dev *hcidev) +{ + struct ohci_hcd *ohcd = NULL; + + dprintf("%s: enter \n", __func__); + if (!hcidev && !hcidev->priv) + return; + + ohcd = hcidev->priv; + write_reg32(&ohcd->regs->control, (OHCI_CTRL_CBSR | OHCI_USB_SUSPEND)); + SLOF_msleep(20); + write_reg32(&ohcd->regs->hcca, cpu_to_le32(0)); + + if (ohcd->pool) { + SLOF_dma_map_out(ohcd->pool_phys, ohcd->pool, OHCI_PIPE_POOL_SIZE); + SLOF_dma_free(ohcd->pool, OHCI_PIPE_POOL_SIZE); + } + if (ohcd->hcca) { + SLOF_dma_map_out(ohcd->hcca_phys, ohcd->hcca, sizeof(struct ohci_hcca)); + SLOF_dma_free(ohcd->hcca, sizeof(struct ohci_hcca)); + } + SLOF_free_mem(ohcd, sizeof(struct ohci_hcd)); + return; +} + +static void ohci_detect(void) +{ + +} + +static void ohci_disconnect(void) +{ + +} + +#define OHCI_CTRL_TDS 3 + +static void ohci_fill_td(struct ohci_td *td, long next, + long req, size_t size, unsigned int attr) +{ + if (size && req) { + td->cbp = cpu_to_le32(req); + td->be = cpu_to_le32(req + size - 1); + } else { + td->cbp = 0; + td->be = 0; + } + td->attr = cpu_to_le32(attr); + td->next_td = cpu_to_le32(next); + + dpprintf("%s: cbp %08X attr %08X next_td %08X be %08X\n", __func__, + le32_to_cpu(td->cbp), le32_to_cpu(td->attr), + le32_to_cpu(td->next_td), le32_to_cpu(td->be)); +} + +static void ohci_fill_ed(struct ohci_ed *ed, long headp, long tailp, + unsigned int attr, long next_ed) +{ + ed->attr = cpu_to_le32(attr); + ed->headp = cpu_to_le32(headp) | (ed->headp & ~EDA_HEADP_MASK_LE); + ed->tailp = cpu_to_le32(tailp); + ed->next_ed = cpu_to_le32(next_ed); + dpprintf("%s: headp %08X tailp %08X next_td %08X attr %08X\n", __func__, + le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp), + le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr)); + +} + +static long ohci_get_td_phys(struct ohci_td *curr, struct ohci_td *start, long td_phys) +{ + dpprintf("position %d\n", curr - start); + return td_phys + (curr - start) * sizeof(*start); +} + +static long ohci_get_td_virt(struct ohci_td *curr, struct ohci_td *start, long td_virt, long total_count) +{ + dpprintf("position %d\n", curr - start); + if ( (curr - start) >= total_count) { + /* busted position, should ignore this */ + return 0; + } + return td_virt + (curr - start) * sizeof(*start); +} + +/* OHCI Spec: 4.4.2.3 HccaDoneHead*/ +static int ohci_process_done_head(struct ohci_hcd *ohcd, + struct ohci_td *td_start, + long __td_start_phys, long total_count) +{ + struct ohci_hcca *hcca; + struct ohci_td *td_phys = NULL, *td_start_phys; + struct ohci_td *td, *prev_td = NULL; + uint32_t reg = 0, time = 0; + int ret = true; + long count; + + count = total_count; + td_start_phys = (struct ohci_td *) __td_start_phys; + hcca = ohcd->hcca; + time = SLOF_GetTimer() + USB_TIMEOUT; + dpprintf("Claiming %ld\n", count); + +again: + mb(); + /* Check if there is an interrupt */ + reg = read_reg32(&ohcd->regs->intr_status); + while(!(reg & OHCI_INTR_STATUS_WD)) + { + if (time < SLOF_GetTimer()) { + printf("Timed out waiting for interrupt %x\n", reg); + return false; + } + mb(); + reg = read_reg32(&ohcd->regs->intr_status); + } + + /* Interrupt is there, read from done_head pointer */ + td_phys = (struct ohci_td *)(uint64_t) le32_to_cpu(hcca->done_head); + if (!td_phys) { + dprintf("Again td_phys null %ld\n"); + goto again; + } + hcca->done_head = 0; + mb(); + + while (td_phys && (count > 0)) { + td = (struct ohci_td *)(uint64_t) ohci_get_td_virt(td_phys, + td_start_phys, + PTR_U32(td_start), + total_count); + + if (!td) { + printf("USB: Error TD null %p\n", td_phys); + break; + } + count--; + dprintf("Claimed %p(%p) td_start %p count %ld\n", + td, td_phys, td_start_phys, count); + dpprintf("%s: cbp %08X attr %08X next_td %08X be %08X\n", + __func__, + le32_to_cpu(td->cbp), le32_to_cpu(td->attr), + le32_to_cpu(td->next_td), le32_to_cpu(td->be)); + mb(); + reg = (le32_to_cpu(td->attr) & TDA_CC) >> 28; + if (reg) { + dprintf("%s: cbp %08X attr %08X next_td %08X be %08X\n", + __func__, + le32_to_cpu(td->cbp), le32_to_cpu(td->attr), + le32_to_cpu(td->next_td), le32_to_cpu(td->be)); + printf("USB: Error %s %p\n", tda_cc_error[reg], td); + if (reg > 3) /* Return negative error code */ + ret = reg * -1; + } + prev_td = td; + td_phys = (struct ohci_td *)(uint64_t) le32_to_cpu(td->next_td); + prev_td->attr |= cpu_to_le32(TDA_DONE); + prev_td->next_td = 0; + mb(); + } + /* clear the WD interrupt status */ + write_reg32(&ohcd->regs->intr_status, OHCI_INTR_STATUS_WD); + mb(); + read_reg32(&ohcd->regs->intr_status); + + if (count > 0) { + dpprintf("Pending count %d\n", count); + goto again; + } + dprintf("TD claims done\n"); + return ret; +} + +/* + * OHCI Spec: + * 4.2 Endpoint Descriptor + * 4.3.1 General Transfer Descriptor + * 5.2.8 Transfer Descriptor Queues + */ +static int ohci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data) +{ + struct ohci_ed *ed; + struct ohci_td *tds, *td, *td_phys; + struct ohci_regs *regs; + struct ohci_hcd *ohcd; + uint32_t datalen; + uint32_t dir, attr = 0; + uint32_t time; + int ret = true, i; + long req_phys = 0, data_phys = 0, td_next = 0, td_count = 0; + unsigned char *dbuf; + + datalen = le16_to_cpu(req->wLength); + dir = (req->bmRequestType & REQT_DIR_IN) ? 1 : 0; + + dprintf("usb-ohci: %s len %d DIR_IN %d\n", __func__, datalen, dir); + + tds = td = (struct ohci_td *) SLOF_dma_alloc(sizeof(*td) * OHCI_CTRL_TDS); + td_phys = (struct ohci_td *) SLOF_dma_map_in(td, sizeof(*td) * OHCI_CTRL_TDS, true); + memset(td, 0, sizeof(*td) * OHCI_CTRL_TDS); + + req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true); + attr = TDA_DP_SETUP | TDA_CC | TDA_TOGGLE_DATA0; + td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys)); + ohci_fill_td(td, td_next, req_phys, sizeof(*req), attr); + td++; td_count++; + + if (datalen) { + data_phys = SLOF_dma_map_in(data, datalen, true); + attr = 0; + attr = (dir ? TDA_DP_IN : TDA_DP_OUT) | TDA_TOGGLE_DATA1 | TDA_CC; + td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys)); + ohci_fill_td(td, td_next, data_phys, datalen, attr); + td++; td_count++; + } + + attr = 0; + attr = (dir ? TDA_DP_OUT : TDA_DP_IN) | TDA_CC | TDA_TOGGLE_DATA1; + td_next = ohci_get_td_phys(td + 1, tds, PTR_U32(td_phys)); + ohci_fill_td(td, 0, 0, 0, attr); + td_count++; + + ed = ohci_pipe_get_ed(pipe); + attr = 0; + attr = EDA_FADDR(pipe->dev->addr) | EDA_MPS(pipe->mps) | EDA_SKIP; + ohci_fill_ed(ed, PTR_U32(td_phys), td_next, attr, 0); + ed->tailp = 0; /* HACK */ + dprintf("usb-ohci: %s - td_start %x td_end %x req %x\n", __func__, + td_phys, td_next, req_phys); + mb(); + ed->attr &= cpu_to_le32(~EDA_SKIP); + + ohcd = pipe->dev->hcidev->priv; + regs = ohcd->regs; + write_reg32(®s->cntl_head_ed, ohci_pipe_get_ed_phys(pipe)); + mb(); + write_reg32(®s->cmd_status, OHCI_CMD_STATUS_CLF); + + time = SLOF_GetTimer() + USB_TIMEOUT; + while ((time > SLOF_GetTimer()) && + ((ed->headp & EDA_HEADP_MASK_LE) != ed->tailp)) + cpu_relax(); + + if ((ed->headp & EDA_HEADP_MASK_LE) == ed->tailp) { + dprintf("%s: packet sent\n", __func__); +#ifdef OHCI_DEBUG_PACKET + dpprintf("Request: "); + dbuf = (unsigned char *)req; + for(i = 0; i < 8; i++) + printf("%02X ", dbuf[i]); + dpprintf("\n"); + if (datalen) { + dbuf = (unsigned char *)data; + dpprintf("Reply: "); + for(i = 0; i < datalen; i++) + printf("%02X ", dbuf[i]); + dpprintf("\n"); + } +#endif + } + else { + printf("%s: timed out - failed\n", __func__); + dpprintf("%s: headp %08X tailp %08X next_td %08X attr %08X\n", + __func__, + le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp), + le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr)); + printf("Request: "); + dbuf = (unsigned char *)req; + for(i = 0; i < 8; i++) + printf("%02X ", dbuf[i]); + printf("\n"); + } + ret = ohci_process_done_head(ohcd, tds, (long)td_phys, td_count); + mb(); + ed->attr |= cpu_to_le32(EDA_SKIP); + mb(); + write_reg32(®s->cntl_head_ed, 0); + write_reg32(®s->cntl_curr_ed, 0); + + SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req)); + if (datalen) + SLOF_dma_map_out(data_phys, data, datalen); + SLOF_dma_map_out(PTR_U32(td_phys), tds, sizeof(*td) * OHCI_CTRL_TDS); + SLOF_dma_free(tds, sizeof(*td) * OHCI_CTRL_TDS); + return (ret > 0) ? true : false; +} + +static int ohci_transfer_bulk(struct usb_pipe *pipe, void *td_ptr, + void *td_phys_ptr, void *data_phys, int datalen) +{ + struct ohci_ed *ed; + struct ohci_td *td, *tds; + struct ohci_regs *regs; + struct ohci_hcd *ohcd; + long td_phys = 0, td_next, ed_phys, ptr, td_count = 0; + uint32_t dir, attr = 0, count; + size_t len, packet_len; + uint32_t time; + int ret = true; + + if (pipe->type != USB_EP_TYPE_BULK) { + printf("usb-ohci: Not a bulk pipe.\n"); + ret = false; + goto end; + } + + dir = (pipe->dir == USB_PIPE_OUT) ? 0 : 1; + count = datalen / OHCI_MAX_BULK_SIZE; + if (count > OHCI_MAX_TDS) { + printf("usb-ohci: buffer size not supported - %d\n", datalen); + ret = false; + goto end; + } + + td = tds = (struct ohci_td *) td_ptr; + td_phys = (long)td_phys_ptr; + dprintf("usb-ohci: %s pipe %p data_phys %p len %d DIR_IN %d td %p td_phys %p\n", + __func__, pipe, data_phys, datalen, dir, td, td_phys); + + if (!tds) { + printf("%s: tds NULL recieved\n", __func__); + ret = false; + goto end; + } + memset(td, 0, sizeof(*td) * OHCI_MAX_TDS); + + len = datalen; + ptr = (long)data_phys; + attr = 0; + attr = (dir ? TDA_DP_IN : TDA_DP_OUT) | TDA_CC | TDA_ROUNDING; + while (len) { + packet_len = (OHCI_MAX_BULK_SIZE < len)? OHCI_MAX_BULK_SIZE : len; + td_next = ohci_get_td_phys((td + 1), tds, td_phys); + ohci_fill_td(td, td_next, ptr, packet_len, attr); + ptr = ptr + packet_len; + len = len - packet_len; + td++; td_count++; + } + + ed = ohci_pipe_get_ed(pipe); + attr = 0; + dir = pipe->dir ? EDA_DIR_IN : EDA_DIR_OUT; + attr = dir | EDA_FADDR(pipe->dev->addr) | EDA_MPS(pipe->mps) + | EDA_SKIP | pipe->dev->speed | EDA_EP(pipe->epno); + td_next = ohci_get_td_phys(td, tds, td_phys); + ohci_fill_ed(ed, td_phys, td_next, attr, 0); + dprintf("usb-ohci: %s - tds %p td %p\n", __func__, td_phys, td_next); + mb(); + ed->attr &= cpu_to_le32(~EDA_SKIP); + + ohcd = pipe->dev->hcidev->priv; + regs = ohcd->regs; + ed_phys = ohci_pipe_get_ed_phys(pipe); + write_reg32(®s->bulk_head_ed, ed_phys); + mb(); + write_reg32(®s->cmd_status, 0x4); + + time = SLOF_GetTimer() + USB_TIMEOUT; + while ((time > SLOF_GetTimer()) && + ((ed->headp & EDA_HEADP_MASK_LE) != ed->tailp)) + cpu_relax(); + + if ((ed->headp & EDA_HEADP_MASK_LE) == ed->tailp) + dprintf("%s: packet sent\n", __func__); + else { + dpprintf("%s: headp %08X tailp %08X next_td %08X attr %08X\n", __func__, + le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp), + le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr)); + } + mb(); + ret = ohci_process_done_head(ohcd, tds, td_phys, td_count); + mb(); + ed->attr |= cpu_to_le32(EDA_SKIP); + mb(); + write_reg32(®s->bulk_head_ed, 0); + write_reg32(®s->bulk_curr_ed, 0); + + if (le32_to_cpu(ed->headp) & EDA_HEADP_HALTED) { + printf("ED Halted\n"); + printf("%s: headp %08X tailp %08X next_td %08X attr %08X\n", __func__, + le32_to_cpu(ed->headp), le32_to_cpu(ed->tailp), + le32_to_cpu(ed->next_ed), le32_to_cpu(ed->attr)); + ed->headp &= ~cpu_to_le32(EDA_HEADP_HALTED); + mb(); + if (ret == USB_STALL) /* Call reset recovery */ + usb_msc_resetrecovery(pipe->dev); + } + +end: + return (ret > 0) ? true : false; +} + +/* Populate the hcca intr region with periodic intr */ +static int ohci_get_pipe_intr(struct usb_pipe *pipe, struct ohci_hcd *ohcd, + char *buf, size_t buflen) +{ + struct ohci_hcca *hcca; + struct ohci_pipe *opipe; + struct ohci_ed *ed; + struct usb_dev *dev; + struct ohci_td *tds, *td; + int32_t count = 0, i; + uint8_t *ptr; + uint16_t mps; + long ed_phys, td_phys, td_next, buf_phys; + + if (!pipe || !ohcd) + return false; + + hcca = ohcd->hcca; + dev = pipe->dev; + if (dev->class != DEV_HID_KEYB && dev->class != DEV_HUB) + return false; + + opipe = ohci_pipe_get_opipe(pipe); + ed = &(opipe->ed); + ed_phys = opipe->ed_phys; + mps = pipe->mps; + ed->attr = cpu_to_le32(EDA_DIR_IN | + EDA_FADDR(dev->addr) | + dev->speed | + EDA_MPS(pipe->mps) | + EDA_SKIP | + EDA_EP(pipe->epno)); + dprintf("%s: pipe %p ed %p dev %p opipe %p\n", __func__, + pipe, ed, dev, opipe); + count = (buflen/mps) + 1; + tds = td = SLOF_dma_alloc(sizeof(*td) * count); + if (!tds) { + printf("%s: alloc failed\n", __func__); + return false; + } + td_phys = SLOF_dma_map_in(td, sizeof(*td) * count, false); + + memset(tds, 0, sizeof(*tds) * count); + memset(buf, 0, buflen); + buf_phys = SLOF_dma_map_in(buf, buflen, false); + opipe->td = td; + opipe->td_phys = td_phys; + opipe->count = count; + opipe->buf = buf; + opipe->buflen = buflen; + opipe->buf_phys = buf_phys; + + ptr = (uint8_t *)buf_phys; + for (i = 0; i < count - 1; i++, ptr += mps) { + td = &tds[i]; + td_next = ohci_get_td_phys(td + 1, &tds[0], td_phys); + td->cbp = cpu_to_le32(PTR_U32(ptr)); + td->attr = cpu_to_le32(TDA_DP_IN | TDA_ROUNDING | TDA_CC); + td->next_td = cpu_to_le32(td_next); + td->be = cpu_to_le32(PTR_U32(ptr) + mps - 1); + dprintf("td %x td++ %x ptr %x be %x\n", + td, le32_to_cpu(td->next_td), + ptr, (PTR_U32(ptr) + mps - 1)); + } + td->next_td = 0; + td_next = ohci_get_td_phys(td, &tds[0], td_phys); + ed->headp = cpu_to_le32(td_phys); + ed->tailp = cpu_to_le32(td_next); + + dprintf("%s: head %08X tail %08X, count %d, mps %d\n", __func__, + le32_to_cpu(ed->headp), + le32_to_cpu(ed->tailp), + count, mps); + ed->next_ed = 0; + + + switch (dev->class) { + case DEV_HID_KEYB: + dprintf("%s: Keyboard class %d\n", __func__, dev->class); + hcca->intr_table[0] = cpu_to_le32(ed_phys); + hcca->intr_table[8] = cpu_to_le32(ed_phys); + hcca->intr_table[16] = cpu_to_le32(ed_phys); + hcca->intr_table[24] = cpu_to_le32(ed_phys); + ed->attr &= cpu_to_le32(~EDA_SKIP); + break; + + case DEV_HUB: + dprintf("%s: HUB class %x\n", __func__, dev->class); + hcca->intr_table[1] = cpu_to_le32(ed_phys); + ed->attr &= cpu_to_le32(~EDA_SKIP); + break; + + default: + dprintf("%s: unhandled class %d\n", __func__, dev->class); + } + return true; +} + +static int ohci_put_pipe_intr(struct usb_pipe *pipe, struct ohci_hcd *ohcd) +{ + struct ohci_hcca *hcca; + struct ohci_pipe *opipe; + struct ohci_ed *ed; + struct usb_dev *dev; + struct ohci_td *td; + long ed_phys; + + if (!pipe || !ohcd) + return false; + + hcca = ohcd->hcca; + dev = pipe->dev; + + if (dev->class != DEV_HID_KEYB && dev->class != DEV_HUB) + return false; + + opipe = ohci_pipe_get_opipe(pipe); + ed = &(opipe->ed); + ed_phys = opipe->ed_phys; + dprintf("%s: td %p td_phys %08lx buf %p buf_phys %08lx\n", __func__, + opipe->td, opipe->td_phys, opipe->buf, opipe->buf_phys); + + ed->attr |= cpu_to_le32(EDA_SKIP); + mb(); + ed->headp = 0; + ed->tailp = 0; + ed->next_ed = 0; + SLOF_dma_map_out(opipe->buf_phys, opipe->buf, opipe->buflen); + SLOF_dma_map_out(opipe->td_phys, opipe->td, sizeof(*td) * opipe->count); + SLOF_dma_free(opipe->td, sizeof(*td) * opipe->count); + + switch (dev->class) { + case DEV_HID_KEYB: + dprintf("%s: Keyboard class %d\n", __func__, dev->class); + hcca->intr_table[0] = cpu_to_le32(ed_phys); + hcca->intr_table[8] = cpu_to_le32(ed_phys); + hcca->intr_table[16] = cpu_to_le32(ed_phys); + hcca->intr_table[24] = cpu_to_le32(ed_phys); + break; + + case DEV_HUB: + dprintf("%s: HUB class %d\n", __func__, dev->class); + hcca->intr_table[1] = cpu_to_le32(ed_phys); + break; + + default: + dprintf("%s: unhandled class %d\n", __func__, dev->class); + } + return true; +} + +static int ohci_init_bulk_ed(struct usb_dev *dev, struct usb_pipe *pipe) +{ + struct ohci_pipe *opipe; + struct ohci_ed *ed; + uint32_t dir; + + if (!pipe || !dev) + return false; + + opipe = ohci_pipe_get_opipe(pipe); + ed = &(opipe->ed); + dir = pipe->dir ? EDA_DIR_IN : EDA_DIR_OUT; + + ed->attr = cpu_to_le32(dir | + EDA_FADDR(dev->addr) | + dev->speed | + EDA_MPS(pipe->mps) | + EDA_SKIP | + EDA_EP(pipe->epno)); + + dprintf("%s: pipe %p attr %x\n", __func__, pipe, + le32_to_cpu(ed->attr)); + return true; +} + +static struct usb_pipe *ohci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, + char *buf, size_t buflen) +{ + struct ohci_hcd *ohcd; + struct usb_pipe *new = NULL; + + dprintf("usb-ohci: %s enter %p\n", __func__, dev); + if (!dev) + return NULL; + + ohcd = (struct ohci_hcd *)dev->hcidev->priv; + if (!ohcd->freelist) { + dprintf("usb-ohci: %s allocating pool\n", __func__); + if (!ohci_alloc_pipe_pool(ohcd)) + return NULL; + } + + new = ohcd->freelist; + ohcd->freelist = ohcd->freelist->next; + if (!ohcd->freelist) + ohcd->end = NULL; + + memset(new, 0, sizeof(*new)); + new->dev = dev; + new->next = NULL; + new->type = ep->bmAttributes & USB_EP_TYPE_MASK; + new->speed = dev->speed; + new->mps = le16_to_cpu(ep->wMaxPacketSize); + new->epno = ep->bEndpointAddress & 0xF; + new->dir = ep->bEndpointAddress & 0x80; + if (new->type == USB_EP_TYPE_INTR) + if (!ohci_get_pipe_intr(new, ohcd, buf, buflen)) + dprintf("usb-ohci: %s alloc_intr failed %p\n", + __func__, new); + if (new->type == USB_EP_TYPE_BULK) + ohci_init_bulk_ed(dev, new); + + dprintf("usb-ohci: %s exit %p\n", __func__, new); + return new; +} + +static void ohci_put_pipe(struct usb_pipe *pipe) +{ + struct ohci_hcd *ohcd; + + dprintf("usb-ohci: %s enter - %p\n", __func__, pipe); + if (!pipe || !pipe->dev) + return; + ohcd = pipe->dev->hcidev->priv; + if (ohcd->end) + ohcd->end->next = pipe; + else + ohcd->freelist = pipe; + + if (pipe->type == USB_EP_TYPE_INTR) + if (!ohci_put_pipe_intr(pipe, ohcd)) + dprintf("usb-ohci: %s alloc_intr failed %p\n", + __func__, pipe); + + ohcd->end = pipe; + pipe->next = NULL; + pipe->dev = NULL; + memset(pipe, 0, sizeof(*pipe)); + dprintf("usb-ohci: %s exit\n", __func__); +} + +static uint16_t ohci_get_last_frame(struct usb_dev *dev) +{ + struct ohci_hcd *ohcd; + struct ohci_regs *regs; + + ohcd = dev->hcidev->priv; + regs = ohcd->regs; + return read_reg32(®s->fm_num); +} + +static int ohci_poll_intr(struct usb_pipe *pipe, uint8_t *data) +{ + struct ohci_pipe *opipe; + struct ohci_ed *ed; + struct ohci_td *head, *tail, *curr, *next; + struct ohci_td *head_phys, *tail_phys, *curr_phys; + uint8_t *ptr = NULL; + unsigned int i, pos; + static uint16_t last_frame; + long ptr_phys = 0; + long td_next; + + if (!pipe || last_frame == ohci_get_last_frame(pipe->dev)) + return 0; + + dprintf("%s: enter\n", __func__); + + last_frame = ohci_get_last_frame(pipe->dev); + opipe = ohci_pipe_get_opipe(pipe); + ed = &opipe->ed; + + head_phys = (struct ohci_td *)(long)(le32_to_cpu(ed->headp) & EDA_HEADP_MASK); + tail_phys = (struct ohci_td *)(long)le32_to_cpu(ed->tailp); + curr_phys = (struct ohci_td *) opipe->td_phys; + pos = (tail_phys - curr_phys + 1) % (opipe->count - 1); + dprintf("pos %d %ld -- %d\n", pos, (tail_phys - curr_phys + 1), + opipe->count); + curr = opipe->td + pos; + head = opipe->td + (head_phys - (struct ohci_td *) opipe->td_phys); + tail = opipe->td + (tail_phys - (struct ohci_td *) opipe->td_phys); + + /* dprintf("%08X %08X %08X %08X\n", + opipe->td_phys, head_phys, tail_phys, curr_phys); + dprintf("%08X %08X %08X %08X\n", opipe->td, head, tail, curr); */ + + if (curr != head) { + ptr = (uint8_t *) ((long)opipe->buf + pipe->mps * pos); + ptr_phys = opipe->buf_phys + pipe->mps * pos; + if (le32_to_cpu(*(uint32_t *)ptr) != 0) { + for (i = 0; i < 8; i++) + data[i] = *(ptr + i); + } + + next = curr + 1; + if (next == (opipe->td + opipe->count - 1)) + next = opipe->td; + + curr->attr = cpu_to_le32(TDA_DP_IN | TDA_ROUNDING | TDA_CC); + curr->next_td = cpu_to_le32(0); + curr->cbp = cpu_to_le32(PTR_U32(ptr_phys)); + curr->be = cpu_to_le32(PTR_U32(ptr_phys + pipe->mps - 1)); + td_next = ohci_get_td_phys(curr, opipe->td, opipe->td_phys); + dprintf("Connecting %p to %p(phys %08lx) ptr %p, " + "ptr_phys %08lx\n", tail, curr, td_next, ptr, ptr_phys); + tail->next_td = cpu_to_le32(td_next); + mb(); + ed->tailp = cpu_to_le32(td_next); + } else + return 0; + + dprintf("%s: exit\n", __func__); + return 1; +} + +struct usb_hcd_ops ohci_ops = { + .name = "ohci-hcd", + .init = ohci_init, + .exit = ohci_exit, + .detect = ohci_detect, + .disconnect = ohci_disconnect, + .get_pipe = ohci_get_pipe, + .put_pipe = ohci_put_pipe, + .send_ctrl = ohci_send_ctrl, + .transfer_bulk = ohci_transfer_bulk, + .poll_intr = ohci_poll_intr, + .usb_type = USB_OHCI, + .next = NULL, +}; + +void usb_ohci_register(void) +{ + usb_hcd_register(&ohci_ops); +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-ohci.h b/qemu/roms/SLOF/lib/libusb/usb-ohci.h new file mode 100644 index 000000000..f4535fddd --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-ohci.h @@ -0,0 +1,217 @@ +/****************************************************************************** + * Copyright (c) 2007, 2012, 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * Definitions for OHCI Controller + * + * USB on the PowerStation: + * ohci0 - port 0 -> not connected + * ohci0 - port 1 - 2 -> Internal connector (J60_USBINT) + * ohci1 - port 0 -> not connected + * ohci1 - port 1 - 2 -> External connector (J10_USBEXT) + */ + +#ifndef USB_OHCI_H +#define USB_OHCI_H + +#include <stdint.h> + +struct ohci_regs { + uint32_t rev; + uint32_t control; + uint32_t cmd_status; + uint32_t intr_status; + uint32_t intr_enable; + uint32_t intr_disable; + uint32_t hcca; + uint32_t period_curr_ed; + uint32_t cntl_head_ed; + uint32_t cntl_curr_ed; + uint32_t bulk_head_ed; + uint32_t bulk_curr_ed; + uint32_t done_head; + uint32_t fm_interval; + uint32_t fm_remaining; + uint32_t fm_num; + uint32_t period_start; + uint32_t ls_threshold; + uint32_t rh_desc_a; + uint32_t rh_desc_b; + uint32_t rh_status; + uint32_t rh_ps[5]; +} __attribute__((packed)); + +#define EDA_FADDR(x) ((x & 0x7F)) +#define EDA_EP(x) ((x & 0x0F) << 7) +#define EDA_DIR_OUT (1 << 11) +#define EDA_DIR_IN (1 << 12) +#define EDA_LOW_SPEED (1 << 13) +#define EDA_SKIP (1 << 14) +#define EDA_SKIP_LE (0x400000) /* avoiding conversions */ +#define EDA_FORMAT_ISO (1 << 15) +#define EDA_MPS(x) ((x & 0x7FF) << 16) + +#define EDA_HEADP_MASK (0xFFFFFFFC) +#define EDA_HEADP_MASK_LE (cpu_to_le32(EDA_HEADP_MASK)) +#define EDA_HEADP_HALTED (0x1) +#define EDA_HEADP_CARRY (0x2) + +struct ohci_ed { + uint32_t attr; + uint32_t tailp; + uint32_t headp; + uint32_t next_ed; +} __attribute__((packed)); + +#define TDA_DONE (1 << 17) +#define TDA_ROUNDING (1 << 18) +#define TDA_DP_SETUP (0 << 19) +#define TDA_DP_OUT (1 << 19) +#define TDA_DP_IN (1 << 20) +#define TDA_DI_NO (0x7 << 21) +#define TDA_TOGGLE_DATA0 (0x02000000) +#define TDA_TOGGLE_DATA1 (0x03000000) +#define TDA_CC (0xF << 28) + +#define TDA_ERROR(x) ((x) * -1) + +/* Table 4-7: Completion Codes */ +const char *tda_cc_error[] = { +#define USB_NOERROR TDA_ERROR(0) + "NOERROR", + "CRC", + "BITSTUFFING", + "DATATOGGLEMISMATCH", +#define USB_STALL TDA_ERROR(4) + "STALL", + "DEVICENOTRESPONDING", + "PIDCHECKFAILURE", + "UNEXPECTEDPID", + "DATAOVERRUN", + "DATAUNDERRUN", + "reserved", + "reserved", + "BUFFEROVERRUN", + "BUFFERUNDERRUN", + "NOT ACCESSED", + "NOT ACCESSED", +}; + +struct ohci_td { + uint32_t attr; + uint32_t cbp; + uint32_t next_td; + uint32_t be; +} __attribute__((packed)); + +#define HCCA_SIZE 256 +#define HCCA_ALIGN (HCCA_SIZE - 1) +#define HCCA_INTR_NUM 32 +struct ohci_hcca { + uint32_t intr_table[HCCA_INTR_NUM]; + uint16_t frame_num; + uint16_t pad1; + uint32_t done_head; + uint32_t reserved[120]; +} __attribute__((packed)); + +struct ohci_pipe { + struct ohci_ed ed; /* has to be aligned at 16 byte address*/ + struct usb_pipe pipe; + struct ohci_td *td; + void *buf; + long ed_phys; + long td_phys; + long buf_phys; + uint32_t buflen; + uint32_t count; + uint8_t pad[0]; +}__attribute__((packed)); + +#define OHCI_PIPE_POOL_SIZE 4096 +#define OHCI_MAX_TDS 256 /* supports 16k buffers, i.e. 64 * 256 */ +#define OHCI_MAX_BULK_SIZE 4096 + +struct ohci_hcd { + struct ohci_hcca *hcca; + struct ohci_regs *regs; + struct usb_hcd_dev *hcidev; + struct usb_pipe *freelist; + struct usb_pipe *end; + struct usb_dev rhdev; + long hcca_phys; + void *pool; + long pool_phys; +}; + +#define OHCI_CTRL_CBSR (3 << 0) +#define OHCI_CTRL_PLE (1 << 2) +#define OHCI_CTRL_CLE (1 << 4) +#define OHCI_CTRL_BLE (1 << 5) +#define OHCI_CTRL_HCFS (3 << 6) +#define OHCI_USB_RESET (0 << 6) +#define OHCI_USB_OPER (2 << 6) +#define OHCI_USB_SUSPEND (3 << 6) +#define OHCI_CTRL_RWC (1 << 9) + +/* OHCI Command Status */ +#define OHCI_CMD_STATUS_HCR (1 << 0) +#define OHCI_CMD_STATUS_CLF (1 << 1) +#define OHCI_CMD_STATUS_BLF (1 << 2) + +/* OHCI Interrupt status */ +#define OHCI_INTR_STATUS_WD (1 << 1) + +/* Root Hub Descriptor A bits */ +#define RHDA_NDP (0xFF) +#define RHDA_PSM_INDIVIDUAL (1 << 8) +#define RHDA_NPS_ENABLE (1 << 9) +#define RHDA_DT (1 << 10) +#define RHDA_OCPM_PERPORT (1 << 11) +#define RHDA_NOCP_ENABLE (1 << 12) + +/* Root Hub Descriptor B bits */ +#define RHDB_PPCM_PORT_POWER (0xFFFE) +#define RHDB_PPCM_GLOBAL_POWER (0x0000) + +#define RH_STATUS_LPSC (1 << 16) +#define RH_STATUS_OCIC (1 << 17) +#define RH_STATUS_CREW (1 << 31) + +#define RH_PS_CCS (1 << 0) +#define RH_PS_PES (1 << 1) +#define RH_PS_PSS (1 << 2) +#define RH_PS_POCI (1 << 3) +#define RH_PS_PRS (1 << 4) +#define RH_PS_PPS (1 << 8) +#define RH_PS_LSDA (1 << 9) + +#define RH_PS_CSC (1 << 16) +#define RH_PS_PESC (1 << 17) +#define RH_PS_PSSC (1 << 18) +#define RH_PS_OCIC (1 << 19) +#define RH_PS_PRSC (1 << 20) + +/*********************************************************************/ +/* Values for USB Frame Timing */ +/* One USB frame (1ms) consists of 12000 bit-times as clock is 12MHz */ +/* controller can be adjusted for performance optimization */ +/* We use standard values (OHCI spec 6.3.1, 5.1.1.4, 5.4, 7.3.4) */ +/*********************************************************************/ +#define FRAME_INTERVAL (((((11999 - 210) * 6) / 7) << 16) | 11999) +#define PERIODIC_START ((11999 * 9) / 10) + + +static inline struct ohci_ed *ohci_pipe_get_ed(struct usb_pipe *pipe); +static inline long ohci_pipe_get_ed_phys(struct usb_pipe *pipe); +static int ohci_alloc_pipe_pool(struct ohci_hcd *ohcd); + +#endif /* USB_OHCI_H */ diff --git a/qemu/roms/SLOF/lib/libusb/usb-slof.c b/qemu/roms/SLOF/lib/libusb/usb-slof.c new file mode 100644 index 000000000..de841f0fb --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-slof.c @@ -0,0 +1,61 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * All functions concerning interface to slof + */ + +#include <string.h> +#include "helpers.h" +#include "usb-core.h" +#include "paflof.h" + +#undef SLOF_DEBUG +//#define SLOF_DEBUG +#ifdef SLOF_DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +int slof_usb_handle(struct usb_dev *dev) +{ + struct slof_usb_dev sdev; + sdev.port = dev->port; + sdev.addr = dev->addr; + sdev.hcitype = dev->hcidev->type; + sdev.num = dev->hcidev->num; + sdev.udev = dev; + + if (dev->class == DEV_HID_KEYB) { + dprintf("Keyboard %ld %ld\n", dev->hcidev->type, dev->hcidev->num); + sdev.devtype = DEVICE_KEYBOARD; + forth_push((long)&sdev); + forth_eval("s\" dev-keyb.fs\" INCLUDED"); + } else if (dev->class == DEV_HID_MOUSE) { + dprintf("Mouse %ld %ld\n", dev->hcidev->type, dev->hcidev->num); + sdev.devtype = DEVICE_MOUSE; + forth_push((long)&sdev); + forth_eval("s\" dev-mouse.fs\" INCLUDED"); + } else if ((dev->class >> 16 & 0xFF) == 8) { + dprintf("MASS Storage device %ld %ld\n", dev->hcidev->type, dev->hcidev->num); + sdev.devtype = DEVICE_DISK; + forth_push((long)&sdev); + forth_eval("s\" dev-storage.fs\" INCLUDED"); + } else if (dev->class == DEV_HUB) { + dprintf("Generic hub device %ld %ld\n", dev->hcidev->type, + dev->hcidev->num); + sdev.devtype = DEVICE_HUB; + forth_push((long)&sdev); + forth_eval("s\" dev-hub.fs\" INCLUDED"); + } + return true; +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-xhci.c b/qemu/roms/SLOF/lib/libusb/usb-xhci.c new file mode 100644 index 000000000..0c3d6e47f --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-xhci.c @@ -0,0 +1,1316 @@ +/***************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <string.h> +#include "usb.h" +#include "usb-core.h" +#include "usb-xhci.h" +#include "tools.h" +#include "paflof.h" + +#undef XHCI_DEBUG +//#define XHCI_DEBUG +#ifdef XHCI_DEBUG +#define dprintf(_x ...) do { printf("%s: ", __func__); printf(_x); } while (0) +#else +#define dprintf(_x ...) +#endif + +static void dump_xhci_regs(struct xhci_hcd *xhcd) +{ +#ifdef XHCI_DEBUG + struct xhci_cap_regs *cap; + struct xhci_op_regs *op; + struct xhci_run_regs *run; + + cap = xhcd->cap_regs; + op = xhcd->op_regs; + run = xhcd->run_regs; + + dprintf("\n"); + dprintf(" - CAPLENGTH %02X\n", read_reg8 (&cap->caplength)); + dprintf(" - HCIVERSION %04X\n", read_reg16(&cap->hciversion)); + dprintf(" - HCSPARAMS1 %08X\n", read_reg32(&cap->hcsparams1)); + dprintf(" - HCSPARAMS2 %08X\n", read_reg32(&cap->hcsparams2)); + dprintf(" - HCSPARAMS3 %08X\n", read_reg32(&cap->hcsparams3)); + dprintf(" - HCCPARAMS %08X\n", read_reg32(&cap->hccparams)); + dprintf(" - DBOFF %08X\n", read_reg32(&cap->dboff)); + dprintf(" - RTSOFF %08X\n", read_reg32(&cap->rtsoff)); + dprintf("\n"); + + dprintf(" - USBCMD %08X\n", read_reg32(&op->usbcmd)); + dprintf(" - USBSTS %08X\n", read_reg32(&op->usbsts)); + dprintf(" - PAGESIZE %08X\n", read_reg32(&op->pagesize)); + dprintf(" - DNCTRL %08X\n", read_reg32(&op->dnctrl)); + dprintf(" - CRCR %016llX\n", read_reg64(&op->crcr)); + dprintf(" - DCBAAP %016llX\n", read_reg64(&op->dcbaap)); + dprintf(" - CONFIG %08X\n", read_reg32(&op->config)); + dprintf("\n"); + + dprintf(" - MFINDEX %08X\n", read_reg32(&run->mfindex)); + dprintf("\n"); +#endif +} + +static void print_port_status(struct xhci_port_regs *prs) +{ +#ifdef XHCI_DEBUG + uint32_t portsc; + uint32_t CCS, PED, PP, PLS, i, PR = 0; + + portsc = read_reg32(&prs->portsc); + dprintf("portsc %08x portpmsc %08x portli %08x\n", + portsc, + read_reg32(&prs->portpmsc), + read_reg32(&prs->portli)); + + if (portsc & PORTSC_CCS) { + printf("CCS "); + CCS = 1; + } + if (portsc & PORTSC_PED) { + printf("PED "); + PED = 1; + } + if (portsc & PORTSC_OCA) + printf("OCA "); + if (portsc & PORTSC_PR) + printf("OCA "); + PLS = (portsc & PORTSC_PLS_MASK) >> 5; + printf("PLS:%d ", PLS); + if (portsc & PORTSC_PP) { + printf("PP "); + PP = 1; + } + printf("PS:%d ", (portsc & PORTSC_PS_MASK) >> 10); + printf("PIC:%d ", (portsc & PORTSC_PIC_MASK) >> 14); + if (portsc & PORTSC_LWS) + printf("LWS "); + if (portsc & PORTSC_CSC) + printf("CSC "); + if (portsc & PORTSC_PEC) + printf("PEC "); + if (portsc & PORTSC_WRC) + printf("WRC "); + if (portsc & PORTSC_OCC) + printf("OCC "); + if (portsc & PORTSC_PRC) + printf("PRC "); + if (portsc & PORTSC_PLC) + printf("PLC "); + if (portsc & PORTSC_CEC) + printf("CEC "); + if (portsc & PORTSC_CAS) + printf("CAS "); + if (portsc & PORTSC_WCE) + printf("WCE "); + if (portsc & PORTSC_WDE) + printf("WDE "); + if (portsc & PORTSC_WOE) + printf("WOE "); + if (portsc & PORTSC_DR) + printf("DR "); + if (portsc & PORTSC_WPR) + printf("WPR "); + printf("\n"); + + for (i = 0 ; i < (sizeof(ps_array_usb3)/sizeof(struct port_state)); i++) { + if (PP == ps_array_usb3[i].PP) { + if (CCS == ps_array_usb3[i].CCS) { + if (PED == ps_array_usb3[i].PED) { + if (PR == ps_array_usb3[i].PR) { + dprintf("%s - PLS %d\n", ps_array_usb3[i].state, PLS); + break; + } + } + } + } + } +#endif + +} + +static inline bool xhci_is_hc_ready(uint32_t *usbsts) +{ + return !(read_reg32(usbsts) & XHCI_USBSTS_CNR); +} + +static inline bool xhci_wait_for_cnr(uint32_t *usbsts) +{ + /* Standard: + * Note: The xHC should halt within 16 ms. of software clearing the + * R/S bit to ‘0’. + * Give some more time... 32ms + */ + int count = 320; + dprintf("Waiting for Controller ready .."); + while (!xhci_is_hc_ready(usbsts)) { + dprintf("."); + count--; + if (!count) { + dprintf(" failed %08X\n", read_reg32(usbsts)); + return false; + } + SLOF_usleep(100); + } + dprintf(" done\n"); + return true; +} + +static bool xhci_hcd_set_runstop(struct xhci_op_regs *op, bool run_req) +{ + uint32_t reg; + + dprintf("Request %s\n", run_req ? "RUN" : "STOP"); + if (!xhci_is_hc_ready(&op->usbsts)) { + dprintf("Controller not ready\n"); + return false; + } + + reg = read_reg32(&op->usbcmd); + if (run_req) + reg |= run_req; + else + reg &= (uint32_t)~1; + dprintf("writing %08X\n", reg); + write_reg32(&op->usbcmd, reg); + mb(); + xhci_wait_for_cnr(&op->usbsts); + return true; +} + +static bool xhci_hcd_reset(struct xhci_op_regs *op) +{ + uint32_t reg; + + /* Check if the controller is halted, else halt it */ + if (!(read_reg32(&op->usbsts) & XHCI_USBSTS_HCH)) { + dprintf("HCHalted not set\n"); + if (!xhci_hcd_set_runstop(op, false)) + return false; + } + + if (read_reg32(&op->usbsts) & XHCI_USBSTS_CNR) { + dprintf("Controller not ready\n"); + return false; + } + + reg = read_reg32(&op->usbcmd) | XHCI_USBCMD_HCRST; + /* Ready to Reset the controller now */ + write_reg32(&op->usbcmd, reg); + xhci_wait_for_cnr(&op->usbsts); + return true; +} + +static void xhci_handle_cmd_completion(struct xhci_hcd *xhcd, + struct xhci_event_trb *event) +{ + uint32_t flags, slot_id, status; + + status = le32_to_cpu(event->status); + flags = le32_to_cpu(event->flags); + slot_id = TRB_SLOT_ID(flags); + if (TRB_STATUS(status) == COMP_SUCCESS) + xhcd->slot_id = slot_id; + else + xhcd->slot_id = 0; +} + +static struct xhci_event_trb *xhci_poll_event(struct xhci_hcd *xhcd, + uint32_t event_type) +{ + struct xhci_event_trb *event; + uint64_t val; + uint32_t flags, time; + int index; + + mb(); + event = (struct xhci_event_trb *)xhcd->ering.deq; + flags = le32_to_cpu(event->flags); + + dprintf("Reading from event ptr %p %08x\n", event, flags); + time = SLOF_GetTimer() + USB_TIMEOUT; + + while ((flags & TRB_CYCLE_STATE) != xhcd->ering.cycle_state) { + mb(); + flags = le32_to_cpu(event->flags); + if (time < SLOF_GetTimer()) + return NULL; + } + + mb(); + flags = le32_to_cpu(event->flags); + switch(TRB_TYPE(flags)) + { + case TRB_CMD_COMPLETION: + dprintf("CMD Completion\n"); + xhci_handle_cmd_completion(xhcd, event); + break; + case TRB_PORT_STATUS: + dprintf("Port status event\n"); + break; + case TRB_TRANSFER_EVENT: + dprintf("XFER event addr %16lx, status %08x, flags %08x\n", + le64_to_cpu(event->addr), + le32_to_cpu(event->status), + le32_to_cpu(event->flags)); + break; + default: + printf("TRB_TYPE %d\n", TRB_TYPE(flags)); + dprintf("Event addr %16lx, status %08x, flags %08x state %d\n", + le64_to_cpu(event->addr), + le32_to_cpu(event->status), + flags, xhcd->ering.cycle_state); + break; + } + xhcd->ering.deq = (uint64_t) (event + 1); + + event->addr = 0; + event->status = 0; + event->flags = cpu_to_le32(xhcd->ering.cycle_state); + + index = xhcd->ering.deq - (uint64_t)xhcd->ering.trbs; + val = xhcd->ering.trbs_dma; + val += (index % XHCI_EVENT_TRBS_SIZE); + if (!(index % XHCI_EVENT_TRBS_SIZE)) { + xhcd->ering.deq = (uint64_t)xhcd->ering.trbs; + xhcd->ering.cycle_state = xhcd->ering.cycle_state ? 0 : 1; + dprintf("Rounding %d\n", xhcd->ering.cycle_state); + } + dprintf("Update start %x deq %x index %d\n", + xhcd->ering.trbs_dma, val, index/sizeof(*event)); + write_reg64(&xhcd->run_regs->irs[0].erdp, val); + return event; +} + +static void xhci_send_cmd(struct xhci_hcd *xhcd, uint32_t field1, + uint32_t field2, uint32_t field3, uint32_t field4) +{ + struct xhci_db_regs *dbr; + struct xhci_command_trb *cmd; + uint32_t val, cycle_state; + + dbr = xhcd->db_regs; + cmd = (struct xhci_command_trb *)xhcd->crseg.enq; + + cmd->field[0] = cpu_to_le32(field1); + cmd->field[1] = cpu_to_le32(field2); + cmd->field[2] = cpu_to_le32(field3); + + val = le32_to_cpu(cmd->field[3]); + cycle_state = (val & 0x1) ? 0 : 1; + val = field4 | cycle_state; + cmd->field[3] = cpu_to_le32(val); + + dprintf("CMD %016lx val %08x cycle_state %d field1 %08x, field2 %08x, field3 %08x field4 %08x\n", + cmd, val, cycle_state, + le32_to_cpu(cmd->field[0]), + le32_to_cpu(cmd->field[1]), + le32_to_cpu(cmd->field[2]), + le32_to_cpu(cmd->field[3]) + ); + + /* Ring the doorbell */ + write_reg32(&dbr->db[0], 0); + xhci_poll_event(xhcd, 0); + cmd++; + xhcd->crseg.enq = (uint64_t)cmd; + return; +} + +static void xhci_send_enable_slot(struct xhci_hcd *xhcd, uint32_t port) +{ + uint32_t field1, field2, field3, field4; + + field1 = 0; + field2 = 0; + field3 = 0; + field4 = TRB_CMD_TYPE(TRB_ENABLE_SLOT); + xhci_send_cmd(xhcd, field1, field2, field3, field4); +} + +static void xhci_send_addr_device(struct xhci_hcd *xhcd, uint32_t slot_id, + uint64_t dma_in_ctx) +{ + uint32_t field1, field2, field3, field4; + + dprintf("Address device %lx, low %x, high %x\n", dma_in_ctx, + TRB_ADDR_LOW(dma_in_ctx), + TRB_ADDR_HIGH(dma_in_ctx)); + field1 = TRB_ADDR_LOW(dma_in_ctx) & ~0xF; + field2 = TRB_ADDR_HIGH(dma_in_ctx); + field3 = 0; + field4 = TRB_CMD_TYPE(TRB_ADDRESS_DEV) | TRB_CMD_SLOT_ID(slot_id); + xhci_send_cmd(xhcd, field1, field2, field3, field4); +} + +static uint32_t xhci_get_epno(struct usb_pipe *pipe) +{ + uint32_t x_epno; + x_epno = pipe->dir | 2 * pipe->epno; + dprintf("EPno %d:%d DIR %d\n", pipe->epno, x_epno, pipe->dir); + return x_epno; +} + +static void xhci_configure_ep(struct xhci_hcd *xhcd, uint32_t slot_id, + uint64_t dma_in_ctx) +{ + uint32_t field1, field2, field3, field4; + + dprintf("Configure EP %lx, low %x, high %x\n", dma_in_ctx, + TRB_ADDR_LOW(dma_in_ctx), + TRB_ADDR_HIGH(dma_in_ctx)); + field1 = TRB_ADDR_LOW(dma_in_ctx) & ~0xF; + field2 = TRB_ADDR_HIGH(dma_in_ctx); + field3 = 0; + field4 = TRB_CMD_TYPE(TRB_CONFIG_EP) | TRB_CMD_SLOT_ID(slot_id); + xhci_send_cmd(xhcd, field1, field2, field3, field4); +} + +static void xhci_init_seg(struct xhci_seg *seg, uint32_t size, uint32_t type) +{ + struct xhci_link_trb *link; + + seg->size = size / XHCI_TRB_SIZE; + seg->next = NULL; + seg->type = type; + seg->cycle_state = 1; + seg->enq = (uint64_t)seg->trbs; + seg->deq = (uint64_t)seg->trbs; + memset((void *)seg->trbs, 0, size); + + link =(struct xhci_link_trb *) (seg->trbs + seg->size - 1); + link->addr = cpu_to_le64(seg->trbs_dma); + link->field2 = 0; + link->field3 = cpu_to_le32(0x1 | TRB_CMD_TYPE(TRB_LINK)); + return; +} + +static bool xhci_alloc_seg(struct xhci_seg *seg, uint32_t size, uint32_t type) +{ + seg->trbs = (union xhci_trb *)SLOF_dma_alloc(size); + if (!seg->trbs) { + dprintf("Alloc failed\n"); + return false; + } + xhci_init_seg(seg, size, type); + seg->trbs_dma = SLOF_dma_map_in((void *)seg->trbs, size, false); + + dprintf(" TRBs %016lX TRBS-DMA %016lX\n", seg->trbs, seg->trbs_dma); + return true; +} + +static void xhci_free_seg(struct xhci_seg *seg, uint32_t size) +{ + if (seg->trbs) { + dprintf(" TRBs %016lX TRBS-DMA %016lX size %x\n", seg->trbs, seg->trbs_dma, size); + SLOF_dma_map_out(seg->trbs_dma, (void *)seg->trbs, size); + SLOF_dma_free((void *)seg->trbs, size); + } + memset(seg, 0, sizeof(*seg)); +} + +#define CTX_SIZE(x) ( (x) ? 64 : 32 ) + +static bool xhci_alloc_ctx(struct xhci_ctx *ctx, uint32_t size, uint32_t type) +{ + ctx->addr = (uint8_t *)SLOF_dma_alloc(size); + if (!ctx->addr) { + dprintf("Alloc failed\n"); + return false; + } + ctx->size = size; + ctx->type = type; + memset((void *)ctx->addr, 0, size); + ctx->dma_addr = SLOF_dma_map_in((void *)ctx->addr, size, false); + dprintf("ctx %llx, ctx_dma %llx\n", ctx->addr, ctx->dma_addr); + return true; +} + +static struct xhci_control_ctx *xhci_get_control_ctx(struct xhci_ctx *ctx) +{ + if (ctx->type == XHCI_CTX_TYPE_INPUT) + return (struct xhci_control_ctx *) ctx->addr; + return NULL; +} + +static struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctx *ctx, uint32_t ctx_size) +{ + uint32_t offset = 0; + + if (ctx->type == XHCI_CTX_TYPE_INPUT) + offset += ctx_size; + return (struct xhci_slot_ctx *)(ctx->addr + offset); +} + +static struct xhci_ep_ctx *xhci_get_ep0_ctx(struct xhci_ctx *ctx, uint32_t ctx_size) +{ + uint32_t offset = 0; + + offset = ctx_size; + if (ctx->type == XHCI_CTX_TYPE_INPUT) + offset += ctx_size; + return (struct xhci_ep_ctx *)(ctx->addr + offset); +} + +static struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_ctx *ctx, uint32_t ctx_size, + uint32_t epno) +{ + uint32_t offset = 0; + + offset = ctx_size * epno; + if (ctx->type == XHCI_CTX_TYPE_INPUT) + offset += ctx_size; + return (struct xhci_ep_ctx *)(ctx->addr + offset); +} + +static void xhci_free_ctx(struct xhci_ctx *ctx, uint32_t size) +{ + SLOF_dma_map_out(ctx->dma_addr, (void *)ctx->addr, size); + SLOF_dma_free((void *)ctx->addr, size); +} + +static uint32_t usb_control_max_packet(uint32_t speed) +{ + uint32_t max_packet = 0; + + switch(speed) + { + case USB_LOW_SPEED: + max_packet = 8; + break; + case USB_FULL_SPEED: + max_packet = 8; + break; + case USB_HIGH_SPEED: + max_packet = 64; + break; + case USB_SUPER_SPEED: + max_packet = 512; + break; + default: + /* should not reach here */ + dprintf("Unknown speed\n"); + } + return max_packet; +} + +static bool xhci_alloc_dev(struct xhci_hcd *xhcd, uint32_t slot_id, uint32_t port) +{ + struct usb_dev *dev; + struct xhci_dev *xdev; + struct xhci_slot_ctx *slot; + struct xhci_control_ctx *ctrl; + struct xhci_ep_ctx *ep0; + uint32_t ctx_size, val; + uint16_t max_packet; + uint32_t newport; + + ctx_size = CTX_SIZE(xhcd->hcc_csz_64); + xdev = &xhcd->xdevs[slot_id]; + xdev->slot_id = slot_id; + xdev->ctx_size = ctx_size; + + /* 4.3.3 Device Slot initialization */ + /* Step 1 */ + if (!xhci_alloc_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE, XHCI_CTX_TYPE_INPUT)) { + dprintf("Failed allocating in_ctx\n"); + return false; + } + + /* Step 2 */ + ctrl = xhci_get_control_ctx(&xdev->in_ctx); + ctrl->a_flags = cpu_to_le32(0x3); /* A0, A1 */ + ctrl->d_flags = 0; + + /* Step 3 */ + slot = xhci_get_slot_ctx(&xdev->in_ctx, ctx_size); + newport = port + 1; + val = LAST_CONTEXT(1) | SLOT_SPEED_SS | (newport << 16); /* FIXME speed, read from PS */ + slot->field1 = cpu_to_le32(val); + slot->field2 = cpu_to_le32(ROOT_HUB_PORT(newport)); /* FIXME how to get port no */ + + /* Step 4 */ + if (!xhci_alloc_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE, TYPE_CTRL)) { + dprintf("Failed allocating control\n"); + goto fail_in_ctx; + } + + /* Step 5 */ + ep0 = xhci_get_ep0_ctx(&xdev->in_ctx, ctx_size); + val = 0; + max_packet = usb_control_max_packet(USB_SUPER_SPEED); + max_packet = 64; + val = EP_TYPE(EP_CTRL) | MAX_BURST(0) | ERROR_COUNT(3) | + MAX_PACKET_SIZE(max_packet); + ep0->field2 = cpu_to_le32(val);; + ep0->deq_addr = cpu_to_le64(xdev->control.trbs_dma | xdev->control.cycle_state); + ep0->field4 = cpu_to_le32(8); + + /* Step 6 */ + if (!xhci_alloc_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE, XHCI_CTX_TYPE_DEVICE)) { + dprintf("Failed allocating out_ctx\n"); + goto fail_control_seg; + } + + /* Step 7 */ + xhcd->dcbaap[slot_id] = cpu_to_le64(xdev->out_ctx.dma_addr); + + /* Step 8 */ + slot = xhci_get_slot_ctx(&xdev->out_ctx, ctx_size); + ep0 = xhci_get_ep0_ctx(&xdev->out_ctx, ctx_size); + + dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4))); + xhci_send_addr_device(xhcd, slot_id, xdev->in_ctx.dma_addr); + mb(); + dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4))); + + dprintf("EP0 f0 %08X f1 %08X %016lX %08X\n", + le32_to_cpu(ep0->field1), + le32_to_cpu(ep0->field2), + le64_to_cpu(ep0->deq_addr), + le32_to_cpu(ep0->field4)); + + /* Step 9 - configure ep */ + ctrl->a_flags = cpu_to_le32(0x1); /* A0 */ + ctrl->d_flags = 0; + xhci_configure_ep(xhcd, slot_id, xdev->in_ctx.dma_addr); + mb(); + dprintf("Slot State %x \n", SLOT_STATE(le32_to_cpu(slot->field4))); + dprintf("USB Device address %d \n", USB_DEV_ADDRESS(le32_to_cpu(slot->field4))); + dprintf("EP0 f0 %08X f1 %08X %016lX %08X\n", + le32_to_cpu(ep0->field1), + le32_to_cpu(ep0->field2), + le64_to_cpu(ep0->deq_addr), + le32_to_cpu(ep0->field4)); + + dev = usb_devpool_get(); + dprintf("allocated device %p\n", dev); + dev->hcidev = xhcd->hcidev; + dev->speed = USB_SUPER_SPEED; + dev->addr = USB_DEV_ADDRESS(slot->field4); + dev->port = newport; + dev->priv = xdev; + xdev->dev = dev; + if (setup_new_device(dev, newport)) + return true; + + xhci_free_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE); +fail_control_seg: + xhci_free_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE); +fail_in_ctx: + xhci_free_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE); + return false; +} + +static void xhci_free_dev(struct xhci_dev *xdev) +{ + xhci_free_seg(&xdev->bulk_in, XHCI_DATA_TRBS_SIZE); + xhci_free_seg(&xdev->bulk_out, XHCI_DATA_TRBS_SIZE); + xhci_free_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE); + xhci_free_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE); + xhci_free_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE); +} + +static bool usb3_dev_init(struct xhci_hcd *xhcd, uint32_t port) +{ + /* Device enable slot */ + xhci_send_enable_slot(xhcd, port); + if (!xhcd->slot_id) { + dprintf("Unable to get slot id\n"); + return false; + } + dprintf("SLOT ID: %d\n", xhcd->slot_id); + if (!xhci_alloc_dev(xhcd, xhcd->slot_id, port)) { + dprintf("Unable to allocate device\n"); + return false; + } + return true; +} + +static int xhci_hub_check_ports(struct xhci_hcd *xhcd) +{ + uint32_t num_ports, portsc, i; + struct xhci_op_regs *op; + struct xhci_port_regs *prs; + struct xhci_cap_regs *cap; + uint32_t xecp_off; + uint32_t *xecp_addr, *base; + uint32_t port_off = 1, port_cnt; + + dprintf("enter\n"); + + op = xhcd->op_regs; + cap = xhcd->cap_regs; + port_cnt = num_ports = read_reg32(&cap->hcsparams1) >> 24; + + /* Read the xHCI extented capability to find usb3 ports and offset*/ + xecp_off = XHCI_HCCPARAMS_XECP(read_reg32(&cap->hccparams)); + base = (uint32_t *)cap; + while (xecp_off > 0) { + xecp_addr = base + xecp_off; + dprintf(stderr, "xecp_off %d %p %p \n", xecp_off, base, xecp_addr); + + if (XHCI_XECP_CAP_ID(read_reg32(xecp_addr)) == XHCI_XECP_CAP_SP && + XHCI_XECP_CAP_SP_MJ(read_reg32(xecp_addr)) == 3 && + XHCI_XECP_CAP_SP_MN(read_reg32(xecp_addr)) == 0) { + port_cnt = XHCI_XECP_CAP_SP_PC(read_reg32(xecp_addr + 2)); + port_off = XHCI_XECP_CAP_SP_PO(read_reg32(xecp_addr + 2)); + dprintf(stderr, "PortCount %d Portoffset %d\n", port_cnt, port_off); + } + base = xecp_addr; + xecp_off = XHCI_XECP_NEXT_PTR(read_reg32(xecp_addr)); + } + if (port_off == 0) /* port_off should always start from 1 */ + return false; + for (i = (port_off - 1); i < (port_off + port_cnt - 1); i++) { + prs = &op->prs[i]; + portsc = read_reg32(&prs->portsc); + if ((portsc & PORTSC_CCS) && + (portsc & PORTSC_PP) && + (portsc & PORTSC_PED)) { + /* Device present and enabled */ + dprintf("Device present on port %d\n", i); + /* Reset the port */ + portsc = read_reg32(&prs->portsc); + portsc = portsc | PORTSC_PR; + write_reg32(&prs->portsc, portsc); + /* FIXME poll for port event */ + SLOF_msleep(20); + xhci_poll_event(xhcd, 0); + portsc = read_reg32(&prs->portsc); + if (portsc & ~PORTSC_PRC) { + dprintf("Port reset complete %d\n", i); + } + print_port_status(prs); + if (!usb3_dev_init(xhcd, (i - (port_off - 1)))) { + dprintf("USB device initialization failed\n"); + } + } + } + dprintf("exit\n"); + return true; +} + +static bool xhci_hcd_init(struct xhci_hcd *xhcd) +{ + struct xhci_op_regs *op; + struct xhci_int_regs *irs; + uint64_t val; + uint32_t reg; + + if (!xhcd) { + dprintf("NULL pointer\n"); + goto fail; + } + + op = xhcd->op_regs; + irs = &xhcd->run_regs->irs[0]; + if (!xhci_hcd_reset(op)) { + dprintf("Reset failed\n"); + goto fail; + } + + write_reg32(&op->config, XHCI_CONFIG_MAX_SLOT); + reg = read_reg32(&xhcd->cap_regs->hccparams); + /* 64byte context !! */ + xhcd->hcc_csz_64 = (reg & XHCI_HCCPARAMS_CSZ) ? 1 : 0; + + if (xhcd->hcc_csz_64) { + printf("usb-xhci: 64 Byte context not supported\n"); + goto fail; + } + /* + * 6.1 Device Context Base Address Array + * + * Allocate memory and initialize + */ + xhcd->dcbaap = (uint64_t *)SLOF_dma_alloc(XHCI_DCBAAP_MAX_SIZE); + if (!xhcd->dcbaap) { + dprintf("Alloc failed\n"); + goto fail; + } + memset((void *)xhcd->dcbaap, 0, XHCI_DCBAAP_MAX_SIZE); + xhcd->dcbaap_dma = SLOF_dma_map_in((void *)xhcd->dcbaap, + XHCI_DCBAAP_MAX_SIZE, false); + dprintf("dcbaap %llx, dcbaap_phys %llx\n", xhcd->dcbaap, xhcd->dcbaap_dma); + write_reg64(&op->dcbaap, xhcd->dcbaap_dma); + + /* + * Command Ring Control - TRB + * FIXME - better way to allocate it... + */ + if (!xhci_alloc_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE, TYPE_COMMAND)) + goto fail_dcbaap; + + val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK; + val = val | (xhcd->crseg.trbs_dma & XHCI_CRCR_CRP_MASK); + write_reg64(&op->crcr, val); + + /* + * Event Ring Control - TRB + * Allocate event TRBS + */ + if (!xhci_alloc_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE, TYPE_EVENT)) + goto fail_crseg; + + /* + * Populate event ring segment table. + * Note: only using one segment. + */ + xhcd->erst.entries = SLOF_dma_alloc(XHCI_EVENT_TRBS_SIZE); + if (!xhcd->erst.entries) + goto fail_ering; + xhcd->erst.dma = SLOF_dma_map_in((void *)xhcd->erst.entries, + XHCI_EVENT_TRBS_SIZE, false); + xhcd->erst.num_segs = XHCI_ERST_NUM_SEGS; + + /* populate entries[0] */ + write_reg64(&xhcd->erst.entries->addr, xhcd->ering.trbs_dma); + write_reg32(&xhcd->erst.entries->size, xhcd->ering.size); + write_reg32(&xhcd->erst.entries->reserved, 0); + + /* populate erdp */ + val = read_reg64(&irs->erdp) & ~XHCI_ERDP_MASK; + val = val | (xhcd->ering.trbs_dma & XHCI_ERDP_MASK); + write_reg64(&irs->erdp, val); + + /* populate erstsz */ + val = read_reg32(&irs->erstsz) & ~XHCI_ERST_SIZE_MASK; + val = val | xhcd->erst.num_segs; + write_reg32(&irs->erstsz, val); + + /* Now write the erstba */ + val = read_reg64(&irs->erstba) & ~XHCI_ERST_ADDR_MASK; + val = val | (xhcd->erst.dma & XHCI_ERST_ADDR_MASK); + write_reg64(&irs->erstba, val); + + dprintf("ERDP %llx TRB-DMA %llx\n", read_reg64(&irs->erdp), + xhcd->ering.trbs_dma); + dprintf("ERST %llx, ERST DMA %llx, size %d\n", + (uint64_t)xhcd->erst.entries, xhcd->erst.dma, + xhcd->erst.num_segs); + + mb(); + if (!xhci_hcd_set_runstop(op, true)) + goto fail_erst_entries; + + if (!xhci_hub_check_ports(xhcd)) + goto fail_erst_entries; + + return true; +fail_erst_entries: + write_reg64(&irs->erstba, 0); + mb(); + SLOF_dma_map_out(xhcd->erst.dma, (void *)xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE); + SLOF_dma_free((void *)xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE); +fail_ering: + xhci_free_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE); +fail_crseg: + val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK; + write_reg64(&op->crcr, val); + mb(); + xhci_free_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE); +fail_dcbaap: + write_reg64(&op->dcbaap, 0); + mb(); + SLOF_dma_map_out(xhcd->dcbaap_dma, (void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); + SLOF_dma_free((void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); +fail: + return false; +} + +static bool xhci_hcd_exit(struct xhci_hcd *xhcd) +{ + struct xhci_op_regs *op; + struct xhci_int_regs *irs; + uint64_t val; + int i; + + if (!xhcd) { + dprintf("NULL pointer\n"); + return false; + } + op = xhcd->op_regs; + + if (!xhci_hcd_set_runstop(op, false)) { + dprintf("NULL pointer\n"); + } + + for (i = 1; i < XHCI_CONFIG_MAX_SLOT; i++) { + if (xhcd->xdevs[i].dev) + xhci_free_dev(&xhcd->xdevs[i]); + } + + irs = &xhcd->run_regs->irs[0]; + write_reg64(&irs->erstba, 0); + mb(); + if (xhcd->erst.entries) { + SLOF_dma_map_out(xhcd->erst.dma, xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE); + SLOF_dma_free(xhcd->erst.entries, XHCI_EVENT_TRBS_SIZE); + } + xhci_free_seg(&xhcd->ering, XHCI_EVENT_TRBS_SIZE); + + val = read_reg64(&op->crcr) & ~XHCI_CRCR_CRP_MASK; + write_reg64(&op->crcr, val); + xhci_free_seg(&xhcd->crseg, XHCI_CRCR_CRP_SIZE); + write_reg64(&op->dcbaap, 0); + if (xhcd->dcbaap) { + SLOF_dma_map_out(xhcd->dcbaap_dma, (void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); + SLOF_dma_free((void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); + } + return true; +} + +static void xhci_init(struct usb_hcd_dev *hcidev) +{ + struct xhci_hcd *xhcd; + + printf(" XHCI: Initializing\n"); + dprintf("device base address %p\n", hcidev->base); + + hcidev->base = (void *)((uint64_t)hcidev->base & ~7); + xhcd = SLOF_alloc_mem(sizeof(*xhcd)); + if (!xhcd) { + printf("usb-xhci: Unable to allocate memory\n"); + return; + } + memset(xhcd, 0, sizeof(*xhcd)); + + hcidev->nextaddr = 1; + hcidev->priv = xhcd; + xhcd->hcidev = hcidev; + xhcd->cap_regs = (struct xhci_cap_regs *)(hcidev->base); + xhcd->op_regs = (struct xhci_op_regs *)(hcidev->base + + read_reg8(&xhcd->cap_regs->caplength)); + xhcd->run_regs = (struct xhci_run_regs *)(hcidev->base + + read_reg32(&xhcd->cap_regs->rtsoff)); + xhcd->db_regs = (struct xhci_db_regs *)(hcidev->base + + read_reg32(&xhcd->cap_regs->dboff)); + dump_xhci_regs(xhcd); + if (!xhci_hcd_init(xhcd)) + printf("usb-xhci: failed to initialize XHCI controller.\n"); + dump_xhci_regs(xhcd); +} + +static void xhci_exit(struct usb_hcd_dev *hcidev) +{ + struct xhci_hcd *xhcd; + + dprintf("%s: enter \n", __func__); + if (!hcidev && !hcidev->priv) { + return; + } + + xhcd = hcidev->priv; + xhci_hcd_exit(xhcd); + SLOF_free_mem(xhcd, sizeof(*xhcd)); + hcidev->priv = NULL; +} + +static void fill_trb_buff(struct xhci_command_trb *cmd, uint32_t field1, + uint32_t field2, uint32_t field3, uint32_t field4) +{ + uint32_t val, cycle_state; + + cmd->field[0] = cpu_to_le32(field1); + cmd->field[1] = cpu_to_le32(field2); + cmd->field[2] = cpu_to_le32(field3); + + val = le32_to_cpu(cmd->field[3]); + cycle_state = (val & 0x1) ? 0 : 1; + val = cycle_state | (field4 & ~0x1); + cmd->field[3] = cpu_to_le32(val); + + dprintf("CMD %016lx val %08x cycle_state %d field1 %08x, field2 %08x, field3 %08x field4 %08x\n", + cmd, val, cycle_state, + le32_to_cpu(cmd->field[0]), + le32_to_cpu(cmd->field[1]), + le32_to_cpu(cmd->field[2]), + le32_to_cpu(cmd->field[3]) + ); + + return; +} + +static void fill_setup_trb(struct xhci_command_trb *cmd, struct usb_dev_req *req, + uint32_t size) +{ + uint32_t field1, field2, field3, field4 = 0; + uint64_t req_raw; + uint32_t datalen = 0, pid = 0; + + req_raw = *((uint64_t *)req); + dprintf("%lx %lx \n", *((uint64_t *)req), req_raw); + /* req_raw is already in right byte order... */ + field1 = cpu_to_le32(TRB_ADDR_HIGH(req_raw)); + field2 = cpu_to_le32(TRB_ADDR_LOW(req_raw)); + field3 = 8; /* ALWAYS 8 */ + + datalen = cpu_to_le16(req->wLength); + if (datalen) { + pid = (req->bmRequestType & REQT_DIR_IN) ? 3 : 2; + field4 = TRB_TRT(pid); + } + field4 |= TRB_CMD_TYPE(TRB_SETUP_STAGE) | TRB_IDT; + fill_trb_buff(cmd, field1, field2, field3, field4); +} + +static void fill_setup_data(struct xhci_command_trb *cmd, void *data, + uint32_t size, uint32_t dir) +{ + uint32_t field1, field2, field3, field4; + + field1 = TRB_ADDR_LOW(data); + field2 = TRB_ADDR_HIGH(data); + field3 = size; + if (dir) + field4 = TRB_DIR_IN; + field4 |= TRB_CMD_TYPE(TRB_DATA_STAGE); + fill_trb_buff(cmd, field1, field2, field3, field4); +} + +static void fill_status_trb(struct xhci_command_trb *cmd, uint32_t dir) +{ + uint32_t field1, field2, field3, field4; + + field1 = 0; + field2 = 0; + field3 = 0; + if (dir) + field4 = TRB_DIR_IN; + + field4 |= TRB_CMD_TYPE(TRB_STATUS_STAGE) | TRB_IOC; + fill_trb_buff(cmd, field1, field2, field3, field4); +} + +static void fill_normal_trb(struct xhci_transfer_trb *trb, void *data, + uint32_t size) +{ + uint32_t field1, field2, field3, field4; + + field1 = TRB_ADDR_LOW(data); + field2 = TRB_ADDR_HIGH(data); + field3 = size; + field4 = TRB_CMD_TYPE(TRB_NORMAL) | TRB_IOC; + fill_trb_buff((struct xhci_command_trb *)trb, field1, field2, field3, field4); +} + +static int xhci_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data) +{ + struct xhci_dev *xdev; + struct xhci_seg *ctrl; + struct xhci_hcd *xhcd; + struct xhci_command_trb *cmd; + struct xhci_db_regs *dbr; + long req_phys = 0, data_phys = 0; + int ret = true; + uint32_t slot_id, pid = 0, datalen = 0; + + if (!pipe->dev || !pipe->dev->hcidev) { + dprintf(" NULL pointer\n"); + return false; + } + + xdev = pipe->dev->priv; + slot_id = xdev->slot_id; + ctrl = &xdev->control; + xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv; + dbr = xhcd->db_regs; + if (!ctrl || !xdev || !xhcd) { + dprintf(" NULL pointer\n"); + return false; + } + + cmd = (struct xhci_command_trb *)ctrl->enq; + req_phys = SLOF_dma_map_in(req, sizeof(struct usb_dev_req), true); + fill_setup_trb(cmd, req, sizeof(*req)); + + cmd++; + datalen = cpu_to_le16(req->wLength); + if (datalen) + pid = 1; + if (datalen) { + data_phys = SLOF_dma_map_in(data, datalen, true); + fill_setup_data(cmd, (void *) data_phys, datalen, pid); + cmd++; + } + + fill_status_trb(cmd, pid); + cmd++; + + /* Ring the doorbell - ep0 */ + write_reg32(&dbr->db[slot_id], 1); + if (!xhci_poll_event(xhcd, 0)) { + dprintf("Command failed\n"); + ret = false; + } + ctrl->enq = (uint64_t) cmd; + SLOF_dma_map_out(req_phys, req, sizeof(struct usb_dev_req)); + if (datalen) + SLOF_dma_map_out(data_phys, data, datalen); + return ret; +} + +static inline struct xhci_pipe *xhci_pipe_get_xpipe(struct usb_pipe *pipe) +{ + struct xhci_pipe *xpipe; + xpipe = container_of(pipe, struct xhci_pipe, pipe); + dprintf("%s: xpipe is %p\n", __func__, xpipe); + return xpipe; +} + +static inline struct xhci_seg *xhci_pipe_get_seg(struct usb_pipe *pipe) +{ + struct xhci_pipe *xpipe; + xpipe = xhci_pipe_get_xpipe(pipe); + return xpipe->seg; +} + +static inline void *xhci_get_trb(struct xhci_seg *seg) +{ + uint64_t val, enq; + uint32_t size; + struct xhci_link_trb *link; + + enq = val = seg->enq; + val = val + XHCI_TRB_SIZE; + size = seg->size * XHCI_TRB_SIZE; + /* TRBs being a cyclic buffer, here we cycle back to beginning. */ + if ((val % size) == 0) { + seg->enq = (uint64_t)seg->trbs; + enq = seg->enq; + seg->enq = seg->enq + XHCI_TRB_SIZE; + val = 0; + seg->cycle_state ^= seg->cycle_state; + link = (struct xhci_link_trb *) (seg->trbs + seg->size - 1); + link->addr = cpu_to_le64(seg->trbs_dma); + link->field2 = 0; + link->field3 = cpu_to_le32(0x1 | TRB_CMD_TYPE(TRB_LINK)); + mb(); + } + else { + seg->enq = seg->enq + XHCI_TRB_SIZE; + } + + return (void *)enq; +} + +static int xhci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, + void *data, int datalen) +{ + struct xhci_dev *xdev; + struct xhci_seg *seg; + struct xhci_hcd *xhcd; + struct xhci_transfer_trb *trb; + struct xhci_db_regs *dbr; + int ret = true; + uint32_t slot_id, epno; + + if (!pipe->dev || !pipe->dev->hcidev) { + dprintf(" NULL pointer\n"); + dprintf(" pipe dev %p hcidev %p\n", pipe->dev, pipe->dev->hcidev); + return false; + } + + xdev = pipe->dev->priv; + slot_id = xdev->slot_id; + seg = xhci_pipe_get_seg(pipe); + xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv; + dbr = xhcd->db_regs; + if (!seg || !xdev || !xhcd) { + dprintf(" NULL pointer\n"); + dprintf(" seg %p xdev %p xhcd %p\n", seg, xdev, xhcd); + return false; + } + + if (datalen > XHCI_MAX_BULK_SIZE) { + printf("usb-xhci: bulk transfer size too big\n"); + return false; + } + + trb = xhci_get_trb(seg); + fill_normal_trb(trb, (void *)data, datalen); + + epno = xhci_get_epno(pipe); + write_reg32(&dbr->db[slot_id], epno); + if (!xhci_poll_event(xhcd, 0)) { + dprintf("Bulk failed\n"); + ret = false; + } + trb->addr = 0; + trb->len = 0; + trb->flags = 0; + mb(); + + return ret; +} + +static int xhci_alloc_pipe_pool(struct xhci_hcd *xhcd) +{ + struct xhci_pipe *xpipe, *curr, *prev; + unsigned int i, count; + long xpipe_phys = 0; + + count = XHCI_PIPE_POOL_SIZE/sizeof(*xpipe); + xhcd->pool = xpipe = SLOF_dma_alloc(XHCI_PIPE_POOL_SIZE); + if (!xpipe) + return -1; + xhcd->pool_phys = xpipe_phys = SLOF_dma_map_in(xpipe, XHCI_PIPE_POOL_SIZE, true); + dprintf("%s: xpipe %p, xpipe_phys %lx\n", __func__, xpipe, xpipe_phys); + + /* Although an array, link them */ + for (i = 0, curr = xpipe, prev = NULL; i < count; i++, curr++) { + if (prev) + prev->pipe.next = &curr->pipe; + curr->pipe.next = NULL; + prev = curr; + } + + if (!xhcd->freelist) + xhcd->freelist = &xpipe->pipe; + else + xhcd->end->next = &xpipe->pipe; + xhcd->end = &prev->pipe; + + return 0; +} + +static void xhci_init_bulk_ep(struct usb_dev *dev, struct usb_pipe *pipe) +{ + struct xhci_hcd *xhcd; + struct xhci_dev *xdev; + struct xhci_seg *seg; + struct xhci_pipe *xpipe; + struct xhci_control_ctx *ctrl; + struct xhci_ep_ctx *ep; + uint32_t x_epno, val, type; + + if (!pipe || !dev || !dev->priv) + return; + + xdev = dev->priv; + xhcd = dev->hcidev->priv; + dprintf("dir %d\n", pipe->dir); + seg = xhci_pipe_get_seg(pipe); + xpipe = xhci_pipe_get_xpipe(pipe); + if (pipe->dir) { + type = EP_BULK_IN; + seg = &xdev->bulk_in; + } + else { + type = EP_BULK_OUT; + seg = &xdev->bulk_out; + } + + if (!seg->trbs) { + if (!xhci_alloc_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK)) { + dprintf("Failed allocating seg\n"); + } + } else { + xhci_init_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK); + } + + pipe->mps = XHCI_MAX_BULK_SIZE; + ctrl = xhci_get_control_ctx(&xdev->in_ctx); + x_epno = xhci_get_epno(pipe); + ep = xhci_get_ep_ctx(&xdev->in_ctx, xdev->ctx_size, x_epno); + val = EP_TYPE(type) | MAX_BURST(0) | ERROR_COUNT(3) | + MAX_PACKET_SIZE(pipe->mps); + ep->field2 = cpu_to_le32(val);; + ep->deq_addr = cpu_to_le64(seg->trbs_dma | seg->cycle_state); + ep->field4 = cpu_to_le32(8); + ctrl->a_flags = cpu_to_le32(BIT(x_epno) | 0x1); + ctrl->d_flags = 0; + xhci_configure_ep(xhcd, xdev->slot_id, xdev->in_ctx.dma_addr); + xpipe->seg = seg; +} + +static struct usb_pipe* xhci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, char *buf, size_t len) +{ + struct xhci_hcd *xhcd; + struct usb_pipe *new = NULL; + + if (!dev) + return NULL; + + xhcd = (struct xhci_hcd *)dev->hcidev->priv; + if (!xhcd->freelist) { + dprintf("usb-xhci: %s allocating pool\n", __func__); + if (xhci_alloc_pipe_pool(xhcd)) + return NULL; + } + + new = xhcd->freelist; + xhcd->freelist = xhcd->freelist->next; + if (!xhcd->freelist) + xhcd->end = NULL; + + memset(new, 0, sizeof(*new)); + new->dev = dev; + new->next = NULL; + new->type = ep->bmAttributes & USB_EP_TYPE_MASK; + new->speed = dev->speed; + new->mps = ep->wMaxPacketSize; + new->dir = (ep->bEndpointAddress & 0x80) >> 7; + new->epno = ep->bEndpointAddress & 0x0f; + + if (new->type == USB_EP_TYPE_BULK) + xhci_init_bulk_ep(dev, new); + + return new; +} + +static void xhci_put_pipe(struct usb_pipe *pipe) +{ + struct xhci_hcd *xhcd; + struct xhci_pipe *xpipe; + + dprintf("usb-xhci: %s enter - %p\n", __func__, pipe); + if (!pipe || !pipe->dev) + return; + xhcd = pipe->dev->hcidev->priv; + + dprintf("dir %d\n", pipe->dir); + if (pipe->type == USB_EP_TYPE_BULK) { + xpipe = xhci_pipe_get_xpipe(pipe); + xpipe->seg = NULL; + } + if (xhcd->end) + xhcd->end->next = pipe; + else + xhcd->freelist = pipe; + + xhcd->end = pipe; + pipe->next = NULL; + pipe->dev = NULL; + memset(pipe, 0, sizeof(*pipe)); + + dprintf("usb-xhci: %s exit\n", __func__); +} + +struct usb_hcd_ops xhci_ops = { + .name = "xhci-hcd", + .init = xhci_init, + .exit = xhci_exit, + .usb_type = USB_XHCI, + .get_pipe = xhci_get_pipe, + .put_pipe = xhci_put_pipe, + .send_ctrl = xhci_send_ctrl, + .transfer_bulk = xhci_transfer_bulk, + .next = NULL, +}; + +void usb_xhci_register(void) +{ + usb_hcd_register(&xhci_ops); +} diff --git a/qemu/roms/SLOF/lib/libusb/usb-xhci.h b/qemu/roms/SLOF/lib/libusb/usb-xhci.h new file mode 100644 index 000000000..faeb07ead --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-xhci.h @@ -0,0 +1,386 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * Definitions for XHCI Controller - Revision 1.0 (5/21/10) + * + */ + +#ifndef USB_XHCI_H +#define USB_XHCI_H + +#include <stdint.h> +#include "usb-core.h" + +#define BIT(x) (1 << x) + +/* 5.3 Host Controller Capability Registers + * Table 19 + */ +struct xhci_cap_regs { + uint8_t caplength; + uint8_t reserved; + uint16_t hciversion; + uint32_t hcsparams1; + uint32_t hcsparams2; + uint32_t hcsparams3; + uint32_t hccparams; +#define XHCI_HCCPARAMS_CSZ BIT(2) +#define XHCI_HCCPARAMS_XECP(x) ((x & 0xFFFF0000) >> 16) + uint32_t dboff; + uint32_t rtsoff; +} __attribute__ ((packed)); + +/* USB 3.0: Section 7 and 7.2 */ +#define XHCI_XECP_CAP_ID(x) ((x & 0xF)) +#define XHCI_XECP_CAP_SP 2 +#define XHCI_XECP_CAP_SP_MN(x) ((x & 0xFF0000) >> 16) +#define XHCI_XECP_CAP_SP_MJ(x) ((x & 0xFF000000) >> 24) +#define XHCI_XECP_CAP_SP_PC(x) ((x & 0xFF00) >> 8) +#define XHCI_XECP_CAP_SP_PO(x) (x & 0xFF) +#define XHCI_XECP_NEXT_PTR(x) ((x & 0xFF00) >> 8) + +/* Table 27: Host Controller USB Port Register Set */ +struct xhci_port_regs { + uint32_t portsc; +#define PORTSC_CCS BIT(0) +#define PORTSC_PED BIT(1) +#define PORTSC_OCA BIT(3) +#define PORTSC_PR BIT(4) +#define PORTSC_PLS_MASK (0xF << 5) +#define PORTSC_PLS_U0 0 +#define PORTSC_PLS_U1 1 +#define PORTSC_PLS_U2 2 +#define PORTSC_PLS_U3 3 +#define PORTSC_PLS_DISABLED 4 +#define PORTSC_PLS_RXDETECT 5 +#define PORTSC_PLS_INACTIVE 6 +#define PORTSC_PLS_POLLING 7 +#define PORTSC_PLS_RECOVERY 8 +#define PORTSC_PLS_HOTRESET 9 +#define PORTSC_PLS_COMP_MODE 10 +#define PORTSC_PLS_TEST_MODE 11 +#define PORTSC_PLS_RESUME 15 +#define PORTSC_PP BIT(9) +#define PORTSC_PS_MASK (0xF << 10) +#define PORTSC_PIC_MASK (0x3 << 14) +#define PORTSC_LWS BIT(16) +#define PORTSC_CSC BIT(17) +#define PORTSC_PEC BIT(18) +#define PORTSC_WRC BIT(19) +#define PORTSC_OCC BIT(20) +#define PORTSC_PRC BIT(21) +#define PORTSC_PLC BIT(22) +#define PORTSC_CEC BIT(23) +#define PORTSC_CAS BIT(24) +#define PORTSC_WCE BIT(25) +#define PORTSC_WDE BIT(26) +#define PORTSC_WOE BIT(27) +#define PORTSC_DR BIT(30) +#define PORTSC_WPR BIT(31) + + uint32_t portpmsc; + uint32_t portli; + uint32_t reserved; +} __attribute__ ((packed)); + +struct port_state { + bool PP; + bool CCS; + bool PED; + bool PR; + uint8_t PLS; + char *state; +}; + + +struct port_state ps_array_usb2[] = { + {1, 0, 0, 0, PORTSC_PLS_U0, "ERROR"} +}; + +struct port_state ps_array_usb3[] = { + {0, 0, 0, 0, PORTSC_PLS_DISABLED, "Powered-OFF"}, + {1, 0, 0, 0, PORTSC_PLS_POLLING, "Polling"}, + {1, 0, 0, 0, PORTSC_PLS_U0, "Polling"}, + {1, 0, 0, 0, PORTSC_PLS_RXDETECT, "*** Disconnected ***"}, + {1, 0, 0, 0, PORTSC_PLS_DISABLED, "Disabled"}, + {1, 0, 0, 0, PORTSC_PLS_INACTIVE, "Error"}, + {1, 0, 0, 0, PORTSC_PLS_TEST_MODE,"Loopback"}, + {1, 0, 0, 0, PORTSC_PLS_COMP_MODE,"Compliancek"}, + {1, 1, 0, 1, PORTSC_PLS_U0, "****** Reset ******"}, + {1, 1, 1, 0, PORTSC_PLS_U0, "****** Enabled ******"}, +}; + +/* 5.4 Host Controller Operational Registers + * Table 26 + */ +struct xhci_op_regs { + uint32_t usbcmd; +#define XHCI_USBCMD_RS BIT(0) +#define XHCI_USBCMD_HCRST BIT(1) + + uint32_t usbsts; +#define XHCI_USBSTS_HCH BIT(0) +#define XHCI_USBSTS_CNR BIT(11) + + uint32_t pagesize; + uint8_t reserved[8]; /* 0C - 13 */ + uint32_t dnctrl; /* Device notification control */ + uint64_t crcr; /* Command ring control */ +#define XHCI_CRCR_CRP_MASK 0xFFFFFFFFFFFFFFC0 +#define XHCI_CRCR_CRR BIT(3) +#define XHCI_CRCR_CRP_SIZE 4096 + + uint8_t reserved1[16]; /* 20 - 2F */ + uint64_t dcbaap; /* Device Context Base Address Array Pointer */ +#define XHCI_DCBAAP_MAX_SIZE 2048 + + uint32_t config; /* Configure */ +#define XHCI_CONFIG_MAX_SLOT 4 + + uint8_t reserved2[964]; /* 3C - 3FF */ + /* USB Port register set */ +#define XHCI_PORT_MAX 256 + struct xhci_port_regs prs[XHCI_PORT_MAX]; +} __attribute__ ((packed)); + +/* + * 5.5.2 Interrupter Register Set + * Table 42: Interrupter Registers + */ +struct xhci_int_regs { + uint32_t iman; + uint32_t imod; + uint32_t erstsz; +#define XHCI_ERST_SIZE_MASK 0xFFFF + uint32_t reserved; + uint64_t erstba; +#define XHCI_ERST_ADDR_MASK (~(0x3FUL)) + uint64_t erdp; +#define XHCI_ERDP_MASK (~(0xFUL)) +} __attribute__ ((packed)); + +/* 5.5 Host Controller Runtime Registers */ +struct xhci_run_regs { + uint32_t mfindex; /* microframe index */ + uint8_t reserved[28]; +#define XHCI_IRS_MAX 1024 + struct xhci_int_regs irs[XHCI_IRS_MAX]; +} __attribute__ ((packed)); + +/* 5.6 Doorbell Registers*/ +struct xhci_db_regs { + uint32_t db[256]; +} __attribute__ ((packed)); + +#define COMP_SUCCESS 1 + +#define TRB_SLOT_ID(x) (((x) & (0xFF << 24)) >> 24) +#define TRB_CMD_SLOT_ID(x) ((x & 0xFF) << 24) +#define TRB_TYPE(x) (((x) & (0x3F << 10)) >> 10) +#define TRB_CMD_TYPE(x) ((x & 0x3F) << 10) +#define TRB_STATUS(x) (((x) & (0xFF << 24)) >> 24) +#define TRB_ADDR_LOW(x) ((uint32_t)((uint64_t)(x))) +#define TRB_ADDR_HIGH(x) ((uint32_t)((uint64_t)(x) >> 32)) +#define TRB_TRT(x) (((x) & 0x3) << 16 ) +#define TRB_DIR_IN BIT(16) +#define TRB_IOC BIT(5) +#define TRB_IDT BIT(6) + +#define TRB_CYCLE_STATE BIT(0) + +struct xhci_transfer_trb { + uint64_t addr; + uint32_t len; + uint32_t flags; +} __attribute__ ((packed)); + +struct xhci_link_trb { + uint64_t addr; + uint32_t field2; + uint32_t field3; +} __attribute__ ((packed)); + +/* Event TRB */ +struct xhci_event_trb { + uint64_t addr; + uint32_t status; + uint32_t flags; +} __attribute__ ((packed)); + +#define TRB_NORMAL 1 +#define TRB_SETUP_STAGE 2 +#define TRB_DATA_STAGE 3 +#define TRB_STATUS_STAGE 4 +#define TRB_ISOCH 5 +#define TRB_LINK 6 +#define TRB_EVENT_DATA 7 +#define TRB_NOOP 8 +#define TRB_ENABLE_SLOT 9 +#define TRB_DISABLE_SLOT 10 +#define TRB_ADDRESS_DEV 11 +#define TRB_CONFIG_EP 12 +#define TRB_EVAL_CNTX 13 +#define TRB_TRANSFER_EVENT 32 +#define TRB_CMD_COMPLETION 33 +#define TRB_PORT_STATUS 34 + +struct xhci_command_trb { + uint32_t field[4]; +}__attribute__ ((packed)); + +union xhci_trb { + struct xhci_event_trb event; + struct xhci_transfer_trb xfer; + struct xhci_command_trb cmd; + struct xhci_link_trb link; +}; + +enum xhci_seg_type { + TYPE_CTRL = 0, + TYPE_BULK, + TYPE_COMMAND, + TYPE_EVENT, +}; + +struct xhci_seg { + union xhci_trb *trbs; + struct xhci_seg *next; + uint64_t enq; + uint64_t deq; + uint64_t trbs_dma; + uint32_t size; + uint32_t cycle_state; + enum xhci_seg_type type; +}; + +#define XHCI_TRB_SIZE 16 +#define XHCI_EVENT_TRBS_SIZE 4096 +#define XHCI_CONTROL_TRBS_SIZE 4096 +#define XHCI_DATA_TRBS_SIZE 4096 +#define XHCI_ERST_NUM_SEGS 1 + +#define XHCI_MAX_BULK_SIZE 0xF000 + +struct xhci_erst_entry { + uint64_t addr; + uint32_t size; + uint32_t reserved; +} __attribute__ ((packed)); + +struct xhci_erst { + struct xhci_erst_entry *entries; + uint64_t dma; + uint32_t num_segs; /* number of segments */ +}; + +struct xhci_control_ctx { + uint32_t d_flags; + uint32_t a_flags; + uint32_t reserved[6]; +} __attribute__ ((packed)); + +struct xhci_slot_ctx { + uint32_t field1; +#define SLOT_SPEED_FS BIT(20) +#define SLOT_SPEED_LS BIT(21) +#define SLOT_SPEED_HS BIT(22) +#define SLOT_SPEED_SS BIT(23) +#define LAST_CONTEXT(x) (x << 27) + + uint32_t field2; +#define ROOT_HUB_PORT(x) ((x & 0xff) << 16) + + uint32_t field3; + uint32_t field4; +#define USB_DEV_ADDRESS(x) (x & 0xFFU) +#define SLOT_STATE(x) ((x >> 27) & 0x1FU) +#define SLOT_STATE_DIS_ENA 0 +#define SLOT_STATE_DEFAULT 1 +#define SLOT_STATE_ADDRESSED 2 +#define SLOT_STATE_CONFIGURED 3 + + + uint32_t reserved[4]; +} __attribute__ ((packed)); + +struct xhci_ep_ctx { + uint32_t field1; + uint32_t field2; +#define MAX_PACKET_SIZE(x) (((x) & 0xFFFF) << 16) +#define MAX_BURST(x) (((x) & 0xFF) << 8) +#define EP_TYPE(x) (((x) & 0x07) << 3) +#define EP_ISOC_OUT 1 +#define EP_BULK_OUT 2 +#define EP_INT_OUT 3 +#define EP_CTRL 4 +#define EP_ISOC_IN 5 +#define EP_BULK_IN 6 +#define EP_INT_IN 7 + +#define ERROR_COUNT(x) (((x) & 0x03) << 1) + + uint64_t deq_addr; + uint32_t field4; + uint32_t reserved[3]; +} __attribute__ ((packed)); + +struct xhci_ctx { + uint8_t type; +#define XHCI_CTX_TYPE_DEVICE 0x1 +#define XHCI_CTX_TYPE_INPUT 0x2 + uint32_t size; + uint8_t *addr; +#define XHCI_CTX_BUF_SIZE 4096 + uint64_t dma_addr; +}; + +struct xhci_dev { + struct usb_dev *dev; + uint32_t slot_id; + struct xhci_ctx in_ctx; + struct xhci_ctx out_ctx; + struct xhci_seg control; + struct xhci_seg bulk_in; + struct xhci_seg bulk_out; + uint32_t ctx_size; +}; + +struct xhci_hcd { + struct xhci_cap_regs *cap_regs; + struct xhci_op_regs *op_regs; + struct xhci_run_regs *run_regs; + struct xhci_db_regs *db_regs; + struct usb_hcd_dev *hcidev; + struct xhci_dev xdevs[XHCI_CONFIG_MAX_SLOT + 1]; + struct usb_pipe *freelist; + struct usb_pipe *end; + uint64_t *dcbaap; + uint64_t dcbaap_dma; + struct xhci_seg ering; + struct xhci_seg crseg; + struct xhci_erst erst; + uint64_t erds_dma; + uint32_t erds_size; + uint32_t slot_id; + uint32_t hcc_csz_64; + void *pool; +#define XHCI_PIPE_POOL_SIZE 4096 + + long pool_phys; +}; + +struct xhci_pipe { + struct usb_pipe pipe; + struct xhci_seg *seg; +}; + +#endif /* USB_XHCI_H */ diff --git a/qemu/roms/SLOF/lib/libusb/usb.code b/qemu/roms/SLOF/lib/libusb/usb.code new file mode 100644 index 000000000..fd92d9e78 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb.code @@ -0,0 +1,162 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * libusb bindings for SLOF - implementation + */ + +#include <usb.h> + + +/************************************************/ +/* Register with the usb-core */ +/* SLOF: USB-OHCI-REGISTER ( -- ) */ +/* LIBNEWUSB: usb_ohci_register(void) */ +/************************************************/ +PRIM(USB_X2d_OHCI_X2d_REGISTER) + usb_ohci_register(); +MIRP + +/************************************************/ +/* Register with the usb-core */ +/* SLOF: USB-EHCI-REGISTER ( -- ) */ +/* LIBNEWUSB: usb_ehci_register(void) */ +/************************************************/ +PRIM(USB_X2d_EHCI_X2d_REGISTER) + usb_ehci_register(); +MIRP + +/************************************************/ +/* Register with the usb-core */ +/* SLOF: USB-XHCI-REGISTER ( -- ) */ +/* LIBNEWUSB: usb_xhci_register(void) */ +/************************************************/ +PRIM(USB_X2d_XHCI_X2d_REGISTER) + usb_xhci_register(); +MIRP + +/************************************************/ +/* Initialize hcidev with the usb-core */ +/* SLOF: USB-HCD-INIT ( hcidev -- ) */ +/* LIBNEWUSB: usb_hcd_init(hcidev) */ +/************************************************/ +PRIM(USB_X2d_HCD_X2d_INIT) + void *hcidev = TOS.a; POP; + usb_hcd_init(hcidev); +MIRP + +/************************************************/ +/* Remove hcidev with the usb-core */ +/* SLOF: USB-HCD-EXIT ( hcidev -- ) */ +/* LIBNEWUSB: usb_hcd_exit(hcidev) */ +/************************************************/ +PRIM(USB_X2d_HCD_X2d_EXIT) + void *hcidev = TOS.a; POP; + usb_hcd_exit(hcidev); +MIRP + +/************************************************/ +/* Initialize hid */ +/* SLOF: USB-HID-INIT ( dev -- true | false )*/ +/* LIBNEWUSB: usb_hid_init(hcidev) */ +/************************************************/ +PRIM(USB_X2d_HID_X2d_INIT) + void *dev = TOS.a; + TOS.n = usb_hid_init(dev); +MIRP + +/************************************************/ +/* Exit hid */ +/* SLOF: USB-HID-EXIT ( dev -- true | false )*/ +/* LIBNEWUSB: usb_hid_exit(hcidev) */ +/************************************************/ +PRIM(USB_X2d_HID_X2d_EXIT) + void *dev = TOS.a; + TOS.n = usb_hid_exit(dev); +MIRP + +/************************************************/ +/* Read usb keyboard for key */ +/* SLOF: USB-READ-KEYB ( dev -- */ +/* ( key | false )) */ +/* LIBNEWUSB: usb_read_keyb */ +/************************************************/ +PRIM(USB_X2d_READ_X2d_KEYB) + void *dev = TOS.a; + TOS.n = usb_read_keyb(dev); +MIRP + +/************************************************/ +/* Is USB KEY available */ +/* SLOF: USB-KEY-AVAILABLE ( dev -- ( true | */ +/* false ))*/ +/* LIBNEWUSB: usb_key_available */ +/************************************************/ +PRIM(USB_X2d_KEY_X2d_AVAILABLE) + void *dev = TOS.a; + TOS.n = usb_key_available(dev); +MIRP + +/************************************************/ +/* Initialize and enumerate generic hub */ +/* SLOF: USB-HUB-INIT ( dev -- true | false ) */ +/* LIBNEWUSB: usb_hub_init */ +/************************************************/ +PRIM(USB_X2d_HUB_X2d_INIT) + void *dev = TOS.a; + TOS.n = usb_hub_init(dev); +MIRP + +/************************************************/ +/* Initialize msc */ +/* SLOF: USB-MSC-INIT ( dev -- true | false )*/ +/* LIBNEWUSB: usb_msc_init(hcidev) */ +/************************************************/ +PRIM(USB_X2d_MSC_X2d_INIT) + void *dev = TOS.a; + TOS.n = usb_msc_init(dev); +MIRP + +/************************************************/ +/* Exit msc */ +/* SLOF: USB-MSC-EXIT ( dev -- true | false )*/ +/* LIBNEWUSB: usb_msc_exit(hcidev) */ +/************************************************/ +PRIM(USB_X2d_MSC_X2d_EXIT) + void *dev = TOS.a; + TOS.n = usb_msc_exit(dev); +MIRP + +/*****************************************************************************/ +/* Transfer data through control endpoint */ +/* SLOF: USB-TRANSFER_CTRL ( dev req data -- true | false ) */ +/* LIBNEWUSB: int usb_transfer_ctrl(void *dev, void *req, void *data) */ +/*****************************************************************************/ +PRIM(USB_X2d_TRANSFER_X2d_CTRL) + void *data = TOS.a; POP; + void *req = TOS.a; POP; + TOS.n = usb_transfer_ctrl(TOS.a, req, data); +MIRP + +/*****************************************************************************/ +/* Transfer data through bulk endpoint */ +/* SLOF: USB-TRANSFER_BULK ( dev dir td td-phys data size -- true | false ) */ +/* LIBNEWUSB: int usb_transfer_bulk(void *dev, int dir, void *td, */ +/* void *td_phys, void *data, int size) */ +/*****************************************************************************/ +PRIM(USB_X2d_TRANSFER_X2d_BULK) + int size = TOS.u; POP; + void *data = TOS.a; POP; + void *td_phys = TOS.a; POP; + void *td = TOS.a; POP; + int dir = TOS.u; POP; + TOS.n = usb_transfer_bulk(TOS.a, dir, td, td_phys, data, size); +MIRP diff --git a/qemu/roms/SLOF/lib/libusb/usb.h b/qemu/roms/SLOF/lib/libusb/usb.h new file mode 100644 index 000000000..fba19d2a1 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * Copyright (c) 2006, 2012, 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * prototypes for libusb implementation used in libusb.code + */ + +#ifndef __LIBUSB_H +#define __LIBUSB_H + +/*******************************************/ +/* SLOF: USB-OHCI-REGISTER */ +/*******************************************/ +extern void usb_ohci_register(void); +/*******************************************/ +/* SLOF: USB-EHCI-REGISTER */ +/*******************************************/ +extern void usb_ehci_register(void); +/*******************************************/ +/* SLOF: USB-XHCI-REGISTER */ +/*******************************************/ +extern void usb_xhci_register(void); +/*******************************************/ +/* SLOF: USB-HCD-INIT */ +/*******************************************/ +extern void usb_hcd_init(void *hcidev); +/*******************************************/ +/* SLOF: USB-HCD-EXIT */ +/*******************************************/ +extern void usb_hcd_exit(void *hcidev); +/*******************************************/ +/* SLOF: USB-HID-INIT */ +/*******************************************/ +extern int usb_hid_init(void *dev); +/*******************************************/ +/* SLOF: USB-HID-EXIT */ +/*******************************************/ +extern int usb_hid_exit(void *dev); +/*******************************************/ +/* SLOF: USB-READ-KEYB */ +/*******************************************/ +extern unsigned char usb_read_keyb(void *dev); +/*******************************************/ +/* SLOF: USB-KEY-AVAILABLE */ +/*******************************************/ +extern unsigned char usb_key_available(void *dev); +/*******************************************/ +/* SLOF: USB-HUB-INIT */ +/*******************************************/ +extern unsigned int usb_hub_init(void *dev); +/*******************************************/ +/* SLOF: USB-MSC-INIT */ +/*******************************************/ +extern int usb_msc_init(void *dev); +/*******************************************/ +/* SLOF: USB-MSC-EXIT */ +/*******************************************/ +extern int usb_msc_exit(void *dev); +/*******************************************/ +/* SLOF: USB-TRANSFER-CTRL */ +/*******************************************/ +extern int usb_transfer_ctrl(void *dev, void *req, void *data); +/*******************************************/ +/* SLOF: USB-TRANSFER-BULK */ +/*******************************************/ +extern int usb_transfer_bulk(void *dev, int dir, void *td, + void *td_phys, void *data, int size); + +#endif diff --git a/qemu/roms/SLOF/lib/libusb/usb.in b/qemu/roms/SLOF/lib/libusb/usb.in new file mode 100644 index 000000000..7ceba7d2d --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb.in @@ -0,0 +1,29 @@ +/****************************************************************************** + * Copyright (c) 2007, 2012, 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +/* + * libusb bindings for SLOF - definitions + */ + +cod(USB-OHCI-REGISTER) +cod(USB-EHCI-REGISTER) +cod(USB-XHCI-REGISTER) +cod(USB-HCD-INIT) +cod(USB-HCD-EXIT) +cod(USB-HID-INIT) +cod(USB-HID-EXIT) +cod(USB-READ-KEYB) +cod(USB-KEY-AVAILABLE) +cod(USB-HUB-INIT) +cod(USB-MSC-INIT) +cod(USB-MSC-EXIT) +cod(USB-TRANSFER-CTRL) +cod(USB-TRANSFER-BULK) diff --git a/qemu/roms/SLOF/lib/libveth/Makefile b/qemu/roms/SLOF/lib/libveth/Makefile new file mode 100644 index 000000000..dd1234af1 --- /dev/null +++ b/qemu/roms/SLOF/lib/libveth/Makefile @@ -0,0 +1,52 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \ + -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) +CPPFLAGS += -I../libhvcall + +LDFLAGS = -nostdlib + +TARGET = ../libveth.a + + +all: $(TARGET) + +SRCS = veth.c + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep diff --git a/qemu/roms/SLOF/lib/libveth/veth.c b/qemu/roms/SLOF/lib/libveth/veth.c new file mode 100644 index 000000000..748730854 --- /dev/null +++ b/qemu/roms/SLOF/lib/libveth/veth.c @@ -0,0 +1,273 @@ +/****************************************************************************** + * Copyright (c) 2011, 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <helpers.h> +#include "veth.h" +#include "libhvcall.h" + +#undef VETH_DEBUG +//#define VETH_DEBUG +#ifdef VETH_DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +/* *** WARNING: We pass our addresses as-is as DMA addresses, + * we -do- rely on the forth code to have enabled TCE bypass + * on our device ! + */ +#define vaddr_to_dma(vaddr) ((uint64_t)vaddr) + +struct ibmveth_buf_desc_fields { + uint32_t flags_len; +#define IBMVETH_BUF_VALID 0x80000000 +#define IBMVETH_BUF_TOGGLE 0x40000000 +#define IBMVETH_BUF_NO_CSUM 0x02000000 +#define IBMVETH_BUF_CSUM_GOOD 0x01000000 +#define IBMVETH_BUF_LEN_MASK 0x00FFFFFF + uint32_t address; +}; + +union ibmveth_buf_desc { + uint64_t desc; + struct ibmveth_buf_desc_fields fields; +}; + +struct ibmveth_rx_q_entry { + uint32_t flags_off; +#define IBMVETH_RXQ_TOGGLE 0x80000000 +#define IBMVETH_RXQ_TOGGLE_SHIFT 31 +#define IBMVETH_RXQ_VALID 0x40000000 +#define IBMVETH_RXQ_NO_CSUM 0x02000000 +#define IBMVETH_RXQ_CSUM_GOOD 0x01000000 +#define IBMVETH_RXQ_OFF_MASK 0x0000FFFF + + uint32_t length; + uint64_t correlator; +}; + +static void *buffer_list; +static void *filter_list; +static uint64_t *rx_bufs; +static uint64_t *rx_bufs_aligned; +static uint32_t cur_rx_toggle; +static uint32_t cur_rx_index; + +#define RX_QUEUE_SIZE 256 +#define RX_BUF_SIZE 2048 +#define RX_BUF_MULT (RX_BUF_SIZE >> 3) + +static struct ibmveth_rx_q_entry *rx_queue; + +static inline uint64_t *veth_get_rx_buf(unsigned int i) +{ + return &rx_bufs_aligned[i * RX_BUF_MULT]; +} + +static int veth_init(net_driver_t *driver) +{ + char *mac_addr; + union ibmveth_buf_desc rxq_desc; + unsigned long rx_queue_len = sizeof(struct ibmveth_rx_q_entry) * + RX_QUEUE_SIZE; + unsigned int i; + long rc; + + if (!driver) + return -1; + + dprintf("veth_init(%02x:%02x:%02x:%02x:%02x:%02x)\n", + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4], mac_addr[5]); + + if (driver->running != 0) + return 0; + + mac_addr = (char *)driver->mac_addr; + cur_rx_toggle = IBMVETH_RXQ_TOGGLE; + cur_rx_index = 0; + buffer_list = SLOF_alloc_mem_aligned(8192, 4096); + filter_list = buffer_list + 4096; + rx_queue = SLOF_alloc_mem_aligned(rx_queue_len, 16); + rx_bufs = SLOF_alloc_mem(2048 * RX_QUEUE_SIZE + 4); + if (!buffer_list || !filter_list || !rx_queue || !rx_bufs) { + printf("veth: Failed to allocate memory !\n"); + goto fail; + } + rx_bufs_aligned = (uint64_t *)(((uint64_t)rx_bufs | 3) + 1); + rxq_desc.fields.address = vaddr_to_dma(rx_queue); + rxq_desc.fields.flags_len = IBMVETH_BUF_VALID | rx_queue_len; + + rc = h_register_logical_lan(driver->reg, + vaddr_to_dma(buffer_list), + rxq_desc.desc, + vaddr_to_dma(filter_list), + (*(uint64_t *)mac_addr) >> 16); + if (rc != H_SUCCESS) { + printf("veth: Error %ld registering interface !\n", rc); + goto fail; + } + for (i = 0; i < RX_QUEUE_SIZE; i++) { + uint64_t *buf = veth_get_rx_buf(i); + union ibmveth_buf_desc desc; + *buf = (uint64_t)buf; + desc.fields.address = vaddr_to_dma(buf); + desc.fields.flags_len = IBMVETH_BUF_VALID | RX_BUF_SIZE; + h_add_logical_lan_buffer(driver->reg, desc.desc); + } + + driver->running = 1; + + return 0; + fail: + if (buffer_list) + SLOF_free_mem(buffer_list, 8192); + if (rx_queue) + SLOF_free_mem(rx_queue, rx_queue_len); + if (rx_bufs) + SLOF_free_mem(rx_bufs, 2048 * RX_QUEUE_SIZE + 4); + return -1; +} + +static int veth_term(net_driver_t *driver) +{ + dprintf("veth_term()\n"); + + if (driver->running == 0) + return 0; + + h_free_logical_lan(driver->reg); + + if (buffer_list) + SLOF_free_mem(buffer_list, 8192); + if (rx_queue) + SLOF_free_mem(rx_queue, sizeof(struct ibmveth_rx_q_entry) * RX_QUEUE_SIZE); + if (rx_bufs) + SLOF_free_mem(rx_bufs, 2048 * RX_QUEUE_SIZE + 4); + + driver->running = 0; + + return 0; +} + +static int veth_receive(char *f_buffer_pc, int f_len_i, net_driver_t *driver) +{ + int packet = 0; + + dprintf("veth_receive()\n"); + + while(!packet) { + struct ibmveth_rx_q_entry *desc = &rx_queue[cur_rx_index]; + union ibmveth_buf_desc bdesc; + void *buf; + + buf = (void *)desc->correlator; + + if ((desc->flags_off & IBMVETH_RXQ_TOGGLE) != cur_rx_toggle) + break; + + if (!(desc->flags_off & IBMVETH_RXQ_VALID)) + goto recycle; + if (desc->length > f_len_i) { + printf("veth: Dropping too big packet [%d bytes]\n", + desc->length); + goto recycle; + } + + packet = desc->length; + memcpy(f_buffer_pc, + buf + (desc->flags_off & IBMVETH_RXQ_OFF_MASK), packet); + recycle: + bdesc.fields.address = vaddr_to_dma(buf); + bdesc.fields.flags_len = IBMVETH_BUF_VALID | RX_BUF_SIZE; + h_add_logical_lan_buffer(driver->reg, bdesc.desc); + + cur_rx_index = (cur_rx_index + 1) % RX_QUEUE_SIZE; + if (cur_rx_index == 0) + cur_rx_toggle ^= IBMVETH_RXQ_TOGGLE; + } + + return packet; +} + +static int veth_xmit(char *f_buffer_pc, int f_len_i, net_driver_t *driver) +{ + union ibmveth_buf_desc tx_desc; + long rc; + + dprintf("veth_xmit(packet at %p, %d bytes)\n", f_buffer_pc, f_len_i); + + tx_desc.fields.address = vaddr_to_dma(f_buffer_pc); + tx_desc.fields.flags_len = IBMVETH_BUF_VALID | f_len_i; + + rc = hv_send_logical_lan(driver->reg, tx_desc.desc, 0, 0, 0, 0, 0); + if (rc != H_SUCCESS) { + printf("veth: Error %ld sending packet !\n", rc); + return -1; + } + + return f_len_i; +} + +net_driver_t *libveth_open(char *mac_addr, int mac_len, char *reg, int reg_len) +{ + net_driver_t *driver; + + driver = SLOF_alloc_mem(sizeof(*driver)); + if (!driver) { + printf("Unable to allocate veth driver\n"); + return NULL; + } + + /* veth uses a 8-byte wide property instead of 6-byte wide MACs */ + if ((mac_len == 8) && (mac_addr[0] == 0) && mac_addr[1] == 0) + mac_addr += 2; + memcpy(driver->mac_addr, mac_addr, 6); + driver->reg = *(uint32_t *)reg; + driver->running = 0; + + if (veth_init(driver)) { + SLOF_free_mem(driver, sizeof(*driver)); + return NULL; + } + + return driver; +} + +void libveth_close(net_driver_t *driver) +{ + if (driver) { + veth_term(driver); + SLOF_free_mem(driver, sizeof(*driver)); + } +} + +int libveth_read(char *buf, int len, net_driver_t *driver) +{ + if (buf) + return veth_receive(buf, len, driver); + + return -1; +} + +int libveth_write(char *buf, int len, net_driver_t *driver) +{ + if (buf) + return veth_xmit(buf, len, driver); + + return -1; +} diff --git a/qemu/roms/SLOF/lib/libveth/veth.code b/qemu/roms/SLOF/lib/libveth/veth.code new file mode 100644 index 000000000..76d14a968 --- /dev/null +++ b/qemu/roms/SLOF/lib/libveth/veth.code @@ -0,0 +1,61 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * libveth Forth wrapper + */ + +#include <veth.h> + +// : libveth-open ( mac-addr-str len reg-str len -- false | [ driver true ] ) +PRIM(LIBVETH_X2d_OPEN) +{ + int reg_len = TOS.u; POP; + char *reg = TOS.a; POP; + int len = TOS.u; POP; + char *mac_addr = TOS.a; + + net_driver_t *net_driver = libveth_open(mac_addr, len, reg, reg_len); + if (net_driver) { + TOS.u = (unsigned long)net_driver; PUSH; + TOS.n = -1; + } else + TOS.n = 0; +} +MIRP + +// : libveth-close ( driver -- ) +PRIM(LIBVETH_X2d_CLOSE) +{ + net_driver_t *driver = TOS.a; POP; + libveth_close(driver); +} +MIRP + + +// : libveth-read ( addr len driver -- actual ) +PRIM(LIBVETH_X2d_READ) +{ + net_driver_t *driver = TOS.a; POP; + int len = TOS.u; POP; + TOS.n = libveth_read(TOS.a, len, driver); +} +MIRP + +// : libveth-write ( addr len driver -- actual ) +PRIM(LIBVETH_X2d_WRITE) +{ + net_driver_t *driver = TOS.a; POP; + int len = TOS.u; POP; + TOS.n = libveth_write(TOS.a, len, driver); +} +MIRP diff --git a/qemu/roms/SLOF/lib/libveth/veth.h b/qemu/roms/SLOF/lib/libveth/veth.h new file mode 100644 index 000000000..23af0eab6 --- /dev/null +++ b/qemu/roms/SLOF/lib/libveth/veth.h @@ -0,0 +1,24 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + ******************************************************************************/ + +#ifndef _VETH_H +#define _VETH_H + +#include <stdint.h> +#include <netdriver.h> + +extern net_driver_t *libveth_open(char *mac_addr, int mac_len, char *reg, int reg_len); +extern void libveth_close(net_driver_t *driver); +extern int libveth_read(char *buf, int len, net_driver_t *driver); +extern int libveth_write(char *buf, int len, net_driver_t *driver); + +#endif diff --git a/qemu/roms/SLOF/lib/libveth/veth.in b/qemu/roms/SLOF/lib/libveth/veth.in new file mode 100644 index 000000000..dc684fe9c --- /dev/null +++ b/qemu/roms/SLOF/lib/libveth/veth.in @@ -0,0 +1,20 @@ +/****************************************************************************** + * Copyright (c) 2013 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * libveth bindings for Forth - definitions + */ + +cod(LIBVETH-OPEN) +cod(LIBVETH-CLOSE) +cod(LIBVETH-READ) +cod(LIBVETH-WRITE) diff --git a/qemu/roms/SLOF/lib/libvirtio/Makefile b/qemu/roms/SLOF/lib/libvirtio/Makefile new file mode 100644 index 000000000..bd6a1fae4 --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/Makefile @@ -0,0 +1,55 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames +CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \ + -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH) +LDFLAGS = -nostdlib + +TARGET = ../libvirtio.a + + +all: $(TARGET) + +SRCS = virtio.c virtio-blk.c p9.c virtio-9p.c virtio-scsi.c virtio-net.c + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + +%.o: %.S + $(CC) $(CPPFLAGS) $(ASFLAGS) -c $< -o $@ + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep + +# Include dependency file if available: +-include Makefile.dep + diff --git a/qemu/roms/SLOF/lib/libvirtio/p9.c b/qemu/roms/SLOF/lib/libvirtio/p9.c new file mode 100644 index 000000000..a55662994 --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/p9.c @@ -0,0 +1,575 @@ +/****************************************************************************** + * Copyright (c) 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <byteorder.h> +#include "p9.h" + + +/* Protocol stack marshaling. */ +uint8_t *sp; + +#define GET_08(s,i) (s)[(i)] +#define GET_16(s,i) le16_to_cpu(*(uint16_t*)(&(s)[(i)])) +#define GET_32(s,i) le32_to_cpu(*(uint32_t*)(&(s)[(i)])) +#define GET_64(s,i) le64_to_cpu(*(uint64_t*)(&(s)[(i)])) + +#define SET_08(s,i,v) (s)[(i)] = (v) +#define SET_16(s,i,v) *(uint16_t*)(&(s)[(i)]) = cpu_to_le16(v) +#define SET_32(s,i,v) *(uint32_t*)(&(s)[(i)]) = cpu_to_le32(v) +#define SET_64(s,i,v) *(uint64_t*)(&(s)[(i)]) = cpu_to_le64(v) + +#define PUT_08(v) sp[0] = (v);sp+=1 +#define PUT_16(v) *(uint16_t*)(&sp[0]) = cpu_to_le16(v);sp+=2 +#define PUT_32(v) *(uint32_t*)(&sp[0]) = cpu_to_le32(v);sp+=4 +#define PUT_64(v) *(uint64_t*)(&sp[0]) = cpu_to_le64(v);sp+=8 + +#define PUT_HD(m,t) PUT_32(0);PUT_08(m);PUT_16(t) +#define PUT_SN(v,n) PUT_16(n);memcpy(sp,(v),(n));sp+=n +#define PUT_ST(v) PUT_16(strlen(v));memcpy(sp,(v),strlen(v));\ + sp+=strlen(v) + +#define GET_SIZE (sp - tx) + + +/* General defines. */ +#define MIN(a,b) ((a)>(b)?(b):(a)) + +#define NOTAG ((uint16_t)~0) +#define NOFID ((uint32_t)~0) +#define TAG 1 +#define BUF_SIZE (8*1024) + +#define VERSION "9P2000.u" +#define UNKNOWN_VER "unknown" + +#define MSG_SIZE 0 +#define MSG_ID 4 +#define MSG_ERR 0x6b +#define MSG_ERR_STR 9 +#define MSG_ERR_STR_LEN 7 +#define MSG_TAG 5 +#define MSG_VER_MSIZE 7 +#define MSG_VER_STR_LEN 11 +#define MSG_VER_STR 13 +#define MSG_WALK_TX_ELMT 15 +#define MSG_WALK_RX_ELMT 7 +#define MSG_SIZE 0 +#define MSG_WALK_MAX_ELMT 16 +#define MSG_QID_SIZE 13 +#define MSG_WALK_RX_HDR_SIZE 9 +#define MSG_OPEN_IOUNIT 20 +#define MSG_OPEN_MODE_MASK 0x5f +#define MSG_READ_COUNT 7 +#define MSG_READ_DATA 11 +#define MSG_STAT_LEN 42 +#define MSG_STAT_TYPE 17 + +#define T_VERSION 100 +#define R_VERSION (T_VERSION + 1) +#define T_ATTACH 104 +#define R_ATTACH (T_ATTACH + 1) +#define T_ERROR 106 +#define R_ERROR (T_ERROR + 1) +#define T_WALK 110 +#define R_WALK (T_WALK + 1) +#define T_OPEN 112 +#define R_OPEN (T_OPEN + 1) +#define T_READ 116 +#define R_READ (T_READ + 1) +#define T_CLUNK 120 +#define R_CLUNK (T_CLUNK + 1) +#define T_STAT 124 +#define R_STAT (T_STAT + 1) + +static p9_transact_t transact; +static void *transact_opaque; +static uint8_t *tx; +static uint8_t *rx; + + +/** + * p9_reg_transport + * + * Registers a transport function for use by the P9 protocol. The transport + * connects the P9 Client (this library) to a server instance. + * + * @param transact_func[in] Function pointer to type p9_transact_t. + * @param tx_buffer[in] TX buffer, must be 8k in size. + * @param rx_buffer[in] RX buffer, must be 8k in size. + */ +void p9_reg_transport(p9_transact_t transact_func, void *opaque, + uint8_t *tx_buffer, uint8_t *rx_buffer) +{ + transact = transact_func; + transact_opaque = opaque; + tx = tx_buffer; + rx = rx_buffer; +} + +/** + * reset_buffers + * + * Reset the RX and TX buffers to BUF_SIZE (8k) and reset the Stack Pointer + * for the TX buffer, which is referenced by the PUT_* macro's. + */ +void reset_buffers(void) +{ + memset(tx, 0, BUF_SIZE); + memset(rx, 0, BUF_SIZE); + sp = tx; +} + +/** + * p9_transaction + * + * Perform a transaction (send/recv) over the registered transport. + * + * @param connection[in|out] Connection object. + * @return 0 = success, -ve = error. + */ +int p9_transaction(p9_connection_t *connection) +{ + int rc; + int tx_size = GET_SIZE; + int rx_size = connection->message_size; + + if (transact == NULL) { + return P9_NO_TRANSPORT; + } + if (tx == NULL || rx == NULL) { + return P9_NO_BUFFER; + } + if (connection->message_size > BUF_SIZE) { + return P9_MSG_SIZE_TOO_BIG; + } + if (tx_size > connection->message_size) { + return P9_MSG_TOO_LONG; + } + + SET_32(tx, MSG_SIZE, tx_size); + rc = transact(transact_opaque, tx, tx_size, rx, &rx_size); + + if (rc != 0) { + return P9_TRANSPORT_ERROR; + } + if (GET_16(tx, MSG_TAG) != GET_16(rx, MSG_TAG)) { + return P9_UNEXPECTED_TAG; + } + if (GET_08(rx, MSG_ID) == MSG_ERR) { + char error_string[200]; + + memset(error_string, 0, 200); + strncpy(error_string, (char *)&rx[MSG_ERR_STR], + MIN(200 - 1, GET_16(rx, MSG_ERR_STR_LEN))); +#ifndef TEST + printf("\nError: %s\n", error_string); +#endif + return P9_R_ERROR; + } + if ((GET_08(tx, MSG_ID) + 1) != GET_08(rx, MSG_ID)) { + return P9_UNEXPECTED_MSG; + } + + return 0; +} + +/** + * p9_version + * + * Called to start a session. Negotiates the maximum message size for the + * P9 protocol. + * + * @param connection[in|out] Connection object, contains message_size. + * @return 0 = success, -ve = error. + * + * @remark + * size[4] Tversion tag[2] msize[4] version[s] + * size[4] Rversion tag[2] msize[4] version[s] + */ +int p9_version(p9_connection_t *connection) +{ + int rc; + char *ver_str; + int ver_len; + + reset_buffers(); + + /* Build message. */ + PUT_HD(T_VERSION, NOTAG); + PUT_32(connection->message_size); + PUT_ST(VERSION); + + /* Send message. */ + rc = p9_transaction(connection); + if (rc != 0) { + return rc; + } + + /* Handle response. */ + connection->message_size = MIN(connection->message_size, + GET_32(rx, MSG_VER_MSIZE)); + + ver_str = (char *)&rx[MSG_VER_STR]; + ver_len = GET_16(rx, MSG_VER_STR_LEN); + if (strncmp(UNKNOWN_VER, ver_str, ver_len) == 0) { + return P9_UNKNOWN_VERSION; + } + + + return 0; +} + +/** + * p9_attach + * + * Called to open a connection for a user to a file tree on the server. There + * is no authorisation undertaken (NOFID). + * + * @param connection[in|out] Connection object, contains uname and aname as + * well as the connection fid and returned qid. + * @return 0 = success, -ve = error. + * + * @remark + * size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] + * size[4] Rattach tag[2] qid[13] + */ +int p9_attach(p9_connection_t *connection) +{ + int rc; + int length = 19 + strlen(connection->uname) + strlen(connection->aname); + + if (length > connection->message_size) { + return P9_MSG_TOO_LONG; + } + + reset_buffers(); + + /* Build message. */ + PUT_HD(T_ATTACH, TAG); + PUT_32(connection->fid); + PUT_32(NOFID); + PUT_ST(connection->uname); + PUT_ST(connection->aname); + PUT_32(~0); /* ??? */ + + /* Send message. */ + rc = p9_transaction(connection); + if (rc != 0) { + return rc; + } + + + return 0; +} + +/** + * p9_clunk + * + * Called when closing a file or connection (or after failed opens). Tells the + * server that the supplied fid is no longer needed by this client. + * + * @param connection[in|out] Connection object. + * @param fid[in] Fid to be clunked (released) on the server. + * @return 0 = success, -ve = error. + * + * @remark + * size[4] Tclunk tag[2] fid[4] + * size[4] Rclunk tag[2] + */ +int p9_clunk(p9_connection_t *connection, uint32_t fid) +{ + int rc; + + reset_buffers(); + + /* Build message. */ + PUT_HD(T_CLUNK, TAG); + PUT_32(fid); + + /* Send message. */ + rc = p9_transaction(connection); + if (rc != 0) { + return rc; + } + + + return 0; +} + +/** + * p9_walk + * + * Walk the provided path to a file (or directory) starting at the directory + * indicated by fid and assigning new_fid to the last successfully walked + * element. If not all elements of the path can be walked then the pos + * pointer is set to the part of the path following the last successful + * walked element. The function can be called again to walk the remainder + * of the path (or produce an error). + * + * @param connection[in] Connection object. + * @param fid[in] Fid to start walk from, must be directory or root (from + * call to p9_attach). + * @param new_fid[in] Fid to be used for the last walked element. + * @param pos[in|out] Position in path that remains to be walked. If the + * path was completely walked without error this will point to the NULL + * at the end of path. + * @return 1 = partial walk, 0 = success, -ve = error. + * + * @remark + * size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) + * size[4] Rwalk tag[2] nwqid[2] nwqid*(qid[13]) + */ +int p9_walk(p9_connection_t *connection, uint32_t fid, uint32_t new_fid, + uint8_t **pos) +{ + int rc; + const char *path = (const char *)*pos; + uint8_t *s_tok; + uint8_t *e_tok; + int element_count = 0; + + if (path == NULL) { + *pos = NULL; + return P9_NULL_PATH; + } + + reset_buffers(); + + /* Build message. */ + PUT_HD(T_WALK, TAG); /* Length to 0, set later. */ + PUT_32(fid); + PUT_32(new_fid); + PUT_16(0); /* Element count to 0, set later. */ + + /* Get elements from path, and append to message. */ + s_tok = (uint8_t *)path; + e_tok = s_tok; + + while (*s_tok != 0) { + while (*s_tok == '/') { + s_tok++; + } + e_tok = s_tok; + while ((*e_tok != '/') && (*e_tok != 0)) { + e_tok++; + } + + /* Check the element is OK. */ + if (strncmp(".", (const char *)s_tok, (e_tok - s_tok)) == 0) { + /* Don't send ".", continue to next. */ + s_tok = e_tok; + continue; + } + int tx_size = (e_tok - s_tok + 2 + GET_SIZE); + int rx_size = ((element_count + 1) * MSG_QID_SIZE + + MSG_WALK_RX_HDR_SIZE); + if ((tx_size > connection->message_size) + || (rx_size > connection->message_size)) { + /* + * Element makes TX msg too long OR expected RX msg + * too long. Move pos to previous element and do + * partial walk. + */ + e_tok = s_tok; + if (*(e_tok - 1) == '/') { + e_tok--; + } + break; + } + + /* Add the element to the message. */ + PUT_SN(s_tok, e_tok - s_tok); + element_count++; + + /* Server supports no more than 16 elements, partial walk. */ + if (element_count == MSG_WALK_MAX_ELMT) { + break; + } + + /* Ready to find the next element. */ + s_tok = e_tok; + } + + if ((element_count == 0) && (strlen(path) > 0)) { + return P9_PATH_ELEMENT_TOO_LONG; + } + + *pos = e_tok; + + /* Update counts and then send message. */ + SET_16(tx, MSG_WALK_TX_ELMT, element_count); + rc = p9_transaction(connection); + if (rc != 0) { + return rc; + } + + /* Check for special return conditions. */ + if (element_count != GET_16(rx, MSG_WALK_RX_ELMT)) { + /* Find the last element successfully walked */ + s_tok = (uint8_t *)path; + e_tok = s_tok; + element_count = GET_16(rx, MSG_WALK_RX_ELMT); + + while (element_count--) { + while (*s_tok == '/') { + s_tok++; + } + + e_tok = s_tok; + + while ((*e_tok != '/') && (*e_tok != 0)) { + e_tok++; + } + + s_tok = e_tok; + } + + *pos = e_tok; + } + if (**pos != 0) { + rc = P9_PARTIAL_WALK; + } + + + return rc; +} + +/** + * p9_open + * + * Opens the file represented by fid with associated mode bit mask. The iounit + * size returned from the server is written to the connection object. + * + * @param file[in|out] File object, contains fid for file. + * @param mode[in] Mode to open with. Bit's 0=R, 1=W, 2=RW, 3=EX, 4=Trunc + * and 6=Delete on Close. + * @return 0 = success, -ve = error. + * + * @remark + * size[4] Topen tag[2] fid[4] mode[1] + * size[4] Ropen tag[2] qid[13] iounit[4] + */ +int p9_open(p9_file_t *file, uint8_t mode) +{ + int rc; + p9_connection_t *connection = file->connection; + + reset_buffers(); + file->iounit = 0; + + /* Build message. */ + PUT_HD(T_OPEN, TAG); + PUT_32(file->fid); + PUT_08(mode & MSG_OPEN_MODE_MASK); + + /* Send message. */ + rc = p9_transaction(connection); + if (rc != 0) { + return rc; + } + + /* Handle response. */ + file->iounit = GET_32(rx, MSG_OPEN_IOUNIT); + + + return 0; +} + +/** + * p9_read + * + * Reads the file in to buffer. + * + * @param file[in] File object, contains fid for file. + * @param buffer[out] Buffer for data. + * @param count[in] Number of bytes to read (less bytes than requested + * may be read). + * @param offset[in] Offset in file to read from. + * @return Bytes read, -ve = error. + * + * @remark + * size[4] Tread tag[2] fid[4] offset[8] count[4] + * size[4] Rread tag[2] count[4] data[count] + */ +int p9_read(p9_file_t *file, uint8_t *buffer, + uint32_t count, uint64_t offset) +{ + int rc; + p9_connection_t *connection = file->connection; + uint32_t got; + + reset_buffers(); + count = MIN((connection->message_size - MSG_READ_DATA), count); + + /* Build message. */ + PUT_HD(T_READ, TAG); + PUT_32(file->fid); + PUT_64(offset); + PUT_32(count); + + /* Send message. */ + rc = p9_transaction(connection); + if (rc != 0) { + return rc; + } + got = GET_32(rx, MSG_READ_COUNT); + if (got > count) { + return P9_READ_UNEXPECTED_DATA; + } + + /* Handle response. */ + memcpy(buffer, &rx[MSG_READ_DATA], got); + + return got; +} + +/** + * p9_stat + * + * Stat's the fid and writes the type and length to the file object. + * + * @param file[in|out] File object, contains fid for file. + * @return 0 = success, -ve = error. + * + * @remark + * size[4] Tstat tag[2] fid[4] + * size[4] Rstat tag[2] size[2] stat[n] + */ +int p9_stat(p9_file_t *file) +{ + int rc; + p9_connection_t *connection = file->connection; + + reset_buffers(); + file->length = 0; + file->type = 0; + + /* Build message. */ + PUT_HD(T_STAT, TAG); + PUT_32(file->fid); + + /* Send message. */ + rc = p9_transaction(connection); + if (rc != 0) { + return rc; + } + + /* Handle response. */ + file->length = GET_64(rx, MSG_STAT_LEN); + file->type = GET_08(rx, MSG_STAT_TYPE); + + + return 0; +} diff --git a/qemu/roms/SLOF/lib/libvirtio/p9.h b/qemu/roms/SLOF/lib/libvirtio/p9.h new file mode 100644 index 000000000..7df9ef441 --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/p9.h @@ -0,0 +1,68 @@ +/****************************************************************************** + * Copyright (c) 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef P9_H +#define P9_H + +#include <stdint.h> + + +#define P9_ERROR -1 +#define P9_UNKNOWN_VERSION -2 +#define P9_R_ERROR -3 +#define P9_MSG_TOO_LONG -4 +#define P9_UNEXPECTED_MSG -5 +#define P9_UNEXPECTED_TAG -6 +#define P9_TRANSPORT_ERROR -7 +#define P9_NO_TRANSPORT -8 +#define P9_NULL_PATH -9 +#define P9_PATH_ELEMENT_TOO_LONG -10 +#define P9_READ_UNEXPECTED_DATA -11 +#define P9_NO_BUFFER -12 +#define P9_MSG_SIZE_TOO_BIG -13 + +#define P9_PARTIAL_WALK 1 + +typedef int (*p9_transact_t)(void *opaque, uint8_t *tx, int tx_size, + uint8_t *rx, int *rx_size); + +typedef struct { + uint32_t message_size; + char *uname; /* User name. */ + char *aname; /* Tree/mount name/path. */ + uint32_t fid; /* Represents mount point. */ +} p9_connection_t; + +typedef struct { + uint32_t fid; /* Identifies the file to P9 server. */ + uint32_t iounit; /* Maximum read size in bytes. */ + uint8_t type; /* Type of file. */ + uint64_t length; /* Length of file. */ + p9_connection_t *connection; +} p9_file_t; + + +void reset_buffers(void); +void p9_reg_transport(p9_transact_t transact_func, void *opaque, + uint8_t *tx_buffer, uint8_t *rx_buffer); +int p9_transaction(p9_connection_t *connection); +int p9_version(p9_connection_t *connection); +int p9_attach(p9_connection_t *connection); +int p9_clunk(p9_connection_t *connection, uint32_t fid); +int p9_walk(p9_connection_t *connection, uint32_t fid, uint32_t new_fid, + uint8_t **pos); +int p9_open(p9_file_t *file, uint8_t mode); +int p9_read(p9_file_t *file, uint8_t *buffer, + uint32_t count, uint64_t offset); +int p9_stat(p9_file_t *file); + +#endif diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-9p.c b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.c new file mode 100644 index 000000000..5a5fd01da --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.c @@ -0,0 +1,336 @@ +/****************************************************************************** + * Copyright (c) 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <byteorder.h> +#include <cpu.h> + +#include "virtio-9p.h" +#include "p9.h" + + +/** + * Notes for 9P Server config: + * + * make distclean; cm make qemu + * sudo cp boot_rom.bin /opt/qemu/share/qemu/slof.bin + * /opt/qemu/bin/qemu-system-ppc64 -M pseries -m 512 -boot d -nographic -fsdev + * local,id=trule,path=/home/trule/virtfs,security_model=none -device + * virtio-9p-spapr,fsdev=trule,mount_tag=trule + * load virtfs:\some\file + */ + +/* We support only one instance due to the (ab)use of globals. We + * use the buffer size as an open marker as well. + */ +static int __buf_size; + + +#define ROOT_FID 1 +#define FILE_FID 2 +#define TAG_SIZE 128 +#define MIN(a,b) ((a)>(b)?(b):(a)) + + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +#ifdef DEBUG +static void dprint_buffer(const char *name, uint8_t *buffer, int length) +{ + int i; + + printf("*** %s ***", name); + + for (i = 0; i < length; i++) { + if (i % 16 == 0) { + printf("\n %04x:", i); + } + + printf(" %02x", buffer[i]); + } + + printf("\n"); +} +#else +#define dprint_buffer(n, b, l) +#endif + +/** + * virtio_9p_transact + * + * Perform a 9P transaction over the VIRTIO queue interface. This function is + * registered with the p9.c library via p9_reg_transport() to provide + * connectivity to the 9P server. + * + * @param tx[in] Data to send, mapped to first queue item. + * @param tx_size[in] Size of data to send. + * @param rx[out] Data to receive, mappend to second queue item. + * @param rx_size[out] Size of data received. + * @return 0 = success, -ve = error. + */ +static int virtio_9p_transact(void *opaque, uint8_t *tx, int tx_size, uint8_t *rx, + int *rx_size) +{ + struct virtio_device *dev = opaque; + struct vring_desc *desc; + int id, i; + uint32_t vq_size; + struct vring_desc *vq_desc; + struct vring_avail *vq_avail; + struct vring_used *vq_used; + volatile uint16_t *current_used_idx; + uint16_t last_used_idx; + + + /* Virt IO queues. */ + vq_size = virtio_get_qsize(dev, 0); + vq_desc = virtio_get_vring_desc(dev, 0); + vq_avail = virtio_get_vring_avail(dev, 0); + vq_used = virtio_get_vring_used(dev, 0); + + last_used_idx = vq_used->idx; + current_used_idx = &vq_used->idx; + + /* Determine descriptor index */ + id = (vq_avail->idx * 3) % vq_size; + + /* TX in first queue item. */ + dprint_buffer("TX", tx, tx_size); + + desc = &vq_desc[id]; + desc->addr = (uint64_t)tx; + desc->len = tx_size; + desc->flags = VRING_DESC_F_NEXT; + desc->next = (id + 1) % vq_size; + + /* RX in the second queue item. */ + desc = &vq_desc[(id + 1) % vq_size]; + desc->addr = (uint64_t)rx; + desc->len = *rx_size; + desc->flags = VRING_DESC_F_WRITE; + desc->next = 0; + + /* Tell HV that the queue is ready */ + vq_avail->ring[vq_avail->idx % vq_size] = id; + mb(); + vq_avail->idx += 1; + virtio_queue_notify(dev, 0); + + /* Receive the response. */ + i = 10000000; + while (*current_used_idx == last_used_idx && i-- > 0) { + // do something better + mb(); + } + if (i == 0) { + return -1; + } + + *rx_size = MIN(*rx_size, le32_to_cpu(*(uint32_t*)(&rx[0]))); + dprint_buffer("RX", rx, *rx_size); + + return 0; +} + +/** + * virtio_9p_init + * + * Establish the VIRTIO connection for use with the 9P server. Setup queues + * and negotiate capabilities. Setup the 9P (Client) library. + * + * @param reg[in] Pointer to device tree node for VIRTIO/9P interface. + * @param tx_buf[in] TX buffer for use by 9P Client lib - 8K in size. + * @param rx_buf[in] TX buffer for use by 9P Client lib - 8K in size. + * @param buf_size Somewhat redundant, buffer size expected to be 8k. + * @return 0 = success, -ve = error. + */ +int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf, + int buf_size) +{ + struct vring_avail *vq_avail; + + /* Check for double open */ + if (__buf_size) + return -1; + __buf_size = buf_size; + + dprintf("%s : device at %p\n", __func__, dev->base); + dprintf("%s : type is %04x\n", __func__, dev->type); + + /* Reset device */ + // XXX That will clear the virtq base. We need to move + // initializing it to here anyway + // + // virtio_reset_device(dev); + + /* Acknowledge device. */ + virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE); + + /* Tell HV that we know how to drive the device. */ + virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER); + + /* Device specific setup - we do not support special features */ + virtio_set_guest_features(dev, 0); + + vq_avail = virtio_get_vring_avail(dev, 0); + vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT; + vq_avail->idx = 0; + + /* Tell HV that setup succeeded */ + virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER + |VIRTIO_STAT_DRIVER_OK); + + /* Setup 9P library. */ + p9_reg_transport(virtio_9p_transact, dev,(uint8_t *)tx_buf, + (uint8_t *)rx_buf); + + dprintf("%s : complete\n", __func__); + return 0; +} + +/** + * virtio_9p_shutdown + */ +void virtio_9p_shutdown(struct virtio_device *dev) +{ + /* Quiesce device */ + virtio_set_status(dev, VIRTIO_STAT_FAILED); + + /* Reset device */ + virtio_reset_device(dev); + + __buf_size = 0; +} + +/** + * virtio_9p_load + * + * Read a file from the 9P Server on the VIRTIO interface. + * + * @param file_name[in] File to read, use Linux style paths. + * @param buffer[out] Where to read the file to. + * @return +ve = amount of data read, -ve = error. + */ +int virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer) +{ + int rc; + uint16_t tag_len; + char tag_name[TAG_SIZE]; + uint64_t offset = 0; + uint8_t *pos = (uint8_t *)file_name; + int start_fid = ROOT_FID; + p9_connection_t connection = { + .message_size = __buf_size, + .fid = ROOT_FID, + .uname = "slof" + }; + p9_file_t file = { + .connection = &connection, + .fid = FILE_FID, + }; + + + /* Get the share name from 9P config space. */ + tag_len = virtio_get_config(dev, 0, sizeof(tag_len)); + if (tag_len >= TAG_SIZE) + tag_len = TAG_SIZE - 1; + __virtio_read_config(dev, tag_name, 2, tag_len); + connection.aname = tag_name; + + /* Connect to the 9P server. */ + dprintf("%s : connecting, tag = %s, user = %s, msgsize = %d\n", + __func__, connection.aname, connection.uname, + connection.message_size); + rc = p9_version(&connection); + if (rc != 0) { + printf("Version check failed, rc = %d\n", rc); + goto cleanup_connection; + } + rc = p9_attach(&connection); + if (rc != 0) { + printf("Attach failed, rc = %d\n", rc); + goto cleanup_connection; + } + dprintf("%s : connected, msgsize = %d\n", __func__, + connection.message_size); + + /* Walk to the file. */ + do { + dprintf("%s : walk path %s\n", __func__, pos); + rc = p9_walk(&connection, start_fid, FILE_FID, &pos); + + if (rc < 0) { /* Some error. */ + printf("Walk failed, rc = %d\n", rc); + goto cleanup_connection; + } + + /* + * If partial walk (*pos != 0) then continue the walk from + * mid point with start_fid updated to current position + * FILE_FID. FILE_FID will then be reused for the result of + * the next call to walk. + */ + start_fid = FILE_FID; + } while (*pos != 0); + + /* Open the file. */ + dprintf("%s : stat and open\n", __func__); + rc = p9_stat(&file); + if (rc != 0) { + printf("Stat failed, rc = %d\n", rc); + goto cleanup_file; + } + rc = p9_open(&file, 0x00); /* TODO find include for "read mode" */ + if (rc != 0) { + printf("Open failed, rc = %d\n", rc); + goto cleanup_file; + } + dprintf("%s : file opened, size %lld\n", __func__, file.length); + + /* Read the file contents to buffer. */ + while (offset < file.length) { + dprintf("%s : read from offset %llu\n", __func__, offset); + rc = p9_read(&file, buffer + offset, + file.length - offset, offset); + dprintf("%s : read done, length was %d\n", __func__, rc); + if (rc < 0) { + printf("Read failed, rc = %d\n", rc); + goto cleanup_file; + } + if (rc == 0) { + break; + } + offset += rc; + rc = 0; + } + + /* Cleanup and disconnect. */ +cleanup_file: + dprintf("%s : clunking file\n", __func__); + p9_clunk(&connection, file.fid); + +cleanup_connection: + dprintf("%s : clunking connection\n", __func__); + p9_clunk(&connection, connection.fid); + + + dprintf("%s : complete, read %llu bytes\n", __func__, offset); + return rc == 0 ? offset : rc; +} diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-9p.h b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.h new file mode 100644 index 000000000..4bf47d078 --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-9p.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * Copyright (c) 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef VIRTIO_9P_H_ +#define VIRTIO_9P_H_ + +#include <stdint.h> + +#include "virtio.h" + +#if 0 +typedef struct { + uint16_t tag_lenth; + char tag[0]; +} virtio_9p_config_t; +#endif +int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf, + int buf_size); +void virtio_9p_shutdown(struct virtio_device *dev); +int virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer); + + +#endif /* VIRTIO_9P_H_ */ diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-blk.c b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.c new file mode 100644 index 000000000..826f2ea0e --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.c @@ -0,0 +1,185 @@ +/****************************************************************************** + * Copyright (c) 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <cpu.h> +#include <helpers.h> +#include "virtio.h" +#include "virtio-blk.h" + +#define DEFAULT_SECTOR_SIZE 512 + +/** + * Initialize virtio-block device. + * @param dev pointer to virtio device information + */ +int +virtioblk_init(struct virtio_device *dev) +{ + struct vring_avail *vq_avail; + int blk_size = DEFAULT_SECTOR_SIZE; + int features; + + /* Reset device */ + // XXX That will clear the virtq base. We need to move + // initializing it to here anyway + // + // virtio_reset_device(dev); + + /* Acknowledge device. */ + virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE); + + /* Tell HV that we know how to drive the device. */ + virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER); + + /* Device specific setup - we support F_BLK_SIZE */ + virtio_set_guest_features(dev, VIRTIO_BLK_F_BLK_SIZE); + + vq_avail = virtio_get_vring_avail(dev, 0); + vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT; + vq_avail->idx = 0; + + /* Tell HV that setup succeeded */ + virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER + |VIRTIO_STAT_DRIVER_OK); + + virtio_get_host_features(dev, &features); + if (features & VIRTIO_BLK_F_BLK_SIZE) { + blk_size = virtio_get_config(dev, + offset_of(struct virtio_blk_cfg, blk_size), + sizeof(blk_size)); + } + + return blk_size; +} + + +/** + * Shutdown the virtio-block device. + * @param dev pointer to virtio device information + */ +void +virtioblk_shutdown(struct virtio_device *dev) +{ + /* Quiesce device */ + virtio_set_status(dev, VIRTIO_STAT_FAILED); + + /* Reset device */ + virtio_reset_device(dev); +} + + +/** + * Read blocks + * @param reg pointer to "reg" property + * @param buf pointer to destination buffer + * @param blocknum block number of the first block that should be read + * @param cnt amount of blocks that should be read + * @return number of blocks that have been read successfully + */ +int +virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt) +{ + struct vring_desc *desc; + int id; + static struct virtio_blk_req blkhdr; + //struct virtio_blk_config *blkconf; + uint64_t capacity; + uint32_t vq_size, time; + struct vring_desc *vq_desc; /* Descriptor vring */ + struct vring_avail *vq_avail; /* "Available" vring */ + struct vring_used *vq_used; /* "Used" vring */ + volatile uint8_t status = -1; + volatile uint16_t *current_used_idx; + uint16_t last_used_idx; + int blk_size = DEFAULT_SECTOR_SIZE; + + //printf("virtioblk_read: dev=%p buf=%p blocknum=%li count=%li\n", + // dev, buf, blocknum, cnt); + + /* Check whether request is within disk capacity */ + capacity = virtio_get_config(dev, + offset_of(struct virtio_blk_cfg, capacity), + sizeof(capacity)); + if (blocknum + cnt - 1 > capacity) { + puts("virtioblk_read: Access beyond end of device!"); + return 0; + } + + blk_size = virtio_get_config(dev, + offset_of(struct virtio_blk_cfg, blk_size), + sizeof(blk_size)); + if (blk_size % DEFAULT_SECTOR_SIZE) { + fprintf(stderr, "virtio-blk: Unaligned sector read %d\n", blk_size); + return 0; + } + + vq_size = virtio_get_qsize(dev, 0); + vq_desc = virtio_get_vring_desc(dev, 0); + vq_avail = virtio_get_vring_avail(dev, 0); + vq_used = virtio_get_vring_used(dev, 0); + + last_used_idx = vq_used->idx; + current_used_idx = &vq_used->idx; + + /* Set up header */ + blkhdr.type = VIRTIO_BLK_T_IN | VIRTIO_BLK_T_BARRIER; + blkhdr.ioprio = 1; + blkhdr.sector = blocknum * blk_size / DEFAULT_SECTOR_SIZE; + + /* Determine descriptor index */ + id = (vq_avail->idx * 3) % vq_size; + + /* Set up virtqueue descriptor for header */ + desc = &vq_desc[id]; + desc->addr = (uint64_t)&blkhdr; + desc->len = sizeof(struct virtio_blk_req); + desc->flags = VRING_DESC_F_NEXT; + desc->next = (id + 1) % vq_size; + + /* Set up virtqueue descriptor for data */ + desc = &vq_desc[(id + 1) % vq_size]; + desc->addr = (uint64_t)buf; + desc->len = cnt * blk_size; + desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE; + desc->next = (id + 2) % vq_size; + + /* Set up virtqueue descriptor for status */ + desc = &vq_desc[(id + 2) % vq_size]; + desc->addr = (uint64_t)&status; + desc->len = 1; + desc->flags = VRING_DESC_F_WRITE; + desc->next = 0; + + vq_avail->ring[vq_avail->idx % vq_size] = id; + mb(); + vq_avail->idx += 1; + + /* Tell HV that the queue is ready */ + virtio_queue_notify(dev, 0); + + /* Wait for host to consume the descriptor */ + time = SLOF_GetTimer() + VIRTIO_TIMEOUT; + while (*current_used_idx == last_used_idx) { + // do something better + mb(); + if (time < SLOF_GetTimer()) + break; + } + + if (status == 0) + return cnt; + + printf("virtioblk_read failed! status = %i\n", status); + + return 0; +} diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-blk.h b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.h new file mode 100644 index 000000000..ac8bf2896 --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-blk.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * Copyright (c) 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * Virtio block device definitions. + * See Virtio Spec, Appendix D, for details + */ + +#ifndef _VIRTIO_BLK_H +#define _VIRTIO_BLK_H + +#include <stdint.h> + + +struct virtio_blk_cfg { + uint64_t capacity; + uint32_t size_max; + uint32_t seg_max; + struct virtio_blk_geometry { + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; + } geometry; + uint32_t blk_size; + uint32_t sectors_max; +} __attribute__ ((packed)) ; + +/* Block request */ +struct virtio_blk_req { + uint32_t type ; + uint32_t ioprio ; + uint64_t sector ; +}; + +/* Block request types */ +#define VIRTIO_BLK_T_IN 0 +#define VIRTIO_BLK_T_OUT 1 +#define VIRTIO_BLK_T_SCSI_CMD 2 +#define VIRTIO_BLK_T_SCSI_CMD_OUT 3 +#define VIRTIO_BLK_T_FLUSH 4 +#define VIRTIO_BLK_T_FLUSH_OUT 5 +#define VIRTIO_BLK_T_BARRIER 0x80000000 + +/* VIRTIO_BLK Feature bits */ +#define VIRTIO_BLK_F_BLK_SIZE (1 << 6) + +extern int virtioblk_init(struct virtio_device *dev); +extern void virtioblk_shutdown(struct virtio_device *dev); +extern int virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt); + +#endif /* _VIRTIO_BLK_H */ diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-net.c b/qemu/roms/SLOF/lib/libvirtio/virtio-net.c new file mode 100644 index 000000000..99c19d952 --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-net.c @@ -0,0 +1,369 @@ +/****************************************************************************** + * Copyright (c) 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * This is the implementation for the Virtio network device driver. Details + * about the virtio-net interface can be found in Rusty Russel's "Virtio PCI + * Card Specification v0.8.10", appendix C, which can be found here: + * + * http://ozlabs.org/~rusty/virtio-spec/virtio-spec.pdf + */ + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <helpers.h> +#include <cache.h> +#include <byteorder.h> +#include "virtio.h" +#include "virtio-net.h" + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +# define dprintf(fmt...) do { printf(fmt); } while(0) +#else +# define dprintf(fmt...) +#endif + +#define sync() asm volatile (" sync \n" ::: "memory") + +/* PCI virtio header offsets */ +#define VIRTIOHDR_DEVICE_FEATURES 0 +#define VIRTIOHDR_GUEST_FEATURES 4 +#define VIRTIOHDR_QUEUE_ADDRESS 8 +#define VIRTIOHDR_QUEUE_SIZE 12 +#define VIRTIOHDR_QUEUE_SELECT 14 +#define VIRTIOHDR_QUEUE_NOTIFY 16 +#define VIRTIOHDR_DEVICE_STATUS 18 +#define VIRTIOHDR_ISR_STATUS 19 +#define VIRTIOHDR_DEVICE_CONFIG 20 +#define VIRTIOHDR_MAC_ADDRESS 20 + +struct virtio_device virtiodev; +struct vqs vq[2]; /* Information about virtqueues */ + +/* See Virtio Spec, appendix C, "Device Operation" */ +struct virtio_net_hdr { + uint8_t flags; + uint8_t gso_type; + uint16_t hdr_len; + uint16_t gso_size; + uint16_t csum_start; + uint16_t csum_offset; + // uint16_t num_buffers; /* Only if VIRTIO_NET_F_MRG_RXBUF */ +}; + +static uint16_t last_rx_idx; /* Last index in RX "used" ring */ + +/** + * Module init for virtio via PCI. + * Checks whether we're reponsible for the given device and set up + * the virtqueue configuration. + */ +static int virtionet_init_pci(struct virtio_device *dev) +{ + int i; + + dprintf("virtionet: doing virtionet_init_pci!\n"); + + if (!dev) + return -1; + + virtiodev.base = dev->base; + virtiodev.type = dev->type; + + /* Reset device */ + virtio_reset_device(&virtiodev); + + /* The queue information can be retrieved via the virtio header that + * can be found in the I/O BAR. First queue is the receive queue, + * second the transmit queue, and the forth is the control queue for + * networking options. + * We are only interested in the receive and transmit queue here. */ + + for (i=VQ_RX; i<=VQ_TX; i++) { + /* Select ring (0=RX, 1=TX): */ + vq[i].id = i-VQ_RX; + ci_write_16(virtiodev.base+VIRTIOHDR_QUEUE_SELECT, + cpu_to_le16(vq[i].id)); + + vq[i].size = le16_to_cpu(ci_read_16(virtiodev.base+VIRTIOHDR_QUEUE_SIZE)); + vq[i].desc = SLOF_alloc_mem_aligned(virtio_vring_size(vq[i].size), 4096); + if (!vq[i].desc) { + printf("memory allocation failed!\n"); + return -1; + } + memset(vq[i].desc, 0, virtio_vring_size(vq[i].size)); + ci_write_32(virtiodev.base+VIRTIOHDR_QUEUE_ADDRESS, + cpu_to_le32((long)vq[i].desc / 4096)); + vq[i].avail = (void*)vq[i].desc + + vq[i].size * sizeof(struct vring_desc); + vq[i].used = (void*)VQ_ALIGN((long)vq[i].avail + + vq[i].size * sizeof(struct vring_avail)); + + dprintf("%i: vq.id = %llx\nvq.size =%x\n vq.avail =%p\nvq.used=%p\n", + i, vq[i].id, vq[i].size, vq[i].avail, vq[i].used); + } + + /* Acknowledge device. */ + virtio_set_status(&virtiodev, VIRTIO_STAT_ACKNOWLEDGE); + + return 0; +} + +/** + * Initialize the virtio-net device. + * See the Virtio Spec, chapter 2.2.1 and Appendix C "Device Initialization" + * for details. + */ +static int virtionet_init(net_driver_t *driver) +{ + int i; + + dprintf("virtionet_init(%02x:%02x:%02x:%02x:%02x:%02x)\n", + driver->mac_addr[0], driver->mac_addr[1], + driver->mac_addr[2], driver->mac_addr[3], + driver->mac_addr[4], driver->mac_addr[5]); + + if (driver->running != 0) + return 0; + + /* Tell HV that we know how to drive the device. */ + virtio_set_status(&virtiodev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER); + + /* Device specific setup - we do not support special features right now */ + virtio_set_guest_features(&virtiodev, 0); + + /* Allocate memory for one transmit an multiple receive buffers */ + vq[VQ_RX].buf_mem = SLOF_alloc_mem((BUFFER_ENTRY_SIZE+sizeof(struct virtio_net_hdr)) + * RX_QUEUE_SIZE); + if (!vq[VQ_RX].buf_mem) { + printf("virtionet: Failed to allocate buffers!\n"); + virtio_set_status(&virtiodev, VIRTIO_STAT_FAILED); + return -1; + } + + /* Prepare receive buffer queue */ + for (i = 0; i < RX_QUEUE_SIZE; i++) { + struct vring_desc *desc; + /* Descriptor for net_hdr: */ + desc = &vq[VQ_RX].desc[i*2]; + desc->addr = (uint64_t)vq[VQ_RX].buf_mem + + i * (BUFFER_ENTRY_SIZE+sizeof(struct virtio_net_hdr)); + desc->len = sizeof(struct virtio_net_hdr); + desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE; + desc->next = i*2+1; + + /* Descriptor for data: */ + desc = &vq[VQ_RX].desc[i*2+1]; + desc->addr = vq[VQ_RX].desc[i*2].addr + sizeof(struct virtio_net_hdr); + desc->len = BUFFER_ENTRY_SIZE; + desc->flags = VRING_DESC_F_WRITE; + desc->next = 0; + + vq[VQ_RX].avail->ring[i] = i*2; + } + sync(); + vq[VQ_RX].avail->flags = VRING_AVAIL_F_NO_INTERRUPT; + vq[VQ_RX].avail->idx = RX_QUEUE_SIZE; + + last_rx_idx = vq[VQ_RX].used->idx; + + vq[VQ_TX].avail->flags = VRING_AVAIL_F_NO_INTERRUPT; + vq[VQ_TX].avail->idx = 0; + + /* Tell HV that setup succeeded */ + virtio_set_status(&virtiodev, VIRTIO_STAT_ACKNOWLEDGE + |VIRTIO_STAT_DRIVER + |VIRTIO_STAT_DRIVER_OK); + + /* Tell HV that RX queues are ready */ + virtio_queue_notify(&virtiodev, VQ_RX); + + driver->running = 1; + + return 0; +} + + +/** + * Shutdown driver. + * We've got to make sure that the hosts stops all transfers since the buffers + * in our main memory will become invalid after this module has been terminated. + */ +static int virtionet_term(net_driver_t *driver) +{ + dprintf("virtionet_term()\n"); + + if (driver->running == 0) + return 0; + + /* Quiesce device */ + virtio_set_status(&virtiodev, VIRTIO_STAT_FAILED); + + /* Reset device */ + virtio_reset_device(&virtiodev); + + driver->running = 0; + + return 0; +} + + +/** + * Transmit a packet + */ +static int virtionet_xmit(char *buf, int len) +{ + struct vring_desc *desc; + int id; + static struct virtio_net_hdr nethdr; + + if (len > BUFFER_ENTRY_SIZE) { + printf("virtionet: Packet too big!\n"); + return 0; + } + + dprintf("\nvirtionet_xmit(packet at %p, %d bytes)\n", buf, len); + + memset(&nethdr, 0, sizeof(nethdr)); + + /* Determine descriptor index */ + id = (vq[VQ_TX].avail->idx * 2) % vq[VQ_TX].size; + + /* Set up virtqueue descriptor for header */ + desc = &vq[VQ_TX].desc[id]; + desc->addr = (uint64_t)&nethdr; + desc->len = sizeof(struct virtio_net_hdr); + desc->flags = VRING_DESC_F_NEXT; + desc->next = id + 1; + + /* Set up virtqueue descriptor for data */ + desc = &vq[VQ_TX].desc[id+1]; + desc->addr = (uint64_t)buf; + desc->len = len; + desc->flags = 0; + desc->next = 0; + + vq[VQ_TX].avail->ring[vq[VQ_TX].avail->idx % vq[VQ_TX].size] = id; + sync(); + vq[VQ_TX].avail->idx += 1; + sync(); + + /* Tell HV that TX queue is ready */ + virtio_queue_notify(&virtiodev, VQ_TX); + + return len; +} + + +/** + * Receive a packet + */ +static int virtionet_receive(char *buf, int maxlen) +{ + int len = 0; + int id; + + if (last_rx_idx == vq[VQ_RX].used->idx) { + /* Nothing received yet */ + return 0; + } + + id = (vq[VQ_RX].used->ring[last_rx_idx % vq[VQ_RX].size].id + 1) + % vq[VQ_RX].size; + len = vq[VQ_RX].used->ring[last_rx_idx % vq[VQ_RX].size].len + - sizeof(struct virtio_net_hdr); + + dprintf("virtionet_receive() last_rx_idx=%i, vq[VQ_RX].used->idx=%i," + " id=%i len=%i\n", last_rx_idx, vq[VQ_RX].used->idx, id, len); + + if (len > maxlen) { + printf("virtio-net: Receive buffer not big enough!\n"); + len = maxlen; + } + +#if 0 + /* Dump packet */ + printf("\n"); + int i; + for (i=0; i<64; i++) { + printf(" %02x", *(uint8_t*)(vq[VQ_RX].desc[id].addr+i)); + if ((i%16)==15) + printf("\n"); + } + prinfk("\n"); +#endif + + /* Copy data to destination buffer */ + memcpy(buf, (void*)vq[VQ_RX].desc[id].addr, len); + + /* Move indices to next entries */ + last_rx_idx = last_rx_idx + 1; + + vq[VQ_RX].avail->ring[vq[VQ_RX].avail->idx % vq[VQ_RX].size] = id - 1; + sync(); + vq[VQ_RX].avail->idx += 1; + + /* Tell HV that RX queue entry is ready */ + virtio_queue_notify(&virtiodev, VQ_RX); + + return len; +} + +net_driver_t *virtionet_open(char *mac_addr, int len, struct virtio_device *dev) +{ + net_driver_t *driver; + + driver = SLOF_alloc_mem(sizeof(*driver)); + if (!driver) { + printf("Unable to allocate virtio-net driver\n"); + return NULL; + } + + memcpy(driver->mac_addr, mac_addr, 6); + driver->running = 0; + + if (virtionet_init_pci(dev)) + goto FAIL; + + if (virtionet_init(driver)) + goto FAIL; + + return driver; + +FAIL: SLOF_free_mem(driver, sizeof(*driver)); + return NULL; +} + +void virtionet_close(net_driver_t *driver) +{ + if (driver) { + virtionet_term(driver); + SLOF_free_mem(driver, sizeof(*driver)); + } +} + +int virtionet_read(char *buf, int len) +{ + if (buf) + return virtionet_receive(buf, len); + return -1; +} + +int virtionet_write(char *buf, int len) +{ + if (buf) + return virtionet_xmit(buf, len); + return -1; +} diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-net.h b/qemu/roms/SLOF/lib/libvirtio/virtio-net.h new file mode 100644 index 000000000..bc7a189f7 --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-net.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Copyright (c) 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef VIRTIO_NET_H +#define VIRTIO_NET_H + +#include <netdriver.h> + +#define RX_QUEUE_SIZE 128 +#define BUFFER_ENTRY_SIZE 1514 + +enum { + VQ_RX = 0, /* Receive Queue */ + VQ_TX = 1, /* Transmit Queue */ +}; + +struct vqs { + uint64_t id; /* Queue ID */ + uint32_t size; + void *buf_mem; + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; +}; + +/* Device is identified by RX queue ID: */ +#define DEVICE_ID vq[0].id + +extern net_driver_t *virtionet_open(char *mac_addr, int len, struct virtio_device *dev); +extern void virtionet_close(net_driver_t *driver); +extern int virtionet_read(char *buf, int len); +extern int virtionet_write(char *buf, int len); + +#endif diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c b/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c new file mode 100644 index 000000000..48289289a --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.c @@ -0,0 +1,145 @@ +/****************************************************************************** + * Copyright (c) 2012 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <cpu.h> +#include <helpers.h> +#include "virtio.h" +#include "virtio-scsi.h" + +int virtioscsi_send(struct virtio_device *dev, + struct virtio_scsi_req_cmd *req, + struct virtio_scsi_resp_cmd *resp, + int is_read, void *buf, uint64_t buf_len) +{ + struct vring_desc *desc; + struct vring_desc *vq_desc; /* Descriptor vring */ + struct vring_avail *vq_avail; /* "Available" vring */ + struct vring_used *vq_used; /* "Used" vring */ + + volatile uint16_t *current_used_idx; + uint16_t last_used_idx; + int id; + uint32_t vq_size, time; + + int vq = VIRTIO_SCSI_REQUEST_VQ; + + vq_size = virtio_get_qsize(dev, vq); + vq_desc = virtio_get_vring_desc(dev, vq); + vq_avail = virtio_get_vring_avail(dev, vq); + vq_used = virtio_get_vring_used(dev, vq); + + last_used_idx = vq_used->idx; + current_used_idx = &vq_used->idx; + + /* Determine descriptor index */ + id = (vq_avail->idx * 3) % vq_size; + + desc = &vq_desc[id]; + desc->addr = (uint64_t)req; + desc->len = sizeof(*req); + desc->flags = VRING_DESC_F_NEXT; + desc->next = (id + 1) % vq_size; + + /* Set up virtqueue descriptor for data */ + desc = &vq_desc[(id + 1) % vq_size]; + desc->addr = (uint64_t)resp; + desc->len = sizeof(*resp); + desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE; + desc->next = (id + 2) % vq_size; + + if (buf && buf_len) { + /* Set up virtqueue descriptor for status */ + desc = &vq_desc[(id + 2) % vq_size]; + desc->addr = (uint64_t)buf; + desc->len = buf_len; + desc->flags = is_read ? VRING_DESC_F_WRITE : 0; + desc->next = 0; + } else + desc->flags &= ~VRING_DESC_F_NEXT; + + vq_avail->ring[vq_avail->idx % vq_size] = id; + mb(); + vq_avail->idx += 1; + + /* Tell HV that the vq is ready */ + virtio_queue_notify(dev, vq); + + /* Wait for host to consume the descriptor */ + time = SLOF_GetTimer() + VIRTIO_TIMEOUT; + while (*current_used_idx == last_used_idx) { + // do something better + mb(); + if (time < SLOF_GetTimer()) + break; + } + + return 0; +} + +/** + * Initialize virtio-block device. + * @param dev pointer to virtio device information + */ +int virtioscsi_init(struct virtio_device *dev) +{ + struct vring_avail *vq_avail; + unsigned int idx = 0; + int qsize = 0; + + /* Reset device */ + // XXX That will clear the virtq base. We need to move + // initializing it to here anyway + // + // virtio_reset_device(dev); + + /* Acknowledge device. */ + virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE); + + /* Tell HV that we know how to drive the device. */ + virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER); + + /* Device specific setup - we do not support special features right now */ + virtio_set_guest_features(dev, 0); + + while(1) { + qsize = virtio_get_qsize(dev, idx); + if (!qsize) + break; + virtio_vring_size(qsize); + + vq_avail = virtio_get_vring_avail(dev, 0); + vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT; + vq_avail->idx = 0; + idx++; + } + + /* Tell HV that setup succeeded */ + virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER + |VIRTIO_STAT_DRIVER_OK); + + return 0; +} + +/** + * Shutdown the virtio-block device. + * @param dev pointer to virtio device information + */ +void virtioscsi_shutdown(struct virtio_device *dev) +{ + /* Quiesce device */ + virtio_set_status(dev, VIRTIO_STAT_FAILED); + + /* Reset device */ + virtio_reset_device(dev); +} diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.h b/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.h new file mode 100644 index 000000000..451ba4d99 --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio-scsi.h @@ -0,0 +1,69 @@ +/****************************************************************************** + * Copyright (c) 2012 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* + * Virtio SCSI Host device definitions. + * See Virtio Spec, Appendix I, for details + */ + +#ifndef _VIRTIO_SCSI_H +#define _VIRTIO_SCSI_H + +#define VIRTIO_SCSI_CDB_SIZE 32 +#define VIRTIO_SCSI_SENSE_SIZE 96 + +#define VIRTIO_SCSI_CONTROL_VQ 0 +#define VIRTIO_SCSI_EVENT_VQ 1 +#define VIRTIO_SCSI_REQUEST_VQ 2 + +struct virtio_scsi_config +{ + uint32_t num_queues; + uint32_t seg_max; + uint32_t max_sectors; + uint32_t cmd_per_lun; + uint32_t event_info_size; + uint32_t sense_size; + uint32_t cdb_size; + uint16_t max_channel; + uint16_t max_target; + uint32_t max_lun; +} __attribute__((packed)); + +/* This is the first element of the "out" scatter-gather list. */ +struct virtio_scsi_req_cmd { + uint8_t lun[8]; + uint64_t tag; + uint8_t task_attr; + uint8_t prio; + uint8_t crn; + char cdb[VIRTIO_SCSI_CDB_SIZE]; +}; + +/* This is the first element of the "in" scatter-gather list. */ +struct virtio_scsi_resp_cmd { + uint32_t sense_len; + uint32_t residual; + uint16_t status_qualifier; + uint8_t status; + uint8_t response; + uint8_t sense[VIRTIO_SCSI_SENSE_SIZE]; +}; + +extern int virtioscsi_init(struct virtio_device *dev); +extern void virtioscsi_shutdown(struct virtio_device *dev); +extern int virtioscsi_send(struct virtio_device *dev, + struct virtio_scsi_req_cmd *req, + struct virtio_scsi_resp_cmd *resp, + int is_read, void *buf, uint64_t buf_len); + +#endif /* _VIRTIO_SCSI_H */ diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.c b/qemu/roms/SLOF/lib/libvirtio/virtio.c new file mode 100644 index 000000000..f9c00a67a --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.c @@ -0,0 +1,241 @@ +/****************************************************************************** + * Copyright (c) 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <cpu.h> +#include <cache.h> +#include <byteorder.h> +#include "virtio.h" + +/* PCI virtio header offsets */ +#define VIRTIOHDR_DEVICE_FEATURES 0 +#define VIRTIOHDR_GUEST_FEATURES 4 +#define VIRTIOHDR_QUEUE_ADDRESS 8 +#define VIRTIOHDR_QUEUE_SIZE 12 +#define VIRTIOHDR_QUEUE_SELECT 14 +#define VIRTIOHDR_QUEUE_NOTIFY 16 +#define VIRTIOHDR_DEVICE_STATUS 18 +#define VIRTIOHDR_ISR_STATUS 19 +#define VIRTIOHDR_DEVICE_CONFIG 20 + + +/** + * Calculate ring size according to queue size number + */ +unsigned long virtio_vring_size(unsigned int qsize) +{ + return VQ_ALIGN(sizeof(struct vring_desc) * qsize + + sizeof(struct vring_avail) + sizeof(uint16_t) * qsize) + + VQ_ALIGN(sizeof(struct vring_used) + + sizeof(struct vring_used_elem) * qsize); +} + + +/** + * Get number of elements in a vring + * @param dev pointer to virtio device information + * @param queue virtio queue number + * @return number of elements + */ +int virtio_get_qsize(struct virtio_device *dev, int queue) +{ + int size = 0; + + if (dev->type == VIRTIO_TYPE_PCI) { + ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT, + cpu_to_le16(queue)); + eieio(); + size = le16_to_cpu(ci_read_16(dev->base+VIRTIOHDR_QUEUE_SIZE)); + } + + return size; +} + + +/** + * Get address of descriptor vring + * @param dev pointer to virtio device information + * @param queue virtio queue number + * @return pointer to the descriptor ring + */ +struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue) +{ + struct vring_desc *desc = 0; + + if (dev->type == VIRTIO_TYPE_PCI) { + ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT, + cpu_to_le16(queue)); + eieio(); + desc = (void*)(4096L * + le32_to_cpu(ci_read_32(dev->base+VIRTIOHDR_QUEUE_ADDRESS))); + } + + return desc; +} + + +/** + * Get address of "available" vring + * @param dev pointer to virtio device information + * @param queue virtio queue number + * @return pointer to the "available" ring + */ +struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue) +{ + return (void*)((uint64_t)virtio_get_vring_desc(dev, queue) + + virtio_get_qsize(dev, queue) * sizeof(struct vring_desc)); +} + + +/** + * Get address of "used" vring + * @param dev pointer to virtio device information + * @param queue virtio queue number + * @return pointer to the "used" ring + */ +struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue) +{ + return (void*)VQ_ALIGN((uint64_t)virtio_get_vring_avail(dev, queue) + + virtio_get_qsize(dev, queue) + * sizeof(struct vring_avail)); +} + + +/** + * Reset virtio device + */ +void virtio_reset_device(struct virtio_device *dev) +{ + if (dev->type == VIRTIO_TYPE_PCI) { + ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, 0); + } +} + + +/** + * Notify hypervisor about queue update + */ +void virtio_queue_notify(struct virtio_device *dev, int queue) +{ + if (dev->type == VIRTIO_TYPE_PCI) { + ci_write_16(dev->base+VIRTIOHDR_QUEUE_NOTIFY, cpu_to_le16(queue)); + } +} + +/** + * Set queue address + */ +void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned int qaddr) +{ + if (dev->type == VIRTIO_TYPE_PCI) { + uint32_t val = qaddr; + val = val >> 12; + ci_write_16(dev->base+VIRTIOHDR_QUEUE_SELECT, + cpu_to_le16(queue)); + eieio(); + ci_write_32(dev->base+VIRTIOHDR_QUEUE_ADDRESS, + cpu_to_le32(val)); + } +} + +/** + * Set device status bits + */ +void virtio_set_status(struct virtio_device *dev, int status) +{ + if (dev->type == VIRTIO_TYPE_PCI) { + ci_write_8(dev->base+VIRTIOHDR_DEVICE_STATUS, status); + } +} + + +/** + * Set guest feature bits + */ +void virtio_set_guest_features(struct virtio_device *dev, int features) + +{ + if (dev->type == VIRTIO_TYPE_PCI) { + ci_write_32(dev->base+VIRTIOHDR_GUEST_FEATURES, bswap_32(features)); + } +} + +/** + * Get host feature bits + */ +void virtio_get_host_features(struct virtio_device *dev, int *features) + +{ + if (dev->type == VIRTIO_TYPE_PCI && features) { + *features = bswap_32(ci_read_32(dev->base+VIRTIOHDR_DEVICE_FEATURES)); + } +} + + +/** + * Get additional config values + */ +uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size) +{ + uint64_t val = ~0ULL; + void *confbase; + + switch (dev->type) { + case VIRTIO_TYPE_PCI: + confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG; + break; + default: + return ~0ULL; + } + switch (size) { + case 1: + val = ci_read_8(confbase+offset); + break; + case 2: + val = ci_read_16(confbase+offset); + break; + case 4: + val = ci_read_32(confbase+offset); + break; + case 8: + /* We don't support 8 bytes PIO accesses + * in qemu and this is all PIO + */ + val = ci_read_32(confbase+offset); + val <<= 32; + val |= ci_read_32(confbase+offset+4); + break; + } + + return val; +} + +/** + * Get config blob + */ +int __virtio_read_config(struct virtio_device *dev, void *dst, + int offset, int len) +{ + void *confbase; + unsigned char *buf = dst; + int i; + + switch (dev->type) { + case VIRTIO_TYPE_PCI: + confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG; + break; + default: + return 0; + } + for (i = 0; i < len; i++) + buf[i] = ci_read_8(confbase + offset + i); + return len; +} diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.code b/qemu/roms/SLOF/lib/libvirtio/virtio.code new file mode 100644 index 000000000..258b9bbda --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.code @@ -0,0 +1,164 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <virtio.h> +#include <virtio-blk.h> +#include <virtio-9p.h> +#include <virtio-scsi.h> +#include <virtio-net.h> + +/******** core virtio ********/ + +// : virtio-vring-size ( queuesize -- ringsize ) +PRIM(virtio_X2d_vring_X2d_size) + TOS.u = virtio_vring_size(TOS.u); +MIRP + +// : virtio-get-qsize ( dev queue -- queuesize ) +PRIM(virtio_X2d_get_X2d_qsize) + int queue = TOS.u; POP; + TOS.u = virtio_get_qsize(TOS.a, queue); +MIRP + +// : virtio-get-config ( dev offset size -- val ) +PRIM(virtio_X2d_get_X2d_config) + int size = TOS.u; POP; + int offset = TOS.u; POP; + TOS.u = virtio_get_config(TOS.a, offset, size); +MIRP + +// : virtio-set-qaddr ( dev queue qaddr -- ) +PRIM(virtio_X2d_set_X2d_qaddr) + unsigned int qaddr = TOS.u; POP; + int queue = TOS.u; POP; + void *dev = TOS.a; POP; + virtio_set_qaddr(dev, queue, qaddr); +MIRP + +/******** virtio-blk ********/ + +// : virtio-blk-init ( dev -- blk-size) +PRIM(virtio_X2d_blk_X2d_init) + void *dev = TOS.a; + TOS.u = virtioblk_init(dev); +MIRP + +// : virtio-blk-shutdown ( dev -- ) +PRIM(virtio_X2d_blk_X2d_shutdown) + void *dev = TOS.a; POP; + virtioblk_shutdown(dev); +MIRP + +// : virtio-blk-read ( dev blkno cnt reg -- #read ) +PRIM(virtio_X2d_blk_X2d_read) + void *dev = TOS.a; POP; + long cnt = TOS.n; POP; + long blkno = TOS.n; POP; + void *buf = TOS.a; + TOS.n = virtioblk_read(dev, buf, blkno, cnt); +MIRP + +/******** virtio-fs ********/ + +// : virtio-fs-init ( dev tx rx size -- success ) +PRIM(virtio_X2d_fs_X2d_init) + int size = TOS.n; POP; + void *rx = TOS.a; POP; + void *tx = TOS.a; POP; + void *dev = TOS.a; + + TOS.n = virtio_9p_init(dev, tx, rx, size) == 0 ? -1 : 0; +MIRP + +// : virtio-fs-shutdown ( dev -- ) +PRIM(virtio_X2d_fs_X2d_shutdown) + void *dev = TOS.a; POP; + + virtio_9p_shutdown(dev); +MIRP + +// : virtio-fs-load ( dev buf str -- #read ) +PRIM(virtio_X2d_fs_X2d_load) + char *str = TOS.a; POP; + void *buf = TOS.a; POP; + void *dev = TOS.a; + + TOS.n = virtio_9p_load(dev, str, buf); +MIRP + +/******** virtio-scsi ********/ + +// : virtio-scsi-init ( dev -- success ) +PRIM(virtio_X2d_scsi_X2d_init) + void *dev = TOS.a; + TOS.u = virtioscsi_init(dev); +MIRP + +// : virtio-scsi-shutdown ( dev -- ) +PRIM(virtio_X2d_scsi_X2d_shutdown) + void *dev = TOS.a; POP; + virtioscsi_shutdown(dev); +MIRP + +// : virtio-scsi-send ( buf_addr buf_len is_read req_ptr rsp_ptr dev -- success) +PRIM(virtio_X2d_scsi_X2d_send) + void *dev = TOS.a; POP; + void *resp = TOS.a; POP; + void *req = TOS.a; POP; + int is_read = !!TOS.n; POP; + uint64_t blen = TOS.n; POP; + void *buf = TOS.a; + TOS.n = virtioscsi_send(dev, req, resp, is_read, buf, blen); +MIRP + +/******** virtio-net ********/ + +// : virtio-net-open ( mac-addr-str len dev -- false | [ driver true ] ) +PRIM(virtio_X2d_net_X2d_open) +{ + void *dev = TOS.a; POP; + int len = TOS.u; POP; + char *mac_addr = TOS.a; + + net_driver_t *net_driver = virtionet_open(mac_addr, len, dev); + + if (net_driver) { + TOS.u = (unsigned long)net_driver; PUSH; + TOS.n = -1; + } else + TOS.n = 0; +} +MIRP + +// : virtio-net-close ( driver -- ) +PRIM(virtio_X2d_net_X2d_close) +{ + net_driver_t *driver = TOS.a; POP; + virtionet_close(driver); +} +MIRP + +// : virtio-net-read ( addr len -- actual ) +PRIM(virtio_X2d_net_X2d_read) +{ + int len = TOS.u; POP; + TOS.n = virtionet_read(TOS.a, len); +} +MIRP + +// : virtio-net-write ( addr len -- actual ) +PRIM(virtio_X2d_net_X2d_write) +{ + int len = TOS.u; POP; + TOS.n = virtionet_write(TOS.a, len); +} +MIRP diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.h b/qemu/roms/SLOF/lib/libvirtio/virtio.h new file mode 100644 index 000000000..d5759b45a --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.h @@ -0,0 +1,90 @@ +/****************************************************************************** + * Copyright (c) 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef _LIBVIRTIO_H +#define _LIBVIRTIO_H + +#include <stdint.h> + +/* Device status bits */ +#define VIRTIO_STAT_ACKNOWLEDGE 1 +#define VIRTIO_STAT_DRIVER 2 +#define VIRTIO_STAT_DRIVER_OK 4 +#define VIRTIO_STAT_FAILED 128 + +#define VIRTIO_TIMEOUT 5000 /* 5 sec timeout */ + +/* Definitions for vring_desc.flags */ +#define VRING_DESC_F_NEXT 1 /* buffer continues via the next field */ +#define VRING_DESC_F_WRITE 2 /* buffer is write-only (otherwise read-only) */ +#define VRING_DESC_F_INDIRECT 4 /* buffer contains a list of buffer descriptors */ + +/* Descriptor table entry - see Virtio Spec chapter 2.3.2 */ +struct vring_desc { + uint64_t addr; /* Address (guest-physical) */ + uint32_t len; /* Length */ + uint16_t flags; /* The flags as indicated above */ + uint16_t next; /* Next field if flags & NEXT */ +}; + +/* Definitions for vring_avail.flags */ +#define VRING_AVAIL_F_NO_INTERRUPT 1 + +/* Available ring - see Virtio Spec chapter 2.3.4 */ +struct vring_avail { + uint16_t flags; + uint16_t idx; + uint16_t ring[]; +}; + + +/* Definitions for vring_used.flags */ +#define VRING_USED_F_NO_NOTIFY 1 + +struct vring_used_elem { + uint32_t id; /* Index of start of used descriptor chain */ + uint32_t len; /* Total length of the descriptor chain which was used */ +}; + +struct vring_used { + uint16_t flags; + uint16_t idx; + struct vring_used_elem ring[]; +}; + +#define VIRTIO_TYPE_PCI 0 /* For virtio-pci interface */ +struct virtio_device { + void *base; /* base address */ + int type; /* VIRTIO_TYPE_PCI or VIRTIO_TYPE_VIO */ +}; + +/* Parts of the virtqueue are aligned on a 4096 byte page boundary */ +#define VQ_ALIGN(addr) (((addr) + 0xfff) & ~0xfff) + +extern unsigned long virtio_vring_size(unsigned int qsize); +extern int virtio_get_qsize(struct virtio_device *dev, int queue); +extern struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue); +extern struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue); +extern struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue); + +extern void virtio_reset_device(struct virtio_device *dev); +extern void virtio_queue_notify(struct virtio_device *dev, int queue); +extern void virtio_set_status(struct virtio_device *dev, int status); +extern void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned int qaddr); +extern void virtio_set_guest_features(struct virtio_device *dev, int features); +extern void virtio_get_host_features(struct virtio_device *dev, int *features); +extern uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size); +extern int __virtio_read_config(struct virtio_device *dev, void *dst, + int offset, int len); + + +#endif /* _LIBVIRTIO_H */ diff --git a/qemu/roms/SLOF/lib/libvirtio/virtio.in b/qemu/roms/SLOF/lib/libvirtio/virtio.in new file mode 100644 index 000000000..c36d127c7 --- /dev/null +++ b/qemu/roms/SLOF/lib/libvirtio/virtio.in @@ -0,0 +1,33 @@ +/****************************************************************************** + * Copyright (c) 2004, 2011 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +cod(virtio-vring-size) +cod(virtio-get-qsize) +cod(virtio-get-config) +cod(virtio-set-qaddr) + +cod(virtio-blk-init) +cod(virtio-blk-shutdown) +cod(virtio-blk-read) + +cod(virtio-scsi-init) +cod(virtio-scsi-shutdown) +cod(virtio-scsi-send) + +cod(virtio-fs-init) +cod(virtio-fs-shutdown) +cod(virtio-fs-load) + +cod(virtio-net-open) +cod(virtio-net-close) +cod(virtio-net-read) +cod(virtio-net-write) |